├── LICENSE
├── README.md
├── setup.py
└── tello
├── __init__.py
├── demo.py
├── frame2html.py
├── index.html
├── stats.py
└── tello.py
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 C灵C
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tello-Python
2 | Control DJI Tello drone with python
3 |
4 |
5 | ## Installation
6 | pip install tello-python
7 |
8 |
9 | ## How to import
10 | import tello
11 |
12 | drone = tello.Tello()
13 |
14 |
15 | ## Examples
16 | import tello
17 |
18 | drone = tello.Tello()
19 |
20 | drone.takeoff()
21 |
22 | drone.forward(100)
23 |
24 | drone.cw(90)
25 |
26 | drone.flip('r')
27 |
28 | drone.streamon()
29 |
30 | drone.land()
31 |
32 |
33 | ### Distance
34 | Required. The distance to fly forward in cm. Has to be between 20 and 500.
35 |
36 |
37 | ### Degrees
38 | Required. The number of degrees to rotate. Has to be between 1 and 360.
39 |
40 |
41 | ## More
42 | For more commands, please refer to the methods and comments in the source code tello.py file
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import setuptools
2 |
3 | with open('README.md', 'r', encoding='utf-8') as fh:
4 | long_description = fh.read()
5 |
6 | setuptools.setup(
7 | name='tello-python',
8 | version='1.1.6',
9 | author='C灵C',
10 | author_email='c0c@cocpy.com',
11 | description='Control DJI Tello drone with Python3',
12 | long_description=long_description,
13 | long_description_content_type='text/markdown',
14 | url='https://github.com/cocpy/Tello-Python',
15 | packages=setuptools.find_packages(),
16 | install_requires=[
17 | 'opencv-python', 'flask', 'paddlepaddle', 'paddlehub'
18 | ],
19 | classifiers=[
20 | 'Programming Language :: Python :: 3',
21 | 'License :: OSI Approved :: MIT License',
22 | 'Operating System :: OS Independent',
23 | ],
24 | )
--------------------------------------------------------------------------------
/tello/__init__.py:
--------------------------------------------------------------------------------
1 | __title__ = 'tello-python'
2 | __author__ = 'C灵C'
3 | __liscence__ = 'MIT'
4 | __copyright__ = 'Copyright 2021 C灵C'
5 | __version__ = '1.1.6'
6 | __all__ = ['tello', 'stats']
7 |
8 | from .tello import Tello
9 | from .stats import Stats
--------------------------------------------------------------------------------
/tello/demo.py:
--------------------------------------------------------------------------------
1 | from tello import tello
2 |
3 |
4 | drone = tello.Tello()
5 |
6 | # 起飞
7 | drone.takeoff()
8 |
9 | # 前进100cm
10 | drone.forward(100)
11 |
12 | # 旋转90°
13 | drone.cw(90)
14 |
15 | # 左翻滚
16 | drone.flip('l')
17 |
18 | # 打开视频流
19 | drone.streamon()
20 |
21 | # 降落
22 | drone.land()
23 |
--------------------------------------------------------------------------------
/tello/frame2html.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, render_template, Response
2 | import cv2
3 |
4 |
5 | class VideoCamera(object):
6 | def __init__(self, frame):
7 | # 通过opencv获取实时视频流
8 | self.video = frame
9 |
10 | def __del__(self):
11 | self.video.release()
12 |
13 | def get_frame(self):
14 | success, image = self.video.read()
15 | # 因为opencv读取的图片并非jpeg格式,因此要用motion JPEG模式需要先将图片转码成jpg格式图片
16 | ret, jpeg = cv2.imencode('.jpg', image)
17 | return jpeg.tobytes()
18 |
19 |
20 | app = Flask(__name__)
21 |
22 |
23 | @app.route('/') # 主页
24 | def index():
25 | # jinja2模板,具体格式保存在index.html文件中
26 | return render_template('index.html')
27 |
28 |
29 | def gen(camera):
30 | while True:
31 | frame = camera.get_frame()
32 | # 使用generator函数输出视频流, 每次请求输出的content类型是image/jpeg
33 | yield (b'--frame\r\n'
34 | b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
35 |
36 |
37 | @app.route('/video_feed') # 这个地址返回视频流响应
38 | def video_feed():
39 | return Response(gen(VideoCamera()), mimetype='multipart/x-mixed-replace; boundary=frame')
40 |
41 |
42 | def run_app():
43 | app.run(host='0.0.0.0', debug=True, port=5000)
44 |
45 |
46 | if __name__ == '__main__':
47 | run_app()
48 |
--------------------------------------------------------------------------------
/tello/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Tello Video
4 |
5 |
6 | Tello Video
7 |
8 |
9 |
--------------------------------------------------------------------------------
/tello/stats.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 |
3 |
4 | class Stats:
5 | def __init__(self, command: str, idi: int):
6 | self.command = command
7 | self.response = None
8 | self.id = idi
9 |
10 | self.start_time = datetime.now()
11 | self.end_time = None
12 | self.duration = None
13 |
14 | def add_response(self, response: str):
15 | self.response = str(response)
16 | # 计算执行命令所需的总时间
17 | self.end_time = datetime.now()
18 | self.duration = (self.end_time-self.start_time).total_seconds()
19 |
20 | def got_response(self):
21 | if self.response is None:
22 | return False
23 | else:
24 | return True
25 |
26 | def get_raw_response(self):
27 | return self.response
28 |
29 | @staticmethod
30 | def numeric_response(data: str):
31 | num_val = ''.join(i for i in data if i.isdigit() or i == '-' or i == '.')
32 | return num_val
33 |
34 | def int_response(self, data: str):
35 | return int(self.numeric_response(data))
36 |
37 | def float_response(self, data: str):
38 | return float(self.numeric_response(data))
39 |
40 | def attitude_response(self):
41 | raw_att = self.response.split(';')
42 | att_data = (self.int_response(raw_att[0]), self.int_response(raw_att[1]), self.int_response(raw_att[2]))
43 | return att_data
44 |
45 | def acceleration_response(self):
46 | raw_acc = self.response.split(';')
47 | acc_data = (self.float_response(raw_acc[0]), self.float_response(raw_acc[1]), self.float_response(raw_acc[2]))
48 | return acc_data
49 |
50 | def temp_response(self):
51 | raw_temp = self.response.split('~')
52 | temp = (self.int_response(raw_temp[0]) + self.int_response(raw_temp[1]))/2
53 | return temp
54 |
55 | def get_response(self):
56 | if 'attitude?' in self.command:
57 | return self.attitude_response()
58 | elif 'acceleration?' in self.command:
59 | return self.acceleration_response()
60 | elif 'temp?' in self.command:
61 | return self.temp_response()
62 | elif 'baro?' in self.command or 'speed?' in self.command:
63 | return self.float_response(self.response)
64 | elif '?' not in self.command:
65 | return self.get_raw_response()
66 | else:
67 | return self.int_response(self.response)
68 |
--------------------------------------------------------------------------------
/tello/tello.py:
--------------------------------------------------------------------------------
1 | import os
2 | import socket
3 | import queue
4 | import threading
5 | import time
6 | import datetime
7 |
8 | import cv2
9 | import numpy as np
10 | import paddlehub as hub
11 | from PIL import Image
12 |
13 | from .stats import Stats
14 | from .frame2html import VideoCamera, run_app
15 |
16 | q = queue.Queue()
17 |
18 |
19 | # q.queue.clear()
20 |
21 |
22 | class Tello:
23 | def __init__(self, te_ip: str = '192.168.10.1', debug: bool = True):
24 | # 在8889上打开本地UDP端口以进行无人机通信
25 | self.local_ip = ''
26 | self.local_port = 8889
27 | self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
28 | self.socket.bind((self.local_ip, self.local_port))
29 |
30 | # 设置无人机IP和端口信息
31 | self.te_ip = te_ip
32 | self.te_port = 8889
33 | self.te_address = (self.te_ip, self.te_port)
34 | self.log = []
35 | self.picture_path = ''
36 | self.file_path = ''
37 | self.frame = None
38 |
39 | # 加载动物识别模型
40 | self.module = hub.Module(name="resnet50_vd_animals")
41 |
42 | # 初始化响应线程
43 | self.receive_thread = threading.Thread(target=self._receive_thread)
44 | self.receive_thread.daemon = True
45 | self.receive_thread.start()
46 |
47 | # 本项目运行时选项
48 | self.stream_state = False
49 | self.camera_state = False
50 | self.color_state = False
51 | self.video_state = False
52 | self.save_state = False
53 | self.picture_state = False
54 | self.animal_state = False
55 | self.flip_frame = False
56 | self.now_color = 0
57 | self.MAX_TIME_OUT = 15.0
58 | self.debug = debug
59 |
60 | # 将无人机设置为命令模式
61 | self.command()
62 |
63 | def send_command(self, command: str, query: bool = False):
64 | # 为出站命令创建新的日志条目
65 | self.log.append(Stats(command, len(self.log)))
66 |
67 | # 向无人机发送命令
68 | self.socket.sendto(command.encode('utf-8'), self.te_address)
69 | # 显示确认消息
70 | if self.debug is True:
71 | print('Send Command: {}'.format(command))
72 |
73 | # 检查命令是否超时(基于MAX_TIME_OUT中的值)
74 | start = time.time()
75 | while not self.log[-1].got_response(): # 在日志中未收到任何响应的情况下运行
76 | now = time.time()
77 | difference = now - start
78 | if difference > self.MAX_TIME_OUT:
79 | print('Connect Time Out!')
80 | break
81 |
82 | # 打印出无人机响应
83 | if self.debug is True and query is False:
84 | print('Response: {}'.format(self.log[-1].get_response()))
85 |
86 | def _receive_thread(self):
87 | while True:
88 | # 检查无人机响应,引发套接字错误
89 | try:
90 | self.response, ip = self.socket.recvfrom(1024)
91 | self.log[-1].add_response(self.response)
92 | except socket.error as exc:
93 | print('Error: {}'.format(exc))
94 |
95 | def _cap_video_thread(self):
96 | # 创建流捕获对象
97 | cap = cv2.VideoCapture('udp://' + self.te_ip + ':11111')
98 | # cap.set(cv2.CAP_PROP_BUFFERSIZE, 2)
99 | while self.stream_state:
100 | ret, frame = cap.read()
101 | while ret:
102 | ret, frame = cap.read()
103 | if self.flip_frame:
104 | frame = cv2.flip(frame, 0)
105 | cv2.imshow("DJI Tello", frame)
106 | q.put(frame)
107 | k = cv2.waitKey(1) & 0xFF
108 | # 如果按Esc键,视频流关闭
109 | if k == 27:
110 | break
111 | cap.release()
112 | cv2.destroyAllWindows()
113 |
114 | def _service_video_thread(self):
115 | while True:
116 | self.frame = q.get()
117 | # k = cv2.waitKey(1) & 0xFF
118 | # 如果按F1键,截图到当前位置
119 | # if k == 0 or self.camera_state:
120 | if self.camera_state:
121 | self.file_path = self.picture_path + '\\' + datetime.datetime.now().strftime('%Y%m%d_%H%M%S') + '.png'
122 | print('图片路径为:', self.file_path)
123 | try:
124 | cv2.imwrite(self.file_path, self.frame)
125 | except Exception as e:
126 | print('保存图片失败')
127 | self.camera_state = False
128 |
129 | # 识别动物
130 | if self.animal_state:
131 | results = self.module.classification(images=[self.frame])
132 | # print(results)
133 | key_value_list = list(results[0].items())
134 | key_first, value_first = key_value_list[0][0], key_value_list[0][1]
135 | if '非动物' != key_first:
136 | # print('检测结果是:', key_first, ',相似度为:', value_first)
137 | cv2.imshow(key_first, self.frame)
138 | self.animal_state = False
139 |
140 | # 显示照片
141 | if self.picture_state:
142 | file = self.file_path
143 | f = Image.open(file).show()
144 | self.picture_state = False
145 |
146 | # 识别当前颜色
147 | if self.color_state:
148 | self.detect_color(self.frame)
149 | self.color_state = False
150 |
151 | # 将视频流发送至http
152 | if self.video_state:
153 | self.video_http(self.frame)
154 | self.video_state = False
155 |
156 | # 保存视频流至本地
157 | if self.save_state:
158 | self.video_save(self.frame)
159 | self.save_state = False
160 |
161 | def wait(self, delay: float):
162 | # 显示等待消息
163 | if self.debug is True:
164 | print('Wait {} Seconds...'.format(delay))
165 |
166 | # 日志条目增加了延迟
167 | self.log.append(Stats('wait', len(self.log)))
168 | # 延迟激活
169 | time.sleep(delay)
170 |
171 | @staticmethod
172 | def video_http(frame):
173 | vc = VideoCamera(frame)
174 | run_app()
175 |
176 | @staticmethod
177 | def video_save(frame):
178 | force = cv2.VideoWriter_fourcc(*'XVID')
179 | out = cv2.VideoWriter('output.avi', force, 20.0, (640, 480))
180 | frame = cv2.flip(frame, 0)
181 | # write the flipped frame
182 | out.write(frame)
183 |
184 | def get_log(self):
185 | return self.log
186 |
187 | def take_picture(self, path=os.getcwd()):
188 | """拍照"""
189 | self.camera_state = True
190 | self.picture_path = path
191 |
192 | def show_picture(self):
193 | """显示照片"""
194 | self.picture_state = True
195 |
196 | def flip_video(self):
197 | """翻转视频,在加装下视镜片的情况下开启"""
198 | self.flip_frame = True
199 |
200 | def identify_animal(self):
201 | """识别动物"""
202 | self.animal_state = True
203 |
204 | def identify_color(self):
205 | """识别当前颜色(红色或绿色)"""
206 | self.color_state = True
207 | time.sleep(0.5)
208 | return self.now_color
209 |
210 | # 以下命令强烈建议配合官方SDK食用
211 | # https://www.ryzerobotics.com/cn/tello/downloads
212 |
213 | # 控制命令
214 | def command(self):
215 | """进入SDK命令模式"""
216 | self.send_command('command')
217 |
218 | def takeoff(self):
219 | """自动起飞,1米左右"""
220 | self.send_command('takeoff')
221 |
222 | def land(self):
223 | """自动降落"""
224 | self.send_command('land')
225 |
226 | def streamon(self):
227 | """打开视频流"""
228 | self.send_command('streamon')
229 | self.stream_state = True
230 |
231 | self.cap_video_thread = threading.Thread(target=self._cap_video_thread)
232 | self.cap_video_thread.daemon = True
233 | self.cap_video_thread.start()
234 |
235 | def streamoff(self):
236 | """关闭视频流"""
237 | self.stream_state = False
238 | self.send_command('streamoff')
239 |
240 | def stream_service_on(self):
241 | """是否开启视频流附加功能,开启视频流会卡顿"""
242 | self.service_video_thread = threading.Thread(target=self._service_video_thread)
243 | self.service_video_thread.daemon = True
244 | self.service_video_thread.start()
245 |
246 | def detect_color(self, frame):
247 | """颜色识别"""
248 | # frame = cv2.imread("test.jpg")
249 | hue_image = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
250 |
251 | low_red_range1 = np.array([110, 43, 0])
252 | high_red_range1 = np.array([180, 255, 255])
253 | threshold_red1 = cv2.inRange(hue_image, low_red_range1, high_red_range1)
254 | res_red1 = cv2.bitwise_and(frame, frame, mask=threshold_red1)
255 |
256 | low_red_range2 = np.array([0, 43, 0])
257 | high_red_range2 = np.array([10, 255, 255])
258 | threshold_red2 = cv2.inRange(hue_image, low_red_range2, high_red_range2)
259 | res_red2 = cv2.bitwise_and(frame, frame, mask=threshold_red2)
260 |
261 | threshold_red = threshold_red1 + threshold_red2
262 | res_red = res_red1 + res_red2
263 |
264 | low_green_range = np.array([35, 43, 46])
265 | high_green_range = np.array([77, 255, 255])
266 | threshold_green = cv2.inRange(hue_image, low_green_range, high_green_range)
267 | res_green = cv2.bitwise_and(frame, frame, mask=threshold_green)
268 |
269 | res = res_red + res_green
270 | if cv2.countNonZero(threshold_green) > 0.5 * np.size(threshold_green):
271 | self.now_color = 'green'
272 | elif ((cv2.countNonZero(threshold_red) > 0.5 * np.size(threshold_red)) & (
273 | cv2.countNonZero(threshold_red) < 0.7 * np.size(threshold_red))):
274 | self.now_color = 'red'
275 | else:
276 | self.now_color = 'none'
277 | # color = cv2.cvtColor(binary, cv2.COLOR_GRAY2BGR)
278 | return self.now_color, res
279 |
280 | def emergency(self):
281 | """停止电机转动"""
282 | self.send_command('emergency')
283 |
284 | def up(self, x: int):
285 | """向上飞x(20-500)厘米"""
286 | self.send_command('up {}'.format(x))
287 |
288 | def down(self, x: int):
289 | """向下飞x(20-500)厘米"""
290 | self.send_command('down {}'.format(x))
291 |
292 | def left(self, x: int):
293 | """向左飞x(20-500)厘米"""
294 | self.send_command('left {}'.format(x))
295 |
296 | def right(self, x: int):
297 | """向右飞x(20-500)厘米"""
298 | self.send_command('right {}'.format(x))
299 |
300 | def forward(self, x: int):
301 | """向前飞x(20-500)厘米"""
302 | self.send_command('forward {}'.format(x))
303 |
304 | def back(self, x: int):
305 | """向后飞x(20-500)厘米"""
306 | self.send_command('back {}'.format(x))
307 |
308 | def cw(self, angle: int):
309 | """顺时针旋转angle°(1-360)"""
310 | self.send_command('cw {}'.format(angle))
311 |
312 | def ccw(self, angle: int):
313 | """逆时针旋转angle°(1-360)"""
314 | self.send_command('ccw {}'.format(angle))
315 |
316 | def flip(self, direction: str):
317 | """朝direction方向翻滚,左侧(left)缩写为l,同理right=r,forward=f,back=b"""
318 | self.send_command('flip {}'.format(direction))
319 |
320 | def go(self, x: int, y: int, z: int, speed: int):
321 | """以设置速度speed(cm / s)飞往坐标(x, y, z)
322 | x: -500 - 500
323 | y: -500 - 500
324 | z: -500 - 500
325 | speed: 10 - 100(cm / s)
326 | x、y、z不能同时在 -20 ~ 20 之间"""
327 | self.send_command('go {} {} {} {}'.format(x, y, z, speed))
328 |
329 | def stop(self):
330 | """"停止运动并悬停,任何时候都可以"""
331 | self.send_command('stop')
332 |
333 | def curve(self, x1: int, y1: int, z1: int, x2: int, y2: int, z2: int, speed: int):
334 | """以设置速度speed( cm/s )飞弧线,经过(x1,y1,z1)到(x2,y2,z2)
335 | 如果弧线半径不在 0.5-10 米范围内,则返回相应提醒
336 | x1, x2: -500 - 500
337 | y1, y2: -500 - 500
338 | z1, z2: -500 - 500
339 | speed: 10-60
340 | x、y、z 不能同时在 -20 ~ 20 之间"""
341 | self.send_command('curve {} {} {} {} {} {} {}'.format(x1, y1, z1, x2, y2, z2, speed))
342 |
343 | def go_mid(self, x: int, y: int, z: int, speed: int, mid: str):
344 | """以设置速度speed(m/s)飞往设置 id 的挑战卡坐标系的(x,y,z)坐标点
345 | mid:
346 | m1/m2/~/m8:对应挑战卡上的挑战卡ID
347 | m-1: 无人机内部算法最快识别到的挑战卡,随机选择一个探测到的挑战卡
348 | m-2: 距离无人机中心距离最近的挑战卡
349 | x: -500 - 500
350 | y: -500 - 500
351 | z: 0 - 500
352 | speed: 10-100 (cm/s)
353 | x、y、z 不能同时在 -20 ~ 20 之间"""
354 | self.send_command('go {} {} {} {} {}'.format(x, y, z, speed, mid))
355 |
356 | def curve_mid(self, x1: int, y1: int, z1: int, x2: int, y2: int, z2: int, speed: int, mid: str):
357 | """以设置速度speed( cm/s )飞弧线,经过设置 mid 的挑战卡坐标系中的(x1,y1,z1)点到(x2,y2,z2)点
358 | 如果弧线半径不在 0.5-10 米范围内,则返回相应提醒
359 | x1, x2: -500 - 500
360 | y1, y2: -500 - 500
361 | z1, z2: 0 - 500
362 | speed: 10-60
363 | x、y、z 不能同时在 -20 ~ 20 之间"""
364 | self.send_command('curve {} {} {} {} {} {} {} {}'.format(x1, y1, z1, x2, y2, z2, speed, mid))
365 |
366 | def jump_mid(self, x: int, y: int, z: int, speed: int, yaw: int, mid1: str, mid2: str):
367 | """飞往 mid1 坐标系的(x,y,z)点后悬停,识别 mid2 的挑战卡
368 | 并在 mid2 坐标系下 (0,0,z) 的位置并旋转向到设置的 偏航yaw 值,( z>0 )"""
369 | self.send_command('jump {} {} {} {} {} {} {}'.format(x, y, z, speed, yaw, mid1, mid2))
370 |
371 | # 设置命令
372 | def set_speed(self, speed: int):
373 | """将当前速度设为 speed cm/s,speed = 10-100"""
374 | self.send_command('speed {}'.format(speed))
375 |
376 | def rc_control(self, a: int, b: int, c: int, d: int):
377 | """设置遥控器的 4 个通道杆量
378 | a: 横滚 (-100~100)
379 | b: 俯仰 (-100~100)
380 | c: 油门 (-100~100)
381 | d: 偏航 (-100~100)
382 | """
383 | self.send_command('rc {} {} {} {}'.format(a, b, c, d))
384 |
385 | def set_wifi(self, ssid: str, passwrd: str):
386 | """更改 无人机 Wi-Fi 密码
387 | ssid: 更改后的 Wi-Fi 账号
388 | passwrd: 更改后的 Wi-Fi 密码
389 | """
390 | self.send_command('wifi {} {}'.format(ssid, passwrd))
391 |
392 | def mon(self):
393 | """"打开挑战卡探测,默认同时打开前视和下视探测"""
394 | self.send_command('mon')
395 |
396 | def moff(self):
397 | """"关闭挑战卡探测"""
398 | self.send_command('moff')
399 |
400 | def mdirection(self, mdir: int):
401 | """mdir=0/1/2
402 | 0 打开下视探测
403 | 1 打开前视探测
404 | 2 同时打开前视和下视探测
405 | * 使用前必须使用 mon 命令打开探测功能
406 | * 单独打开前视或者下视探测时,探测频率为20Hz,同时打开前视和下视时,将交替探测,单个反向的探测频率为 10Hz"""
407 | self.send_command('mdirection {}'.format(mdir))
408 |
409 | def ap2sta(self, ssid: str, passwrd: str):
410 | """将Tello转为 station 模式,并连入到 AP
411 | ssid: 要连接的 Wi-Fi 账号
412 | passwrd: 要连接的 Wi-Fi 密码"""
413 | self.send_command('ap {} {}'.format(ssid, passwrd))
414 |
415 | # 读取命令
416 | def get_speed(self):
417 | """获取当前设置速度speed(cm/s),speed(10-100)"""
418 | self.send_command('speed?', True)
419 | return self.log[-1].get_response()
420 |
421 | def get_battery(self):
422 | """获取当前电池剩余电量的百分比值 x,x = (10-100)"""
423 | self.send_command('battery?', True)
424 | return self.log[-1].get_response()
425 |
426 | def get_time(self):
427 | """获取电机运转时间(s)"""
428 | self.send_command('time?', True)
429 | return self.log[-1].get_response()
430 |
431 | def get_wifi(self):
432 | """获得 Wi-Fi 信噪比"""
433 | self.send_command('wifi?', True)
434 | return self.log[-1].get_response()
435 |
436 | def get_sdk(self):
437 | """获得 无人机 SDK 版本号 xx(>=20)"""
438 | self.send_command('sdk?', True)
439 | return self.log[-1].get_response()
440 |
441 | def get_sn(self):
442 | """获得 无人机 SN 码 生产序列号"""
443 | self.send_command('sn?', True)
444 | return self.log[-1].get_response()
445 |
446 | def get_height(self):
447 | """获取高度,新版本中已停用"""
448 | self.send_command('height?', True)
449 | return self.log[-1].get_response()
450 |
451 | def get_temp(self):
452 | """获取温度,新版本中已停用"""
453 | self.send_command('temp?', True)
454 | return self.log[-1].get_response()
455 |
456 | def get_attitude(self):
457 | """获取飞行姿态,新版本中已停用"""
458 | self.send_command('attitude?', True)
459 | return self.log[-1].get_response()
460 |
461 | def get_baro(self):
462 | """获取压力,新版本中已停用"""
463 | self.send_command('baro?', True)
464 | return self.log[-1].get_response()
465 |
466 | def get_acceleration(self):
467 | """获取加速度,新版本中已停用"""
468 | self.send_command('acceleration?', True)
469 | return self.log[-1].get_response()
470 |
471 | def get_tof(self):
472 | """获取飞行时间,新版本中已停用"""
473 | self.send_command('tof?', True)
474 | return self.log[-1].get_response()
475 |
--------------------------------------------------------------------------------