传感器与数据采集:机器人的“感官世界”
做机器人这行,我经常跟学生说一句话:地图建得准不准,七成看传感器。你算法写得再漂亮,传感器数据一塌糊涂,后面全是白搭。今天咱们就来聊聊机器人最常用的三种“感官”——激光雷达、深度相机和惯性测量单元。
我个人习惯把传感器分成两类:外部传感器(看世界的)和内部传感器(感受自己的)。激光雷达和深度相机属于前者,IMU属于后者。它们各有各的脾气,你得摸透了才能用好。
2.1 激光雷达(LiDAR):精确的距离“尺子”
激光雷达的原理其实不复杂——发射激光束,测量反射回来的时间,算出距离。这就是所谓的ToF(Time of Flight)原理。你想想看,光速是恒定的,只要计时够准,距离就能算得够精确。
我在项目中遇到过最头疼的问题,就是激光雷达在玻璃墙面前的表现。激光直接穿透玻璃,打到后面的物体再反射回来,或者干脆被镜面反射到别处去。嗯,这里要注意:LiDAR在透明、镜面、吸光材质面前会“失明”。
2.1.1 数据格式:点云与激光扫描
激光雷达输出的数据,常见有两种格式:
- 单线激光雷达:输出一维距离数组,每个角度对应一个距离值。比如RPLIDAR A1,每圈采集360个点,角度分辨率1°。
- 多线激光雷达:输出三维点云,每个点包含(x, y, z)坐标和反射强度。比如Velodyne VLP-16,16根激光束同时扫描。
下面是一个典型的单线激光雷达数据片段(ROS的LaserScan消息格式):
# LaserScan消息示例
header:
seq: 1234
stamp: 1623456789.123
frame_id: "laser_frame"
angle_min: -3.14159 # 起始角度(弧度)
angle_max: 3.14159 # 终止角度
angle_increment: 0.0174533 # 角度分辨率(约1°)
time_increment: 0.0
scan_time: 0.1 # 扫描周期(10Hz)
range_min: 0.15 # 最小有效距离
range_max: 12.0 # 最大有效距离
ranges: [0.5, 0.52, 0.48, ...] # 距离数组(360个值)
intensities: [15, 12, 18, ...] # 反射强度数组
2.2 深度相机(RGB-D):看得见,摸得着
深度相机,说白了就是给普通相机加了个“测距”功能。它输出的每个像素不仅有颜色(RGB),还有深度值(D),所以叫RGB-D相机。
主流的深度相机有三种技术路线:
| 技术类型 | 原理 | 代表产品 | 优点 | 缺点 |
|---|---|---|---|---|
| 结构光 | 投射红外光斑,通过变形计算深度 | Kinect v1, Intel RealSense D415 | 室内精度高,成本低 | 室外失效,有效距离短(~5m) |
| ToF | 发射红外脉冲,测量飞行时间 | Kinect v2, Azure Kinect | 帧率高,室外可用 | 分辨率低,多机干扰 |
| 双目立体 | 两个相机视差计算深度 | ZED, Intel RealSense D435i | 室内外通用,无主动光 | 弱纹理区域失效,计算量大 |
2.2.1 从深度图到点云
深度相机输出的原始数据是一张深度图——每个像素值代表该点到相机的距离。要生成三维点云,需要做一步坐标变换:
# 从深度图生成点云(伪代码)
def depth_to_pointcloud(depth_image, camera_intrinsics):
fx = camera_intrinsics.fx # 焦距x
fy = camera_intrinsics.fy # 焦距y
cx = camera_intrinsics.cx # 光心x
cy = camera_intrinsics.cy # 光心y
points = []
for v in range(height):
for u in range(width):
z = depth_image[v, u] # 深度值
if z == 0 or z > max_range:
continue # 无效点跳过
x = (u - cx) * z / fx
y = (v - cy) * z / fy
points.append([x, y, z])
return points
你可能会问:为什么深度图里会有0值?嗯,这是因为深度相机在某些区域测不到距离——比如太远的物体、吸光表面、或者边缘遮挡处。我建议在生成点云时,一定要做无效点过滤,否则后面建图会多出一堆“幽灵点”。
2.3 惯性测量单元(IMU):机器人的“内耳”
IMU就像人的内耳前庭系统,感知自身的加速度和角速度。它由加速度计和陀螺仪组成,有些还带磁力计。
我个人觉得,IMU是SLAM系统里最“狡猾”的传感器。为什么?因为它有漂移。加速度计积分得到速度,速度积分得到位置——每次积分都会累积误差。你想想看,一个静止的IMU,理论上加速度应该是0,但实际上它会输出一个微小的噪声。积分10秒后,位置误差可能已经漂了好几米。
2.3.1 IMU数据与里程计
IMU的原始数据长这样:
# IMU消息示例(ROS的Imu消息格式)
header:
seq: 5678
stamp: 1623456790.456
frame_id: "imu_link"
orientation: # 姿态(四元数)
x: 0.0
y: 0.0
z: 0.0
w: 1.0
angular_velocity: # 角速度(rad/s)
x: 0.01
y: -0.02
z: 0.005
linear_acceleration: # 线加速度(m/s²)
x: 0.12
y: -0.08
z: 9.81 # 重力加速度
注意看,z轴的加速度是9.81——这是重力。做IMU里程计时,第一步就是去除重力分量,否则你算出来的位置会一直往天上飘。
IMU里程计的计算流程大致如下:
- 姿态更新:对角速度积分,得到当前姿态(四元数或欧拉角)
- 重力补偿:用姿态将加速度从机体坐标系转换到世界坐标系,减去重力
- 速度积分:对补偿后的加速度积分,得到速度
- 位置积分:对速度积分,得到位置
2.4 传感器数据融合:1+1 > 2
单独用LiDAR,建图精度高但帧率低(10Hz);单独用IMU,帧率高(200Hz)但漂移严重。把它们结合起来,就能取长补短。
下面这张图展示了多传感器融合的典型架构:
在实际项目中,我常用的融合策略是:LiDAR做前端匹配,IMU做运动预测,RGB-D提供纹理信息辅助回环检测。这样即使LiDAR在快速旋转时丢帧,IMU也能顶上去,保证位姿估计不中断。
- LiDAR:精度高,适合建图,但怕玻璃和强光
- RGB-D:信息丰富,适合特征匹配,但受距离和光照限制
- IMU:高频但漂移,只能做短时预测
- 三者融合,才能构建稳定可靠的SLAM系统