跳转至

传感器融合工程实践

引言

本页面是传感器融合专题的工程实践部分,涵盖扩展卡尔曼滤波器(EKF)的 Python 实现、互补滤波器、ROS 2 robot_localization 包的配置与使用、融合系统调试技巧、异常值剔除以及性能评测方法。

扩展卡尔曼滤波器实现

EKF 算法概述

扩展卡尔曼滤波器(Extended Kalman Filter, EKF)通过一阶泰勒展开将非线性系统线性化,然后应用标准卡尔曼滤波。两个核心步骤:

预测步骤

更新步骤

GPS + IMU 融合示例

import numpy as np

class EKF_GPS_IMU:
    """GPS + IMU 融合的简化 EKF 实现"""

    def __init__(self, dt=0.01):
        self.dt = dt
        # 状态: [x, y, vx, vy, yaw]
        self.x = np.zeros(5)
        self.P = np.eye(5) * 1.0

        # 过程噪声
        self.Q = np.diag([0.1, 0.1, 0.5, 0.5, 0.01])

        # GPS 观测噪声 (位置)
        self.R_gps = np.diag([2.0, 2.0])  # GPS 精度 ~2m

        # IMU 观测噪声 (加速度 + 角速度)
        self.R_imu = np.diag([0.1, 0.1, 0.01])

    def predict(self, accel, gyro):
        """IMU 预测步骤"""
        x, y, vx, vy, yaw = self.x
        dt = self.dt

        # 将 body 系加速度转换到世界系
        ax_w = accel[0] * np.cos(yaw) - accel[1] * np.sin(yaw)
        ay_w = accel[0] * np.sin(yaw) + accel[1] * np.cos(yaw)

        # 状态转移
        self.x = np.array([
            x + vx * dt + 0.5 * ax_w * dt**2,
            y + vy * dt + 0.5 * ay_w * dt**2,
            vx + ax_w * dt,
            vy + ay_w * dt,
            yaw + gyro * dt
        ])

        # 雅可比矩阵
        F = np.eye(5)
        F[0, 2] = dt
        F[1, 3] = dt
        F[2, 4] = (-accel[0] * np.sin(yaw) - accel[1] * np.cos(yaw)) * dt
        F[3, 4] = (accel[0] * np.cos(yaw) - accel[1] * np.sin(yaw)) * dt

        self.P = F @ self.P @ F.T + self.Q

    def update_gps(self, gps_pos):
        """GPS 位置更新"""
        H = np.zeros((2, 5))
        H[0, 0] = 1.0  # 观测 x
        H[1, 1] = 1.0  # 观测 y

        z = np.array(gps_pos)
        y = z - H @ self.x  # 新息 (innovation)

        S = H @ self.P @ H.T + self.R_gps
        K = self.P @ H.T @ np.linalg.inv(S)

        self.x = self.x + K @ y
        self.P = (np.eye(5) - K @ H) @ self.P

    def get_state(self):
        return self.x.copy()

# 使用示例
ekf = EKF_GPS_IMU(dt=0.01)

for i in range(10000):
    # IMU 数据 (100Hz)
    accel = read_accelerometer()  # [ax, ay] in body frame
    gyro = read_gyroscope()       # yaw rate
    ekf.predict(accel, gyro)

    # GPS 数据 (1Hz)
    if i % 100 == 0:
        gps = read_gps()  # [x, y]
        ekf.update_gps(gps)

    state = ekf.get_state()

互补滤波器

互补滤波器(Complementary Filter)是一种简单高效的融合方法,特别适合 IMU 姿态估计。核心思想:利用陀螺仪的短期精度和加速度计的长期稳定性。

class ComplementaryFilter:
    """IMU 姿态互补滤波器"""

    def __init__(self, alpha=0.98, dt=0.01):
        self.alpha = alpha  # 陀螺仪权重 (0.95-0.99)
        self.dt = dt
        self.roll = 0.0
        self.pitch = 0.0

    def update(self, gyro, accel):
        # 陀螺仪积分(短期准确)
        self.roll += gyro[0] * self.dt
        self.pitch += gyro[1] * self.dt

        # 加速度计估计(长期稳定)
        accel_roll = np.arctan2(accel[1], accel[2])
        accel_pitch = np.arctan2(-accel[0],
                                  np.sqrt(accel[1]**2 + accel[2]**2))

        # 互补融合
        self.roll = self.alpha * self.roll + (1 - self.alpha) * accel_roll
        self.pitch = self.alpha * self.pitch + (1 - self.alpha) * accel_pitch

        return self.roll, self.pitch

越大越信任陀螺仪(高频响应好),越小越信任加速度计(低频稳定)。

ROS 2 robot_localization 配置

robot_localization 是 ROS 2 中最常用的传感器融合包,提供 EKF 和 UKF 两种滤波器节点。

基本配置

