简介这份资源面向计算机视觉初学者与需要落地深度感知的开发者围绕YOLOv5目标检测与RealSense深度相机测距的联合实现展开解决“识别目标同时获取其空间距离”的典型需求可用于机器人避障、智能监控、抓取定位等场景。压缩包共46个文件约3.53MB以18个Python脚本为核心配合8个yaml与4个yml模型配置、4个md说明文档、2个sh权重下载脚本及Dockerfile等部署文件另含1个ipynb教程笔记与示例图片覆盖模型配置、推理检测、深度对齐与工程化运行等环节。资源中提供了realsensedetect.py等关键脚本读者可据此理解如何将YOLOv5的检测框与深度图对齐从而在识别类别的同时输出目标距离并借助配置文件切换不同规模的模型。目前已有429人学习下载适合希望快速跑通检测加测距流程、并参考目录组织与依赖配置的读者。1. 从 RGB 到点云realsense 深度相机配 yolov5 到底在解决什么很多做目标检测的兄弟都遇到过这个尴尬模型框得挺准但框里那个东西离相机到底是 0.5 米还是 3 米全靠猜。普通 USB 摄像头只能给你二维像素坐标深度信息是彻底缺失的。这份资源干的事很直接——把 Intel RealSense 深度相机和 YOLOv5 焊在一起在跑目标检测的同时用深度图把每个检测框对应的物理距离算出来。它适合做机器人抓取、自动避障、体积测量、人员距离监控这类需要“知道目标在哪、更要知道目标多远”的场景。资源包里已经带了realsensedetect.py这个核心脚本还有完整的 YOLOv5 工程目录和权重下载脚本不是那种只丢一个模型文件的半成品。下面我按实际拆包和跑通的顺序把选型理由、环境配置、代码逻辑和踩过的坑一条条讲清楚。2. 环境搭建与 RealSense 驱动从零把相机和推理环境跑起来2.1 为什么选 RealSense D435i 而不是普通双目或结构光RealSense D435i 属于主动红外立体视觉方案左红外相机、右红外相机加一个红外点阵投射器。它的深度计算不依赖环境可见光纹理在室内弱光或者白墙面前也能出深度图这点比纯双目方案稳得多。D435i 比 D435 多了一个 IMU如果你后面要做视觉惯性里程计或者手持扫描这个 IMU 能省掉外挂一个模块的麻烦。深度范围官方标称 0.3 米到 3 米左右比较可靠再远精度掉得厉害。视场角方面深度相机约 87°×58°RGB 相机约 69°×42°两者视野不完全重合所以做像素对齐的时候不能直接拿 RGB 的内参去套深度图必须走rs2_align对齐流程。选它的核心理由是 SDK 成熟、Python 绑定完善、和 OpenCV 的 Mat 转换有现成例子不像某些国产深度相机得自己啃私有协议。2.2 驱动安装与 pyrealsense2 的版本匹配RealSense 在 Linux 下的驱动安装有个经典坑内核版本和librealsense2的版本对不上导致rs-enumerate-devices能识别设备但取流就崩。我一般先确认内核uname -r # 常见做法是 5.15 或 5.19 这类 LTS 内核兼容性最好然后装 SDK。Ubuntu 下最稳的是走 apt 源不要直接下源码编译除非你要改底层sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-key F6E65AC044F831AC80A06380C8B3A55A6F3EFCDE sudo add-apt-repository deb https://librealsense.intel.com/Debian/apt-repo $(lsb_release -cs) main -u sudo apt-get install librealsense2-dkms librealsense2-utils librealsense2-dev装完插上相机跑realsense-viewer能看到深度流和 RGB 流同时出图说明驱动层没问题。Python 侧用 pip 装pyrealsense2注意版本要和 SDK 大版本对齐比如 SDK 是 2.54.xpyrealsense2 也尽量用 2.54.x混用容易出现RuntimeError: No device connected这种玄学报错。pip install pyrealsense22.54.2.5687 pip install opencv-python numpy torch torchvisionYOLOv5 的依赖在资源包的requirements.txt里直接pip install -r requirements.txt即可。权重文件不用手动找包里有个download_weights.sh跑一下会把yolov5s.pt拉到weights/目录。如果你要用自己训练的模型把.pt文件丢进weights/然后在脚本里改路径就行。2.3 验证相机数据流与内参读取在写检测脚本之前先单独验证一下深度流和 RGB 流能不能同时拿到并且把内参打印出来。这一步能帮你排除掉后面 80% 的“框对了但距离不对”的问题。import pyrealsense2 as rs import numpy as np pipeline rs.pipeline() config rs.config() config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30) config.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 30) profile pipeline.start(config) # 读取深度相机内参 depth_sensor profile.get_device().first_depth_sensor() depth_scale depth_sensor.get_depth_scale() print(fDepth scale: {depth_scale}) # 一般是 0.001即深度图单位是毫米 # 读取 RGB 内参 color_profile profile.get_stream(rs.stream.color) color_intrinsics color_profile.as_video_stream_profile().get_intrinsics() print(fColor intrinsics: fx{color_intrinsics.fx}, fy{color_intrinsics.fy}, fppx{color_intrinsics.ppx}, ppy{color_intrinsics.ppy}) pipeline.stop()depth_scale是关键参数它告诉你深度图里一个单位代表多少米。D435i 默认是 0.001也就是深度图里读到 1000 就是 1 米。fx/fy是焦距ppx/ppy是主点坐标后面做像素到三维点反投影的时候要用。如果这里读出来的内参和官方标称值差很多说明相机没标定好建议先跑一遍rs-enumerate-devices -c看出厂标定是否完整。3. 检测与测距的代码逻辑realsensedetect.py 里到底怎么算距离3.1 深度图与 RGB 图对齐为什么必须先 alignRealSense 的深度相机和 RGB 相机物理位置不同同一个物体在两张图上的像素坐标有视差。如果你直接拿 RGB 图上检测框的中心点去深度图上取深度值取到的很可能是背景的深度距离就会偏大。正确做法是用rs.align把深度图对齐到 RGB 相机视角这样两张图的像素坐标就一一对应了。align_to rs.stream.color align rs.align(align_to) frames pipeline.wait_for_frames() aligned_frames align.process(frames) depth_frame aligned_frames.get_depth_frame() color_frame aligned_frames.get_color_frame() depth_image np.asanyarray(depth_frame.get_data()) color_image np.asanyarray(color_frame.get_data())对齐之后depth_image和color_image的尺寸都是 640×480同一个(x, y)坐标在两张图上指的是同一个物理点。这一步不做后面测距就是开盲盒。3.2 检测框中心区域取深度中值而不是单点YOLOv5 输出检测框后最朴素的做法是取框中心一个像素的深度值。但深度图有噪声单点取值容易跳变尤其是物体边缘或者反光表面。我一般取框中心区域一个 5×5 或 10×10 的窗口然后取中值这样距离读数稳定得多。def get_distance(depth_image, box, depth_scale, window5): x1, y1, x2, y2 box cx int((x1 x2) / 2) cy int((y1 y2) / 2) h, w depth_image.shape # 取中心窗口注意边界裁剪 x_start max(0, cx - window) x_end min(w, cx window) y_start max(0, cy - window) y_end min(h, cy window) patch depth_image[y_start:y_end, x_start:x_end] # 过滤掉 0 值无效深度 valid patch[patch 0] if len(valid) 0: return None median_depth np.median(valid) return median_depth * depth_scale # 转成米window参数控制取样窗口大小太小不稳定太大容易把背景深度混进来。5 到 10 之间比较合适。valid patch[patch 0]这行是必须的深度图里值为 0 表示该像素没有有效深度读数直接参与中值计算会把结果拉偏。3.3 把检测结果和距离叠加到画面上拿到检测框和距离后用 OpenCV 画框和文字。这里有个细节中文字符 OpenCV 的putText不支持会显示成问号。要么用英文要么用 PIL 转一道。我一般直接写英文省事。import cv2 import torch model torch.hub.load(ultralytics/yolov5, custom, pathweights/yolov5s.pt) model.conf 0.5 # 置信度阈值 model.iou 0.45 # NMS IoU 阈值 while True: frames pipeline.wait_for_frames() aligned_frames align.process(frames) depth_frame aligned_frames.get_depth_frame() color_frame aligned_frames.get_color_frame() if not depth_frame or not color_frame: continue depth_image np.asanyarray(depth_frame.get_data()) color_image np.asanyarray(color_frame.get_data()) results model(color_image) detections results.xyxy[0].cpu().numpy() # x1, y1, x2, y2, conf, cls for det in detections: x1, y1, x2, y2, conf, cls det box [x1, y1, x2, y2] dist get_distance(depth_image, box, depth_scale) label f{model.names[int(cls)]} {conf:.2f} if dist is not None: label f {dist:.2f}m cv2.rectangle(color_image, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) cv2.putText(color_image, label, (int(x1), int(y1) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) cv2.imshow(RealSense YOLOv5, color_image) if cv2.waitKey(1) 0xFF ord(q): breakmodel.conf和model.iou是两个最常调的参数。conf调低会检出更多目标但误检增加调高则漏检增加。iou控制 NMS 合并重叠框的力度目标密集时适当调低。results.xyxy[0]拿到的是绝对像素坐标直接可以用。距离叠加到 label 里一眼就能看到每个框的物理距离。4. 避坑与排查那些让我调了半天的翻车现场4.1 深度图全黑或大片 0 值现象realsense-viewer里深度流正常但 Python 脚本里读出来的depth_image大部分是 0。原因通常是曝光时间太短或者红外投射器没开。D435i 在自动曝光模式下遇到近处物体可能过曝导致深度计算失败。解决办法是在启动流之后手动设置曝光和增益depth_sensor profile.get_device().first_depth_sensor() depth_sensor.set_option(rs.option.enable_auto_exposure, 0) depth_sensor.set_option(rs.option.exposure, 33000) # 微秒 depth_sensor.set_option(rs.option.gain, 16)曝光值根据实际场景调室内一般 20000 到 40000 之间。另外确认rs.option.emitter_enabled是 1投射器关了深度图也会大面积失效。4.2 检测框对了但距离明显偏大现象人站在 1 米处读出来 2.5 米。原因几乎都是没做 align深度图还是深度相机视角检测框中心对应的像素在深度图上其实是背景。解决就是老老实实加rs.align(rs.stream.color)并且确保depth_frame是从aligned_frames里取的不是从原始frames里取的。这个坑我见过太多人踩代码里少一行 align调一下午。4.3 pyrealsense2 导入报错或找不到设备现象import pyrealsense2报ModuleNotFoundError或者No device connected。前者是 pip 装的时候和系统 Python 版本不匹配用python -c import sys; print(sys.version)确认版本然后pip install pyrealsense2指定对应 wheel。后者多半是 USB 线的问题D435i 必须用 USB 3.0 线插在 USB 2.0 口上能识别但取流会失败。换线换口别犹豫。4.4 YOLOv5 推理速度慢导致画面卡顿现象检测一帧要 200 毫秒以上视频流看着像幻灯片。原因可能是用了yolov5x.pt这种大模型或者没开 GPU。解决办法换yolov5s.pt在脚本里加model.cuda()并且把输入尺寸从 640 降到 416 或 320。model.conf调高一点也能减少后处理时间。如果是在树莓派 5 上部署建议用 ONNX Runtime 或者 TensorRT 加速纯 PyTorch 推理帧率很难看。4.5 深度值和 RGB 画面不同步现象画面里手在动但距离读数滞后明显。原因是wait_for_frames默认等的是深度和 RGB 都就绪的帧如果两个流帧率不一致会丢帧。解决办法是把两个流的帧率设成一样比如都是 30fps并且用frames.get_timestamp()检查时间戳差。差太多的话考虑用rs2::syncer做硬件同步或者干脆降帧率到 15fps 换取稳定性。5. 进阶技巧把单点测距升级成区域测距与多目标跟踪5.1 用深度直方图过滤背景干扰单窗口取中值在目标贴着背景时容易把背景深度混进来。更稳的做法是在检测框内做深度直方图取峰值附近的深度值。具体来说把框内所有有效深度值统计成直方图找到最大 bin然后取该 bin 附近 ±50 毫米范围内的深度中值。这样即使框内混入了背景像素只要目标像素占多数结果依然准。def get_distance_hist(depth_image, box, depth_scale, bin_size50): x1, y1, x2, y2 [int(v) for v in box] roi depth_image[y1:y2, x1:x2] valid roi[roi 0] if len(valid) 0: return None hist, edges np.histogram(valid, binsnp.arange(0, valid.max() bin_size, bin_size)) peak_idx np.argmax(hist) peak_center (edges[peak_idx] edges[peak_idx 1]) / 2 # 取峰值附近 ±bin_size 范围内的深度中值 mask (valid peak_center - bin_size) (valid peak_center bin_size) if mask.sum() 0: return None return np.median(valid[mask]) * depth_scalebin_size控制直方图分辨率50 毫米对应深度图里 50 个单位depth_scale0.001 时。这个函数比简单窗口中值多花不了几毫秒但稳定性提升明显尤其是目标边缘和背景深度接近的时候。5.2 多目标距离排序与最近目标锁定在避障场景里你往往只关心最近的那个目标。可以在每帧检测后把所有目标的距离算出来按距离排序只对最近的目标做特殊标记比如画红框、发报警信号。dists [] for det in detections: x1, y1, x2, y2, conf, cls det d get_distance_hist(depth_image, [x1, y1, x2, y2], depth_scale) if d is not None: dists.append((d, det)) dists.sort(keylambda x: x[0]) if dists: nearest_dist, nearest_det dists[0] x1, y1, x2, y2, conf, cls nearest_det cv2.rectangle(color_image, (int(x1), int(y1)), (int(x2), int(y2)), (0, 0, 255), 3) cv2.putText(color_image, fNEAREST {nearest_dist:.2f}m, (int(x1), int(y1) - 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)这段代码把最近目标用红框标出距离文字也单独显示。如果你接的是单片机或者 PLC可以把nearest_dist通过串口发出去做简单的阈值报警。注意排序前要过滤掉None值否则会报TypeError。5.3 验证测距精度的一个土办法拿一把卷尺把相机固定好让一个纸箱或者人站在已知距离上比如 1 米、1.5 米、2 米每个距离读 10 次看均值和标准差。D435i 在 1 米处误差一般在 ±1 厘米以内2 米处 ±3 厘米左右超过 3 米误差会到 ±10 厘米以上。如果误差明显大于这个范围先检查 align 有没有做再检查曝光和增益是不是合适。我习惯在脚本里加一个按键触发打印当前所有检测框的距离值方便对着卷尺核对。从那以后我每次部署新相机都强制走一遍这个卷尺验证流程不然心里没底。希望帮到你。本文还有配套的精品资源点击获取