ardrone_sdk 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 Bastian Venthur
2
+ Copyright (c) 2015-2021, Lily Foster <lily@lily.flowers>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ this software and associated documentation files (the "Software"), to deal in
6
+ the Software without restriction, including without limitation the rights to
7
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
+ the Software, and to permit persons to whom the Software is furnished to do so,
9
+ subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,54 @@
1
+ Metadata-Version: 2.4
2
+ Name: ardrone_sdk
3
+ Version: 0.4.0
4
+ Summary: A Python library for controlling the Parrot AR.Drone 2.0 over a network
5
+ Author-email: Lily Foster <lily@lily.flowers>
6
+ License-Expression: MIT
7
+ Project-URL: Source, https://github.com/lilyinstarlight/python-ardrone
8
+ Project-URL: Tracker, https://github.com/lilyinstarlight/python-ardrone/issues
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Operating System :: POSIX
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Classifier: Operating System :: MacOS :: MacOS X
14
+ Classifier: Operating System :: Microsoft :: Windows
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: C
18
+ Classifier: Topic :: Scientific/Engineering
19
+ Classifier: Topic :: System :: Hardware :: Hardware Drivers
20
+ Requires-Python: >=3.8
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: Pillow
24
+ Dynamic: license-file
25
+
26
+ python-ardrone
27
+ ==============
28
+
29
+ _**Note: This library is looking for testers and/or maintainers! I have not had access myself to an AR.Drone 2.0 for a while, but there are several important unreleased changes that need testing on actual hardware as well as [stuff that still needs to be done](TODO.md) to make the library more robust. Open a new issue on this repository if you are interested!**_
30
+
31
+ A Python library for controlling the Parrot AR.Drone 2.0 over a network.
32
+
33
+ Usage
34
+ -----
35
+
36
+ ```python
37
+ import ardrone
38
+
39
+ drone = ardrone.ARDrone()
40
+
41
+ drone.takeoff()
42
+ drone.land()
43
+
44
+ print(drone.navdata['demo']['battery'])
45
+
46
+ drone.image.show()
47
+
48
+ drone.halt()
49
+ ```
50
+
51
+ Thanks
52
+ ------
53
+
54
+ Thanks to Bastian Venthur for making the beginnings on which this library was based at https://github.com/venthur/python-ardrone!
@@ -0,0 +1,29 @@
1
+ python-ardrone
2
+ ==============
3
+
4
+ _**Note: This library is looking for testers and/or maintainers! I have not had access myself to an AR.Drone 2.0 for a while, but there are several important unreleased changes that need testing on actual hardware as well as [stuff that still needs to be done](TODO.md) to make the library more robust. Open a new issue on this repository if you are interested!**_
5
+
6
+ A Python library for controlling the Parrot AR.Drone 2.0 over a network.
7
+
8
+ Usage
9
+ -----
10
+
11
+ ```python
12
+ import ardrone
13
+
14
+ drone = ardrone.ARDrone()
15
+
16
+ drone.takeoff()
17
+ drone.land()
18
+
19
+ print(drone.navdata['demo']['battery'])
20
+
21
+ drone.image.show()
22
+
23
+ drone.halt()
24
+ ```
25
+
26
+ Thanks
27
+ ------
28
+
29
+ Thanks to Bastian Venthur for making the beginnings on which this library was based at https://github.com/venthur/python-ardrone!
@@ -0,0 +1,3 @@
1
+ from ardrone.drone import ARDrone
2
+
3
+ __all__ = ['ARDrone']
@@ -0,0 +1,169 @@
1
+ import socket
2
+ import struct
3
+ import threading
4
+
5
+ import ardrone.constant
6
+
7
+
8
+ def f2i(f):
9
+ """Interpret IEEE-754 floating-point value as signed integer.
10
+
11
+ Arguments:
12
+ f -- floating point value
13
+ """
14
+ return struct.unpack('i', struct.pack('f', f))[0]
15
+
16
+
17
+ class ATCommand(object):
18
+ def __init__(self, host):
19
+ """
20
+ Open a new AT command socket
21
+
22
+ Parameters:
23
+ host -- destination address
24
+ """
25
+ self.host = host
26
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
27
+
28
+ self.seq = 1
29
+ self.interval = 0.2
30
+
31
+ self.lock = threading.Lock()
32
+ self.comwdg_timer = threading.Timer(self.interval, self.comwdg)
33
+
34
+ def halt(self):
35
+ """
36
+ Halts communication with the drone
37
+ """
38
+ with self.lock:
39
+ self.comwdg_timer.cancel()
40
+
41
+ def ref(self, takeoff, emergency=False):
42
+ """
43
+ Basic behaviour of the drone: take-off/landing, emergency stop/reset)
44
+
45
+ Parameters:
46
+ takeoff -- True: Takeoff / False: Land
47
+ emergency -- True: Turn off the engines
48
+ """
49
+ p = 0b10001010101000000000000000000
50
+ if takeoff:
51
+ p |= 0b1000000000
52
+ if emergency:
53
+ p |= 0b100000000
54
+ self.at('REF', [p])
55
+
56
+ def pcmd(self, progressive, lr, fb, vv, va):
57
+ """
58
+ Makes the drone move (translate/rotate).
59
+
60
+ Parameters:
61
+ progressive -- True: enable progressive commands, False: disable (i.e.
62
+ enable hovering mode)
63
+ lr -- left-right tilt: float [-1..1] negative: left, positive: right
64
+ rb -- front-back tilt: float [-1..1] negative: forwards, positive:
65
+ backwards
66
+ vv -- vertical speed: float [-1..1] negative: go down, positive: rise
67
+ va -- angular speed: float [-1..1] negative: spin left, positive: spin
68
+ right
69
+
70
+ The above float values are a percentage of the maximum speed.
71
+ """
72
+ p = 1 if progressive else 0
73
+ self.at('PCMD', [p, float(lr), float(fb), float(vv), float(va)])
74
+
75
+ def ftrim(self):
76
+ """
77
+ Tell the drone it's lying horizontally.
78
+ """
79
+ self.at('FTRIM')
80
+
81
+ def zap(self, stream):
82
+ """
83
+ Selects which video stream to send on the video UDP port.
84
+
85
+ Parameters:
86
+ stream -- Integer: video stream to broadcast
87
+ """
88
+ # FIXME: improve parameters to select the modes directly
89
+ self.at('ZAP', [stream])
90
+
91
+ def config(self, option, value):
92
+ """Set configuration parameters of the drone."""
93
+ self.at('CONFIG', [str(option), str(value)])
94
+
95
+ def comwdg(self):
96
+ """
97
+ Reset communication watchdog.
98
+ """
99
+ self.at('COMWDG')
100
+
101
+ def aflight(self, flag):
102
+ """
103
+ Makes the drone fly autonomously.
104
+
105
+ Parameters:
106
+ flag -- Integer: 1: start flight, 0: stop flight
107
+ """
108
+ self.at('AFLIGHT', [flag])
109
+
110
+ def pwm(self, m1, m2, m3, m4):
111
+ """
112
+ Sends control values directly to the engines, overriding control loops.
113
+
114
+ Parameters:
115
+ m1 -- Integer: front left command
116
+ m2 -- Integer: front right command
117
+ m3 -- Integer: back right command
118
+ m4 -- Integer: back left command
119
+ """
120
+ self.at('PWM', [m1, m2, m3, m4])
121
+
122
+ def led(self, anim, f, d):
123
+ """
124
+ Control the drones LED.
125
+
126
+ Parameters:
127
+ anim -- Integer: animation to play
128
+ f -- Float: frequency in HZ of the animation
129
+ d -- Integer: total duration in seconds of the animation
130
+ """
131
+ self.at('LED', [anim, float(f), d])
132
+
133
+ def anim(self, anim, d):
134
+ """
135
+ Makes the drone execute a predefined movement (animation).
136
+
137
+ Parameters:
138
+ anim -- Integer: animation to play
139
+ d -- Integer: total duration in seconds of the animation
140
+ """
141
+ self.at('ANIM', [anim, d])
142
+
143
+ def at(self, command, params=[]):
144
+ """
145
+ Encodes and sends AT command
146
+
147
+ Parameters:
148
+ command -- the command
149
+ params -- a list of elements which can be either int, float or string
150
+ """
151
+ params_str = []
152
+ for p in params:
153
+ if type(p) == int:
154
+ params_str.append('{:d}'.format(p))
155
+ elif type(p) == float:
156
+ params_str.append('{:d}'.format(f2i(p)))
157
+ elif type(p) == str:
158
+ params_str.append('"{:s}"'.format(p))
159
+
160
+ with self.lock:
161
+ self.comwdg_timer.cancel()
162
+
163
+ msg = 'AT*{:s}={:d}{:s}\r'.format(command, self.seq, ''.join(',' + param for param in params_str))
164
+ self.sock.sendto(msg.encode(), (self.host, ardrone.constant.COMMAND_PORT))
165
+
166
+ self.seq += 1
167
+
168
+ self.comwdg_timer = threading.Timer(self.interval, self.comwdg)
169
+ self.comwdg_timer.start()
@@ -0,0 +1,73 @@
1
+ import fcntl
2
+ import sys
3
+ import termios
4
+ import os
5
+
6
+ import ardrone
7
+
8
+
9
+ def main():
10
+ if len(sys.argv) != 1 and len(sys.argv) != 2:
11
+ print('usage: ' + sys.argv[0] + ' [host]')
12
+ sys.exit(1)
13
+
14
+ fd = sys.stdin.fileno()
15
+
16
+ oldterm = termios.tcgetattr(fd)
17
+ newattr = termios.tcgetattr(fd)
18
+ newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
19
+ termios.tcsetattr(fd, termios.TCSANOW, newattr)
20
+
21
+ oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
22
+ fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
23
+
24
+ if len(sys.argv) >= 2:
25
+ drone = ardrone.ARDrone(sys.argv[1])
26
+ else:
27
+ drone = ardrone.ARDrone()
28
+
29
+ try:
30
+ while 1:
31
+ try:
32
+ c = sys.stdin.read(1).lower()
33
+
34
+ if c == 'p':
35
+ break
36
+ elif c == 'a':
37
+ drone.move_left()
38
+ elif c == 'd':
39
+ drone.move_right()
40
+ elif c == 'w':
41
+ drone.move_forward()
42
+ elif c == 's':
43
+ drone.move_backward()
44
+ elif c == ' ':
45
+ drone.land()
46
+ elif c == '\n':
47
+ drone.takeoff()
48
+ elif c == 'q':
49
+ drone.turn_left()
50
+ elif c == 'e':
51
+ drone.turn_right()
52
+ elif c == '1':
53
+ drone.move_up()
54
+ elif c == '2':
55
+ drone.hover()
56
+ elif c == '3':
57
+ drone.move_down()
58
+ elif c == 't':
59
+ drone.reset()
60
+ elif c == 'x':
61
+ drone.hover()
62
+ elif c == 'y':
63
+ drone.trim()
64
+ except IOError:
65
+ pass
66
+ finally:
67
+ termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
68
+ fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
69
+ drone.halt()
70
+
71
+
72
+ if __name__ == '__main__':
73
+ main()
@@ -0,0 +1,3 @@
1
+ NAVDATA_PORT = 5554
2
+ VIDEO_PORT = 5555
3
+ COMMAND_PORT = 5556
@@ -0,0 +1,134 @@
1
+ """
2
+ Python library for the AR.Drone.
3
+ """
4
+
5
+ import time
6
+ import multiprocessing
7
+
8
+ import PIL.Image
9
+
10
+ import ardrone.at
11
+ import ardrone.network
12
+
13
+
14
+ class ARDrone(object):
15
+ """ARDrone Class.
16
+
17
+ Instantiate this class to control your drone and receive decoded video and
18
+ navdata.
19
+ """
20
+
21
+ def __init__(self, host='192.168.1.1'):
22
+ self.host = host
23
+
24
+ self.speed = 0.2
25
+
26
+ self.atcmd = ardrone.at.ATCommand(self.host)
27
+ self.atcmd.config('general:navdata_demo', 'TRUE')
28
+ self.atcmd.config('control:altitude_max', '20000')
29
+ self.video_pipe, video_pipe_other = multiprocessing.Pipe()
30
+ self.nav_pipe, nav_pipe_other = multiprocessing.Pipe()
31
+ self.com_pipe, com_pipe_other = multiprocessing.Pipe()
32
+ self.network_process = ardrone.network.ARDroneNetworkProcess(self.host, nav_pipe_other, video_pipe_other, com_pipe_other)
33
+ self.network_process.start()
34
+ self.ipc_thread = ardrone.network.IPCThread(self)
35
+ self.ipc_thread.start()
36
+
37
+ self.image = PIL.Image.new('RGB', (640, 360))
38
+ self.navdata = dict()
39
+
40
+ self.time = 0
41
+
42
+ def takeoff(self):
43
+ """Make the drone takeoff."""
44
+ self.atcmd.ref(True)
45
+
46
+ def land(self):
47
+ """Make the drone land."""
48
+ self.atcmd.ref(False)
49
+
50
+ def hover(self):
51
+ """Make the drone hover."""
52
+ self.atcmd.pcmd(False, 0, 0, 0, 0)
53
+
54
+ def move_left(self):
55
+ """Make the drone move left."""
56
+ self.atcmd.pcmd(True, -self.speed, 0, 0, 0)
57
+
58
+ def move_right(self):
59
+ """Make the drone move right."""
60
+ self.atcmd.pcmd(True, self.speed, 0, 0, 0)
61
+
62
+ def move_up(self):
63
+ """Make the drone rise upwards."""
64
+ self.atcmd.pcmd(True, 0, 0, self.speed, 0)
65
+
66
+ def move_down(self):
67
+ """Make the drone decent downwards."""
68
+ self.atcmd.pcmd(True, 0, 0, -self.speed, 0)
69
+
70
+ def move_forward(self):
71
+ """Make the drone move forward."""
72
+ self.atcmd.pcmd(True, 0, -self.speed, 0, 0)
73
+
74
+ def move_backward(self):
75
+ """Make the drone move backwards."""
76
+ self.atcmd.pcmd(True, 0, self.speed, 0, 0)
77
+
78
+ def turn_left(self):
79
+ """Make the drone rotate left."""
80
+ self.atcmd.pcmd(True, 0, 0, 0, -self.speed)
81
+
82
+ def turn_right(self):
83
+ """Make the drone rotate right."""
84
+ self.atcmd.pcmd(True, 0, 0, 0, self.speed)
85
+
86
+ def reset(self):
87
+ """Toggle the drone's emergency state."""
88
+ self.atcmd.ref(False, True)
89
+ time.sleep(0.1)
90
+ self.atcmd.ref(False, False)
91
+
92
+ def trim(self):
93
+ """Flat trim the drone."""
94
+ self.atcmd.ftrim
95
+
96
+ def set_cam(self, cam):
97
+ """Set active camera.
98
+
99
+ Valid values are 0 for the front camera and 1 for the bottom camera
100
+ """
101
+ self.atcmd.config('video:video_channel', cam)
102
+
103
+ def set_speed(self, speed):
104
+ """Set the drone's speed.
105
+
106
+ Valid values are floats from [0..1]
107
+ """
108
+ self.speed = speed
109
+
110
+ def halt(self):
111
+ """Shutdown the drone.
112
+
113
+ This method does not land or halt the actual drone, but the
114
+ communication with the drone. You should call it at the end of your
115
+ application to close all sockets, pipes, processes and threads related
116
+ with this object.
117
+ """
118
+ self.atcmd.halt()
119
+ self.ipc_thread.stop()
120
+ self.ipc_thread.join()
121
+ self.network_process.terminate()
122
+ self.network_process.join()
123
+
124
+ def move(self, lr, fb, vv, va):
125
+ """Makes the drone move (translate/rotate).
126
+
127
+ Parameters:
128
+ lr -- left-right tilt: float [-1..1] negative: left, positive: right
129
+ fb -- front-back tilt: float [-1..1] negative: forwards, positive:
130
+ backwards
131
+ vv -- vertical speed: float [-1..1] negative: go down, positive: rise
132
+ va -- angular speed: float [-1..1] negative: spin left, positive: spin
133
+ right"""
134
+ self.atcmd.pcmd(True, lr, fb, vv, va)
@@ -0,0 +1,82 @@
1
+ import struct
2
+
3
+
4
+ def decode(packet):
5
+ """Decode a navdata packet."""
6
+ offset = 0
7
+
8
+ _ = struct.unpack_from('IIII', packet, offset)
9
+ s = _[1]
10
+ state = dict()
11
+ state['fly'] = s & 1 # FLY MASK : (0) ardrone is landed, (1) ardrone is flying
12
+ state['video'] = s >> 1 & 1 # VIDEO MASK : (0) video disable, (1) video enable
13
+ state['vision'] = s >> 2 & 1 # VISION MASK : (0) vision disable, (1) vision enable
14
+ state['control'] = s >> 3 & 1 # CONTROL ALGO (0) euler angles control, (1) angular speed control
15
+ state['altitude'] = s >> 4 & 1 # ALTITUDE CONTROL ALGO : (0) altitude control inactive (1) altitude control active
16
+ state['user_feedback_start'] = s >> 5 & 1 # USER feedback : Start button state
17
+ state['command'] = s >> 6 & 1 # Control command ACK : (0) None, (1) one received
18
+ state['fw_file'] = s >> 7 & 1 # Firmware file is good (1)
19
+ state['fw_ver'] = s >> 8 & 1 # Firmware update is newer (1)
20
+ state['fw_upd'] = s >> 9 & 1 # Firmware update is ongoing (1)
21
+ state['navdata_demo'] = s >> 10 & 1 # Navdata demo : (0) All navdata, (1) only navdata demo
22
+ state['navdata_bootstrap'] = s >> 11 & 1 # Navdata bootstrap : (0) options sent in all or demo mode, (1) no navdata options sent
23
+ state['motors'] = s >> 12 & 1 # Motor status : (0) Ok, (1) Motors problem
24
+ state['com_lost'] = s >> 13 & 1 # Communication lost : (1) com problem, (0) Com is ok
25
+ state['vbat_low'] = s >> 15 & 1 # VBat low : (1) too low, (0) Ok
26
+ state['user_el'] = s >> 16 & 1 # User Emergency Landing : (1) User EL is ON, (0) User EL is OFF
27
+ state['timer_elapsed'] = s >> 17 & 1 # Timer elapsed : (1) elapsed, (0) not elapsed
28
+ state['angles_out_of_range'] = s >> 19 & 1 # Angles : (0) Ok, (1) out of range
29
+ state['ultrasound'] = s >> 21 & 1 # Ultrasonic sensor : (0) Ok, (1) deaf
30
+ state['cutout'] = s >> 22 & 1 # Cutout system detection : (0) Not detected, (1) detected
31
+ state['pic_version'] = s >> 23 & 1 # PIC Version number OK : (0) a bad version number, (1) version number is OK
32
+ state['atcodec_thread_on'] = s >> 24 & 1 # ATCodec thread ON : (0) thread OFF (1) thread ON
33
+ state['navdata_thread_on'] = s >> 25 & 1 # Navdata thread ON : (0) thread OFF (1) thread ON
34
+ state['video_thread_on'] = s >> 26 & 1 # Video thread ON : (0) thread OFF (1) thread ON
35
+ state['acq_thread_on'] = s >> 27 & 1 # Acquisition thread ON : (0) thread OFF (1) thread ON
36
+ state['ctrl_watchdog'] = s >> 28 & 1 # CTRL watchdog : (1) delay in control execution (> 5ms), (0) control is well scheduled
37
+ state['adc_watchdog'] = s >> 29 & 1 # ADC Watchdog : (1) delay in uart2 dsr (> 5ms), (0) uart2 is good
38
+ state['com_watchdog'] = s >> 30 & 1 # Communication Watchdog : (1) com problem, (0) Com is ok
39
+ state['emergency'] = s >> 31 & 1 # Emergency landing : (0) no emergency, (1) emergency
40
+
41
+ data = dict()
42
+ data['state'] = state
43
+ data['header'] = _[0]
44
+ data['sequence'] = _[2]
45
+ data['vision'] = _[3]
46
+
47
+ offset += struct.calcsize('IIII')
48
+
49
+ demo_fields = [
50
+ 'ctrl_state',
51
+ 'battery',
52
+ 'theta',
53
+ 'phi',
54
+ 'psi',
55
+ 'altitude',
56
+ 'vx',
57
+ 'vy',
58
+ 'vz',
59
+ 'num_frames'
60
+ ]
61
+ angles = ['theta', 'phi', 'psi']
62
+ while True:
63
+ try:
64
+ id_nr, size = struct.unpack_from('HH', packet, offset)
65
+ offset += struct.calcsize('HH')
66
+ except struct.error:
67
+ break
68
+
69
+ values = []
70
+ for i in range(size - struct.calcsize('HH')):
71
+ values.append(struct.unpack_from('c', packet, offset)[0])
72
+ offset += struct.calcsize('c')
73
+
74
+ if id_nr == 0:
75
+ values = struct.unpack_from('IIfffIfffI', b''.join(values))
76
+ demo = dict(zip(demo_fields, values))
77
+ for a in angles:
78
+ demo[a] = int(demo[a] / 1000)
79
+
80
+ data['demo'] = demo
81
+
82
+ return data
@@ -0,0 +1,111 @@
1
+ """
2
+ This module provides access to the data provided by the AR.Drone.
3
+ """
4
+
5
+ import select
6
+ import socket
7
+ import struct
8
+ import threading
9
+ import multiprocessing
10
+
11
+ import PIL.Image
12
+
13
+ import ardrone.constant
14
+ import ardrone.navdata
15
+ import ardrone.video
16
+
17
+
18
+ class ARDroneNetworkProcess(multiprocessing.Process):
19
+ """ARDrone Network Process.
20
+
21
+ This process collects data from the video and navdata port, converts the
22
+ data and sends it to the IPCThread.
23
+ """
24
+
25
+ def __init__(self, host, nav_pipe, video_pipe, com_pipe):
26
+ multiprocessing.Process.__init__(self)
27
+ self.nav_pipe = nav_pipe
28
+ self.video_pipe = video_pipe
29
+ self.com_pipe = com_pipe
30
+ self.host = host
31
+
32
+ def run(self):
33
+ video_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
34
+ video_socket.connect((self.host, ardrone.constant.VIDEO_PORT))
35
+
36
+ nav_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
37
+ nav_socket.setblocking(False)
38
+ nav_socket.sendto(b'\x01\x00\x00\x00', (self.host, ardrone.constant.NAVDATA_PORT))
39
+
40
+ stopping = False
41
+ while not stopping:
42
+ inputready, outputready, exceptready = select.select([nav_socket, video_socket, self.com_pipe], [], [])
43
+ for i in inputready:
44
+ if i == video_socket:
45
+ # get first few bytes of header
46
+ data = video_socket.recv(12, socket.MSG_WAITALL)
47
+ if len(data) != 12:
48
+ continue
49
+
50
+ # decode relevant portions of the header
51
+ sig_p, sig_a, sig_v, sig_e, version, codec, header, payload = struct.unpack('4cBBHI', data)
52
+
53
+ # check signature (and ignore packet otherwise)
54
+ if sig_p != b'P' or sig_a != b'a' or sig_v != b'V' or sig_e != b'E':
55
+ continue
56
+
57
+ # get remaining frame
58
+ data += video_socket.recv(header - 12 + payload, socket.MSG_WAITALL)
59
+
60
+ try:
61
+ # decode the frame
62
+ image = ardrone.video.decode(data)
63
+ self.video_pipe.send(image)
64
+ except ardrone.video.DecodeError:
65
+ pass
66
+ elif i == nav_socket:
67
+ while 1:
68
+ try:
69
+ data, _ = nav_socket.recvfrom(65535)
70
+ except IOError:
71
+ # we consumed every packet from the socket and
72
+ # continue with the last one
73
+ break
74
+ navdata = ardrone.navdata.decode(data)
75
+ self.nav_pipe.send(navdata)
76
+ elif i == self.com_pipe:
77
+ _ = self.com_pipe.recv()
78
+ stopping = True
79
+ break
80
+ video_socket.close()
81
+ nav_socket.close()
82
+
83
+
84
+ class IPCThread(threading.Thread):
85
+ """Inter Process Communication Thread.
86
+
87
+ This thread collects the data from the ARDroneNetworkProcess and forwards
88
+ it to the ARDrone.
89
+ """
90
+
91
+ def __init__(self, drone):
92
+ threading.Thread.__init__(self)
93
+ self.drone = drone
94
+ self.stopping = False
95
+
96
+ def run(self):
97
+ while not self.stopping:
98
+ inputready, outputready, exceptready = select.select([self.drone.video_pipe, self.drone.nav_pipe], [], [], 1)
99
+ for i in inputready:
100
+ if i == self.drone.video_pipe:
101
+ while self.drone.video_pipe.poll():
102
+ width, height, image = self.drone.video_pipe.recv()
103
+ self.drone.image = PIL.Image.frombuffer('RGB', (width, height), image, 'raw', 'RGB', 0, 1)
104
+ elif i == self.drone.nav_pipe:
105
+ while self.drone.nav_pipe.poll():
106
+ navdata = self.drone.nav_pipe.recv()
107
+ self.drone.navdata = navdata
108
+
109
+ def stop(self):
110
+ """Stop the IPCThread activity."""
111
+ self.stopping = True
@@ -0,0 +1,168 @@
1
+ #include <Python.h>
2
+
3
+ #include <libavcodec/avcodec.h>
4
+ #include <libavformat/avformat.h>
5
+ #include <libavutil/imgutils.h>
6
+ #include <libswscale/swscale.h>
7
+
8
+ struct PaVE {
9
+ uint8_t signature[4]; // "PaVE"
10
+ uint8_t version; // protocol version
11
+ uint8_t video_codec; // codec of frame
12
+ uint16_t header_size; // size of this header
13
+ uint32_t payload_size; // size of payload frame
14
+ uint16_t encoded_stream_width; // encoded width
15
+ uint16_t encoded_stream_height; // encoded height
16
+ uint16_t display_width; // actual width
17
+ uint16_t display_height; // actual height
18
+
19
+ uint32_t frame_number; // current frame
20
+ uint32_t timestamp; // timestamp in milliseconds of frame
21
+ uint8_t total_chunks; // number of packets for frame (unused)
22
+ uint8_t chunck_index; // current packet number for frame (unused)
23
+ uint8_t frame_type; // I-frame or P-frame
24
+ uint8_t control; // control command (e.g. end of stream, advertised frames)
25
+ uint32_t stream_byte_position_lw; // lower word of byte position in stream
26
+ uint32_t stream_byte_position_uw; // upper word of byte position in stream
27
+ uint16_t stream_id; // current stream this frame is associated with
28
+ uint8_t total_slices; // number of slices in frame
29
+ uint8_t slice_index; // position of current slice
30
+ uint8_t header1_size; // size of SPS in frame (h.264 only)
31
+ uint8_t header2_size; // size of PPS in frame (h.264 only)
32
+ uint8_t reserved1[2]; // padding to align to 48 bytes
33
+ uint32_t advertised_size; // size of advertised frame
34
+ uint8_t reserved2[12]; // padding to align to 64 bytes
35
+ } __attribute__ ((packed));
36
+
37
+ static PyObject * VideoDecodeError;
38
+
39
+ static PyObject * video_decode(PyObject * self, PyObject * args);
40
+
41
+ static PyMethodDef VideoMethods[] = {
42
+ {"decode", video_decode, METH_VARARGS, "decode a PaVE video packet into an RGB image buffer"},
43
+ {NULL, NULL, 0, NULL}
44
+ };
45
+
46
+ static struct PyModuleDef videomodule = {
47
+ PyModuleDef_HEAD_INIT,
48
+ "video",
49
+ NULL,
50
+ -1,
51
+ VideoMethods
52
+ };
53
+
54
+ const AVCodec * codec;
55
+ AVCodecContext * context;
56
+ AVFrame * frame;
57
+ struct SwsContext * sws_context;
58
+
59
+ PyMODINIT_FUNC PyInit_video(void) {
60
+ PyObject * module;
61
+
62
+ module = PyModule_Create(&videomodule);
63
+ if (module == NULL)
64
+ return NULL;
65
+
66
+ VideoDecodeError = PyErr_NewException("ardrone.video.DecodeError", NULL, NULL);
67
+ Py_INCREF(VideoDecodeError);
68
+ PyModule_AddObject(module, "DecodeError", VideoDecodeError);
69
+
70
+ codec = avcodec_find_decoder(AV_CODEC_ID_H264);
71
+ if (!codec) {
72
+ PyErr_SetString(VideoDecodeError, "could not find h.264 decoder");
73
+ return NULL;
74
+ }
75
+
76
+ context = avcodec_alloc_context3(codec);
77
+ if (!context) {
78
+ PyErr_NoMemory();
79
+ return NULL;
80
+ }
81
+
82
+ if (avcodec_open2(context, codec, NULL) < 0) {
83
+ PyErr_SetString(VideoDecodeError, "could not open h.264 codec");
84
+ return NULL;
85
+ }
86
+
87
+ frame = av_frame_alloc();
88
+ if (!frame) {
89
+ PyErr_NoMemory();
90
+ return NULL;
91
+ }
92
+
93
+ return module;
94
+ }
95
+
96
+ static PyObject * video_decode(PyObject * self, PyObject * args) {
97
+ unsigned char * data;
98
+ int data_size;
99
+
100
+ struct PaVE header;
101
+ unsigned char * payload;
102
+
103
+ AVPacket * packet;
104
+
105
+ unsigned char * image;
106
+ int image_width;
107
+ int image_height;
108
+ int image_size;
109
+
110
+ unsigned char * image_data[1];
111
+ int image_linesize[1];
112
+
113
+ PyObject * py_image;
114
+
115
+ if (!PyArg_ParseTuple(args, "s#", &data, &data_size))
116
+ return NULL;
117
+
118
+ header = *((struct PaVE *)data);
119
+ payload = data + header.header_size;
120
+
121
+ if (memcmp(header.signature, "PaVE", 4) != 0) {
122
+ PyErr_SetString(VideoDecodeError, "packet did not have correct signature");
123
+ return NULL;
124
+ }
125
+
126
+ if (header.header_size + header.payload_size != (unsigned int)data_size) {
127
+ PyErr_SetString(VideoDecodeError, "packet size did not match expected size from header");
128
+ return NULL;
129
+ }
130
+
131
+ packet = av_packet_alloc();
132
+ if (!packet) {
133
+ PyErr_NoMemory();
134
+ return NULL;
135
+ }
136
+
137
+ packet->pts = AV_NOPTS_VALUE;
138
+ packet->dts = AV_NOPTS_VALUE;
139
+ packet->data = payload;
140
+ packet->size = header.payload_size;
141
+
142
+ if (avcodec_send_packet(context, packet) != 0 || avcodec_receive_frame(context, frame) != 0) {
143
+ av_packet_free(&packet);
144
+ PyErr_SetString(VideoDecodeError, "could not decode frame");
145
+ return NULL;
146
+ }
147
+
148
+ av_packet_free(&packet);
149
+
150
+ image_width = frame->width;
151
+ image_height = frame->height;
152
+
153
+ image_size = av_image_get_buffer_size(AV_PIX_FMT_RGB24, image_width, image_height, 1)*sizeof(uint8_t);
154
+
155
+ image = (unsigned char *)av_malloc(image_size);
156
+
157
+ image_data[0] = image;
158
+ image_linesize[0] = image_size/image_height;
159
+
160
+ sws_context = sws_getCachedContext(sws_context, context->width, context->height, AV_PIX_FMT_YUV420P, context->width, context->height, AV_PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
161
+ sws_scale(sws_context, (const unsigned char * const *)frame->data, frame->linesize, 0, frame->height, image_data, image_linesize);
162
+
163
+ py_image = Py_BuildValue("iiy#", image_width, image_height, image, image_size);
164
+
165
+ av_free(image);
166
+
167
+ return py_image;
168
+ }
@@ -0,0 +1,54 @@
1
+ Metadata-Version: 2.4
2
+ Name: ardrone_sdk
3
+ Version: 0.4.0
4
+ Summary: A Python library for controlling the Parrot AR.Drone 2.0 over a network
5
+ Author-email: Lily Foster <lily@lily.flowers>
6
+ License-Expression: MIT
7
+ Project-URL: Source, https://github.com/lilyinstarlight/python-ardrone
8
+ Project-URL: Tracker, https://github.com/lilyinstarlight/python-ardrone/issues
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Operating System :: POSIX
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Classifier: Operating System :: MacOS :: MacOS X
14
+ Classifier: Operating System :: Microsoft :: Windows
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: C
18
+ Classifier: Topic :: Scientific/Engineering
19
+ Classifier: Topic :: System :: Hardware :: Hardware Drivers
20
+ Requires-Python: >=3.8
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: Pillow
24
+ Dynamic: license-file
25
+
26
+ python-ardrone
27
+ ==============
28
+
29
+ _**Note: This library is looking for testers and/or maintainers! I have not had access myself to an AR.Drone 2.0 for a while, but there are several important unreleased changes that need testing on actual hardware as well as [stuff that still needs to be done](TODO.md) to make the library more robust. Open a new issue on this repository if you are interested!**_
30
+
31
+ A Python library for controlling the Parrot AR.Drone 2.0 over a network.
32
+
33
+ Usage
34
+ -----
35
+
36
+ ```python
37
+ import ardrone
38
+
39
+ drone = ardrone.ARDrone()
40
+
41
+ drone.takeoff()
42
+ drone.land()
43
+
44
+ print(drone.navdata['demo']['battery'])
45
+
46
+ drone.image.show()
47
+
48
+ drone.halt()
49
+ ```
50
+
51
+ Thanks
52
+ ------
53
+
54
+ Thanks to Bastian Venthur for making the beginnings on which this library was based at https://github.com/venthur/python-ardrone!
@@ -0,0 +1,18 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ setup.py
5
+ ardrone/__init__.py
6
+ ardrone/at.py
7
+ ardrone/client.py
8
+ ardrone/constant.py
9
+ ardrone/drone.py
10
+ ardrone/navdata.py
11
+ ardrone/network.py
12
+ ardrone/video.c
13
+ ardrone_sdk.egg-info/PKG-INFO
14
+ ardrone_sdk.egg-info/SOURCES.txt
15
+ ardrone_sdk.egg-info/dependency_links.txt
16
+ ardrone_sdk.egg-info/entry_points.txt
17
+ ardrone_sdk.egg-info/requires.txt
18
+ ardrone_sdk.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ ardrone-client = ardrone.client:main
@@ -0,0 +1 @@
1
+ Pillow
@@ -0,0 +1,2 @@
1
+ ardrone
2
+ dist
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ardrone_sdk"
7
+ version = "0.4.0"
8
+ description = "A Python library for controlling the Parrot AR.Drone 2.0 over a network"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.8"
12
+ authors = [
13
+ { name = "Lily Foster", email = "lily@lily.flowers" },
14
+ ]
15
+ dependencies = [
16
+ "Pillow",
17
+ ]
18
+ classifiers = [
19
+ "Development Status :: 4 - Beta",
20
+ "Intended Audience :: Developers",
21
+ "Operating System :: POSIX",
22
+ "Operating System :: POSIX :: Linux",
23
+ "Operating System :: MacOS :: MacOS X",
24
+ "Operating System :: Microsoft :: Windows",
25
+ "Programming Language :: Python",
26
+ "Programming Language :: Python :: 3",
27
+ "Programming Language :: C",
28
+ "Topic :: Scientific/Engineering",
29
+ "Topic :: System :: Hardware :: Hardware Drivers",
30
+ ]
31
+
32
+ [project.urls]
33
+ Source = "https://github.com/lilyinstarlight/python-ardrone"
34
+ Tracker = "https://github.com/lilyinstarlight/python-ardrone/issues"
35
+
36
+ [project.scripts]
37
+ ardrone-client = "ardrone.client:main"
38
+
39
+ [tool.setuptools.packages.find]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,9 @@
1
+ from setuptools import setup, Extension
2
+
3
+ video = Extension(
4
+ name="ardrone.video",
5
+ libraries=["avcodec", "avformat", "avutil", "swscale"],
6
+ sources=["ardrone/video.c"],
7
+ )
8
+
9
+ setup(ext_modules=[video])