传感器原理:激光雷达、IMU、轮式编码器与视觉传感器
做机器人导航,说白了就是回答三个问题:我在哪?我要去哪?怎么去?而传感器,就是回答第一个问题的眼睛和耳朵。这一章,我把最常用的四种传感器掰开揉碎讲清楚。
我个人习惯把传感器分成两类:外部传感器(感知环境)和内部传感器(感知自身状态)。激光雷达和视觉属于前者,IMU和轮式编码器属于后者。你想想看,如果只有眼睛没有平衡感,走路肯定摔跤;反过来只有平衡感没有眼睛,那就是盲人摸象。
激光雷达工作原理
激光雷达,简称LiDAR。它的核心原理其实很简单:发射激光,测量反射回来的时间。光速是已知的,时间差一算,距离就出来了。
核心公式:距离 = (光速 × 飞行时间) / 2
除以2是因为激光走了个来回。
我在项目中遇到过最坑的事——激光雷达在强阳光下直接罢工。为什么?因为太阳光里也有红外成分,会淹没激光信号。所以室外选型时,一定要看抗环境光能力这个参数。
市面上常见的激光雷达分两种:
| 类型 | 测距方式 | 典型距离 | 适用场景 |
|---|---|---|---|
| 三角测距 | 激光照射,CMOS接收偏移量 | 0.1m - 8m | 室内扫地机、服务机器人 |
| TOF(飞行时间) | 直接测量脉冲往返时间 | 0.5m - 100m+ | 室外AGV、自动驾驶 |
我的选型建议:室内导航用三角测距就够了,便宜且精度够用。室外项目别省这个钱,直接上TOF。我曾经为了省成本在室外用了三角测距,结果下午三点以后数据全是噪点,教训深刻。
激光雷达输出的数据叫点云。一帧点云包含几百到几十万个点,每个点有(x, y, z)坐标和反射强度。做SLAM时,我们就是靠这些点来匹配环境特征的。
// 激光雷达数据解析示例(伪代码)
struct LaserPoint {
float angle; // 角度(弧度)
float distance; // 距离(米)
float intensity; // 反射强度(0-255)
};
// 一帧数据通常包含 360° 或 270° 的扫描点
std::vector<LaserPoint> scan = lidar.getScan();
IMU惯性测量单元
IMU这东西,说白了就是机器人的内耳前庭。它告诉你:我现在是倾斜的、我在加速、我在旋转。
一个典型的IMU包含三个传感器:
- 加速度计:测量三轴加速度(包含重力分量)
- 陀螺仪:测量三轴角速度
- 磁力计(可选):测量地磁场方向,辅助航向
嗯,这里要注意——IMU有个天生的毛病叫漂移。加速度计积分得到速度,速度积分得到位置,每一步都有误差,积分几次以后位置就飞到天边去了。
避坑指南:我曾经做过一个纯IMU定位的项目,开机前10秒还挺准,30秒后位置误差超过1米,2分钟后完全不可用。所以IMU不能单独用于定位,必须配合其他传感器做融合。
那IMU到底有什么用?两个核心场景:
- 短时高精度推算:激光雷达或视觉失效时(比如快速旋转、进入白墙区域),IMU能顶住1-2秒
- 姿态估计:机器人爬坡时,IMU能实时输出俯仰角和横滚角,这对导航决策至关重要
// IMU数据融合示例(互补滤波)
float complementaryFilter(float accAngle, float gyroRate, float dt) {
const float alpha = 0.98; // 权重系数
static float angle = 0;
angle = alpha * (angle + gyroRate * dt) + (1 - alpha) * accAngle;
return angle;
}
轮式编码器
轮式编码器,我愿称之为最被低估的传感器。它装在电机轴上,每转一圈输出固定数量的脉冲。通过数脉冲,就能知道轮子转了多少圈,结合轮径就能算出走了多远。
编码器分两种:
| 类型 | 输出信号 | 精度 | 成本 |
|---|---|---|---|
| 增量式编码器 | A/B两相脉冲(可判方向) | 取决于线数 | 低 |
| 式编码器 | 直接输出角度值 | 高 | 高 |
关键公式:
位移 = (脉冲数 / 每转脉冲数) × 轮子周长
速度 = 位移 / 时间间隔
但轮式编码器有个致命缺陷——打滑。轮子空转的时候,编码器以为你走了1米,实际上你还在原地。我在做仓储AGV时就吃过这个亏,地面有点水渍,编码器里程计直接崩了,机器人以为自己到了货架前,实际上还差半米。
我的经验:编码器数据一定要做有效性校验。比如突然出现一个不可能的高速度,大概率是打滑了,这时候应该降低里程计的置信度,多依赖激光或视觉数据。
视觉传感器
视觉传感器,就是摄像头。但机器人用的摄像头和手机摄像头不太一样。我们更关心的是:能不能在弱光下工作?帧率够不够?有没有全局快门?
视觉传感器在导航中的典型用法:
- 单目视觉:一个摄像头,通过运动恢复结构(SFM)估算深度
- 双目视觉:两个摄像头,利用视差直接计算深度
- RGB-D相机:直接输出深度图,比如Kinect、RealSense
我个人最常用的是双目视觉。为什么?单目需要运动才能算深度,静止时就是个瞎子;RGB-D在室外强光下基本废掉。双目虽然计算量大,但胜在稳定。
注意:视觉传感器对光照极其敏感。我在一个工厂项目里,白天窗户进来的阳光导致一半画面过曝,特征点全丢了。后来加了ND滤镜才解决。如果你做室内导航,记得考虑光照变化。
// 视觉特征提取示例(使用ORB特征)
cv::Ptr<cv::ORB> orb = cv::ORB::create(500); // 提取500个特征点
std::vector<cv::KeyPoint> keypoints;
cv::Mat descriptors;
orb->detectAndCompute(image, cv::noArray(), keypoints, descriptors);
传感器融合:为什么需要组合使用?
看到这里你应该明白了——没有完美的传感器。激光雷达怕强光、怕镜面反射;IMU会漂移;编码器会打滑;视觉怕光照变化。
所以实际工程中,我们做的是传感器融合。最常见的组合是:
- 激光雷达 + IMU:室内导航标配,激光提供观测,IMU填补激光帧间的运动估计
- 视觉 + IMU:VIO(视觉惯性里程计),手机AR和无人机常用
- 激光 + 视觉 + IMU + 编码器:全冗余方案,用于自动驾驶等安全关键场景
下面这张图展示了多传感器融合的典型数据流:
核心原则:融合不是简单平均,而是根据每个传感器的置信度做加权。比如编码器打滑时置信度降低,激光雷达数据良好时权重提高。这就是卡尔曼滤波在做的事。