news 2026/10/2 18:14:43

Tello TT无人机+YOLOv5:目标识别追踪与单目测距实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Tello TT无人机+YOLOv5:目标识别追踪与单目测距实战

简介:这份资源面向K12阶段学生与AI入门教师,提供基于YOLOv5与大疆教育无人机Tello TT的目标识别、检测、追踪与测距完整方案,将深度学习理论落地到真实飞行场景,解决从模型训练到无人机部署的全流程问题。压缩包共1667个文件,约269.13MB,其中758张jpg图像与694个txt标注构成“旗”“圈”两类目标数据集,46个py脚本负责训练与推理,94个yaml与9个pt文件对应网络配置和训练好的权重,另有说明文档、Dockerfile及若干sh脚本辅助环境搭建与运行。目前已有679人学习下载。读者可直接复用预训练模型与标注数据,省去从零采集与训练的成本,同时借助操作说明理解环境配置、模型加载、视频流处理与无人机控制等环节,适合作为STEM课程实践、竞赛项目或课外拓展的参考素材。

1. 从 Tello TT 到 YOLOv5:一套能飞起来的目标识别与追踪测距方案长什么样

手里有一台大疆教育无人机 Tello TT,又想把 YOLOv5 跑成能识别、能追、还能测距的完整链路,这件事的难点从来不在模型本身,而在「机载算力、图传延迟、坐标换算」这三件事怎么串起来。Tello TT 自带 SDK,能拿到视频流和飞行指令接口,但它没有机载 GPU,所有推理都得放到地面端或边缘设备上跑,再把控制指令回传。所以这套方案的本质是:地面端用 YOLOv5 做目标检测,把检测框中心点转成无人机机体坐标系下的偏航、俯仰、距离三个量,再通过 Tello SDK 发 rc 控制指令实现追踪,同时用单目测距或已知目标尺寸反推距离。它适合做毕设、做无人机视觉入门、做教育场景演示的从业者,也适合想把 YOLOv5 从「只会跑图片」推进到「闭环控制真实设备」的人。下面按环境配置、数据准备、训练调参、机载联调、避坑、进阶技巧的顺序,把这条链路拆开讲清楚。

2. 环境配置与 Tello TT 通信链路搭建:从 conda 到第一帧图传

2.1 为什么用 conda 隔离 YOLOv5 环境而不是直接 pip

YOLOv5 对 PyTorch、torchvision、numpy 的版本耦合比较紧,尤其是做后处理和导出 ONNX 的时候,版本错一位就可能出现 NMS 报错或者输出维度对不上。我一般用 conda 建一个独立环境,把 Python 锁在 3.8 到 3.9 之间,PyTorch 按 CUDA 版本选。如果你机器上没有 NVIDIA 显卡,用 CPU 推理也能跑,但帧率会掉到个位数,追踪基本没法闭环,所以建议至少有一块 6G 显存的卡。

conda create -n tello_yolo python=3.9 -y conda activate tello_yolo # 按你的 CUDA 版本选,下面以 cu118 为例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt

这段命令的逻辑是:先建环境避免污染全局,再装和 CUDA 匹配的 PyTorch,最后拉 YOLOv5 仓库并装依赖。参数上要注意--index-url后面的 cu118 要和你nvidia-smi显示的 CUDA 版本对应,装完用python -c "import torch; print(torch.cuda.is_available())"验证,返回 True 才算通。requirements.txt 里会装 opencv、pandas、matplotlib 这些,如果卡在某个包上,多半是网络问题,换源重试即可。

2.2 Tello TT 的 SDK 通信与视频流接入

Tello TT 的通信分两条链路:一条是命令链路,走 UDP 8889 端口,用来发 takeoff、land、rc 这类指令;另一条是视频流,走 UDP 11111 端口,拿到的是 H.264 裸流。常见做法是用 djitellopy 这个库封装命令链路,视频流用 OpenCV 的 VideoCapture 接udp://0.0.0.0:11111。

from djitellopy import Tello import cv2 tello = Tello() tello.connect() print("电量:", tello.get_battery()) tello.streamon() # 开启视频流 frame_read = tello.get_frame_read() while True: frame = frame_read.frame # BGR 格式 if frame is None: continue frame = cv2.resize(frame, (960, 720)) cv2.imshow("Tello", frame) if cv2.waitKey(1) & 0xFF == ord('q'): break tello.streamoff() tello.end()

