传感器特性与误差模型:IMU、GPS、摄像头、激光雷达

做传感器融合这些年,我最大的体会就是:不了解传感器的脾气,融合就是瞎搞。每个传感器都有自己的性格——有的爱飘,有的爱抖,有的时好时坏。你得摸透它们,才能让它们好好配合。

这一章,咱们就聊聊四种最常见的传感器:IMU、GPS、摄像头、激光雷达。我会结合自己的项目经验,把它们的特性、误差来源和建模方法讲清楚。

IMU:又爱又恨的“惯性导航”

IMU(惯性测量单元)是我用得最多的传感器。它包含加速度计和陀螺仪,能测出物体的加速度和角速度。说白了,就是靠“积分”来推算位置和姿态。

优点很明显:

  • 完全自主,不依赖外部信号
  • 更新频率高(100Hz-1000Hz)
  • 短时间精度不错

缺点也很致命:

  • 误差会随时间累积(漂移)
  • 对温度敏感
  • 低频噪声大

核心误差来源:

  • 零偏(Bias):静止时输出不为零。我遇到过最夸张的一次,某款MEMS陀螺仪零偏达到0.5°/s,积分10秒就偏了5度。
  • 随机游走(Random Walk):噪声积分后产生的随机漂移。这玩意儿没法完全消除,只能靠滤波抑制。
  • 尺度因子误差:输出值与真实值的比例偏差。一般标定后能控制在1%以内。
  • 交轴耦合:一个轴的运动会影响到另一个轴的输出。高端IMU会做正交补偿。

误差建模方法:

我习惯用Allan方差来分析IMU噪声特性。它能帮你区分出量化噪声、角度随机游走、零偏不稳定性、速率随机游走等不同成分。

// 简化的IMU误差模型(C语言风格)
typedef struct {
    float bias_acc[3];      // 加速度计零偏
    float bias_gyro[3];     // 陀螺仪零偏
    float scale_acc[3];     // 加速度计尺度因子
    float scale_gyro[3];    // 陀螺仪尺度因子
    float noise_acc[3];     // 加速度计噪声标准差
    float noise_gyro[3];    // 陀螺仪噪声标准差
} ImuErrorModel;

// 应用误差模型
void applyImuError(float acc_raw[3], float gyro_raw[3], 
                   ImuErrorModel *model, float acc_out[3], float gyro_out[3]) {
    for (int i = 0; i < 3; i++) {
        // 减去零偏,补偿尺度因子
        acc_out[i] = (acc_raw[i] - model->bias_acc[i]) * (1.0f + model->scale_acc[i]);
        gyro_out[i] = (gyro_raw[i] - model->bias_gyro[i]) * (1.0f + model->scale_gyro[i]);
        // 加上高斯噪声(实际项目中用随机数生成)
        acc_out[i] += gaussian_random() * model->noise_acc[i];
        gyro_out[i] += gaussian_random() * model->noise_gyro[i];
    }
}

我的经验:IMU上电后前几分钟零偏变化最大。我一般会做“上电自标定”——静止采集30秒数据,取平均作为初始零偏。这招能省掉不少麻烦。

GPS:精度看环境,别太信它

GPS(全球定位系统)是室外定位的主力。它靠卫星信号测距,精度受环境影响很大。

特性一览:

  • 定位,无累积误差
  • 更新频率低(1Hz-10Hz)
  • 开阔环境下精度2-5米(单点定位)
  • 城市峡谷、隧道内信号差

误差来源:

误差类型 典型大小 说明
电离层延迟 2-5米 白天比晚上严重,太阳活动期更糟
对流层延迟 0.5-1米 与温度、湿度、气压有关
卫星钟差 1-2米 卫星自身时钟偏差
多径效应 1-10米 高楼反射信号,最头疼的问题
接收机噪声 0.5-1米 硬件本身的热噪声

误差建模:

我一般用高斯分布来近似GPS噪声,但要注意——实际GPS误差不是高斯分布。多径效应会导致长尾分布,用卡尔曼滤波时得小心。

// GPS误差模型(简化版)
typedef struct {
    double lat;          // 纬度(度)
    double lon;          // 经度(度)
    float hdop;          // 水平精度因子
    float vdop;          // 垂直精度因子
    float std_horiz;     // 水平标准差(米)
    float std_vert;      // 垂直标准差(米)
} GpsMeasurement;

// 根据HDOP计算标准差
void computeGpsStd(GpsMeasurement *gps) {
    // 经验公式:单点定位时,UERE(用户等效距离误差)约2.5米
    float uere = 2.5f;  
    gps->std_horiz = uere * gps->hdop;
    gps->std_vert = uere * gps->vdop;
}

避坑指南:我曾经在深圳做测试,GPS在高层建筑间跳了20多米。后来加了“速度约束”——如果GPS位置变化超过物理可能的速度,直接丢弃。这招简单但有效。

摄像头:信息丰富,但别被它骗了

摄像头是视觉传感器,能提供最丰富的环境信息。但它的误差模型比IMU和GPS复杂得多。

特性:

  • 高分辨率,细节丰富
  • 受光照影响大(逆光、暗光、反光)
  • 单目无法直接测距
  • 计算量大