# ekf.yaml
ekf_filter_node:
  ros__parameters:
    frequency: 30.0
    sensor_timeout: 0.1
    two_d_mode: false
    publish_tf: true

    map_frame: map
    odom_frame: odom
    base_link_frame: base_link
    world_frame: odom

    # 里程计输入(编码器)
    odom0: /wheel_odom
    odom0_config: [false, false, false,   # x, y, z
                   false, false, false,   # roll, pitch, yaw
                   true,  true,  false,   # vx, vy, vz
                   false, false, true,    # vroll, vpitch, vyaw
                   false, false, false]   # ax, ay, az

    # IMU 输入
    imu0: /imu/data
    imu0_config: [false, false, false,   # x, y, z
                  true,  true,  true,    # roll, pitch, yaw
                  false, false, false,   # vx, vy, vz
                  true,  true,  true,    # vroll, vpitch, vyaw
                  true,  true,  true]    # ax, ay, az
    imu0_remove_gravitational_acceleration: true

    # GPS 输入(需要 navsat_transform_node 转换)
    odom1: /odometry/gps
    odom1_config: [true,  true,  false,
                   false, false, false,
                   false, false, false,
                   false, false, false,
                   false, false, false]

    # 过程噪声协方差(对角线元素)
    process_noise_covariance: [0.05, 0.05, 0.06, 0.03, 0.03, 0.06,
                               0.025, 0.025, 0.04, 0.01, 0.01, 0.02,
                               0.01, 0.01, 0.015]

双 EKF 架构

对于需要全局定位的机器人,推荐使用双 EKF 架构:

  • EKF Local(odom 帧):融合连续传感器(编码器 + IMU),提供平滑的局部里程计
  • EKF Global(map 帧):在 Local 基础上融合绝对定位(GPS/AMCL),提供全局位姿
# launch 文件片段
from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='robot_localization',
            executable='ekf_node',
            name='ekf_local',
            parameters=['ekf_local.yaml'],
            remappings=[('odometry/filtered', '/odom/local')],
        ),
        Node(
            package='robot_localization',
            executable='ekf_node',
            name='ekf_global',
            parameters=['ekf_global.yaml'],
            remappings=[('odometry/filtered', '/odom/global')],
        ),
        Node(
            package='robot_localization',
            executable='navsat_transform_node',
            parameters=['navsat.yaml'],
        ),
    ])

异常值剔除

马氏距离门控

马氏距离(Mahalanobis Distance)门控通过检查新息是否在合理范围内来剔除异常测量:

其中 是新息协方差。当 超过阈值(如 分布的 95% 分位数)时拒绝该测量。

def mahalanobis_gate(z, z_pred, S, threshold=5.991):
    """
    马氏距离门控(2 自由度 95% 阈值 = 5.991)
    """
    innovation = z - z_pred
    d_sq = innovation.T @ np.linalg.inv(S) @ innovation
    return d_sq < threshold  # True 表示接受

传感器健康监测

class SensorHealthMonitor:
    def __init__(self, timeout=1.0, max_consecutive_rejects=5):
        self.last_update_time = {}
        self.consecutive_rejects = {}
        self.timeout = timeout
        self.max_rejects = max_consecutive_rejects

    def check_timeout(self, sensor_name, current_time):
        if sensor_name in self.last_update_time:
            dt = current_time - self.last_update_time[sensor_name]
            if dt > self.timeout:
                return False  # 超时
        self.last_update_time[sensor_name] = current_time
        return True

    def record_reject(self, sensor_name):
        self.consecutive_rejects[sensor_name] = \
            self.consecutive_rejects.get(sensor_name, 0) + 1
        if self.consecutive_rejects[sensor_name] > self.max_rejects:
            print(f"警告: {sensor_name} 连续 {self.max_rejects} 次被拒绝")

    def record_accept(self, sensor_name):
        self.consecutive_rejects[sensor_name] = 0

调试技巧

常见问题诊断

症状 可能原因 排查方法
位姿漂移 IMU 偏差未校正 检查静止时 IMU 读数
位姿跳变 GPS 多径效应 检查 GPS 精度因子(DOP)
协方差发散 过程噪声 Q 过小 增大 Q 矩阵对应项
滤波器延迟 传感器时间戳不准 检查时间同步
估计震荡 观测噪声 R 过小 增大 R 矩阵对应项
融合后精度不如单传感器 外参标定错误 重新标定传感器外参

可视化调试

# ROS 2 中可视化融合结果
ros2 launch robot_localization ekf.launch.py

# 在 RViz 中查看:
# - /odom/filtered (融合后里程计)
# - /odom/filtered 的协方差椭圆
# - 各传感器原始数据对比

# 记录数据用于离线分析
ros2 bag record /odom/filtered /wheel_odom /imu/data /gps/fix

性能评测

EVO 评测工具

EVO(Evaluation of Odometry and SLAM)是评测定位精度的标准工具:

# 安装
pip install evo

# 绝对轨迹误差 (ATE)
evo_ape tum ground_truth.txt estimated.txt -p --plot_mode xy

# 相对位姿误差 (RPE)
evo_rpe tum ground_truth.txt estimated.txt -p --delta 1 --delta_unit m

# 多种方法对比
evo_traj tum ekf.txt ukf.txt gps_only.txt --ref ground_truth.txt -p

关键指标

指标 定义 典型值(室外机器人)
ATE RMSE 绝对轨迹误差均方根 < 0.5 m
RPE(1m) 每米相对位姿误差 < 1%
更新频率 融合输出频率 > 30 Hz
端到端延迟 传感器到输出延迟 < 50 ms

参考资料

  1. Moore, T., & Stouch, D. (2016). A Generalized Extended Kalman Filter Implementation for the Robot Operating System. Intelligent Autonomous Systems.
  2. robot_localization 文档:https://docs.ros.org/en/humble/p/robot_localization/
  3. Grupp, M. (2017). EVO — Python Package for the Evaluation of Odometry and SLAM. https://github.com/MichaelGruworked/evo
  4. Solà, J. (2017). Quaternion Kinematics for the Error-State Kalman Filter. arXiv:1711.02508.
  5. Madgwick, S. O. (2010). An Efficient Orientation Filter for Inertial and Inertial/Magnetic Sensor Arrays. Report.