简介:本资源是一套面向K12教育与高校课程设计、毕业设计的计算机视觉实战项目,基于YOLOv5深度学习框架与大疆Tello TT教育无人机,完整实现旗标与圆圈目标的实时检测、动态追踪及单目测距功能。适用于深度学习、CV图像识别、模式识别方向的学习者与毕设开发者,尤其适合缺乏硬件集成经验但具备Python和PyTorch基础的中级实践者。压缩包共1679个文件(249.27MB),含775张标注图像(jpg)、694份标签文本(txt)、94个配置文件(yaml)、46个核心脚本(py)、8个训练好的模型权重(pt)及操作说明文档等,结构清晰、模块解耦,支持开箱即用与二次开发。已有2185人学习下载,配套TensorBoard日志(events.out.tfevents)便于复现训练过程,所有组件经实机验证,可直接部署至Tello TT完成端到端飞行识别任务。
1. 项目概述:当YOLOv5遇见Tello TT,一个完整的AI无人机视觉方案
最近在整理过去的项目资料,翻出了这个基于YOLOv5和大疆教育无人机Tello TT的目标识别、追踪与测距的完整项目包。这算是我在边缘计算与无人机视觉结合领域,一个比较有代表性的落地实践。当时做这个项目的初衷,是想验证一下在资源极其受限的嵌入式设备(Tello TT的机载计算单元)上,能否流畅运行一个经过优化的目标检测模型,并实现实时的追踪与距离估算。结果证明,通过合理的模型裁剪、部署策略和算法优化,是完全可行的。这个项目包包含了从数据集准备、模型训练、到最终在Tello TT上部署运行的全套源码、文档和预训练模型,可以说是一个“开箱即用”的解决方案。
对于刚接触无人机视觉或者YOLOv5的朋友来说,这个项目是一个绝佳的起点。它不仅仅是一个代码仓库,更是一个完整的工程实践案例。你可以从中学习到如何将一个前沿的计算机视觉算法,适配到具体的硬件平台上,并解决实际应用中遇到的各种问题,比如图像传输延迟、机载算力瓶颈、目标丢失重捕获等等。无论你是学生想做一个炫酷的毕业设计,还是开发者想为无人机增加智能“眼睛”,这个项目都能提供清晰的路径和可复现的代码。
2. 核心思路与技术选型解析
2.1 为什么是YOLOv5 + Tello TT?
这个组合的选择,背后有非常实际的工程考量。首先看硬件端,大疆的Tello TT是一款面向教育和开发者的可编程无人机。它的优势在于提供了相对开放的SDK(特别是通过djitellopy这样的Python库),可以让我们通过Wi-Fi用代码精确控制它的起飞、降落、移动以及获取机载摄像头视频流。Tello TT本身计算能力有限,但它可以作为我们算法的“眼睛”和“手脚”,将视频流实时传输到算力更强的设备上处理,或者运行轻量级模型。
而YOLOv5,作为目标检测领域的“当红炸子鸡”,以其出色的速度-精度平衡和友好的工程化体验著称。相比于它的前辈YOLOv3/v4,YOLOv5的代码结构更清晰,训练流程更简单,并且提供了从n(纳米)到x(特大)一系列不同大小的预训练模型,方便我们根据硬件性能进行选择。对于Tello TT这样的场景,我们通常不会选择最大的YOLOv5x模型,而是倾向于YOLOv5s甚至更小的YOLOv5n,以确保推理速度能满足实时性要求(例如达到15-30 FPS)。
这个项目的核心思路,就是利用Tello TT的摄像头捕捉实时画面,通过Wi-Fi将视频流传输到一台算力更强的计算机(可以是笔记本,也可以是树莓派4B或Jetson Nano等边缘设备)上。在这台计算机上,运行我们训练好的YOLOv5模型,对每一帧图像进行目标检测,识别出特定的物体(比如人、球、汽车等)。然后,算法会根据目标在图像中的位置和大小,计算出控制指令(比如让无人机向左转以保持目标在画面中央),再通过SDK发送回Tello TT,实现追踪。同时,结合已知的目标实际尺寸和它在图像中占据的像素大小,可以估算出无人机与目标之间的近似距离。
2.2 方案架构与数据流拆解
整个系统的运行流程可以拆解为以下几个核心环节,理解这个数据流对后续的开发和调试至关重要:
视频流获取:通过
djitellopy库连接到Tello TT,启动它的摄像头,并以H.264编码格式获取原始视频流。这里的一个关键点是处理解码。Tello传输的是编码后的字节流,我们需要在接收端(我们的处理电脑)上使用OpenCV或FFmpeg进行解码,将其转换为一张张RGB图像(numpy数组),才能送入YOLOv5模型。目标检测推理:将解码后的图像帧,按照YOLOv5模型要求的输入尺寸(如640x640)进行预处理(包括缩放、归一化等),然后送入加载好的PyTorch模型中进行前向传播。模型会输出一系列检测框,每个框包含类别标签、置信度分数以及边界框坐标(x_center, y_center, width, height,通常是归一化到0-1的值)。
目标追踪逻辑:检测到目标后,我们需要一个策略来在连续的帧之间关联同一个目标,避免每一帧都当成新目标。本项目通常采用一种简单高效的“最近邻”追踪器。其原理是:在上一帧中,我们记录下目标框的中心位置;在当前帧,在所有检测到的目标框中,寻找与上一帧目标中心点欧氏距离最近的那个框,认为是同一个目标。如果距离超过某个阈值,则认为目标丢失,需要重新捕获。更复杂的场景可以使用SORT或DeepSORT等算法,但对于Tello TT这种相对简单的追踪任务,最近邻法在大多数情况下已经足够稳定。
控制指令生成:追踪的核心是生成控制指令。我们设定一个“死区”(比如图像中心区域)。如果目标框的中心点偏离了图像中心,我们就计算它在X轴和Y轴上的偏移量。根据偏移量的大小和方向,生成相应的控制命令,例如:目标偏左,则发送
rc_control命令让无人机向右平移(或向右旋转)。PID控制器在这里可以派上用场,让无人机的移动更加平滑,而不是“抽搐式”的调整。距离估算:单目测距是一个有挑战但很有趣的功能。其基本原理是利用相似三角形。我们需要预先知道被追踪目标的实际物理尺寸(例如,一个标准足球的直径约为22厘米)。当目标在图像中被检测到时,我们可以测量其边界框的高度或宽度(以像素为单位)。根据相机焦距(可以通过相机标定获得,或使用Tello TT的近似值)和以下公式,可以估算距离:
距离 = (目标实际尺寸 * 图像高度(像素)) / (目标在图像中的像素高度 * 传感器单位像素尺寸)。由于Tello TT的相机参数并非精确已知,且镜头存在畸变,这种方法得到的距离是近似值,但对于判断目标远近、调整追踪策略(如目标太近时后退)非常有帮助。
注意:单目测距的精度严重依赖于相机内参的准确性和目标尺寸的先验知识。对于要求不高的演示或教育项目,使用一个经验校准值即可。若追求精度,需要对Tello TT的摄像头进行专门的标定。
3. 环境搭建与核心依赖详解
3.1 基础软件环境准备
要跑通这个项目,你需要准备一台装有Python的电脑。强烈推荐使用Anaconda来创建独立的Python环境,避免包版本冲突。以下是详细的步骤和版本选择建议:
创建Conda环境:打开终端(Linux/macOS)或Anaconda Prompt(Windows),执行以下命令创建一个名为
tello_yolo的新环境,并指定Python版本为3.8。Python 3.8是一个在兼容性和稳定性上比较折中的选择,对PyTorch和OpenCV的支持都很好。conda create -n tello_yolo python=3.8 conda activate tello_yolo安装PyTorch:这是YOLOv5运行的基础。访问PyTorch官网,根据你的操作系统、包管理工具(我们选Conda)和CUDA版本(如果你有NVIDIA显卡且想用GPU加速推理)来获取安装命令。如果没有GPU,就安装CPU版本。例如,对于Windows系统且无CUDA:
conda install pytorch torchvision torchaudio cpuonly -c pytorch安装完成后,在Python中运行
import torch; print(torch.__version__)和print(torch.cuda.is_available())来验证安装和CUDA是否可用。克隆YOLOv5官方仓库:YOLOv5的代码、模型定义和工具脚本都在其GitHub仓库里。我们需要将其克隆到本地。
git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txt执行
pip install -r requirements.txt会安装YOLOv5所需的所有依赖,包括OpenCV-Python、Matplotlib、Pandas等。这一步可能会花费一些时间。
3.2 项目专属依赖与工具库安装
在YOLOv5的基础环境之外,我们还需要安装与Tello TT通信和控制相关的库。
安装djitellopy:这是与Tello系列无人机通信最流行的Python库之一,它封装了Tello的SDK命令,提供了非常直观的API。
pip install djitellopy这个库允许我们通过几行代码就实现连接、获取视频流、发送控制指令等所有操作。
可选但推荐的库:
- NumPy:科学计算基础,通常已在requirements中安装。
- OpenCV-python (cv2):图像处理和视频解码的核心,也已在requirements中。确保版本在4.5以上。
- Pillow (PIL):图像处理,YOLOv5的数据加载会用到。
- seaborn / matplotlib:用于绘制训练过程中的损失曲线、精度曲线等,方便分析模型性能。
3.3 Tello TT硬件连接与初始化
在代码运行前,需要确保你的电脑和Tello TT在同一Wi-Fi网络下。通常的操作流程是:
- 打开Tello TT的电源。
- 等待其启动完成,指示灯进入“等待连接”状态(通常是慢速闪烁)。
- 在你的电脑上,连接到Tello TT创建的Wi-Fi热点,热点名称通常类似于“TELLO-XXXXXX”。
- 记录下Tello TT的IP地址,默认通常是
192.168.10.1。
在代码中,初始化连接非常简单:
from djitellopy import Tello tello = Tello() tello.connect() # 连接到默认IP和端口 print(f"电池电量:{tello.get_battery()}%")如果连接成功,会打印出当前的电池电量。务必在电池电量充足(建议高于50%)的情况下进行实验,避免飞行过程中突然断电。
4. 数据集准备与YOLOv5模型训练实战
4.1 构建专属数据集
虽然项目包中提供了训练好的模型和可能的数据集,但理解如何制作自己的数据集是至关重要的。如果你想检测新的目标(比如你的水杯、宠物猫),就需要自己动手。
图像采集:最直接的方式就是用Tello TT在空中悬停,对着你的目标物体从不同角度、不同距离、不同光照条件下拍摄视频。然后使用OpenCV或者视频编辑软件,将视频按一定间隔(如每秒2-5帧)抽帧,保存为图片。图片数量建议至少每类目标200-300张,越多越好,多样性越强,模型泛化能力越好。
图像标注:我们需要为每张图片中的目标物体画上边界框并打上标签。推荐使用LabelImg或Roboflow这类工具。
- LabelImg:开源免费,本地使用。用矩形框框出目标,并输入类别名(如“person”, “dog”)。
- Roboflow:在线平台,功能更强大,支持团队协作、数据增强、自动标注等。 标注完成后,工具会为每张图片生成一个同名的
.txt文件,其内容格式为:<class_id> <x_center> <y_center> <width> <height>。这里的坐标和宽高都是归一化到0-1的值,相对于整张图片的宽高。
组织数据集目录:YOLOv5要求特定的目录结构。通常如下所示:
custom_dataset/ ├── images/ │ ├── train/ # 存放训练集图片 │ └── val/ # 存放验证集图片 └── labels/ ├── train/ # 存放训练集标签txt文件 └── val/ # 存放验证集标签txt文件你需要按照一定比例(如8:2)将图片和对应的标签文件分别放入
train和val文件夹。创建数据集配置文件:在YOLOv5目录下,创建一个YAML文件(如
tello_dataset.yaml),用来告诉模型你的数据在哪里、有哪些类别。# tello_dataset.yaml path: ../custom_dataset # 数据集根目录 train: images/train # 训练集相对路径 val: images/val # 验证集相对路径 # 类别数量 nc: 2 # 类别名称列表 names: ['person', 'ball']
4.2 YOLOv5模型训练全流程
准备好数据集后,就可以开始训练了。训练的目的是让模型学会从图片中找出我们标注的目标。
选择预训练模型:YOLOv5提供了从
yolov5n.pt(最小最快)到yolov5x.pt(最大最准)的预训练模型。使用预训练模型进行迁移学习,可以大大加快训练速度并提升最终精度。对于Tello TT这种算力有限的场景,强烈建议从yolov5s.pt或yolov5n.pt开始。将下载好的.pt文件放在YOLOv5目录下。启动训练:在YOLOv5根目录下,运行训练命令。这是一个最基础的示例:
python train.py --img 640 --batch 16 --epochs 100 --data tello_dataset.yaml --weights yolov5s.pt --device 0--img 640: 输入图像的尺寸,保持640即可。--batch 16: 批处理大小,根据你的GPU内存调整。如果内存不足,就减小这个数(如8, 4)。CPU训练则用--batch 1。--epochs 100: 训练轮数,通常100-300轮足够。--data: 指定我们刚才创建的数据集配置文件路径。--weights: 指定预训练权重路径。--device 0: 指定使用第0块GPU。如果是CPU,则改为--device cpu。
监控训练过程:训练开始后,终端会打印每一轮(epoch)的损失(loss)和评估指标(如mAP@0.5)。同时,YOLOv5会在
runs/train/exp目录下生成一系列可视化结果:results.png: 损失函数和评估指标随训练轮数的变化曲线。这是判断模型是否在正常学习、是否过拟合的关键。val_batchX_labels.jpg:验证集上的预测示例,可以直观看到模型检测得对不对。- 训练结束后,最佳模型会保存在
runs/train/exp/weights/best.pt。
实操心得:训练初期,重点关注
train/box_loss,train/obj_loss,train/cls_loss这几个损失值是否在稳步下降。如果波动剧烈或长时间不降,可能是学习率太大、数据有问题或模型结构不适合。验证集的mAP值在后期趋于平稳时,就可以考虑停止训练了,继续训练可能收益不大。
5. 模型优化与轻量化部署策略
5.1 模型剪枝与量化
直接从YOLOv5官方训练得到的.pt模型,虽然比原版小,但在树莓派或Jetson Nano上实时运行可能仍有压力。为了在Tello TT的伴侣计算机(边缘设备)上获得更高的FPS,我们可以对模型进行优化。
模型剪枝:移除网络中冗余的、贡献度低的神经元或通道,从而减小模型体积和计算量。YOLOv5本身没有内置剪枝工具,但我们可以使用第三方库如
torch-pruning。基本流程是:加载训练好的模型,分析各层的重要性(例如通过计算通道的L1范数),剪掉重要性低的通道,然后对剪枝后的模型进行微调(fine-tune)以恢复精度。这个过程需要一定的专业知识,且可能引入精度损失。模型量化:将模型权重和激活值从高精度的浮点数(如FP32)转换为低精度的整数(如INT8)。这能显著减少模型大小和内存占用,并利用硬件(如GPU的Tensor Core,或CPU的INT8指令集)加速推理。PyTorch提供了方便的量化API。
import torch from torch.quantization import quantize_dynamic # 动态量化(对全连接层和LSTM效果较好,对卷积层支持有限) model_fp32 = torch.load('best.pt')['model'].float() model_int8 = quantize_dynamic(model_fp32, {torch.nn.Linear}, dtype=torch.qint8) torch.save(model_int8.state_dict(), 'best_int8.pt')对于YOLOv5这种卷积网络,更推荐使用训练后静态量化或量化感知训练,但这需要准备校准数据集,流程更复杂。一个更简单粗暴但有效的方法是使用ONNX Runtime或TensorRT进行部署,它们都提供了优秀的量化工具链,能将YOLOv5模型转换为高度优化的INT8引擎,在边缘设备上获得数倍的性能提升。
5.2 转换为ONNX并部署
ONNX是一种开放的模型格式,可以让模型在不同的框架(PyTorch, TensorFlow等)和不同的硬件推理引擎(ONNX Runtime, TensorRT, OpenVINO等)之间自由转换和运行,是部署的“中间桥梁”。
导出ONNX模型:YOLOv5提供了便捷的导出脚本。
python export.py --weights runs/train/exp/weights/best.pt --include onnx --img 640 --batch 1这会在
best.pt同目录下生成一个best.onnx文件。--batch 1指定了批处理大小为1,适合实时单帧推理。使用ONNX Runtime推理:安装ONNX Runtime库后,我们可以用极简的代码加载和运行ONNX模型,通常比原生PyTorch推理更快。
import onnxruntime as ort import numpy as np # 创建推理会话,指定使用CPU或GPU providers = ['CUDAExecutionProvider', 'CPUExecutionProvider'] if ort.get_device() == 'GPU' else ['CPUExecutionProvider'] session = ort.InferenceSession('best.onnx', providers=providers) # 准备输入数据(需要预处理图像) input_name = session.get_inputs()[0].name # img_np 是预处理后的numpy数组,形状为[1, 3, 640, 640] outputs = session.run(None, {input_name: img_np}) # outputs 包含了检测结果将原有的PyTorch推理代码替换为ONNX Runtime,通常能获得10%-30%的速度提升,且内存占用更稳定。
6. 核心代码模块深度解析
6.1 视频流解码与预处理管道
这是整个系统的入口,稳定高效地获取图像帧是后续所有处理的基础。Tello TT的视频流是H.264编码的,djitellopy的get_frame_read()方法返回的是一个包含原始字节流的对象。
import cv2 from djitellopy import Tello tello = Tello() tello.connect() tello.streamon() # 开启视频流 frame_reader = tello.get_frame_read() # 获取帧读取器 while True: # 获取当前帧 frame = frame_reader.frame if frame is None: continue # 图像预处理 # 1. 颜色空间转换:Tello返回的是BGR格式,YOLOv5需要RGB img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 2. 调整大小:缩放到模型输入尺寸,同时保持长宽比进行填充,避免变形 img_resized, ratio, pad = letterbox(img_rgb, new_shape=(640, 640), auto=False) # 3. 归一化:将像素值从0-255缩放到0-1 img_normalized = img_resized / 255.0 # 4. 转换维度:从HWC变为CHW,并增加批次维度 img_input = img_normalized.transpose(2, 0, 1) img_input = np.expand_dims(img_input, axis=0).astype(np.float32) # 此时,img_input就是可以送入模型推理的张量了这里的letterbox函数是YOLOv5中一个非常实用的函数,它能在保持图像原始比例的前提下进行缩放,并在上下或左右填充灰色像素,确保图像不变形。这比简单的cv2.resize直接拉伸效果要好得多,尤其对于长宽比差异大的目标。
6.2 目标检测与追踪器实现
拿到预处理好的图像张量后,我们调用模型进行推理,然后处理输出结果。
def detect_and_track(model, img_input, previous_center): """ 执行检测和追踪 Args: model: 加载好的YOLOv5模型(或ONNX会话) img_input: 预处理后的输入张量 previous_center: 上一帧目标中心点坐标 (x, y),归一化到0-1 Returns: current_center: 当前帧目标中心点 bbox: 当前帧目标边界框 (x1, y1, x2, y2),像素坐标 distance: 估算的距离 """ # 模型推理 if isinstance(model, ort.InferenceSession): # ONNX Runtime outputs = model.run(None, {model.get_inputs()[0].name: img_input}) pred = torch.tensor(outputs[0]) # 转换为torch tensor方便处理 else: # PyTorch with torch.no_grad(): pred = model(img_input) # 应用非极大值抑制(NMS),过滤掉重叠的、低置信度的框 pred = non_max_suppression(pred, conf_thres=0.5, iou_thres=0.45)[0] current_center = None bbox = None distance = None if pred is not None and len(pred) > 0: # 假设我们只追踪置信度最高的那个目标 det = pred[0] # 将归一化坐标转换回原图坐标 # 注意:需要逆操作letterbox的缩放和填充 det[:, :4] = scale_coords(img_input.shape[2:], det[:, :4], frame.shape).round() x1, y1, x2, y2 = det[0, :4].cpu().numpy().astype(int) bbox = (x1, y1, x2, y2) # 计算当前帧目标中心点(归一化) img_h, img_w = frame.shape[:2] cx_pixel = (x1 + x2) / 2 cy_pixel = (y1 + y2) / 2 current_center = (cx_pixel / img_w, cy_pixel / img_h) # 追踪逻辑:如果上一帧有目标,则关联;否则,直接使用当前检测结果 if previous_center is not None: # 计算与上一帧目标的距离 dist = np.sqrt((current_center[0]-previous_center[0])**2 + (current_center[1]-previous_center[1])**2) if dist > 0.2: # 距离阈值,超过则认为目标丢失或切换 print("目标可能丢失,重新捕获") # 这里可以触发重新搜索的逻辑 else: print("发现新目标,开始追踪") # 距离估算 (示例:假设目标实际高度为0.5米) object_real_height = 0.5 # 单位:米 # 相机焦距(像素单位),这是一个需要校准的参数!此处为示例值。 focal_length_px = 600 pixel_height = y2 - y1 if pixel_height > 0: distance = (object_real_height * focal_length_px) / pixel_height return current_center, bbox, distance这个函数集成了检测、追踪和测距的核心逻辑。non_max_suppression是目标检测后处理的关键步骤,用于剔除重复框。追踪器这里用了最简单的“最近邻”和“最高置信度”策略,在实际项目中,你可能需要实现一个更鲁棒的追踪器来应对遮挡和快速运动。
6.3 控制指令生成与PID调节
得到目标在当前帧的位置后,我们需要将其转换为无人机的控制指令。一个简单的比例控制器(P控制器)可能就够用,但引入积分和微分(PID控制器)能让无人机的运动更加平滑。
class PIDController: def __init__(self, kp, ki, kd, max_output=100): self.kp = kp self.ki = ki self.kd = kd self.max_output = max_output self.integral = 0 self.previous_error = 0 def compute(self, error, dt): """计算控制输出""" self.integral += error * dt derivative = (error - self.previous_error) / dt if dt > 0 else 0 output = self.kp * error + self.ki * self.integral + self.kd * derivative # 限制输出范围 output = max(-self.max_output, min(self.max_output, output)) self.previous_error = error return int(output) # 初始化PID控制器,分别控制左右(横滚)和前后(俯仰)通道 pid_roll = PIDController(kp=0.5, ki=0.01, kd=0.05) # 控制左右平移 pid_pitch = PIDController(kp=0.5, ki=0.01, kd=0.05) # 控制前后平移 def generate_control_command(target_center_norm, img_center_norm=(0.5, 0.5), distance=None): """ 根据目标位置生成控制指令 Args: target_center_norm: 目标中心点(归一化) img_center_norm: 图像中心点(归一化),默认为(0.5, 0.5) distance: 估算的距离 Returns: rc_commands: 发送给Tello的rc控制命令 [左右, 前后, 上下, 旋转] """ if target_center_norm is None: return [0, 0, 0, 0] # 无目标,停止运动 error_x = target_center_norm[0] - img_center_norm[0] # 水平误差 error_y = target_center_norm[1] - img_center_norm[1] # 垂直误差 # 设置死区,避免微小抖动导致无人机不停晃动 deadzone = 0.05 if abs(error_x) < deadzone: error_x = 0 if abs(error_y) < deadzone: error_y = 0 # 使用PID计算控制量 dt = 1/30 # 假设帧率为30fps roll_control = -pid_roll.compute(error_x, dt) # 注意符号:目标偏右(error_x>0),需要向左平移(roll负值) pitch_control = pid_pitch.compute(error_y, dt) # 目标偏下(error_y>0),需要向前平移(pitch正值) # 根据距离调整上下(油门)控制 throttle_control = 0 if distance is not None: desired_distance = 2.0 # 期望保持2米距离 error_d = distance - desired_distance # 一个简单的P控制器控制高度 throttle_control = int(-0.3 * error_d) # 距离太近就下降,太远就上升 throttle_control = max(-50, min(50, throttle_control)) # 限制幅度 # Tello的rc控制命令范围是-100到100 rc_commands = [roll_control, pitch_control, throttle_control, 0] # 旋转控制设为0 return rc_commands最后,在主循环中,将计算出的rc_commands通过tello.send_rc_control(*rc_commands)发送给无人机即可。PID参数的调节(kp,ki,kd)需要根据实际飞行效果进行,这是一个“调参”的过程,没有固定最优值,需要耐心尝试。
7. 实战调试与常见问题排查
在实际飞行测试中,你肯定会遇到各种各样的问题。下面是我在项目中踩过的一些“坑”以及解决方法。
7.1 视频流卡顿与延迟高
- 问题现象:画面更新慢,控制指令延迟严重,导致追踪总是慢半拍,无人机动作剧烈。
- 可能原因与排查:
- Wi-Fi信号不稳定:Tello TT与电脑之间的Wi-Fi连接质量是关键。确保两者距离不要太远,中间没有太多遮挡。可以尝试用
ping 192.168.10.1测试延迟和丢包率。 - 图像处理耗时过长:在低性能电脑上,YOLOv5的推理可能很慢。使用
time模块对解码、推理、后处理等各阶段计时,找到瓶颈。 - 视频流分辨率过高:
djitellopy默认获取的是720p的流。如果不需要高清画面,可以在连接后尝试降低分辨率(如果SDK支持),或者获取流后在代码中立即缩放到更小的尺寸(如320x240)再进行检测,能极大提升速度。
- Wi-Fi信号不稳定:Tello TT与电脑之间的Wi-Fi连接质量是关键。确保两者距离不要太远,中间没有太多遮挡。可以尝试用
- 解决方案:
- 优先保证Wi-Fi环境良好。
- 使用更小的YOLOv5模型(如
yolov5n)。 - 将模型转换为ONNX并使用ONNX Runtime推理,甚至尝试TensorRT或OpenVINO进一步加速。
- 降低处理帧率,不一定每帧都处理,可以每2帧或3帧处理一次。
- 在图像送入模型前,先进行下采样。
7.2 目标丢失与追踪不稳定
- 问题现象:目标快速移动或被短暂遮挡后,无人机就跟丢了,或者错误地追踪到背景物体。
- 可能原因与排查:
- 检测置信度阈值过高或过低:
conf_thres太高,会漏检;太低,会引入大量误检,干扰追踪器。 - 追踪算法太简单:如前所述,简单的最近邻法在复杂场景下容易失败。
- 无人机运动导致画面模糊:无人机自身剧烈运动会导致图像模糊,影响检测精度。
- 检测置信度阈值过高或过低:
- 解决方案:
- 调整
conf_thres和iou_thres,在验证集上找到一个平衡点。 - 实现或集成更鲁棒的追踪算法,如ByteTrack或DeepSORT。ByteTrack特别擅长处理低置信度检测框,能有效减少目标丢失。
- 在控制指令中加入平滑滤波,让无人机运动更柔和,减少画面抖动。例如,对计算出的
rc_commands进行移动平均滤波。 - 引入“搜索模式”:当目标丢失超过一定帧数后,让无人机缓慢旋转或上升,扩大视野重新搜索目标。
- 调整
7.3 距离估算不准
- 问题现象:测距结果波动大,或者与真实距离偏差明显。
- 可能原因与排查:
- 相机焦距参数不准:这是最主要的原因。公式中的
focal_length_px是一个估计值。 - 目标实际尺寸不准确:公式中的
object_real_height需要尽可能精确。 - 目标边界框检测不稳定:框的高度
pixel_height每一帧都在变化,导致距离跳动。 - 镜头畸变:广角镜头边缘的畸变会影响像素测量的准确性。
- 相机焦距参数不准:这是最主要的原因。公式中的
- 解决方案:
- 相机标定:使用棋盘格对Tello TT的摄像头进行标定,获取精确的内参矩阵(包含焦距
fx,fy)和畸变系数。这是一个标准步骤,OpenCV有现成的calibrateCamera函数。 - 使用已知尺寸的标定物:在固定距离下,测量标定物在图像中的像素高度,反推出焦距。
- 对测距结果进行滤波:使用卡尔曼滤波或简单的移动平均,平滑连续帧的距离估计值,减少抖动。
- 认识到单目测距的局限性,它更适合相对距离的判断(如“变近了”或“变远了”),而非绝对精确的测距。
- 相机标定:使用棋盘格对Tello TT的摄像头进行标定,获取精确的内参矩阵(包含焦距
7.4 无人机控制响应异常
- 问题现象:发送了指令但无人机没反应,或者反应与预期相反。
- 可能原因与排查:
- RC指令范围错误:Tello的
send_rc_control四个参数(左右、前后、上下、旋转)的取值范围是**-100到100**。超出这个范围可能被忽略。 - 指令发送频率过高或过低:Tello的SDK对指令发送频率有要求。通常需要以至少10Hz的频率持续发送指令,否则无人机会进入“安全模式”而停止。但发送过快也可能导致缓冲区溢出。
- PID参数不当:
kp太大导致震荡,ki太大导致积分饱和,kd太大对噪声敏感。
- RC指令范围错误:Tello的
- 解决方案:
- 确保生成的
rc_commands每个值都在[-100, 100]区间内。 - 在主循环中,确保以稳定的频率(如20-30Hz)调用
tello.send_rc_control。即使没有移动指令,也要发送[0,0,0,0]。 - 从较小的PID参数开始调试。先调
kp,让无人机能响应但不过冲;然后加入小的kd来抑制超调;最后再考虑加入很小的ki来消除静态误差。这是一个需要耐心和观察的过程。
- 确保生成的
这个项目从构思到实现,再到稳定运行,是一个典型的软硬件结合的系统工程。它涉及计算机视觉、嵌入式系统、控制理论和软件工程多个领域。最大的体会是,仿真环境再完美,也比不上一次真实的飞行测试。在实飞中暴露的问题(延迟、抖动、光线变化、风扰)是在电脑前很难完全模拟的。因此,迭代开发、快速试错、耐心调试是成功的关键。建议先从室内无风环境、追踪静止或缓慢移动的目标开始,逐步增加难度。希望这个详细拆解能帮你少走弯路,顺利打造出属于自己的智能追踪无人机。
本文还有配套的精品资源,点击获取