深度相机
概述
深度相机(RGB-D Camera)同时提供彩色图像和逐像素深度信息,是机器人3D感知的核心传感器。相比纯RGB相机,深度相机直接输出三维数据,大幅简化了避障、抓取和SLAM等任务的实现。
深度感知原理
主要技术路线
| 技术 | 原理 | 精度 | 范围 | 室外性能 | 代表产品 |
|---|---|---|---|---|---|
| 主动双目(Active Stereo) | IR投射+双IR相机 | 高 | 0.3-10m | 中 | RealSense D435i |
| 被动双目(Passive Stereo) | 双RGB相机+视差匹配 | 中 | 0.5-20m | 好 | ZED 2i |
| 结构光(Structured Light) | 编码图案投射+解码 | 最高 | 0.2-4m | 差 | Kinect Azure |
| 飞行时间(ToF) | 测量光飞行时间 | 中 | 0.1-5m | 中 | PMD Flexx2 |
深度精度模型
深度误差通常与距离的平方成正比:
\[
\sigma_Z \propto \frac{Z^2}{fB}
\]
其中 \(Z\) 是距离,\(f\) 是焦距,\(B\) 是基线长度。
Intel RealSense系列
RealSense D435i
| 参数 | 值 |
|---|---|
| 深度技术 | 主动红外立体视觉 |
| 深度分辨率 | 1280 x 720 @90fps |
| 深度范围 | 0.3 - 10m |
| RGB分辨率 | 1920 x 1080 @30fps |
| FOV(深度) | 87° x 58° |
| FOV(RGB) | 69° x 42° |
| 基线 | 50mm |
| IMU | BMI055 (加速度计+陀螺仪) |
| 接口 | USB 3.0 Type-C |
| 尺寸 | 90 x 25 x 25mm |
| 功耗 | ~2.5W |
| 价格 | ~$250 |
适用场景:SLAM、避障、机械臂操作的通用选择。
RealSense D455
| 参数 | D435i | D455 |
|---|---|---|
| 基线 | 50mm | 95mm |
| 深度范围 | 0.3-10m | 0.4-20m |
| IMU | BMI055 | BMI085(更精确) |
| RGB FOV | 69°x42° | 90°x65° |
| 深度精度@4m | ~2% | <1% |
| 尺寸 | 90x25x25mm | 124x26x29mm |
| 价格 | ~$250 | ~$350 |
D435i vs D455选择
- D435i:尺寸小巧,适合空间受限的场景(如小型机器人、机械臂末端)
- D455:更长基线=更好的远距离深度精度,适合移动机器人导航
RealSense D405
| 参数 | 值 |
|---|---|
| 深度技术 | 主动红外立体视觉 |
| 最佳深度范围 | 0.07 - 0.7m |
| 深度分辨率 | 1280 x 720 @90fps |
| 基线 | 18mm |
| 尺寸 | 42 x 42 x 23mm |
| 价格 | ~$200 |
适用场景:近距离精细操作(如机械臂末端的手眼相机)。
RealSense编程
import pyrealsense2 as rs
import numpy as np
import cv2
# 配置管道
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() # ~0.001 (1mm)
# 对齐深度到彩色
align = rs.align(rs.stream.color)
# 后处理滤波器
decimation = rs.decimation_filter()
spatial = rs.spatial_filter()
temporal = rs.temporal_filter()
hole_filling = rs.hole_filling_filter()
try:
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()
# 应用后处理
depth_frame = decimation.process(depth_frame)
depth_frame = spatial.process(depth_frame)
depth_frame = temporal.process(depth_frame)
# 转为numpy数组
depth_image = np.asanyarray(depth_frame.get_data())
color_image = np.asanyarray(color_frame.get_data())
# 深度转米
depth_meters = depth_image * depth_scale
# 获取特定像素的3D坐标
depth_intrin = depth_frame.profile.as_video_stream_profile().intrinsics
pixel = [320, 240]
depth = depth_frame.get_distance(pixel[0], pixel[1])
point_3d = rs.rs2_deproject_pixel_to_point(
depth_intrin, pixel, depth
)
print(f"3D Point: {point_3d}")
finally:
pipeline.stop()
RealSense + ROS2
# 安装ROS2包装器
sudo apt install ros-humble-realsense2-camera
# 启动相机节点
ros2 launch realsense2_camera rs_launch.py \
depth_module.profile:=640x480x30 \
rgb_camera.profile:=640x480x30 \
enable_gyro:=true \
enable_accel:=true \
align_depth.enable:=true \
pointcloud.enable:=true
OAK-D系列(Luxonis)
OAK-D Lite
| 参数 | 值 |
|---|---|
| 深度技术 | 被动双目(立体IR) |
| 深度分辨率 | 640 x 400 @60fps |
| 深度范围 | 0.35 - 10m |
| RGB | 4K @30fps (IMX214) |
| AI加速 | Intel Myriad X VPU (4 TOPS) |
| 接口 | USB 3.0 Type-C |
| 特色 | 片上神经网络推理 |
| 价格 | ~$150 |
核心优势:内置AI加速器,可在相机端直接运行目标检测等模型,减轻主控负担。
import depthai as dai
# 创建管道
pipeline = dai.Pipeline()
# 配置RGB相机
cam_rgb = pipeline.create(dai.node.ColorCamera)
cam_rgb.setPreviewSize(300, 300)
cam_rgb.setInterleaved(False)
# 配置神经网络(MobileNet-SSD)
nn = pipeline.create(dai.node.MobileNetDetectionNetwork)
nn.setBlobPath("mobilenet-ssd.blob")
nn.setConfidenceThreshold(0.5)
# 连接
cam_rgb.preview.link(nn.input)
# 输出
xout_nn = pipeline.create(dai.node.XLinkOut)
xout_nn.setStreamName("nn")
nn.out.link(xout_nn.input)
with dai.Device(pipeline) as device:
q_nn = device.getOutputQueue("nn", maxSize=4, blocking=False)
while True:
detections = q_nn.get().detections
for det in detections:
print(f"Label: {det.label}, Confidence: {det.confidence:.2f}")
ZED 2i(Stereolabs)
规格
| 参数 | 值 |
|---|---|
| 深度技术 | 被动双目 |
| 分辨率 | 2x 2208x1242 (4.2MP each) |
| 深度范围 | 0.3 - 20m |
| 基线 | 120mm |
| FOV | 110° (H) x 70° (V) |
| IMU | 加速度计+陀螺仪+磁力计 |
| 接口 | USB 3.0 |
| 特色 | 内置SLAM、物体检测、身体追踪 |
| 防水 | IP66 |
| 价格 | ~$450 |
优势:
- 大基线(120mm)提供更好的远距离深度精度
- IP66防水防尘,适合室外机器人
- SDK内置视觉SLAM和AI功能
- 被动双目在室外阳光下仍可正常工作
import pyzed.sl as sl
zed = sl.Camera()
# 配置
init_params = sl.InitParameters()
init_params.camera_resolution = sl.RESOLUTION.HD720
init_params.camera_fps = 60
init_params.depth_mode = sl.DEPTH_MODE.ULTRA
init_params.coordinate_units = sl.UNIT.METER
zed.open(init_params)
# 启用位置跟踪
tracking_params = sl.PositionalTrackingParameters()
zed.enable_positional_tracking(tracking_params)
image = sl.Mat()
depth = sl.Mat()
pose = sl.Pose()
while True:
if zed.grab() == sl.ERROR_CODE.SUCCESS:
zed.retrieve_image(image, sl.VIEW.LEFT)
zed.retrieve_measure(depth, sl.MEASURE.DEPTH)
# 获取位姿(SLAM)
state = zed.get_position(pose)
translation = pose.get_translation()
print(f"Position: {translation.get()}")
Orbbec系列
| 型号 | 深度技术 | 范围 | 分辨率 | 价格 | 特色 |
|---|---|---|---|---|---|
| Femto Bolt | iToF | 0.25-5.5m | 640x576@30fps | ~$300 | Azure Kinect替代 |
| Femto Mega | iToF | 0.25-5.5m | 640x576@30fps | ~$450 | 板载计算 |
| Astra 2 | 结构光 | 0.3-6m | 640x400@30fps | ~$100 | 低成本 |
| Gemini 2 | 双目 | 0.15-10m | 1280x800@30fps | ~$200 | 适合机械臂 |
深度相机对比
| 特性 | RealSense D435i | RealSense D455 | OAK-D Lite | ZED 2i | Orbbec Femto |
|---|---|---|---|---|---|
| 技术 | 主动双目 | 主动双目 | 被动双目 | 被动双目 | iToF |
| 深度范围 | 0.3-10m | 0.4-20m | 0.35-10m | 0.3-20m | 0.25-5.5m |
| 精度@1m | ~2mm | ~1.5mm | ~5mm | ~3mm | ~2mm |
| 室外性能 | 中 | 中 | 中 | 好 | 中 |
| IMU | 有 | 有(更好) | 有 | 有 | 有 |
| AI加速 | 无 | 无 | 4 TOPS | 无 | 无 |
| ROS2支持 | 官方 | 官方 | 官方 | 官方 | 官方 |
| 防水 | 否 | 否 | 否 | IP66 | 否 |
| 价格 | $250 | $350 | $150 | $450 | $300 |
深度精度与距离关系
立体视觉深度误差
\[
\sigma_Z = \frac{Z^2}{fB} \sigma_d
\]
其中 \(\sigma_d\) 是视差匹配误差(通常0.5-1像素)。
| 距离 | D435i (B=50mm) | D455 (B=95mm) | ZED 2i (B=120mm) |
|---|---|---|---|
| 1m | ~2mm | ~1mm | ~0.8mm |
| 3m | ~18mm | ~10mm | ~7mm |
| 5m | ~50mm | ~26mm | ~21mm |
| 10m | ~200mm | ~105mm | ~83mm |
如何改善远距离深度精度
- 增大基线(但会增加近距离盲区)
- 提高分辨率(减小 \(\sigma_d\))
- 融合其他传感器(如LiDAR)
参见:多传感器融合 了解深度相机与其他传感器的融合方法。
深度图后处理
常用滤波器
import cv2
import numpy as np
def postprocess_depth(depth_image):
"""深度图后处理流水线"""
# 1. 移除无效值
depth_image[depth_image == 0] = np.nan
# 2. 中值滤波(去除离群点)
depth_filtered = cv2.medianBlur(
depth_image.astype(np.float32), 5
)
# 3. 双边滤波(保边平滑)
depth_smooth = cv2.bilateralFilter(
depth_filtered, d=9, sigmaColor=75, sigmaSpace=75
)
# 4. 空洞填充(最近邻插值)
mask = np.isnan(depth_smooth)
if mask.any():
from scipy.ndimage import distance_transform_edt
indices = distance_transform_edt(
mask, return_distances=False, return_indices=True
)
depth_smooth = depth_smooth[tuple(indices)]
return depth_smooth
深度图转点云
import numpy as np
import open3d as o3d
def depth_to_pointcloud(depth_image, color_image, intrinsics):
"""深度图转点云"""
fx, fy, cx, cy = intrinsics
h, w = depth_image.shape
u, v = np.meshgrid(np.arange(w), np.arange(h))
z = depth_image
x = (u - cx) * z / fx
y = (v - cy) * z / fy
points = np.stack([x, y, z], axis=-1).reshape(-1, 3)
colors = color_image.reshape(-1, 3) / 255.0
# 过滤无效点
valid = z.reshape(-1) > 0
points = points[valid]
colors = colors[valid]
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(points)
pcd.colors = o3d.utility.Vector3dVector(colors[:, ::-1]) # BGR→RGB
return pcd
小结
- 主动双目(RealSense)在室内综合性能最佳
- 被动双目(ZED)在室外和远距离表现更好
- 深度精度与距离平方成反比,基线越大远距离越准
- D435i是最通用的选择,D455适合需要更好远距离精度的场景
- OAK-D的片上AI加速是独特优势
- 后处理滤波可以显著改善深度图质量
参考资料
- Intel RealSense SDK: https://github.com/IntelRealSense/librealsense
- Stereolabs ZED SDK: https://www.stereolabs.com/developers
- Luxonis DepthAI: https://docs.luxonis.com/
- Orbbec SDK: https://developer.orbbec.com.cn/