欢迎光临
我们一直在努力

IMU(惯性测量单元)--基础

从本节起,小编将以一个全新的系列,来认真分享ROS的基础知识,区分于前面的试水写作,该系列将会更加精炼,可复刻程度更高,话不多说,我们马上开始。

第一节,我们将创建一个工作空间,并且之后的项目实践都将在其基础上实现,小编将在接下来的文章中给出文件的结构和源代码,读者只需在理解的基础上加以实践即可。

# 1. 创建工作空间目录
mkdir -p ~/Desktop/wireshark_ws/src
cd ~/Desktop/wireshark_ws/src

# 2. 初始化ROS工作空间
catkin_init_workspace

# 3. 回到根目录完成初始编译(生成基础目录)
cd ..
catkin_make
source devel/setup.bash

IMU = 惯性测量单元(Inertial Measurement Unit)

是一种测量物体运动状态的传感器模块,用来感知加速度、角速度、倾角等物理量。

核心组成

一个标准 IMU 通常包含:

  • 加速度计:测线性加速度(前后、左右、上下)
  • 陀螺仪:测角速度 / 旋转(翻滚、俯仰、偏航)
  • 高端 IMU 还会加:磁力计(相当于电子罗盘)
IMU的作用
  • 知道设备朝哪、怎么动、动得多快
  • 计算姿态、方向、位移、速度

我们需要创建的imu_filter的文件结构

