从零构建YOLOv5+RealSense深度感知模型:实战数据集制作与测距应用
1. 环境准备与工具安装
第一次接触YOLOv5和RealSense的组合时,我花了两天时间才把环境搭好。现在回想起来,其实只要按步骤来,半小时就能搞定。首先需要准备一台性能还行的电脑,建议至少GTX 1660以上的显卡,因为训练模型时CUDA加速真的很重要。
安装Python环境时,我强烈推荐使用Anaconda创建虚拟环境。这样可以避免各种依赖冲突。我常用的命令是:
conda create -n yolov5 python=3.8 conda activate yolov5接下来安装PyTorch,这里有个小技巧:先去PyTorch官网用他们的配置生成器获取安装命令。我实测过,直接用pip安装的版本有时CUDA支持会有问题。安装完记得验证一下CUDA是否可用:
import torch print(torch.cuda.is_available()) # 应该返回TrueRealSense SDK的安装稍微麻烦些。在Windows上可以直接用Intel提供的安装包,Linux下则需要从源码编译。我遇到过最坑的问题是USB3.0接口识别,如果相机老是断开连接,试试换根质量好的USB线。
2. 数据采集实战技巧
用RealSense采集数据时,很多人会忽略深度信息的校准。我发现最佳做法是先让相机预热5分钟,等深度传感器稳定后再开始采集。采集环境的光线也很关键,强光直射会导致深度图出现大量噪点。
这段Python代码可以同时保存彩色图和深度图:
import pyrealsense2 as rs import numpy as np import cv2 pipeline = rs.pipeline() config = rs.config() config.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 30) config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30) pipeline.start(config) try: for i in range(100): # 采集100张 frames = pipeline.wait_for_frames() color_frame = frames.get_color_frame() depth_frame = frames.get_depth_frame() if not color_frame or not depth_frame: continue color_image = np.asanyarray(color_frame.get_data()) depth_image = np.asanyarray(depth_frame.get_data()) cv2.imwrite(f'color_{i}.jpg', color_image) np.save(f'depth_{i}.npy', depth_image) # 深度图建议保存为npy格式 finally: pipeline.stop()采集时要注意目标物体的多样性,包括不同角度、距离和光照条件。我建议每个类别至少采集200张以上,太少会导致模型泛化能力差。
3. 数据标注的坑与技巧
LabelImg虽然简单易用,但在处理大量数据时效率太低。我后来改用CVAT(Computer Vision Annotation Tool),它支持多人协作标注,还能直接导出YOLO格式。不过新手还是建议先用LabelImg熟悉标注流程。
标注时有几个容易出错的地方:
- 边界框不要贴得太紧,留2-3个像素的余量
- 遮挡超过15%的物体要标记truncated属性
- 相似物体在不同尺度下要分别标注
标注完成后,记得检查XML文件中的尺寸是否一致。我遇到过因为标注时缩放窗口导致坐标错误的情况,可以用这个脚本批量检查:
import xml.etree.ElementTree as ET import os for xml_file in os.listdir('annotations'): tree = ET.parse(f'annotations/{xml_file}') root = tree.getroot() size = root.find('size') width = int(size.find('width').text) height = int(size.find('height').text) assert width == 640 and height == 480, f"{xml_file}尺寸错误"4. 模型训练与调优
准备好数据后,创建YOLOv5需要的yaml配置文件。这里有个细节很多人忽略:类别名称的顺序很重要,必须和标注时保持一致。我的mydata.yaml通常长这样:
train: ../mytrain/images/train/ val: ../mytrain/images/val/ nc: 3 # 类别数 names: ['person', 'car', 'bottle'] # 按字母顺序排列更方便管理开始训练前,建议先用小批量数据测试流程是否通畅:
python train.py --img 640 --batch 16 --epochs 3 --data mydata.yaml --weights yolov5s.pt训练过程中要重点关注这几个指标:
- mAP@0.5:高于0.8说明模型不错
- Precision和Recall的平衡
- 损失函数的下降曲线
如果出现过拟合,可以尝试:
- 增加数据增强参数
- 减小模型尺寸(换成yolov5n)
- 添加Dropout层
5. 深度信息融合实战
将YOLOv5的检测结果与RealSense的深度图结合时,坐标转换是关键。RealSense返回的深度值是毫米为单位的,需要转换为米:
depth_value = depth_frame.get_distance(x, y) # 获取的是米为单位为了提高测距精度,我开发了一个多点采样取中值的算法:
def get_robust_distance(depth_frame, bbox, sample_points=20): """通过多采样提高深度测量精度""" x_center = (bbox[0] + bbox[2]) // 2 y_center = (bbox[1] + bbox[3]) // 2 width = bbox[2] - bbox[0] distances = [] for _ in range(sample_points): # 在目标区域内随机采样 x = random.randint(int(x_center - width/4), int(x_center + width/4)) y = random.randint(int(y_center - width/4), int(y_center + width/4)) dist = depth_frame.get_distance(x, y) if 0 < dist < 10: # 过滤异常值 distances.append(dist) if not distances: return None # 取中间80%的数据的平均值 distances = sorted(distances) trim = int(len(distances) * 0.1) return sum(distances[trim:-trim]) / len(distances[trim:-trim])6. 性能优化技巧
在实际部署时,我发现有几点可以显著提升性能:
- 异步处理:将图像采集、目标检测和深度计算放在不同线程
from threading import Thread import queue image_queue = queue.Queue(maxsize=1) result_queue = queue.Queue(maxsize=1) def capture_thread(): while True: frames = pipeline.wait_for_frames() color_frame = frames.get_color_frame() image_queue.put(np.asanyarray(color_frame.get_data())) def detection_thread(): while True: img = image_queue.get() # 执行检测... result_queue.put(results) Thread(target=capture_thread, daemon=True).start() Thread(target=detection_thread, daemon=True).start()- 模型量化:将模型转为FP16或INT8格式,速度可提升2-3倍
python export.py --weights best.pt --include onnx --half- ROI处理:只对检测到的目标区域计算深度,减少计算量
7. 常见问题解决方案
在项目开发过程中,我踩过不少坑,这里分享几个典型问题的解决方法:
问题1:RealSense深度图与彩色图对齐不准
- 解决方法:使用align_to_color对齐
align = rs.align(rs.stream.color) frames = align.process(frames)问题2:YOLOv5检测框抖动严重
- 解决方法:添加简单的跟踪算法
from collections import deque class SimpleTracker: def __init__(self, max_len=5): self.history = deque(maxlen=max_len) def update(self, bbox): self.history.append(bbox) return np.mean(self.history, axis=0)问题3:远距离测距不准
- 解决方法:根据距离动态调整采样区域大小
def get_dynamic_sample_size(distance): base_size = 0.2 # 20%的bbox大小 scale = min(max(distance / 5.0, 0.5), 2.0) # 限制在0.5-2倍之间 return base_size * scale最后要提醒的是,实时显示时用OpenCV的imshow性能很差,可以考虑用PyQt或者Web前端来展示结果。我在一个项目中将检测结果通过WebSocket发送到网页端显示,帧率提升了近3倍。