逻辑说明:connect()会握手并返回电量,streamon()让飞机开始推流,get_frame_read()起一个后台线程持续解码,主循环里直接取最新帧。参数上,Tello TT 图传默认 960x720,延迟在 100 到 200 毫秒之间,这个延迟对慢速追踪够用,对快速机动就不够,所以后面控制指令要加死区和限幅。注意frame_read.frame拿到的是 BGR,直接喂给 YOLOv5 前要确认通道顺序,YOLOv5 内部会做 BGR 到 RGB 的转换,不用手动转。

2.3 把 YOLOv5 推理接进图传循环的最小闭环

先不急着做追踪,先把「取帧 → 推理 → 画框 → 显示」跑通,确认帧率和延迟能接受,再往上加控制逻辑。这一步是后面所有事情的地基。

import torch import cv2 from djitellopy import Tello model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True) model.conf = 0.4 # 置信度阈值 model.iou = 0.45 # NMS 的 IoU 阈值 tello = Tello() tello.connect() tello.streamon() frame_read = tello.get_frame_read() while True: frame = frame_read.frame if frame is None: continue results = model(frame) annotated = results.render()[0] # 画好框的图 cv2.imshow("YOLOv5 on Tello", annotated) if cv2.waitKey(1) & 0xFF == ord('q'): break tello.streamoff() tello.end()

逻辑说明:torch.hub.load会下载 yolov5s 预训练权重,第一次跑需要联网。model.conf和model.iou是两个必调参数,conf 调低会出更多框但误检增加,调高会漏检;iou 控制重叠框合并,目标密集时调低一点。results.render()直接返回带框的 numpy 图,省去手动画框。跑通后看左上角帧率,如果低于 10 帧,要么换更小的模型(yolov5n),要么把输入尺寸从 640 降到 416。

3. 数据集准备与 YOLOv5 训练自己的数据集:从标注到 mAP 曲线

3.1 采集与标注:用 Tello 自己拍还是用公开数据集

做目标追踪,数据集最好和实际飞行场景一致。我一般先用 Tello 悬停在不同高度、不同光照下录几段视频,再用脚本抽帧,每 10 帧抽一张,避免相邻帧太像导致过拟合。抽完用 labelImg 或 Roboflow 标注,类别就设你要追的那个目标,比如「person」「car」「ball」。如果目标有公开数据集,比如 COCO 里已有的类别,可以直接拿 COCO 的子集先训一版,再用自己的数据微调,这样收敛快很多。

标注格式选 YOLO 格式,每张图对应一个 txt,每行是class_id x_center y_center width height,全部归一化到 0 到 1。常见坑是标注时框超出图像边界,训练时会报错或者学歪,所以标注完要写个脚本检查一遍。

import os import cv2 def check_labels(img_dir, lbl_dir): for name in os.listdir(lbl_dir): path = os.path.join(lbl_dir, name) with open(path) as f: for line in f: parts = line.strip().split() if len(parts) != 5: print("格式错误:", path) continue _, x, y, w, h = map(float, parts) if not (0 <= x <= 1 and 0 <= y <= 1 and 0 < w <= 1 and 0 < h <= 1): print("越界:", path, line.strip()) check_labels("images/train", "labels/train")

逻辑说明:这段脚本遍历标签文件,检查每行是不是 5 个字段,以及归一化坐标是否在合法范围。参数上,x_center和y_center必须在 0 到 1 之间,width和height必须大于 0 且不超过 1。发现越界就回去重新标,别指望训练时自动修正。

3.2 目录结构与 data.yaml 的写法

YOLOv5 对目录结构有约定,常见做法是建datasets/mytello目录,下面分images/train、images/val、labels/train、labels/val。然后写一个data.yaml指向这些路径,并列出类别名。

path: ./datasets/mytello train: images/train val: images/val nc: 1 names: ['target']

逻辑说明:path是数据集根目录,train和val是相对路径,nc是类别数,names是类别名列表,顺序要和标注里的 class_id 对应。常见错误是nc写成 0 或者和 names 长度不一致,训练时会直接报 assert 失败。改完 yaml 用python train.py --data data.yaml --img 640 --batch 16 --epochs 100 --weights yolov5s.pt启动训练。

