
简介这份基于YOLOv5与大疆教育无人机Tello TT的完整项目包主要面向正在准备毕业设计、课程设计或需要深度学习与计算机视觉项目实战的本科生与研究生。资源围绕无人机视角下的旗、圈两类目标识别展开同时涵盖检测、跟踪与测距功能模型已经过训练调优并配有可直接运行的源码、标注数据集、训练好的权重文件以及详细操作说明文档既可作为毕业设计完整方案也适合在此基础上替换数据集、调整配置并训练其他模型。压缩包共包含1672个文件整体大小约269.18MB核心文件包括758张JPG图像、694个TXT标注或说明文本、94个YAML配置、50个Python脚本和9个PT模型权重另有少量PNG、XML、Shell脚本、Markdown文档等辅助材料覆盖数据准备、模型训练、推理验证和部署配置等主要环节目录结构清晰便于按需查找。目前已有283人学习下载整体完整度和可操作性较高若基础较好还可参考现有代码继续扩展实践价值和学习价值都比较突出。1. 一条 UDP 链路没通你就算有模型也追不了这套 Tello TT 识别追踪方案到底在解决什么很多第一次接触“基于yolov5大疆教育无人机Tello TT实现目标识别检测追踪测距”的工程师第一反应是去跑训练脚本结果卡在第一步无人机连不上、视频流黑屏、发指令没回应。反直觉的是这个项目真正的难点不在yolov5本身而在于Tello TT的通信链路和数据变化率。Tello TT是一台面向教育的四轴无人机机载算力很有限跑不动完整yolov5所以常见的落地方式是地面站PC或树莓派通过Wi-Fi接收它的视频流跑推理再把控制量通过UDP发回去。这套思路能解决的问题很具体让无人机识别一个目标、追踪它、并估算机体重心到目标的距离。适合正在做无人机课设、机器人竞赛或巡检demo的开发者也适合想把手里的yolov5模型接到真实运动设备上的人。读完这篇你能用最小代价把“识别-追踪-测距”整条链路在本地跑通并且知道哪些环节是玄学、哪些坑值得提前绕开。2. 从 Tello TT 到 yolov5识别、追踪、测距的链路是怎么搭起来的2.1 Tello TT 能给你什么视频流、SDK 与计算瓶颈Tello TT 本质上是 Tello EDU 的教育版本支持 SDK 2.0通过 Wi-Fi 和 UDP 与外部设备通信。常用端口有三个指令端口 8889、状态端口 8890、视频流端口 11111。地面站先连接 Tello 的 Wi-Fi发送command让无人机进入 SDK 模式之后可以发takeoff、land、streamon、rc等指令。视频流默认是 H.264 编码OpenCV 可以直接拉流读取。这套 SDK 的最大好处是协议简单几行 socket 就能控制最大的瓶颈是 Wi-Fi 吞吐和机载算力。Tello TT 的机身只能做光流定位、气压计定高这类轻量任务不可能在板载端跑一个几十 MB 的 yolov5 模型。所以工程上要采用“机载摄像头”加“地面算力”的组合。我一般建议在项目初期先做链路自测不要急着跑训练。把 Tello TT 连上电脑用cv2.VideoCapture(udp://0.0.0.0:11111)拉视频流再用 socket 发一条battery?指令确认通信双向正常。只要这两个点通后面追踪测距才有意义。2.2 yolov5 放在哪跑机载、PC 与树莓派的取舍yolov5 的部署位置决定了整个系统的实时性和稳定性。常见的做法是地面 PC 直接运行模型因为 Tello 的 Wi-Fi 延迟一般有几十毫秒到一百毫秒不等PC 端的 GPU 推理可以尽量缩短单帧耗时。PC 方案适合刚起步的开发者调试方便还能看到可视化窗口。另一种做法是把模型部署在树莓派 4B 或 5 上靠模型量化降低成本板体积和功耗但不建议在基础阶段碰因为 Tello 的视频流在 Wi-Fi 拥塞时会掉帧树莓派再叠加推理延迟很容易让 PID 控制失效。如果后期要做跟随小车或者机载边缘计算可以考虑把 yolov5 模型导出成 ONNX 或 TensorRT再配合 RKNN 在 RK3568 这类 NPU 上推理。但在 Tello TT 这个平台上地面站方案仍然是性价比最高的。选型时还要注意一个细节Tello 的视频流只有 960×720 的分辨率帧率在 30fps 左右这意味着输入给 yolov5 的帧不能太大。用 640×640 作为推理尺寸就够了拉到 1280 只会增加耗时对十几米外的小目标没有本质提升。2.3 测距不是从检测框里直接读出来的单目测距的基本逻辑很多第一次做追踪测距的人会问检测框的宽度能不能直接换算成距离能但前提是你要先做标定。Tello TT 只带一颗前视单目摄像头没有深度传感器所以距离只能通过几何模型估算。常用的方法是针孔相机模型distance real_width * focal_length / pixel_width其中 real_width 是被测目标在实际空间中的宽度pixel_width 是目标在画面中检测框的像素宽度focal_length 是焦距。焦距不需要查表可以在一个已知距离 D 下放一个已知宽度 W 的目标测量它的像素宽度 P算出 focal_length P * D / W。之后在飞行中每次检测到目标都套用这个公式。但要注意这个公式建立在目标平面与相机成像平面近似平行的前提下。如果目标倾斜视觉宽度会变小距离会被高估。所以实际项目中要么把“测距”限定在目标姿态稳定的场景要么用一段时间内检测框宽度的中位数来平滑波动。3. 把识别、追踪、测距串起来最小可复现的 Python 控制流3.1 用 yolov5 做目标识别检测最小推理脚本拿到这套方案里的“训练好的模型”后第一步是写一个本地推理脚本确认模型能正常输出检测框。不要一上来就接无人机先用普通视频或摄像头验证。下面这个脚本是常见的地面推理入口。import cv2 import torch # 加载本地训练好的模型custom 指向 best.pt model torch.hub.load(ultralytics/yolov5, custom, pathbest.pt, force_reloadTrue) model.conf 0.25 # 置信度阈值 model.iou 0.45 # NMS 的 IoU 阈值 cap cv2.VideoCapture(0) # 先本地摄像头验证后续换成 Tello 视频流地址 while True: ret, frame cap.read() if not ret: break # 推理results.xyxy[0] 是检测框坐标、置信度、类别 results model(frame) det results.xyxy[0].cpu().numpy() for x1, y1, x2, y2, conf, cls in det: # 画框和标签 cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) label f{results.names[int(cls)]} {conf:.2f} cv2.putText(frame, label, (int(x1), int(y1) - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) cv2.imshow(detect, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码的逻辑很直白先通过torch.hub.load加载本地权重路径best.pt指向方案里训练好的模型然后循环读取摄像头画面把每一帧喂给模型results.xyxy[0]里的每一行对应一个目标包含左上角和右下角坐标、置信度、类别索引。model.conf和model.iou是后处理阶段最重要的两个参数前者过滤低置信度目标后者控制重叠框的合并。对无人机场景来说conf 调太高会漏检远处目标调太低会出现抖动框建议从 0.25 起步。这里先不接 Tello是为了让推理逻辑先稳定避免把通信问题和模型问题混在一起排查。3.2 追踪把像素误差变成无人机的飞行速度追踪的本质是闭环控制。yolov5 给出目标框后计算目标中心点和画面中心的偏差再把偏差映射成 Tello 的rc指令速度。Tello SDK 中rc left_right_velocity forward_backward_velocity up_down_velocity yaw_velocity的取值范围是 -100 到 100单位是 cm/s 或度/秒。控制目标就是把目标中心保持在画面中心附近。import socket import time class TelloController: def __init__(self, tello_ip192.168.10.1): self.sock socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.tello_ip tello_ip self.tello_port 8889 self.sock.settimeout(3) def send_command(self, cmd): self.sock.sendto(cmd.encode(), (self.tello_ip, self.tello_port)) try: response, _ self.sock.recvfrom(128) return response.decode() except socket.timeout: return timeout def rc(self, lr, fb, ud, yaw): self.send_command(frc {lr} {fb} {ud} {yaw}) # 简化版追踪控制器 class PID: def __init__(self, kp0.15, ki0.002, kd0.06): self.kp kp self.ki ki self.kd kd self.integral 0 self.last_error 0 def update(self, error, dt): self.integral error * dt derivative (error - self.last_error) / dt if dt 0 else 0 output self.kp * error self.ki * self.integral self.kd * derivative self.last_error error return max(-100, min(100, int(output))) # 假设 frame_width 是画面宽度的一半target_center 是检测框中心横坐标 frame_width 320 yaw_pid PID(kp0.12, ki0.001, kd0.04) lr_pid PID(kp0.10, ki0.001, kd0.03) while True: # 伪代码从结果中取第一个检测框的中心 # center_x, center_y, width, height get_detection_from_yolov5(frame) center_x 320 error_x center_x - frame_width yaw_speed yaw_pid.update(error_x, 0.05) # 控制无人机旋转追踪目标 tello.rc(0, 0, 0, yaw_speed) time.sleep(0.05)这里给出的是控制框架实际使用时要把get_detection_from_yolov5替换成基于results.xyxy[0]的解析逻辑。重点说三个参数kp决定响应速度太大会让无人机左右震荡ki负责消除稳态误差但无人机本身是浮动的积分调太大容易过冲kd能抑制震荡但 Tello 视频流有延迟微分项对噪声很敏感往往要调小甚至置零。我自己的习惯是先只用 Kp 跑到平稳再逐步加 Ki 和 Kd否则很容翻车。3.3 测距基于已知宽度的像素比例法测距功能可以和检测框共用同一份推理结果。在检测到目标后取目标框的像素宽度代入标定好的焦距和已知实际宽度就能算出距离。为了让距离更平稳常见做法是对连续 5 到 10 帧的像素宽度做排序取中位数而不是直接使用单帧数值。import numpy as np KNOWN_WIDTH_M 0.30 # 目标实际宽度单位米 DISTANCE_AT_CALIBRATION 1.5 # 标定时目标距离镜头的距离单位米 PIXEL_WIDTH_AT_CALIBRATION 120 # 标定时测得的目标像素宽度 focal_length PIXEL_WIDTH_AT_CALIBRATION * DISTANCE_AT_CALIBRATION / KNOWN_WIDTH_M width_history [] def estimate_distance(pixel_width): if pixel_width 0: return None return KNOWN_WIDTH_M * focal_length / pixel_width for pixel_width in detection_results: width_history.append(pixel_width) if len(width_history) 7: width_history.pop(0) stable_width np.median(width_history) distance estimate_distance(stable_width) print(fdistance: {distance:.2f} m)这段代码里的核心是焦距标定。PIXEL_WIDTH_AT_CALIBRATION需要你在正式飞行前把目标放在离镜头 1.5 米处用检测结果读取一次像素宽度填进去。这个值会随目标形状、光线阴影变化所以飞行中尽量用中位数滤波。另一个容易忽略的点是检测框包含目标的头尾或手脚时像素宽度会突变这时候距离也会突变。所以测距不能只看框宽还要结合置信度变化和连续帧的判别消除误检造成的跳变。4. 训练自己的目标识别模型数据集准备与 yolov5 超参数4.1 从标注框到 YOLO 格式数据集结构与转换脚本标题里的方案自带一套数据集但工程落地时经常要换成自己的目标。yolov5 支持两种主流标注格式一种是 YOLO 的 txt 格式每行是class x_center y_center width height坐标都归一化到 0~1另一种是 Pascal VOC 的 XML 格式。很多标注工具默认导出 XML所以第一步往往是写转换脚本。import os import xml.etree.ElementTree as ET def convert(size, box): dw 1.0 / size[0] dh 1.0 / size[1] x (box[0] box[1]) / 2.0 y (box[2] box[3]) / 2.0 w box[1] - box[0] h box[3] - box[2] return x * dw, y * dh, w * dw, h * dh def voc_to_yolo(xml_path, out_dir): tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) out_file os.path.join(out_dir, os.path.basename(xml_path).replace(.xml, .txt)) with open(out_file, w) as f: for obj in root.iter(object): cls_name obj.find(name).text box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) x_c, y_c, w, h convert((img_w, img_h), (xmin, xmax, ymin, ymax)) f.write(f{class_id_map[cls_name]} {x_c:.6f} {y_c:.6f} {w:.6f} {h:.6f}\n)转换脚本里的class_id_map需要你提前定义好类别名和 ID 的对应关系比如{person: 0, cone: 1}。yolov5 训练时对数据集目录结构有固定要求常见做法是这样组织datasets/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ ├── data.yamldata.yaml里写明train、val路径和nc类别数。这一步很容易踩坑标签文件和数据文件夹必须严格同名后缀不同而已如果有一张图片没有标注文件yolov5 训练时也会报警告甚至跳过。我一般会写个脚本扫描一遍确保每张 train 图片都有对应 txt避免训练到一半才发现漏标。4.2 yolov5 训练命令与超参数别只改 batch 和 epochsyolov5 训练自己的数据集时最基础的训练命令是这样的python train.py --img 640 --batch 16 --epochs 100 --data data.yaml --weights yolov5s.pt在 Tello TT 场景下我建议把模型体积和推理速度放在首位所以常用yolov5n或yolov5s作为预训练权重。--img 640和--batch 16是大多数显存 8G 以上显卡都能跑的参数显存不够就先降 batch 到 8 或 4不要降--img。更关键的是 yolov5 内置的超参数文件hyp.scratch.yaml里面包含lr0、lrf、momentum、weight_decay、warmup_epochs以及数据增强相关的mosaic、hsv_h、fliplr等参数。默认值对通用目标效果好但如果你的目标是小物体需要把mosaic保持开启因为马赛克增强能增加小目标的上下文信息如果目标本身很单一比如只识别锥桶可以把hsv_h调小减少颜色扰动避免模型过拟合到不稳定的色偏。训练过程中还有两个容易被忽视的后处理环节conf_thres和iou_thres影响最终验证指标但模型训练时的损失函数并不直接对应这些阈值。所以你训练完发现 PR 曲线很好实际飞起来识别却乱跳问题往往不在模型而在推理时的后处理阈值没调好。这个我会在第 5 章展开。5. 避坑Tello 连不上、识别框跳变、测距漂移的排查笔记5.1 通信与视频流起飞前的三类典型问题排在第一的是“无人机收不到指令”。现象socket 发送command后没有收到ok发送takeoff也没有反应。原因多数是地面设备的网卡没有连上 Tello 的 Wi-Fi或者没有先进入 SDK 模式。Tello 开机后默认是普通飞行模式必须发command并收到ok才能接受 SDK 指令。解决方法是先检查 Wi-Fi 连接状态再用一个串行流程发送连接 Wi-Fi - 发command- 等待回应 - 发battery?验证。不要一次性把command和takeoff连续发出去Tello 是单线程处理指令的前一条没响应后一条会被丢弃。第二个问题是视频流黑屏。现象OpenCV 的VideoCapture打开了地址但读出来是灰色画面。原因往往是 Tello 还没执行streamon或者视频流端口被占用。解决方法是先通过 socket 发送streamon再创建VideoCapture(udp://0.0.0.0:11111)注意这里的目标地址是本地监听端口不是 Tello 的 IP。有不少人把地址写成了192.168.10.1:11111那会连不上。H.264 流还需要环境有相应的解码器Windows 下建议用带解码能力的 OpenCV 版本或者先用ffmpeg测试一下端口是否有数据。第三个问题是飞行中控制延迟突然变大。现象目标已经偏出画面无人机还在原地过一两秒才反应。原因大概率是地面站性能不够或后台有浏览器等应用抢占了 Wi-Fi 带宽。解决方法是飞行时关闭其他占用网卡的程序把 yolov5 的推理输入尺寸降到 416x416并把thread参数调大用两个线程分别处理视频流读取和模型推理。5.2 识别追踪测距三处让新老手一起翻车的细节识别框乱跳是最常见的。现象同一帧里目标明明很稳定人为框却忽大忽小追踪时直升机左右抽动。原因不一定是模型而是推理输出的原始框没有做时序平滑。yolov5 每个框的 IoU 和 NMS 处理是逐帧独立的在无人机悬停带来的震动下连续帧的检测框像素宽度会出现 10% 以上的波动。解决方法是给检测框宽度和中心点做一阶低通滤波或者把 5 帧内宽度取中位数再送去 PID 控制器。这个处理比调 PID 参数更有效很多新人在调参上死磕忘了问题出在输入噪声。测距漂移是另一个高发区。现象无人机静止目标也在原地但显示的距离从 1.4 米跳到 2.1 米。原因有两个一是检测框包含背景像素导致宽度偏大二是目标的可见姿态在变化。比如人侧身时框常数变窄距离就被高估。解决方法是让检测框直接用矩形宽高而不是外接多边形同时把距离估算结果限制在合理范围内超过 10 米直接置为无效。Tello 的定位能力在 10 米外本来也不稳定距离值没必要当真。最后一个是模型部署到树莓派后帧率暴跌。现象在 PC 上能跑 20fps换到树莓派 4B 上变成 3fps。原因是没有用量化模型和推理引擎。解决方法是把 yolov5s 导出 ONNX再用 OpenCV DNN 模块或 RKNN 工具链做 int8 量化。量化后精度会掉一些但追踪用的阈值本来就保留余量只要置信度阈值不低于 0.3影响可控。如果目标是极端小目标可以用yolov5n加上扩大输入分辨率的方式平衡精度和速度。6. 把模型压到能塞进边缘设备再谈稳定追踪的进阶技巧当整套识别追踪测距在地面 PC 上跑通后下一步推荐做模型压缩和飞行日志记录。yolov5 有三种常见压缩路径一是换骨架从yolov5s改成yolov5n参数量直接缩小到三分之一二是做 ONNX 导出后使用半精度 FP16 推理在保持精度基本不变的情况下把帧率提起来三是做 int8 量化适合部署到 RK3568 或树莓派 5 这类设备上。我在做 Tello TT 项目时最终部署用的是yolov5n加 ONNX Runtime输入尺寸 480x480在树莓派 5 上能做到 12~15fps对追踪控制在 10 米内够用。除了压缩模型还需要建立飞行记录的习惯。Tello 的状态数据会通过 8890 端口以;分隔的字符串持续上报包含高度、姿态角、速度、电量等信息。把这些数据和 yolov5 的检测结果按时间戳对齐写进 CSV是排查追不上目标、测距忽远忽近的直接证据。比如我遇到过追踪时无人机总是慢半拍看日志才发现是 Tello 的 yaw 速度指令在连续发送时被丢弃因为 SDK 对指令频率有限制超过 20Hz 就会丢包。把累计误差改为超过 15 像素才发送rc指令后问题立刻消失。现在我做无人机定位项目时会先用一秒记录一次状态数据来校准控制周期再上线 PID这个习惯帮我排掉了不少黑匣子问题。这个方向值得深耕的地方在于后续两个延伸一个是把单目测距升级成多视角测距比如让无人机绕目标拍摄一段短轨迹用运动结构恢复出深度提高测距精度另一个是把目标检测从单帧改成跟踪器例如结合 ByteTrack 或 DeepSORT让检测断裂时追踪器能预测目标位置避免无人机追着丢失目标空转。我在做这一环节时最大的教训是先保证系统在室内弱光场景能稳定追踪锥桶再去碰复杂的算法叠加。因为 Tello 的光流定高在光照差时本来就会漂移这时候哪怕目标识别再好距离估算也会跟着偏。希望你在上手时也能先跑通通信链路再谈模型调好 PID 再谈测距绕开我当年走过的弯路。希望这篇笔记能帮到你。本文还有配套的精品资源点击获取