误差来源:

  • 内参误差:焦距、主点、畸变参数不准。标定不好,后面全白搭。
  • 外参误差:摄像头安装位置和角度偏差。我见过因为摄像头松动导致融合结果飘移的案例。
  • 特征匹配误差:图像特征点匹配错误。纹理少的地方尤其严重。
  • 运动模糊:快速运动时图像模糊,特征提取精度下降。

误差建模:

摄像头误差很难用简单模型描述。我通常用重投影误差来评估——把3D点投影到图像上,看和实际像素位置的偏差。

// 摄像头投影模型(针孔模型)
void projectPoint(float X, float Y, float Z, 
                  float fx, float fy, float cx, float cy,
                  float *u, float *v) {
    // 归一化坐标
    float x = X / Z;
    float y = Y / Z;
    // 像素坐标
    *u = fx * x + cx;
    *v = fy * y + cy;
}

// 重投影误差计算
float computeReprojectionError(float X, float Y, float Z,
                               float u_obs, float v_obs,
                               float fx, float fy, float cx, float cy) {
    float u_proj, v_proj;
    projectPoint(X, Y, Z, fx, fy, cx, cy, &u_proj, &v_proj);
    float du = u_obs - u_proj;
    float dv = v_obs - v_proj;
    return sqrtf(du*du + dv*dv);
}

我的习惯:做视觉SLAM时,我会把重投影误差的阈值设成2个像素。超过这个值的匹配点,大概率是误匹配,直接扔掉。宁可少用,不可错用。

激光雷达:精度高,但别太依赖

激光雷达(LiDAR)是自动驾驶的“眼睛”。它直接测量距离,精度高,但也不是万能的。

特性:

  • 直接测距,精度厘米级
  • 不受光照影响
  • 点云稀疏(相对摄像头)
  • 价格贵,功耗高

误差来源:

  • 测距误差:激光飞行时间测量不准。典型值±2cm。
  • 角度误差:扫描电机角度偏差。16线激光雷达的垂直角度误差可达0.1°。
  • 反射率影响:黑色物体吸收激光,测距变差。我遇到过黑色轮胎测距误差超过10cm。
  • 多回波:激光穿过玻璃或雨滴时产生多个回波。

误差建模:

激光雷达的测距误差通常用高斯模型,但角度误差得用系统误差模型。

// 激光雷达点误差模型
typedef struct {
    float range;         // 测距值(米)
    float azimuth;       // 水平角(弧度)
    float elevation;     // 垂直角(弧度)
    float intensity;     // 反射强度
    float range_std;     // 测距标准差(米)
    float angle_std;     // 角度标准差(弧度)
} LidarPoint;

// 添加误差到点云
void addLidarNoise(LidarPoint *pt) {
    // 测距噪声(高斯)
    pt->range += gaussian_random() * pt->range_std;
    // 角度噪声(高斯)
    pt->azimuth += gaussian_random() * pt->angle_std;
    pt->elevation += gaussian_random() * pt->angle_std;
}

避坑指南:我曾经在雨天测试激光雷达,点云里全是噪点。后来加了“强度滤波”——反射强度低于某个阈值的点直接丢弃。虽然损失了一些有效点,但整体数据质量提升明显。

四种传感器对比总结

做个表格,一目了然:

传感器 优点 缺点 典型误差 适用场景
IMU 自主、高频 漂移、累积误差 零偏0.1-1°/s 短时定位、姿态估计
GPS 定位、无漂移 低频、受环境影响大 2-10米 室外导航、全局定位
摄像头 信息丰富、成本低 光照敏感、计算量大 1-3像素(重投影) 视觉SLAM、目标检测
激光雷达 精度高、不受光照 稀疏、价格高 1-5cm 高精度建图、避障

知识体系结构图

下面这张图,是我自己梳理的传感器误差模型知识框架。你看一眼,心里就有数了。

传感器误差模型知识体系 IMU GPS 摄像头 激光雷达 ↓ 误差来源 ↓ IMU误差 • 零偏(Bias) • 随机游走 • 尺度因子误差 • 交轴耦合 GPS误差 • 电离层延迟 • 对流层延迟 • 卫星钟差 • 多径效应 摄像头误差 • 内参误差 • 外参误差 • 特征匹配误差 • 运动模糊 激光雷达误差 • 测距误差 • 角度误差 • 反射率影响 • 多回波 ↓ 建模方法 ↓ 误差建模方法 Allan方差分析 | 高斯噪声模型 | 重投影误差 | 系统误差标定 ↓ 融合策略 ↓ 卡尔曼滤波 | 粒子滤波 | 图优化 | 因子图

嗯,这张图把整个知识脉络串起来了。从传感器特性到误差来源,再到建模方法和融合策略,每一步都环环相扣。

做传感器融合,说白了就是跟误差打交道。你越了解每个传感器的“脾气”,就越能设计出鲁棒的融合算法。我在项目中吃过不少亏,也积累了一些经验,上面这些内容都是实战中总结出来的。

记住一句话:没有完美的传感器,只有好的融合算法。

可可精品资料库