3.3 训练参数怎么设:batch、img、epochs 与超参数

训练参数没有万能值,但有几个经验区间。--img默认 640,如果你的目标在画面里很小,可以提到 960 或 1280,但显存占用会翻倍。--batch在 6G 显存上跑 640 一般能到 16,跑 960 就只能到 8 或 4。--epochs看数据量,几千张图跑 100 到 300 轮,看 mAP 曲线不再上升就可以停。

YOLOv5 的超参数在data/hyp.scratch.yaml里,学习率lr0默认 0.01,微调时可以降到 0.001。数据增强里mosaic默认 1.0,小目标多的时候可以保留,但如果你追的目标经常被遮挡,mosaic太强反而会让模型学不到完整目标,可以降到 0.5。训练过程中看runs/train/exp下的results.png,重点看mAP@0.5和box_loss,如果 loss 震荡不降,多半是学习率太大或者标注有问题。

python train.py \ --data data.yaml \ --img 640 \ --batch 16 \ --epochs 150 \ --weights yolov5s.pt \ --hyp data/hyp.scratch.yaml \ --project runs/train \ --name tello_exp

逻辑说明:--weights指定预训练权重,从 yolov5s.pt 开始比从零训快很多。--project和--name控制输出目录,方便多次实验对比。训练完最好的权重在runs/train/tello_exp/weights/best.pt,后面部署就用这个。

4. 目标追踪与单目测距:把检测框变成飞行指令

4.1 从检测框到偏航、俯仰、距离的换算

检测框给出的是像素坐标,要变成无人机能执行的控制量,需要做三步:先算框中心相对画面中心的偏移,再映射到偏航和俯仰的角速度,最后用框的宽度或高度反推距离。假设画面宽 W、高 H,框中心为 (cx, cy),则水平偏移dx = (cx - W/2) / (W/2),范围在 -1 到 1 之间;垂直偏移dy = (cy - H/2) / (H/2)。偏航角速度yaw_rate = -dx * Kp_yaw,俯仰用up_down = dy * Kp_pitch,这里的 Kp 是比例系数,一般从 30 到 60 试起。

距离估算用相似三角形:如果你知道目标的真实宽度real_w,镜头焦距f(像素单位),框的像素宽度pixel_w,则距离D = real_w * f / pixel_w。焦距可以用已知距离下拍一张标定图反推。这个测距是单目的,精度受目标姿态影响,目标侧对镜头时误差会很大,所以实际用的时候我会加一个卡尔曼滤波平滑距离,或者干脆只用距离做前后速度控制,不做精确定位。

def box_to_cmd(cx, cy, w, h, frame_w, frame_h, real_w, focal): dx = (cx - frame_w / 2) / (frame_w / 2) dy = (cy - frame_h / 2) / (frame_h / 2) yaw_rate = int(-dx * 50) # 左右 up_down = int(dy * 40) # 上下 distance = real_w * focal / max(w, 1) # 距离目标 1.5 米,远了前进,近了后退 forward = int((distance - 1.5) * 30) forward = max(min(forward, 40), -40) # 限幅 return yaw_rate, up_down, forward, distance

逻辑说明:dx和dy归一化到 -1 到 1,乘上比例系数得到速度指令。forward用距离误差乘系数,并限幅在 -40 到 40 之间,防止飞机猛冲。参数上,real_w和focal要提前标定,focal可以用focal = pixel_w * D / real_w在已知距离 D 下反推。限幅值根据你的飞行安全范围调,室内建议不超过 30。

4.2 用 PID 平滑控制指令并下发 rc

直接比例控制会有抖动,飞机一抖图传就糊,检测框跟着跳,形成正反馈。常见做法是加一个简单的 PID,P 管响应,I 消稳态误差,D 抑制震荡。Tello 的 rc 指令格式是rc left_right forward_backward up_down yaw,每个值 -100 到 100。

class PID: def __init__(self, kp, ki, kd): self.kp, self.ki, self.kd = kp, ki, kd self.prev_err = 0 self.integral = 0 def update(self, err, dt=0.05): self.integral += err * dt derivative = (err - self.prev_err) / dt self.prev_err = err return self.kp * err + self.ki * self.integral + self.kd * derivative pid_yaw = PID(0.6, 0.0, 0.1) pid_fwd = PID(0.5, 0.0, 0.08) # 在主循环里 err_yaw = -dx yaw_cmd = int(pid_yaw.update(err_yaw)) err_dist = distance - 1.5 fwd_cmd = int(pid_fwd.update(err_dist)) tello.send_rc_control(0, fwd_cmd, 0, yaw_cmd)

