3、传感器数据接入:让机器人真正“感知”世界

传感器数据接入,说白了就是给机器人装上眼睛、耳朵和平衡感。没有这一步,导航算法再牛也是纸上谈兵。我个人习惯把传感器接入比作“人吃饭”——数据质量不好,后面消化(规划)肯定出问题。

这一章我们重点聊三样东西:激光雷达、里程计、IMU。它们各自扮演什么角色?怎么配才能让 Nav2 跑得稳?嗯,咱们一个一个来。

核心观点:传感器数据接入不是简单的“插上线就能用”。驱动配置、坐标系对齐、时间戳同步,任何一个环节出问题,导航都会翻车。

3.1 激光雷达驱动配置

激光雷达是 Nav2 的“主视觉”。它提供二维点云数据,用于建图、定位和避障。我见过不少新手直接跑默认参数,结果地图歪了还找不到原因。

先看一个典型的 RPLIDAR A1 驱动配置(launch 文件片段):

<node pkg="rplidar_ros" exec="rplidar_node" name="rplidar_node">
  <param name="serial_port" value="/dev/ttyUSB0"/>
  <param name="serial_baudrate" value="115200"/>
  <param name="frame_id" value="laser_frame"/>
  <param name="angle_compensate" value="true"/>
  <param name="scan_mode" value="Standard"/>
</node>

这里有几个关键点:

  • serial_port:别写死。我建议用 udev 规则绑定,否则换个 USB 口就找不到设备。
  • frame_id:必须和你的 TF 树一致。我习惯统一叫 laser_frame,然后在 URDF 里把它挂到机器人基座上。
  • scan_mode:不同模式影响扫描频率和精度。Standard 模式够用,但如果你需要高速移动,可以试试 Boost 模式。
小技巧:调试激光雷达时,先跑 rviz2 看 /scan 话题。如果点云是乱的,八成是波特率或串口权限问题。

我曾经在一个项目里被激光雷达坑惨了——点云总是断断续续。查了两天,最后发现是 USB 延长线太长导致信号衰减。嗯,硬件问题有时候比软件更隐蔽。

3.2 里程计数据发布

里程计告诉机器人“我走了多远”。Nav2 用它来做短期预测和状态估计。常见的里程计来源有两种:轮式编码器和视觉/激光里程计。

先看轮式里程计的标准发布方式:

# 伪代码示例:发布 Odometry 消息
from nav_msgs.msg import Odometry
from geometry_msgs.msg import TransformStamped

odom_msg = Odometry()
odom_msg.header.stamp = self.get_clock().now().to_msg()
odom_msg.header.frame_id = "odom"
odom_msg.child_frame_id = "base_footprint"

# 位置和姿态
odom_msg.pose.pose.position.x = x
odom_msg.pose.pose.position.y = y
odom_msg.pose.pose.orientation.z = sin(yaw/2)
odom_msg.pose.pose.orientation.w = cos(yaw/2)

# 速度
odom_msg.twist.twist.linear.x = vx
odom_msg.twist.twist.angular.z = wz

self.odom_pub.publish(odom_msg)

这里有个容易踩的坑:frame_id 和 child_frame_id 必须和 TF 树匹配。我见过有人把 child_frame_id 写成 base_link,结果定位直接崩了。

字段 含义 常见错误
header.frame_id 里程计坐标系,通常是 "odom" 写成 "map" 或 "base_link"
child_frame_id 机器人基座坐标系,通常是 "base_footprint" 写成 "laser_frame"
pose 相对于 odom 的位姿 忘记更新协方差矩阵
twist 当前速度 单位搞错(m/s vs cm/s)
注意:里程计数据会随时间漂移。纯轮式里程计在长距离导航时误差会累积,这时候就需要 IMU 或激光匹配来修正。

3.3 IMU 数据融合

IMU 提供角速度和加速度。它不直接用于定位,但能大幅提升状态估计的平滑度。我个人习惯把 IMU 当作“稳定器”——尤其在机器人急转弯或经过颠簸路面时,IMU 能补上里程计的短板。

IMU 数据融合通常有两种方式:

  • 松耦合:IMU 和里程计各自独立发布,由 robot_localization 包做 EKF 融合。这种方式简单,适合大多数场景。
  • 紧耦合:IMU 数据直接喂进状态估计器。精度更高,但实现复杂。我一般只在需要高精度定位时用。

下面是一个 robot_localization 的 EKF 配置片段:

ekf_filter_node:
  ros__parameters:
    frequency: 30.0
    sensor_timeout: 0.1
    two_d_mode: true
    publish_tf: true
    map_frame: map
    odom_frame: odom
    base_link_frame: base_footprint
    world_frame: odom

    # 里程计输入
    odom0: /odom
    odom0_config: [true, true, false,
                   false, false, true,
                   true, true, false,
                   false, false, true,
                   true, true, false]

    # IMU 输入
    imu0: /imu/data
    imu0_config: [false, false, false,
                  true, true, true,
                  false, false, false,
                  true, true, true,
                  false, false, false]

注意 odom0_config 和 imu0_config 这两个数组。它们告诉 EKF 哪些维度参与融合。我刚开始用的时候总搞混顺序,后来发现官方文档里有个对照表,直接复制粘贴最保险。

避坑指南:IMU 的 yaw 角漂移很严重。如果你只用 IMU 做航向估计,记得定期用激光或视觉数据重置。我曾经在一个室内项目里忘了做这件事,结果机器人转了十圈后,航向偏了 30 度。

3.4 传感器消息类型解析

ROS2 里每种传感器都有标准消息类型。理解它们的数据结构,能帮你更快地调试问题。

传感器 消息类型 关键字段
激光雷达 sensor_msgs/LaserScan ranges[], angle_min, angle_max, range_min, range_max
里程计 nav_msgs/Odometry pose.pose, twist.twist, pose.covariance
IMU sensor_msgs/Imu orientation, angular_velocity, linear_acceleration
点云 sensor_msgs/PointCloud2 fields[], data, width, height

举个例子,LaserScan 的 ranges 数组长度由 angle_min、angle_max 和 angle_increment 决定。如果你发现点云有空洞,先检查 range_min 和 range_max 是否合理。我遇到过有人把 range_max 设成 0.1,结果雷达直接“瞎了”。

3.5 传感器数据接入整体流程

下面这张图展示了传感器数据从硬件到 Nav2 的完整链路。我建议你把它存下来,调试时对照着看。

传感器数据接入流程 传感器数据接入整体流程 激光雷达硬件 轮式编码器 IMU 硬件 rplidar 驱动 编码器驱动 IMU 驱动 /scan (LaserScan) /odom (Odometry) /imu/data (Imu) robot_localization (EKF) /odom_filtered (Nav2 输入) 注意:坐标系必须一致 odom → base_footprint → laser_frame

从这张图你能看到,数据从硬件到驱动,再到标准消息,最后通过 EKF 融合成 Nav2 能用的 /odom_filtered。每一步都有坑,但只要你把坐标系和时间戳对齐了,后面就顺了。

重要提醒:传感器数据的时间戳必须单调递增。如果出现时间倒流(比如系统时钟被 NTP 回拨),EKF 会直接罢工。我建议在嵌入式平台上用硬件时间戳,或者至少用 use_sim_time:=true 统一管理。

好了,传感器接入这块就聊到这儿。记住一句话:数据质量决定导航上限。驱动配好了,消息发对了,融合调稳了,你的机器人就成功了一半。