ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

YOLOv5+RealSense D435i目标三维定位实战指南

YOLOv5+RealSense D435i目标三维定位实战指南 简介基于RealSense D435i相机并结合PyTorch实现的YOLOv5目标检测工程包可在实时检测目标类别的同时输出其在相机坐标系下的三维位置信息特别适合机器人抓取、无人机避障、AR叠加等场景。压缩包共收录73个文件以27个.py源码和25个.yaml配置为主同时附带PyTorch权重、Dockerfile、运行说明、模型结构定义及多种平台发布脚本整体大小仅16.33MB组织紧凑。项目支持通过修改模型配置轻松切换yolov5s等预训练模型或替换为自己的权重代码兼顾CPU与GPU运行已在Win10PyTorch1.10.2CUDA11.3和Ubuntu16.04PyTorch1.7.1CPU下验证通过。同时也提供了REST API示例与AWS上传脚本便于将检测功能封装成服务或部署到云端。当前已有122人学习非常适合正在研究视觉定位与目标检测相结合的开发者参考。1. 用YOLOv5框住目标只是拿到定位结果的起点很多人第一次把YOLOv5跑在RealSense D435i上会遇到一个看起来莫名其妙的问题彩色图上框得准准的目标算出来的三维坐标却跟实际距离差十几厘米。问题不在YOLOv5的检测精度而在于D435i的深度图和彩色图各自拥有一套坐标系两个传感器在物理位置上并不重合。这个标题要解决的是把二维检测框转成三维空间坐标的完整链路YOLOv5负责从彩色图里识别目标的类别和像素位置D435i的深度流负责补充每个像素的纵深最后用相机内参做一次像素坐标到相机坐标的换算。整个方案不需要自己训练模型用预训练权重就能跑通适合机器人抓取、导航避障、AR叠加这类需要目标空间位置的应用场景。2. D435i相机标定与坐标系换算定位结果的地基2.1 D435i输出的数据流哪些值得关注D435i在运行时同时输出彩色图、深度图和红外图。深度图的格式是z16每个像素用16位整数表示该点到相机平面的距离单位是毫米。这里有个关键认知深度值并不总是有效。目标太近或太远、表面强反光、纯黑色吸光物体都会让某个像素的深度值变成0。检测框中心刚好落在这种区域时直接用0值参与坐标换算结果必然是错的所以后面的代码需要先判断深度值是否为0再决定是否跳过这个目标。D435i的深度参数对定位结果影响很大几个重要参数先在表格里列清楚参数项典型设置对目标定位的影响深度分辨率640x480分辨率越高远距离测距越稳但CPU占用越高彩色分辨率640x480与深度分辨率一致省去缩放步骤帧率30 FPS目标快速移动时保持30 FPS避免动态误差深度值单位z16毫米换算成米时统一除以1000有效量程约0.2m~10m超出量程的深度值返回0须过滤分辨率选择上D435i最大支持1280x720的深度输出但640x480在机械臂抓取、移动机器人这类场景里已经够用。目标距离通常在0.5m到2m之间这个范围内640x480的深度分辨率足以分辨几个厘米级的空间差异。处理1280x720的深度图会让对齐运算消耗更多CPU时间而定位精度的提升非常有限所以大多数实际项目都默认选640x480。2.2 像素坐标到相机坐标的换算公式要从检测框得到空间位置核心是针孔相机模型。相机内参中有四个关键值fx、fy、cx、cy分别代表焦距和光心坐标。给定像素坐标(u, v)和该点的深度值z毫米目标在相机坐标系下的坐标为x (u - cx) * z / fx y (v - cy) * z / fyz本身不需要再转换如果x和y也要用毫米结果就是毫米如果习惯用米把x、y、z一起除以1000即可。内参的获取非常直接RealSense SDK把内参挂在帧的profile上import pyrealsense2 as rs # 从彩色帧获取内参 intrin color_frame.profile.as_video_stream_profile().intrinsics # fx和fy是焦距像素单位ppx和ppy是光心坐标 print(intrin.fx, intrin.fy, intrin.ppx, intrin.ppy)intrin.fx和intrin.fy是焦距intrin.ppx和intrin.ppy是光心坐标。需要特别提醒一点如果深度图已经对齐到彩色坐标系内参必须取彩色相机的如果直接使用未对齐的深度图则要取深度相机的内参。两者混用是定位偏差最常见的来源。还有一个容易踩的坑SDK提供了depth_frame.get_distance(u, v)方法返回的是以米为单位的深度值而通过numpy索引depth数组得到的是毫米值。两种写法都行但混用会导致结果相差1000倍。建议统一用numpy数组索引的方式因为后面的深度滤波、预处理都要基于numpy操作。2.3 用rs.align把深度图对齐到彩色图D435i的深度传感器与彩色传感器在物理位置上不重合左右偏差大约几厘米这导致同一个目标在两路图像上的像素位置不同距离越近偏差越明显。直接从彩色图的检测框坐标去深度图上取值算出的坐标会偏。常见做法是用rs.align创建一个对齐器每次取帧后把深度帧对齐到彩色坐标系import pyrealsense2 as rs 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) # 深度对齐到彩色坐标系align对象只需要创建一次 align rs.align(rs.stream.color) frames pipeline.wait_for_frames() aligned_frames align.process(frames) color_frame aligned_frames.get_color_frame() depth_frame aligned_frames.get_depth_frame()对齐完成后depth_frame的像素坐标系与color_frame完全一致检测框坐标就可以直接用于深度数组的索引。对齐操作有计算开销但640x480分辨率下性能影响不大。如果CPU占用仍然过高可以先把彩色和深度分辨率统一降到640x480还不够就降帧率到15 FPS。3. 用PyTorch跑通YOLOv5检测的最小环境坐标系和深度数据处理清楚了接下来把目标检测模型在当前机器上跑起来。这章的环境配置内容不多但每一步都可能卡住值得按顺序过一遍。3.1 创建Python环境并安装PyTorch目标检测部分依赖PyTorch框架先准备一个干净的环境。我习惯用conda创建独立环境避免多个项目的依赖互相干扰。Python版本选3.9与PyTorch的适配范围最宽conda create -n yolo_d435i python3.9 -y conda activate yolo_d435i pip install torch torchvisionPyTorch的安装命令取决于本机是否有NVIDIA GPU以及CUDA版本。有显卡的机器推荐去pytorch官网用对应CUDA版本的命令安装无GPU的环境直接装CPU版推理YOLOv5s在640x480输入下约5到10 FPS调试验证完全够用pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu安装后可以快速验证PyTorch是否可用import torch print(torch.__version__) print(torch.cuda.is_available())无GPU时torch.cuda.is_available()返回False是正常的YOLOv5会自动回退到CPU推理不需要额外配置。这里唯一要注意的是PyTorch版本与Python版本的配套关系Python 3.9对应PyTorch 2.x都没问题如果conda创建环境时选了Python 3.12部分PyTorch版本会找不到对应wheel。3.2 安装pyrealsense2与YOLOv5依赖D435i的Python SDK通过pip安装包名是pyrealsense2pip install pyrealsense2安装后建议先确认相机能被识别避免后面把时间花在排查硬件连接上。YOLOv5的依赖通过torch.hub加载时不需要手动安装整个仓库但缺的依赖包还是要补齐。常规做法是clone官方仓库并安装requirements.txtgit clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt如果网络条件不佳torch.hub在第一次加载时下载超时可以单独下载yolov5s.pt权重文件放到项目目录再用本地路径加载。更省事的方式是直接把官方仓库clone下来后先用它的detect.py跑通推理确认环境没问题再接自己的相机数据。依赖汇总如下依赖项安装方式作用pytorchpip install torch torchvisionYOLOv5推理框架pyrealsense2pip install pyrealsense2D435i数据读取与对齐yolov5torch.hub加载或git clone目标检测模型与预处理工具3.3 加载YOLOv5模型并调整推理参数模型加载用torch.hub一行就够。yolov5s是YOLOv5网络结构中参数量最小的变体约700万参数精度和速度的平衡适合实时定位场景import torch model torch.hub.load(ultralytics/yolov5, yolov5s, pretrainedTrue) model.conf 0.5 # 置信度阈值 model.iou 0.45 # NMS的IoU阈值model.conf和model.iou是推理阶段最常用的两个参数。conf设太低会出现大量误检框后续算出的三维坐标没有使用价值设太高又会漏掉低置信度的真实目标。如果不确定该选什么值可以先跑一段视频统计检测结果的置信度分布再决定。从YOLOv5输出的results对象可以直接拿到检测框坐标、类别和置信度df results.pandas().xyxy[0] # 字段: xmin, ymin, xmax, ymax, confidence, class, name在D435i定位场景中后续只需要这些字段里的坐标和类别名pandas格式方便逐行处理不需要再手动解析模型输出。4. 把YOLOv5检测框和D435i深度合成为目标坐标检测环境和模型就绪接下来把两个部分串成一条完整的处理链路。这一章的代码可以直接运行也可以作为后续二次开发的基础框架。4.1 端到端主循环代码核心逻辑分三步读取并对齐帧、YOLOv5推理、逐框换算坐标。import cv2 import torch import numpy as np import pyrealsense2 as rs # 初始化D435i 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) align rs.align(rs.stream.color) # 加载YOLOv5 model torch.hub.load(ultralytics/yolov5, yolov5s, pretrainedTrue) model.conf 0.5 model.iou 0.45 try: while True: # 1. 读取并对齐一帧 frames pipeline.wait_for_frames() aligned_frames align.process(frames) color_frame aligned_frames.get_color_frame() depth_frame aligned_frames.get_depth_frame() if not color_frame or not depth_frame: continue img np.asanyarray(color_frame.get_data()) depth np.asanyarray(depth_frame.get_data()) # 彩色相机内参对齐后必须用它 intrin color_frame.profile.as_video_stream_profile().intrinsics # 2. YOLOv5推理 results model(img) dets results.pandas().xyxy[0] for _, det in dets.iterrows(): x1 int(det[xmin]) y1 int(det[ymin]) x2 int(det[xmax]) y2 int(det[ymax]) label det[name] # 检测框中心像素坐标 uc (x1 x2) // 2 vc (y1 y2) // 2 # 3. 取深度并换算坐标 z_mm depth[vc, uc] if z_mm 0: continue x_m (uc - intrin.ppx) * z_mm / (intrin.fx * 1000) y_m (vc - intrin.ppy) * z_mm / (intrin.fy * 1000) z_m z_mm / 1000 print(f{label}: ({x_m:.3f}, {y_m:.3f}, {z_m:.3f}) m) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, f{label} {z_m:.2f}m, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow(YOLOv5 D435i, img) if cv2.waitKey(1) 0xFF ord(q): break finally: pipeline.stop() cv2.destroyAllWindows()这段代码覆盖了完整的定位流程。变量uc和vc是检测框中心的像素坐标depth[vc, uc]取出该点深度值随后用内参完成像素坐标向相机坐标的转换。打印出来的三维坐标已经统一为米命令行输出的格式适合先拿真实场景验证数值合理性。严格说这个流程不算三维目标检测它是在二维检测的基础上融合深度信息属于目标检测与深度估计的组合。如果需要输出物体的完整三维包围盒或姿态还要额外引入点云处理和位姿估计那是另一条技术路线。4.2 关键参数调整与异常情况处理主循环里有几个参数直接决定输出质量列一个表方便对照调整参数推荐值调整依据model.conf0.4~0.5目标小或遮挡多时降低误检多时调高model.iou0.45重叠框多时降低到0.4以下深度值判断0为无效直接索引depth数组时用0判断get_distance返回0.0检测框中心点边框几何中心目标被遮挡时改用可见部分的质心多目标互相遮挡时检测框中心可能落在前景目标边缘的深度突变处。遇到这种情况可以改用检测框下半部分的中心点或者去掉框顶部包含背景的部分再做质心估计。实际项目中如果预训练权重没有覆盖你的目标类别检测框质量不够好坐标计算自然受影响这时就需要用yolov5训练自己的数据集单独训练再替换权重文件。深度值为0的情况需要过滤但过滤后目标坐标缺失又会破坏连续跟踪。工程上常用的处理办法是在检测框中心周围取一个5x5或9x9的窗口对窗口内非零深度值排序后取中值避免单个坏点导致整帧坐标跳变。这个技巧在下一章单独展开。还有一个容易被忽略的参数intrin的获取位置。只要对齐目标是rs.stream.color就必须在color_frame的profile上取内参。如果错拿到深度相机的内参坐标换算的结果会有系统性偏差而且距离越远偏差越明显。4.3 坐标换算的单元验证把坐标换算从主循环里拆出来单独写成一个函数方便在离线数据上逐个验证def pixel_to_camera(uc, vc, depth, intrin): 将像素坐标和深度值换算为相机坐标返回米 z_mm depth[vc, uc] if z_mm 0: return None x_m (uc - intrin.ppx) * z_mm / (intrin.fx * 1000) y_m (vc - intrin.ppy) * z_mm / (intrin.fy * 1000) z_m z_mm / 1000 return x_m, y_m, z_m这个函数要求传入的depth是已经对齐到彩色坐标系的深度图intrin是彩色内参。封装的好处是可以脱离相机和模型单独测试坐标数学是否正确也可以在对齐、内参、单位这三个环节之间快速定位问题。5. 定位精度验证与三个实战处理技巧5.1 用卷尺做一次直接的距离验证代码跑通之后先别急着接机械臂或导航。把相机固定在桌边在正前方0.5米、1米、1.5米处各放一个箱子让检测框框住箱子中心对比打印出来的z值与卷尺实测值。偏差小于5%说明整个链路是通的偏差稳定在固定值优先检查内参是否用错偏差随距离变化检查深度图对齐方向是否搞反。这一步能过滤掉大部分低级错误。5.2 中心点邻域深度取中值抗坏点干扰直接取检测框中心的深度值最怕中心落在深度空洞或反光点上。常见做法是取中心周围邻域的非零深度值做中值滤波# 以检测框中心为原点取5x5区域注意边界裁剪 h, w depth.shape y0, y1 max(vc - 2, 0), min(vc 3, h) x0, x1 max(uc - 2, 0), min(uc 3, w) patch depth[y0:y1, x0:x1] valid patch[patch 0] if valid.size 0: continue # 该区域全部无效跳过 z_mm int(np.median(valid))中值比均值更抗离群值深度图上的孤立坏点不会把结果拉偏。如果检测目标在图像里占的像素很少邻域窗口不能开太大否则会把背景深度带进来。这类小目标检测场景下建议先观察检测框尺寸再决定窗口大小。5.3 将采集与推理拆到不同线程实时定位对帧率敏感。D435i以30 FPS输出数据如果YOLOv5推理一帧耗时超过33毫秒单线程主循环就会开始丢帧。常见做法是把相机采集放在一个线程深度对齐和推理放在另一个线程用队列传递帧数据。这种模式下定位输出频率由推理耗时决定但不会因为采集阻塞而积累延迟。先把单线程版本跑通确认每一帧的三维坐标稳定后再把采集与推理放到独立线程里去提帧率。本文还有配套的精品资源点击获取
返回列表