逻辑说明:PID 的update接收误差,返回控制量。dt是循环周期,按实际帧率填。参数上,先调 P 到飞机能朝目标转但不震荡,再加 D 压震荡,I 一般室内不用加,加了容易积分饱和。send_rc_control四个参数分别是左右、前后、上下、偏航,这里只用了前后和偏航,左右和上下留 0。注意每次发指令前要判断检测是否有效,丢帧时发 0 让飞机悬停,别让上一次指令一直生效。

4.3 追踪丢失与重捕获的处理

目标丢失是常态,遮挡、快速移动、光照突变都会导致丢框。我的做法是维护一个丢失计数器,连续 N 帧没检测到就进入搜索模式:原地缓慢偏航一圈,同时降低 conf 阈值重新找。如果超过一定时间还找不到,就悬停等待或者降落。这个逻辑不复杂,但能避免飞机追着空气跑。

lost_frames = 0 MAX_LOST = 15 if len(results.xyxy[0]) > 0: lost_frames = 0 # 取置信度最高的框 box = results.xyxy[0][results.xyxy[0][:, 4].argmax()] # ... 计算控制量 else: lost_frames += 1 if lost_frames < MAX_LOST: tello.send_rc_control(0, 0, 0, 0) # 悬停 else: tello.send_rc_control(0, 0, 0, 20) # 缓慢偏航搜索

逻辑说明:results.xyxy[0]是检测结果,每行是 x1,y1,x2,y2,conf,cls。取 conf 最大的框作为追踪目标,避免多个目标来回跳。MAX_LOST设 15 帧,按 20 帧率算大约 0.75 秒,太短会频繁进搜索,太长会跟丢。搜索时的偏航速度给 20,别给太大,否则图传糊了更找不到。

5. 避坑与排查:Tello TT + YOLOv5 联调时最容易翻车的 5 个点

5.1 图传延迟导致控制震荡

现象:飞机一追目标就左右摆,越摆越大。原因:图传延迟 100 到 200 毫秒,加上推理时间,从取帧到发指令可能过了 300 毫秒,PID 的 D 项按当前误差算,实际控制的是 300 毫秒前的状态,相位滞后导致震荡。解决:降低 P 和 D 的增益,把控制周期和推理周期解耦,推理跑在一个线程,控制跑在另一个线程用最新结果,或者直接降帧率到 10 帧让延迟占比小一点。

5.2 检测框抖动导致距离估算跳变

现象:距离读数在 1 米到 3 米之间乱跳,飞机前后抽搐。原因:单目测距对框宽非常敏感,框宽抖几个像素,距离就跳几十厘米。解决:对框宽做滑动平均,或者对距离做一阶低通滤波,d_smooth = 0.8 * d_prev + 0.2 * d_new,牺牲一点响应换稳定。另外 conf 阈值别设太低,低 conf 的框通常不准。

5.3 类别不匹配导致追踪错对象

现象:明明要追人,结果追着背景里的车跑。原因:预训练模型有 80 类,你的场景里可能同时出现多个类别,代码里取了 conf 最大的框,但没过滤类别。解决:在取框前先按类别过滤,results.xyxy[0][results.xyxy[0][:, 5] == target_cls],只保留你要追的类。如果自己训的模型只有一类,这个问题自然没有,所以做专用场景建议自己训。

5.4 电量低时图传和指令延迟变大

现象:飞了几分钟后追踪变迟钝,指令响应慢。原因:Tello TT 电量低于 30% 后,图传码率和电机响应都会降,这是保护机制。解决:别等低电量才测,每次测试前充满,单次飞行控制在 5 分钟内。另外get_battery()可以实时读电量,低于 40% 就准备降落,别硬飞。

5.5 室内光流定位漂移导致追踪基准偏移

