基于YOLOv5的大疆Tello TT无人机目标检测追踪与测距

发布时间:2026/9/24 19:43:26
基于YOLOv5的大疆Tello TT无人机目标检测追踪与测距 简介基于YOLOv5与大疆教育无人机Tello TT构建的毕设级项目包面向目标识别、检测与追踪测距场景覆盖旗、圈两类目标的检测任务。数据集包含已标注的旗、圈样本模型经过训练调优附带完整源码、PyTorch权重文件、YAML配置及操作说明可直接运行复现如有基础也可修改代码扩展训练其他目标便于二次开发。压缩包共1672个文件、约269MB包含758张JPG图像、694个TXT标注、94个YAML配置、50个Python脚本、9个PT权重以及Markdown文档、XML标注和训练日志等数据、配置、训练产出与说明文档一应俱全。项目从数据准备、模型训练到无人机端推理部署均有清晰支撑既可作为课程设计、期末大作业或毕业设计的现成方案也能为深度学习CV初学者提供完整工程参考。目前已有283人学习使用适合需要边做边学的读者快速上手。1. 从“大疆教育无人机Tello TT YOLOv5”这个组合说起接手这个方向的工程师或者学生大多卡在同一个地方Tello TT能起飞能看视频流但一旦要求“自动识别某个目标并跟着它飞、同时告诉你离它几米”官方SDK里一个现成接口都没有。于是不少人去找“基于yolov5大疆教育无人机Tello TT实现目标识别检测追踪测距”这类完整源码希望拿过来就能跑。我的结论先放在前面这个方向真正能复现的核心不是代码包而是把 Tello TT 的视频流、YOLOv5 的推理输出、飞控指令的转速控制这三件事串成一条闭环。其中最容易跑通的是“地面站接收视频流 本地算 YOLOv5 回发控制指令”机载部署只能算进阶选项。这篇文章把这套方案从数据集准备到避坑排查完整讲一遍适合已经写过 Python、用过 OpenCV但还没把一个检测模型真正飞到无人机上的读者。2. 训练自己的YOLOv5目标检测模型数据集、超参数与导出2.1 数据集公开类别还是自建数据先想清楚你要识别什么。如果目标只是“人”或“车”那不需要训练直接下载 YOLOv5 官方预训练的 yolov5s.pt 跑results.names里已经有 COCO 的 80 类。但如果要识别特定物体比如安全帽、锥桶、某种飞机标识就一定要训练自己的数据集。常见做法是先用公开数据集验证流程。比如 yolov5安全帽数据集 就是一个很好的入门练习它已经按 YOLO 格式整理好。一般格式是这样dataset/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/labels 里的每个 txt 和同名图片对应每行代表一个目标格式是class_id x_center y_center width height四个数值都归一化到 0~1 之间。这一步很多新手会翻车labelImg 标注出来的坐标是像素值必须转换成归一化坐标转换公式x_center (x_min x_max) / 2 / image_width y_center (y_min y_max) / 2 / image_height box_width (x_max - x_min) / image_width box_height (y_max - y_min) / image_height我自己一般写一个简单脚本批量转换不要手动改。如果你不想自己标也可以找格式现成的数据集但要注意两点第一图片分辨率尽量贴近 Tello TT 实际传回来的画面尺寸否则推理时缩放到 640 会损失很多小目标特征第二类别最好不超过 5 类因为 Tello TT 的链路延迟不可控模型参数量越大检测耗时越长追踪越容易断。所以我建议入门阶段用安全帽数据集练手重点是把训练到导出的流程跑通。2.2 训练命令、超参数与模型导出环境方面最省事的是用 conda yolov5 一套装好。我的习惯是新建一个干净环境不和你其他项目打架conda create -n yolov5 python3.8 -y conda activate yolov5 pip install -r requirements.txt训练前先写一个数据配置文件例如helmet.yamltrain: dataset/images/train val: dataset/images/val nc: 1 names: [helmet]训练命令我一般是这样python train.py --data helmet.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 50 --cache几个参数解释一下。--weights yolov5s.pt表示用小型模型做微调Tello TT 后续如果要机载部署yolov5s 基本是上限再大的 model 跑不动。--batch 16取决于你的显存显存不够就降到 8但 batch 太小会导致 BN 统计不稳定模型容易发散。--epochs 50是安全起步值正常情况下 30 轮以后 mAP 就不再明显上涨但为了最终最好的权重多训一点没关系。训练结束后在runs/train/exp/weights/下会出现best.pt和last.pt。部署到检测脚本之前我建议先导出成 TorchScript这样不依赖原始仓库的 Python 代码加载更快python export.py --weights runs/train/exp/weights/best.pt --include torchscript --img 640导出后生成best.torchscript。如果你习惯用 OpenCV 而不是 PyTorch也可以导出成 ONNX但后续要用 onnxruntime 加载多一层依赖。对于 Tello TT 的场景TorchScript 体积小加载快够用了。2.3 模型评估与“训练好的模型”复用网上标着“完整源码 训练好的模型”的压缩包最容易出问题的地方就是别人训练的模型类别和你要的不一致。所以拿到别人的 best.pt 后第一件事不是跑推理而是打印它的类别import torch model torch.hub.load(ultralytics/yolov5, custom, pathbest.pt, force_reloadTrue) print(model.names)如果输出的是{0: helmet}而不是{0: person, 1: car}你得确认你的检测目标能不能对应上。硬要用一个没训练过该类别的模型去识别出来的结果全是玄学不能通过调阈值挽救。评估自己训练的模型我习惯看results.png里的 mAP0.5 曲线。这个指标大于 0.7 时Tello TT 上勉强能追踪低于 0.5 就别上真机了先回去补数据。需要提醒的是YOLOv5 的 mAP 是在 COCO 标准下计算的和实际无人机视角下的检测效果有差距。因为 Tello TT 飞行过程中目标会出现运动模糊、旋转角度变化这些在静态测试集里体现不出来。所以模型评估只有参考意义真实表现必须用 Tello TT 录一段视频离线测。3. 在Tello TT上部署YOLOv5识别地面站与机载方案3.1 Tello TT的视频流链路怎么拿Tello TT 开机后自己会发出一个 WiFi 热点电脑连上这个热点后通过 UDP 协议向192.168.10.1:8889发 SDC 命令视频流则通过 UDP 接收。这套机制在生产环境里很稳定但缺点是链路只能一对一不能同时让地面站和遥控器都控制。所以第一步是手机、电脑都连到同一个 Tello TT 热点上。Python 里最通用的库是 djitellopy它把底层通信封装好了。连接和开启视频流核心就这几行from djitellopy import Tello tello Tello() tello.connect() tello.streamon()很多人卡在connect()报错或超时。原因通常是电脑系统把 WiFi 自动切换开了或者 Tello TT 的上电指示灯还没亮完就发指令。正解是先等它开机完成然后手动连接热点最后再执行脚本。connect()需要返回电池电量如果返回负数或报错说明链路没握手上。3.2 地面站运行YOLOv5识别最小脚本地面站方案是最稳的因为 YOLOv5 跑在你的电脑上算力不受限。一个最小可跑的识别脚本长这样import cv2 import torch from djitellopy import Tello # 加载模型自定义权重或官方预训练权重 model torch.hub.load(ultralytics/yolov5, custom, pathruns/train/exp/weights/best.pt, force_reloadTrue) tello Tello() tello.connect() tello.streamon() frame_reader tello.get_frame_read() while True: frame frame_reader.frame # 读取一帧 results model(frame, size640) # 推理 annotated_frame results.render()[0] # 画框后的图像 cv2.imshow(Tello TT YOLOv5, annotated_frame) if cv2.waitKey(1) 0xFF ord(q): break tello.streamoff()这个脚本至少印证了链路是通的。两个参数值得解释size640是模型输入尺寸Tello TT 视频流默认 960x720缩放成 640 检测没问题如果你的电脑 CPU 推理慢改成 416 能明显提速但小目标召回率会下降。force_reloadTrue表示每次都从本地重新加载模型避免 torch.hub 缓存旧权重——我踩过改完模型不生效的坑就是这个缓存导致的。3.3 机载部署树莓派5上的推理与性能边界如果非要让 Tello TT 携带设备自己识别那只能外挂一个计算板。常见做法是挂树莓派5上部署自己训练的yolov5模型因为树莓派5的 CPU 比前代强不少能跑到人形目标的基本实时。但 Tello TT 毕竟是 300g 级无人机挂上树莓派、电池和镜头后续航会从 13 分钟掉到 8 分钟甚至更短。我实测过树莓派5跑 yolov5n输入 416x416CPU 推理大约 5~8 FPS这还没有算相机采集和飞控通信的时间。实际闭环很难做到平滑目标稍微动快一点就丢失。所以我的建议是如果你是做课程设计或比赛优先地面站方案把识别和追踪逻辑调试好如果导师或比赛规则硬性要求“无人机自己算”那也要选 yolov5n TorchScript而且必须对系统做实时性限制。比如检测线程独立运行只将最新检测结果给控制线程不用每帧都等推理完成。这种解耦也是 Python 经典项目完整源码里最值得学的设计比盲目堆模型参数有用得多。4. 目标追踪与测距实现从像素坐标到飞行指令4.1 追踪如何用检测框生成控制误差识别出目标后追踪的核心是“目标框中心”与“图像中心”的偏差。偏差越大无人机转得越快。控制量可以简单用一个比例控制器也就是 P 控制import cv2 import torch from djitellopy import Tello model torch.hub.load(ultralytics/yolov5, custom, pathbest.pt) tello Tello() tello.connect() tello.streamon() frame_reader tello.get_frame_read() while True: frame frame_reader.frame results model(frame, size640) boxes results.xyxy[0] # [x1, y1, x2, y2, conf, class] if len(boxes) 0: x1, y1, x2, y2, conf, cls boxes[0].cpu().numpy() target_cx (x1 x2) / 2 target_cy (y1 y2) / 2 img_h, img_w frame.shape[:2] error_x (target_cx - img_w / 2) / (img_w / 2) error_y (target_cy - img_h / 2) / (img_h / 2) yaw_speed int(20 * error_x) # 左右旋转速度 ud_speed int(-20 * error_y) # 上下速度注意图像y轴向下 tello.send_rc_control(0, 0, ud_speed, yaw_speed)这里把误差归一化到 -1 到 1是为了避免图像分辨率影响控制增益。参数 20 是最大速度限制Tello TT 的 yaw 速度范围是 -100 到 10020 已经比较温和。如果目标框经常冲出画面可以提高到 30如果一直来回震荡就降到 10。send_rc_control的四个参数分别是左右、前后、上下、旋转速度都是 int。特别注意error_y需要取负号因为图像坐标系 y 轴向下而飞控的“向上”是正方向。这个反转关系不搞清楚飞机会朝反方向跑。4.2 测距单目视觉像素比例法的参数标定追踪时测距比追踪更讲究。Tello TT 没有激光或双目只能用单目视觉估计距离。最朴素也最有效的方法是“标定宽度法”利用透视投影中目标尺寸与距离成反比KNOWN_WIDTH 0.3 # 目标实际宽度单位米 F_PIXEL 500 # 待标定的等效焦距单位像素距离公式distance KNOWN_WIDTH * F_PIXEL / target_width_pixel这个公式的前提是F_PIXEL已知。标定方法很简单把目标放在距离相机 1 米的地方测出它的像素宽度w_ref那么F_PIXEL w_ref * 1.0 / KNOWN_WIDTH。之后用这个F_PIXEL去算任意距离。注意Tello TT 的相机是定焦广角画面边缘畸变明显。目标如果位于画面边缘测出来距离会虚大。所以测距时最好只使用画面中心 60% 区域的目标或者先用相机标定去畸变。对快速原型来说我一般不做去畸变靠控制环路让目标始终保持在画面中心附近这样畸变影响就小很多。实际代码里从检测框得到目标宽度target_width_pixel x2 - x1 if target_width_pixel 0: distance KNOWN_WIDTH * F_PIXEL / target_width_pixel测出来的是近似距离误差在 10% 以内就算及格。如果要求更准可以把KNOWN_WIDTH换成目标高度因为人在运动过程中宽度随姿态变化很大而身高相对稳定。我自己做人体追踪时用的是身高而不是肩宽稳定性好很多。4.3 一套完整的追踪测距循环代码把前面几节拼起来就是一个能飞的追踪测距主循环。为了安全我加了“丢失目标就悬停”的逻辑import time import cv2 import torch from djitellopy import Tello model torch.hub.load(ultralytics/yolov5, custom, pathbest.pt) tello Tello() tello.connect() tello.streamon() tello.takeoff() frame_reader tello.get_frame_read() KNOWN_WIDTH 0.3 F_PIXEL 500 LOST_THRESHOLD 20 # 连续多少帧没检测到目标就悬停 lost_count 0 try: while True: frame frame_reader.frame results model(frame, size640) boxes results.xyxy[0] if len(boxes) 0: lost_count 0 x1, y1, x2, y2, conf, cls boxes[0].cpu().numpy() center_x (x1 x2) / 2 target_width x2 - x1 # 测距 distance KNOWN_WIDTH * F_PIXEL / target_width if target_width 0 else -1 print(fdistance: {distance:.2f} m) # 追踪 error_x (center_x - frame.shape[1] / 2) / (frame.shape[1] / 2) yaw_speed int(15 * error_x) tello.send_rc_control(0, 0, 0, yaw_speed) else: lost_count 1 if lost_count LOST_THRESHOLD: tello.send_rc_control(0, 0, 0, 0) # 悬停 print(目标丢失悬停等待...) break time.sleep(0.05) finally: tello.send_rc_control(0, 0, 0, 0) tello.land()参数解释time.sleep(0.05)让控制周期约 20Hz这个频率下 Tello TT 能响应同时不会因为指令太频繁阻塞 UDP。LOST_THRESHOLD 20代表 1 秒没有目标就悬停防止乱飞。print输出的距离可以用于后面离线分析。这个循环只是个骨架真正跑飞之前必须在地面试验。很多人在这一步遇到起飞后立刻偏移或螺旋下降这部分我会在下一节专门说。5. 避坑排查Tello TTYOLOv5路上最常见的5个坑5.1 现象连上 Tello TT 的 WiFi但tello.connect()长时间卡住或返回负电量原因大多数是电脑网络代理干扰了 UDP 通信或者 Tello TT 固件还在启动中。我第一次调试时一直等不到电量查了半天发现是系统自动连接了隔壁的 WiFi。解决先手动断开所有其他网络只保留 Tello TT 的热点。然后给 Tello TT 上电等指示灯从红色变成黄色再跑脚本。如果仍然卡住在代码里加循环重试while True: try: tello Tello() tello.connect() break except Exception as e: print(重试连接..., e) time.sleep(2)5.2 现象画面流畅但 YOLOv5 推理后视频显示明显卡顿这是地面站方案的典型问题。Tello TT 视频流本身 30 帧但model(frame)是同步的推理一帧要 0.2 秒整个循环就被拖到 5 FPS。解决用双线程一个线程只读帧一个线程只推理。或者降低size到 416 或 320。如果还卡把检测模型从 yolov5s 换到 yolov5n。不要指望用多线程彻底解决因为消费级电脑 CPU 推理 yolov5s 本身就要 100ms 以上只能从模型侧压缩。我一般会把模型输入固定在 416对于目标识别为主的任务损失不大但速度提升一倍。5.3 现象目标明明是红色但模型就是检测不出来原因大概率是训练数据里目标形态与现场差异太大或角度、光照、运动模糊导致特征丢失。Tello TT 飞行高度一般在 1.5 米以上目标尺寸在 640 分辨率下可能只有 30 像素。YOLOv5 对小于 15 像素的目标基本无能为力。解决不要先调模型先调飞行高度。让无人机离目标近一点保证检测框最窄边不小于 50 像素。同时检查数据集里是否包含俯拍视角图片如果没有用 Tello TT 悬停拍摄一批训练数据再增量训练。这一步最费时间但也是最值的后悔药。5.4 现象测距结果忽远忽近明明不动距离却从 2 米跳到 4 米原因是检测框宽度抖动。目标检测框不是稳定的只要目标稍微侧身宽度就变小距离就变大了。这是单目测距的固有缺陷。解决对宽度做卡尔曼滤波或简单滑动平均。我常用方法就是维护一个宽度队列width_history [] def smooth_width(w): width_history.append(w) if len(width_history) 5: width_history.pop(0) return sum(width_history) / len(width_history)另外同一个目标在不同距离下宽度变化不线性所以近距离1 米内测距误差很大远距离5 米以上像素分辨力不足。可信区间基本在 1.5 米到 3 米之间。5.5 现象无人机起飞后直接朝一个方向飞走控制不到原因通常是send_rc_control的坐标轴理解错误。我在 4.1 节特别强调过 y 轴反转。还有可能是控制频率太慢导致飞控认为指令超时自动进入降落模式。Tello TT 要求控制指令发送间隔不能超过 15 秒但实际要平稳控制必须保持至少 10Hz 以上。解决先不要起飞。把无人机平放在桌上只发送脚偏航指令确认转向正确后再升空。升空时高度不要超过 0.8 米旁边安排人持保护罩准备随时手抓。如果发现乱飞立刻按键盘紧急停机键或运行tello.emergency()。6. 调优与验证让追踪测距从“演示”走向“可用”代码能跑起来不等于能交付。我给自己的项目定的验收标准是这样的目标静止时无人机 5 秒内能悬停在目标正前方目标以每秒 0.3 米速度平移时追踪不丢失测距误差在 15% 以内。达不到这个标准我不会上真机。离线验证是第一步。把 Tello TT 放在桌面不开机用手机录一段目标移动视频然后在电脑上跑完整的追踪测距循环把检测框和距离打印到终端。对比视频里目标实际移动轨迹确认算法逻辑正确。这一步能筛掉 80% 的代码问题。在线调试时我是这样做的先用胶带把无人机固定在一个带轮子的小车上不给桨叶供电只让它在地面跟着目标跑。确认追踪方向正确后再装上桨叶起飞高度 1 米只做偏航和升降。前后方向的运动测试放在最后因为最容易撞人。PID 参数不要一次给大。先用纯 P 控制yaw_speed kp * error_xkp 在 10 到 30 之间。如果目标在画面中心来回振荡说明 kp 太大如果偏航跟不上说明 kp 太小。调好 P 再加 I 消除稳态误差但 I 项一定要设置积分限幅否则风力扰动会让积分爆掉。我自己的习惯是每调出一个稳定参数就立刻记到注释里并保存一份飞行录像。这样即便炸机后忘记参数也有录像可以复盘。这套方案做到最后你会发现真正的难点不在 YOLOv5也不在 Tello TT而在于“让一个会飘的无人机持续盯住一个会动的东西”。这需要一点点抠延迟、抠滤波、抠控制周期。希望这篇笔记能帮你少走一段我当时踩过的路也希望你第一次试飞时旁边有个草坪。祝顺利。本文还有配套的精品资源点击获取