这个结构是 ROS 为了实现标准化、可复用、可维护的软件生态而设计的,主要有以下几个原因:

  • 标准化:所有 ROS 包都遵循相同的目录结构,开发者可以快速理解任何一个包的布局,降低学习成本。
  • 编译自动化:catkin 编译系统会自动扫描 src/ 目录下的代码,并根据 CMakeLists.txt 自动生成构建规则,无需手动编写 Makefile。
  • 依赖管理:package.xml 中声明的依赖关系,让 ROS 可以自动检查和安装所需的库和包,确保项目可移植。
  • 功能分离:
    • src/ 专注于核心算法实现;
    • cfg/ 专注于运行时参数调整;
    • 配置文件和源代码分离,便于维护和扩展。
  • cfg文件夹

    MyStuff.cfg

    #!/usr/bin/env python
    from dynamic_reconfigure.parameter_generator_catkin import *

    gen = ParameterGenerator()

    gen.add("twoKp", double_t, 0, "2* accelerometer proportional gain", 0.01, 0.0, 0.1)
    gen.add("twoKi", double_t, 0, "2* integral gain", 0.002, 0.0, 0.01)

    exit(gen.generate("imu_filter", "imu_filter", "MyStuff"))

    以下是举例说明,帮助读者理解,

    #! /usr/bin/env python
    # 声明脚本解释器:指定使用系统默认的Python环境运行该脚本(ROS脚本标准开头)

    # 定义该动态参数配置所属的ROS包名(需与package.xml中的包名对应)
    PACKAGE = 'filter'

    # 导入ROS动态参数配置的核心生成器模块(catkin版本专用)
    from dynamic_reconfigure.parameter_generator_catkin import *

    # 创建参数生成器对象,用于定义可动态调整的参数
    gen = ParameterGenerator()

    # 定义第一个动态参数:twoKp(加速度计比例增益的2倍)
    # 参数格式:gen.add(参数名, 类型, 级别, 描述, 默认值, 最小值, 最大值)
    # – 'double':参数类型为浮点型
    # – 0:参数级别(0为通用级别,无需权限)
    # – 0.01:默认值,0~0.1是可调范围
    gen.add("twoKp", 'double', 0, "2* accelerometer proportional gain", 0.01, 0, 0.1);

    # 定义第二个动态参数:twoKi(积分增益的2倍)
    # – 0.002:默认值,0~0.01是可调范围,用于IMU滤波算法的积分项调整
    gen.add("twoKi", 'double', 0, "2* integral gain", 0.002, 0, 0.01);

    # 生成动态参数配置文件并退出脚本
    # 参数格式:gen.generate(包名, 节点名/命名空间, 配置名)
    # 生成后可通过rqt_reconfigure工具可视化调整twoKp/twoKi参数,无需重启节点
    exit(gen.generate(PACKAGE, "filter", "MyStuff"))

    这段代码是 ROS 动态参数配置文件(.cfg),核心逻辑:

    基于 dynamic_reconfigure 生成名为 MyStuff 的参数配置,定义 twoKp、twoKi 两个可动态调整的浮点参数,指定默认值、范围和描述,供运行时调参使用。

    MyStuff2.cfg

    #!/usr/bin/env python
    from dynamic_reconfigure.parameter_generator_catkin import *

    gen = ParameterGenerator()

    gen.add("beta", double_t, 0, "Madgwick filter beta", 0.1088, 0.0, 0.5)

    exit(gen.generate("imu_filter", "imu_filter", "MyStuff2"))

    src文件夹

    bias_calculator.cpp:

    IMU 偏置计算器,用于计算陀螺仪 / 加速度计的静态偏置,为后续滤波提供校准数据。

    #include <ros/ros.h>
    #include <sensor_msgs/Imu.h>

    double sum_gx = 0, sum_gy = 0, sum_gz = 0;
    int count = 0;

    void imu_callback(const sensor_msgs::Imu::ConstPtr& msg)
    {
    sum_gx += msg->angular_velocity.x;
    sum_gy += msg->angular_velocity.y;
    sum_gz += msg->angular_velocity.z;
    count++;

    ROS_INFO("Bias: %.6f %.6f %.6f | samples: %d",
    sum_gx/count, sum_gy/count, sum_gz/count, count);
    }

    int main(int argc, char** argv)
    {
    ros::init(argc, argv, "bias_calculator");
    ros::NodeHandle nh;

    ros::Subscriber sub = nh.subscribe("/imu/data", 50, imu_callback);
    ros::spin();

    return 0;
    }

    Madgwick_filter.cpp:

    Madgwick 滤波算法实现,通过梯度下降法融合 IMU 数据,输出高精度姿态角(四元数 / 欧拉角)。

    #include <ros/ros.h>
    #include <sensor_msgs/Imu.h>
    #include <geometry_msgs/Quaternion.h>
    #include <tf/transform_datatypes.h>

    #include "imu_filter/MyStuff2Config.h"

    ros::Publisher pub;

    double beta = 0.1088;

    double q0 = 1, q1 = 0, q2 = 0, q3 = 0;

    void imu_callback(const sensor_msgs::Imu::ConstPtr& msg)
    {
    double gx = msg->angular_velocity.x;
    double gy = msg->angular_velocity.y;
    double gz = msg->angular_velocity.z;

    double ax = msg->linear_acceleration.x;
    double ay = msg->linear_acceleration.y;
    double az = msg->linear_acceleration.z;

    double recipNorm;
    double s0, s1, s2, s3;
    double qDot1, qDot2, qDot3, qDot4;
    double _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3;

    gx *= 0.5;
    gy *= 0.5;
    gz *= 0.5;

    recipNorm = 1.0 / sqrt(ax*ax + ay*ay + az*az);
    ax *= recipNorm;
    ay *= recipNorm;
    az *= recipNorm;

    _2q0 = 2.0 * q0;
    _2q1 = 2.0 * q1;
    _2q2 = 2.0 * q2;
    _2q3 = 2.0 * q3;
    _4q0 = 4.0 * q0;
    _4q1 = 4.0 * q1;
    _4q2 = 4.0 * q2;
    _8q1 = 8.0 * q1;
    _8q2 = 8.0 * q2;
    q0q0 = q0 * q0;
    q1q1 = q1 * q1;
    q2q2 = q2 * q2;
    q3q3 = q3 * q3;

    s0 = -_2q2 * (ax – _2q1*az – _2q0*ay) + _2q1 * (ay + _2q0*ax – _2q3*az);
    s1 = _2q3 * (ax – _2q1*az – _2q0*ay) + _2q0 * (ay + _2q0*ax – _2q3*az) – 4.0*q1*(1.0 – 2.0*q1q1 – 2.0*q2q2);
    s2 = -_2q0 * (ax – _2q1*az – _2q0*ay) + _2q3 * (ay + _2q0*ax – _2q3*az) – 4.0*q2*(1.0 – 2.0*q1q1 – 2.0*q2q2);
    s3 = _2q1 * (ax – _2q1*az – _2q0*ay) + _2q2 * (ay + _2q0*ax – _2q3*az);

    recipNorm = 1.0 / sqrt(s0*s0 + s1*s1 + s2*s2 + s3*s3);
    s0 *= recipNorm;
    s1 *= recipNorm;
    s2 *= recipNorm;
    s3 *= recipNorm;

    qDot1 = -q1*gx – q2*gy – q3*gz – beta*s0;
    qDot2 = q0*gx + q2*gz – q3*gy – beta*s1;
    qDot3 = q0*gy – q1*gz + q3*gx – beta*s2;
    qDot4 = q0*gz + q1*gy – q2*gx – beta*s3;

    q0 += qDot1;
    q1 += qDot2;
    q2 += qDot3;
    q3 += qDot4;

    recipNorm = 1.0 / sqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3);
    q0 *= recipNorm;
    q1 *= recipNorm;
    q2 *= recipNorm;
    q3 *= recipNorm;

    geometry_msgs::Quaternion q;
    q.w = q0;
    q.x = q1;
    q.y = q2;
    q.z = q3;

    pub.publish(q);
    }

    int main(int argc, char** argv)
    {
    ros::init(argc, argv, "madgwick_imu_filter");
    ros::NodeHandle nh;

    pub = nh.advertise<geometry_msgs::Quaternion>("imu/madgwick_quaternion", 10);
    ros::Subscriber sub = nh.subscribe("/imu/data", 10, imu_callback);

    ros::spin();
    return 0;
    }

    Mahony_filter.cpp:

    Mahony 滤波算法实现,通过互补滤波融合 IMU 数据,计算量更小,适合资源受限的场景。

    #include <ros/ros.h>
    #include <sensor_msgs/Imu.h>
    #include <geometry_msgs/Quaternion.h>
    #include <tf/transform_datatypes.h>

    #include "imu_filter/MyStuffConfig.h"

    ros::Publisher pub;

    double twoKp = 0.01;
    double twoKi = 0.002;

    double q0 = 1, q1 = 0, q2 = 0, q3 = 0;
    double integralFBx = 0, integralFBy = 0, integralFBz = 0;

    void imu_callback(const sensor_msgs::Imu::ConstPtr& msg)
    {
    double gx = msg->angular_velocity.x;
    double gy = msg->angular_velocity.y;
    double gz = msg->angular_velocity.z;

    double ax = msg->linear_acceleration.x;
    double ay = msg->linear_acceleration.y;
    double az = msg->linear_acceleration.z;

    double recipNorm;
    double halfvx, halfvy, halfvz;
    double halfex, halfey, halfez;
    double qa, qb, qc;

    if(!((ax == 0.0) && (ay == 0.0) && (az == 0.0))) {
    recipNorm = 1.0 / sqrt(ax * ax + ay * ay + az * az);
    ax *= recipNorm;
    ay *= recipNorm;
    az *= recipNorm;

    halfvx = q1 * q3 – q0 * q2;
    halfvy = q0 * q1 + q2 * q3;
    halfvz = q0 * q0 – 0.5 + q3 * q3;

    halfex = ay * halfvz – az * halfvy;
    halfey = az * halfvx – ax * halfvz;
    halfez = ax * halfvy – ay * halfvx;

    if(twoKi > 0.0) {
    integralFBx += twoKi * halfex;
    integralFBy += twoKi * halfey;
    integralFBz += twoKi * halfez;

    gx += integralFBx;
    gy += integralFBy;
    gz += integralFBz;
    }

    gx += twoKp * halfex;
    gy += twoKp * halfey;
    gz += twoKp * halfez;
    }

    qa = q0;
    qb = q1;
    qc = q2;

    q0 += (-qb * gx – qc * gy – q3 * gz) * 0.5;
    q1 += ( qa * gx + qc * gz – q3 * gy) * 0.5;
    q2 += ( qa * gy – qb * gz + q3 * gx) * 0.5;
    q3 += ( qa * gz + qb * gy – qc * gx) * 0.5;

    recipNorm = 1.0 / sqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3);
    q0 *= recipNorm;
    q1 *= recipNorm;
    q2 *= recipNorm;
    q3 *= recipNorm;

    geometry_msgs::Quaternion q;
    q.w = q0;
    q.x = q1;
    q.y = q2;
    q.z = q3;

    pub.publish(q);
    }

    int main(int argc, char** argv)
    {
    ros::init(argc, argv, "mahony_imu_filter");
    ros::NodeHandle nh;

    pub = nh.advertise<geometry_msgs::Quaternion>("imu/mahony_quaternion", 10);
    ros::Subscriber sub = nh.subscribe("/imu/data", 10, imu_callback);

    ros::spin();
    return 0;
    }

    这三份源码是 ROS Melodic 下的标准实现

  • bias_calculator.cpp 用于 IMU 静态偏置校准,消除硬件误差;
  • Madgwick_filter.cpp 高精度滤波,依赖 beta 参数;
  • Mahony_filter.cpp 轻量级滤波,依赖 twoKp/twoKi 参数;
  • 所有参数均可通过 rqt_reconfigure 实时调整,无需重启节点。
  • package.xml

    <?xml version="1.0"?>
    <package format="2">
    <name>imu_filter</name>
    <version>0.0.0</version>
    <description>imu_filter</description>
    <maintainer email="a@a.com">a</maintainer>
    <license>BSD</license>

    <buildtool_depend>catkin</buildtool_depend>

    <build_depend>roscpp</build_depend>
    <build_depend>sensor_msgs</build_depend>
    <build_depend>geometry_msgs</build_depend>
    <build_depend>tf</build_depend>
    <build_depend>tf2</build_depend>
    <build_depend>dynamic_reconfigure</build_depend>

    <exec_depend>roscpp</exec_depend>
    <exec_depend>sensor_msgs</exec_depend>
    <exec_depend>geometry_msgs</exec_depend>
    <exec_depend>tf</exec_depend>
    <exec_depend>tf2</exec_depend>
    <exec_depend>dynamic_reconfigure</exec_depend>
    </package>

    CMakeLists.txt

    cmake_minimum_required(VERSION 2.8.3)
    project(imu_filter)

    # 1. 查找所有依赖(顺序固定)
    find_package(catkin REQUIRED COMPONENTS
    roscpp
    sensor_msgs
    geometry_msgs
    tf
    tf2
    dynamic_reconfigure
    )

    # 2. 生成动态参数头文件(仅针对需要的两个滤波器)
    generate_dynamic_reconfigure_options(
    cfg/MyStuff.cfg
    cfg/MyStuff2.cfg
    )

    # 3. Catkin 包导出配置(顺序固定)
    catkin_package(
    CATKIN_DEPENDS roscpp sensor_msgs geometry_msgs tf tf2 dynamic_reconfigure
    )

    # 4. 头文件包含路径(顺序固定)
    include_directories(
    ${catkin_INCLUDE_DIRS}
    )

    # 5. 第一步:创建可执行目标(严格按「先创建,后操作」)
    add_executable(madgwick_imu_filter src/Madgwick_filter.cpp)
    add_executable(mahony_imu_filter src/Mahony_filter.cpp)
    add_executable(bias_calculator src/bias_calculator.cpp)

    # 6. 第二步:添加依赖(仅给需要动态参数的两个目标加)
    add_dependencies(madgwick_imu_filter ${PROJECT_NAME}_gencfg ${catkin_EXPORTED_TARGETS})
    add_dependencies(mahony_imu_filter ${PROJECT_NAME}_gencfg ${catkin_EXPORTED_TARGETS})
    # bias_calculator 无动态参数,无需添加此依赖

    # 7. 第三步:链接库(所有目标都需要,顺序在创建和依赖之后)
    target_link_libraries(madgwick_imu_filter ${catkin_LIBRARIES})
    target_link_libraries(mahony_imu_filter ${catkin_LIBRARIES})
    target_link_libraries(bias_calculator ${catkin_LIBRARIES})

    编译验证步骤

    cd ~/Desktop/wireshark_ws
    catkin_make clean # 清空之前的错误缓存
    catkin_make # 执行编译

    imu_filter 包的核心功能

    这个包包含 3 个可执行文件,分工明确,都是围绕 IMU(惯性测量单元)的实用功能:

    可执行文件核心功能适用场景
    madgwick_imu_filter 基于Madgwick 算法融合 IMU 的角速度 + 加速度数据,输出四元数(姿态) 低计算量、快速收敛的姿态解算
    mahony_imu_filter 基于Mahony 算法融合 IMU 数据,输出四元数(姿态) 精度更高、鲁棒性更好的姿态解算
    bias_calculator 计算陀螺仪偏置(静止时的角速度平均值) 校准 IMU、消除陀螺仪零漂
    补充说明:
    • 两个滤波算法都是无磁强计版本(只用到角速度 + 加速度),适合没有磁力计的 IMU;
    • 输出的四元数可以直接转换成欧拉角(滚转 / 俯仰 / 偏航),用于机器人姿态控制、导航等场景;
    • 偏置计算器是 IMU 校准的基础步骤,能大幅提升姿态解算的精度。

    如何使用 imu_filter(完整步骤)

    前置条件:
  • 你的 ROS 环境中已经有发布 IMU 数据的节点(话题名默认是 /imu/data,包含 angular_velocity 和 linear_acceleration 字段);
  • 已编译好 imu_filter 包,并激活工作空间:source ~/Desktop/wireshark_ws/devel/setup.bash。
  • 1. 第一步:校准陀螺仪偏置(必做,提升精度)

    陀螺仪静止时会有微小的零漂(比如显示 0.01 rad/s 但实际没动),先校准:

    # 1. 确保IMU静止放置(放在水平桌面,不要动)
    # 2. 运行偏置计算器
    rosrun imu_filter bias_calculator
    # 3. 此时终端会持续打印:Bias: 0.001234 0.000567 0.000890 | samples: 123
    # 等待采样数达到500+(数据稳定),记录下最终的偏置值(比如:gx_bias=0.0012, gy_bias=0.0006, gz_bias=0.0009)

    2. 第二步:运行姿态解算节点(二选一)
    方式 1:运行 Madgwick 滤波器

    # 1. 先启动IMU数据发布节点(比如你的IMU驱动,示例:rosrun imu_tools imu_publisher)
    # 2. 运行Madgwick滤波器
    rosrun imu_filter madgwick_imu_filter
    # 3. 查看输出的姿态四元数
    rostopic echo /imu/madgwick_quaternion

    方式 2:运行 Mahony 滤波器

    rosrun imu_filter mahony_imu_filter
    # 查看输出
    rostopic echo /imu/mahony_quaternion

    3. 第三步:(可选)将四元数转换成欧拉角

    如果需要更直观的 “滚转 / 俯仰 / 偏航” 角度,可以用 ROS 自带工具转换:

    # 订阅四元数话题,转换成欧拉角(单位:弧度)
    rostopic echo /imu/madgwick_quaternion –noarr | python -c "
    import sys
    import math
    for line in sys.stdin:
    if 'w:' in line or 'x:' in line or 'y:' in line or 'z:' in line:
    exec(line.strip())
    if '—' in line:
    # 四元数转欧拉角(roll/pitch/yaw)
    roll = math.atan2(2*(w*x + y*z), 1-2*(x*x + y*y))
    pitch = math.asin(2*(w*y – z*x))
    yaw = math.atan2(2*(w*z + x*y), 1-2*(y*y + z*z))
    print(f'Roll: {roll:.3f} rad ({roll*180/math.pi:.1f}°), Pitch: {pitch:.3f} rad ({pitch*180/math.pi:.1f}°), Yaw: {yaw:.3f} rad ({yaw*180/math.pi:.1f}°)')
    w=x=y=z=0
    "

    三、进阶使用:修改参数 / 话题名

    如果你的 IMU 话题不是 /imu/data,或想调整滤波参数,可以通过 ROS 参数重配置:

    1. 修改 IMU 订阅话题

    # 运行时指定话题名(比如你的IMU话题是 /my_imu/data)
    rosrun imu_filter madgwick_imu_filter _imu_topic:=/my_imu/data

    2. 调整滤波算法参数(动态参数)

    我们之前配置的 cfg 文件支持运行时动态调参,无需重启节点:

    # 启动动态调参工具
    rosrun rqt_reconfigure rqt_reconfigure
    # 在界面中找到 imu_filter 节点,可调整:
    # – Mahony算法:twoKp(比例增益)、twoKi(积分增益)
    # – Madgwick算法:beta(增益系数)
    # 调整后实时生效,能直观看到姿态解算效果的变化

    四、实用建议

  • 校准优先:每次使用 IMU 前,先运行 bias_calculator 校准陀螺仪偏置,零漂会大幅降低;
  • 算法选择:
    • 追求速度选 Madgwick(计算量小,适合嵌入式);
    • 追求精度选 Mahony(鲁棒性更好,抗噪声);
  • 数据要求:IMU 发布的 /imu/data 话题必须包含 angular_velocity(角速度)和 linear_acceleration(线加速度),否则滤波器会失效;
  • 可视化:可以用 rviz 查看四元数对应的姿态
  • rviz
    # 添加「Pose」显示,话题选择 /imu/madgwick_quaternion,就能看到IMU的姿态变化

    赞(0)
    未经允许不得转载:171主机测评 » IMU(惯性测量单元)--基础
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址