现象:飞机悬停时自己慢慢飘,检测框跟着飘,追踪目标越来越偏。原因:Tello TT 室内靠光流和下视摄像头定位,地面纹理少或者光线暗时定位会漂。解决:在地面铺一块有纹理的垫子,或者开灯保证光照充足。如果还是漂,可以在控制里加一个基于画面中心的偏置补偿,但治标不治本,室内飞行尽量选纹理丰富的地面。

6. 进阶技巧:用 TensorRT 加速推理并把测距误差压到 10% 以内

前面跑通之后,如果帧率还是不够,或者测距精度不满意,可以往两个方向优化。第一个方向是推理加速。YOLOv5 支持导出 TensorRT 引擎,在 NVIDIA 显卡上能比 PyTorch 快 2 到 3 倍。导出命令是python export.py --weights best.pt --include engine --device 0 --img 640,导出后在代码里用torch.hub.load加载 engine 文件,或者用trt运行时直接跑。注意 TensorRT 引擎和显卡架构绑定,换机器要重新导出。

第二个方向是测距标定。单目测距的误差主要来自焦距标定不准和目标姿态变化。我的做法是:在 1 米、2 米、3 米三个距离各拍 20 张目标正面图,用focal = pixel_w * D / real_w算出 60 个焦距值,取中位数作为标定焦距,这样比单次标定稳。然后在实际测距时,如果目标框的宽高比和标定时差异超过 30%,说明目标侧对镜头,这时候距离读数不可信,直接丢弃或者只用来做前后速度的粗略控制。

import numpy as np def calibrate_focal(samples): # samples: [(pixel_w, real_distance, real_w), ...] focals = [pw * d / rw for pw, d, rw in samples] return np.median(focals) samples = [(120, 1.0, 0.3), (60, 2.0, 0.3), (40, 3.0, 0.3)] focal = calibrate_focal(samples) print("标定焦距:", focal)

逻辑说明:samples里每个元组是像素宽、真实距离、真实宽度。calibrate_focal算出每个样本的焦距取中位数,避免个别异常值带偏。参数上,real_w要量准,目标宽度量错 1 厘米,距离就错 3% 以上。标定完把focal写进配置,后面测距直接用。

最后一个习惯:每次改完控制参数,先在室内低高度(0.5 米)悬停测试,确认飞机不抽风再放高。我踩过最大的坑就是参数没调好直接飞 2 米,结果飞机追着目标撞墙。这套方案值不值得做,取决于你是不是需要一个能闭环的真实设备验证环境,如果只是跑通 YOLOv5 推理,那不用无人机也能做;但如果你要验证「感知到控制」的完整链路,Tello TT 是成本最低的选择之一。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 18:10:15

从梦狐残神到任务暂缓,ABAP 如何让业务安静等待,并在合适的时候恢复

我们正在讨论的「梦狐残神」,有一个很适合拿来理解企业软件的细节。目标仍然存在,战斗仍然继续,但这个目标暂时不能正常行动。程序开发里也经常需要这样的处理,订单不能立刻释放,接口消息不能立刻重试,后台任务不能立刻再次调用故障中的外部服务。业务没有被删除,也没有…

作者头像 李华
网站建设 2026/10/2 18:09:50

VB+SQL Server连锁超市进销存系统:从数据库设计到小票打印完整实现

简介&#xff1a;这份课程设计报告面向高校信息管理与信息系统、计算机相关专业学生&#xff0c;围绕连锁超市进销存管理信息系统的分析与设计展开&#xff0c;可作为课程设计、毕业设计选题的完整参考方案。报告从设计背景与可行性分析入手&#xff0c;依次覆盖系统功能设计、…

作者头像 李华
网站建设 2026/10/2 18:08:44

Oracle 19C Data Guard 实战:RHEL 7.6+ASM 备库搭建与切换全解析

简介&#xff1a;面向Oracle DBA及运维工程师的实操型部署指南&#xff0c;聚焦在RHEL 7.6&#xff08;Maipo&#xff09;环境下搭建Oracle 19C ASM与DataGuard一体化高可用架构&#xff0c;解决双节点物理备库部署中路径规划、网络配置、依赖包检查等常见问题。资料为一份PDF文…

作者头像 李华
网站建设 2026/10/2 18:07:48

10m河南省土地覆盖数据处理全流程:从解压到分析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 18:06:21

Gemini Cli 登录失败排查:把 OAuth 凭据改到 TaoToken 统一通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华