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) |
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 哪些维度参与融合。我刚开始用的时候总搞混顺序,后来发现官方文档里有个对照表,直接复制粘贴最保险。
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 的完整链路。我建议你把它存下来,调试时对照着看。
从这张图你能看到,数据从硬件到驱动,再到标准消息,最后通过 EKF 融合成 Nav2 能用的 /odom_filtered。每一步都有坑,但只要你把坐标系和时间戳对齐了,后面就顺了。
use_sim_time:=true 统一管理。
好了,传感器接入这块就聊到这儿。记住一句话:数据质量决定导航上限。驱动配好了,消息发对了,融合调稳了,你的机器人就成功了一半。