第3章 传感器选型与驱动开发
做SLAM导航系统,传感器就是机器人的「眼睛」和「小脑」。选错了传感器,算法再牛也白搭。这一章我带你从工程角度把激光雷达、IMU、轮式里程计挨个捋一遍,顺便把ROS2驱动写明白。
3.1 激光雷达选型——别只看线数
很多人一上来就问「32线还是16线?」。其实选激光雷达,我习惯先看使用场景。室内导航?2D雷达就够了,比如RPLIDAR A1/A2,性价比高。室外或者需要3D感知?那得考虑机械式或固态雷达。
我个人比较关注这几个参数:
- 测距半径:室内10-20米足够,室外至少30米以上。
- 角分辨率:0.3°以下建图才细腻。0.6°以上边缘容易锯齿。
- 帧率:10Hz是底线,移动机器人最好15Hz以上。
- 抗环境光:室外强光下很多雷达会丢点,选的时候看看有没有抗干扰算法。
💡 我的经验: 之前做一个园区物流项目,贪便宜买了某款低端3D雷达,一到下午阳光斜射就疯狂丢点,最后换了Ouster OS1才稳定。选型时别只看参数表,最好拿实际场景测一测。
3.2 IMU标定——小东西,大脾气
IMU这东西,出厂时都有偏差。零偏、尺度因子、轴间耦合……不标定直接上SLAM,位姿估计分分钟漂到天上去。我常用的标定方法是六面法:把IMU静止放在六个正交方向,每个方向采集几十秒数据。
标定流程大致是:
- 固定IMU,采集静止数据(至少30秒)。
- 绕X轴旋转90°,采集;再绕Y轴、Z轴重复。
- 用最小二乘拟合零偏和尺度。
- 验证:标定后静止时加速度模值应接近9.8 m/s²。
⚠️ 注意: 我曾经在标定时忽略了温度影响,冬天和夏天零偏差了近10%。后来我都在恒温箱里做标定,或者用imu_utils这类工具做在线温补。千万别小看温度漂移。
3.3 轮式里程计模型——差速底盘怎么算?
两轮差速模型是基础。左右轮编码器分别输出脉冲数,换算成轮子移动距离,再推算机器人位姿。公式其实不复杂:
// 伪代码:差速里程计更新
double d_left = (left_ticks - last_left_ticks) * ticks_to_meters;
double d_right = (right_ticks - last_right_ticks) * ticks_to_meters;
double d_center = (d_left + d_right) / 2.0;
double d_theta = (d_right - d_left) / wheel_base;
x += d_center * cos(theta + d_theta/2);
y += d_center * sin(theta + d_theta/2);
theta += d_theta;
这里wheel_base是左右轮间距,ticks_to_meters需要根据轮子直径和编码器线数算。我习惯在代码里加一个标定参数,因为轮子直径会磨损,跑一段时间就得重新量一下。
🔧 避坑指南: 轮子打滑是里程计最大的敌人。我曾在瓷砖地面上做测试,急转弯时里程计直接偏了15°。后来加了IMU航向修正,效果好了很多。纯里程计只适合短距离、低打滑场景。
3.4 ROS2驱动编写——从硬件到话题
ROS2驱动说白了就是把传感器数据包装成标准消息发出去。以激光雷达为例,我一般写一个lidar_driver节点:
- 初始化串口或以太网接口。
- 解析数据包(注意校验和、帧头)。
- 填充
sensor_msgs::msg::LaserScan或PointCloud2。 - 用
rclcpp::Publisher发布。
下面是一个极简的ROS2驱动骨架(C++):
#include <rclcpp/rclcpp.hpp>
#include <sensor_msgs/msg/laser_scan.hpp>
class LidarDriver : public rclcpp::Node {
public:
LidarDriver() : Node("lidar_driver") {
pub_ = this->create_publisher<sensor_msgs::msg::LaserScan>("scan", 10);
timer_ = this->create_wall_timer(
std::chrono::milliseconds(100),
std::bind(&LidarDriver::publish_scan, this));
}
private:
void publish_scan() {
auto msg = sensor_msgs::msg::LaserScan();
msg.header.stamp = this->now();
msg.header.frame_id = "laser_frame";
// ... 填充 ranges, intensities 等
pub_->publish(msg);
}
rclcpp::Publisher<sensor_msgs::msg::LaserScan>::SharedPtr pub_;
rclcpp::TimerBase::SharedPtr timer_;
};
IMU驱动类似,发布sensor_msgs::msg::Imu。轮式里程计则发布nav_msgs::msg::Odometry,同时广播TF变换。我个人习惯把里程计和TF放在同一个节点里,减少延迟。
⚡ 效率小技巧: 在嵌入式平台上,尽量用
rclcpp::Publisher的QoS设置为rclcpp::QoS(10).best_effort(),减少丢包和延迟。我曾在树莓派上跑,默认QoS导致扫描数据卡顿,改成best_effort后流畅很多。
3.5 本章小结
传感器选型是系统工程,没有「最好」,只有「最合适」。激光雷达看场景,IMU必须标定,轮式里程计要结合运动模型。驱动开发的核心是稳定、低延迟地把数据送进ROS2管道。嗯,这些基础打牢了,后面SLAM和导航才能跑得稳。