适用平台:ROS 2 Humble、C++17
覆盖传感器:机械式/固态 LiDAR、RGB/RGB-D 相机、毫米波雷达、IMU、GNSS
工程包:multi_sensor_calibration
主标定文件:config/master_calibration.yaml
1. 目标与边界
该模块不替代 Kalibr、OpenCalib、厂商标定程序或专业优化器,而是负责把各类标定工具产生的结果统一管理,并完成上线前的自动检查:
- 统一保存相机内参、传感器外参和质量状态;
- 从主标定文件发布静态 TF;
- 检查各传感器时间戳、数据超时和相对时间偏差;
- 自动评估 LiDAR–LiDAR / RGB-D 点云的地面高度一致性;
- 将 LiDAR 点投影到相机图像,生成可视化叠加图;
- 根据高分辨率主内参生成裁剪、缩放后的运行分辨率
CameraInfo; - 导出可写回 URDF/Xacro 的 fixed-joint 片段;
- 形成可重复执行的采集、标定、验证和验收流程。
模块按以下原则工作:
先时间同步 ↓ 再单传感器内参/噪声标定 ↓ 再求外参 ↓ 多帧、多个区域验证 ↓ 写入 master_calibration.yaml ↓ 导出 URDF / static TF ↓ 导航、建图和感知闭环验证2. 对参考标定文档的继承与改进
参考文档采用“16 线机械 LiDAR 为基准,手工修正固态 LiDAR 与多路 RGB-D 外参”的路线。该路线中的关键经验被保留:
- 从 URDF 中读取外参初值;
- 将不同传感器点云按时间戳配对;
- 最低帧率为 10 Hz 时,使用
0.1 s作为人工选帧上限; - 在 CloudCompare 中给点云设置不同颜色并应用初始变换;
- 在多个地面位置比较两组点云的
z值; - 将相邻地面点高度差控制在
5 cm以内; - 得到结果后更新 URDF,并再次通过 TF 检查;
- 多路 RGB-D 使用同一参考 LiDAR,检查各相机点云地面高度的一致性。
本工程在此基础上增加了以下能力:
| 原人工步骤 | 工程化实现 |
|---|---|
| 手工寻找最近时间戳 PCD | select_nearest_timestamp_pairs.py |
| 人工选几个地面点比较 z | pointcloud_alignment_validator_node网格化统计 |
| 只验证单帧 | ROS 2 在线多帧连续验证 |
| 容易混淆 TF 方向 | 主文件强制parent -> child约定 |
| 直接修改 URDF | 先写主标定文件,再导出 URDF 片段 |
| 仅看点云重合 | 同时输出 MAE、RMSE、P95 和匹配网格数 |
| 相机外参只看 RGB-D 点云 | 增加 LiDAR 到图像投影叠加 |
| 分辨率变化后手工改内参 | 自动派生CameraInfo |
2.1 TF 方向必须统一
ROS 2 命令:
ros2 run tf2_ros tf2_echo<target_frame><source_frame>表示查询source_frame中的点如何变换到target_frame。
例如:
ros2 run tf2_ros tf2_echo laser_link livox1_link查询的是:
T_laser_livox1而master_calibration.yaml保存的是与 URDF fixed joint 一致的:
T_parent_child两者不一定是同一个直接变换。禁止把tf2_echo输出不加判断地写入任意<origin>。应先确认 TF 树路径和变换方向。
3. 软件架构
multi_sensor_calibration │ ├── calibration_manager_node │ ├── 加载 master_calibration.yaml │ ├── 校验 frame、内参和数值合法性 │ ├── 发布已启用的 static TF │ ├── /reload │ └── /export_urdf │ ├── timestamp_monitor_node │ ├── 检查消息是否超时 │ ├── 检查时间戳倒退 │ └── 检查相对参考传感器的最新时间偏差 │ ├── pointcloud_alignment_validator_node │ ├── 近似时间同步两路 PointCloud2 │ ├── 将目标点云转换到参考坐标系 │ ├── XY 网格中提取最低 z 作为地面高度 │ └── 输出 MAE / RMSE / P95 / PASS │ ├── lidar_camera_projection_validator_node │ ├── 同步图像与点云 │ ├── 读取 CameraInfo │ ├── TF 变换到相机光学坐标系 │ └── 发布深度着色投影图和诊断 │ └── camera_info_deriver_node ├── 读取高分辨率 master intrinsics ├── 应用 crop + resize ├── 发布派生 CameraInfo └── 保存标准 camera_info YAML4. 主标定文件
4.1 外参定义
extrinsic:parent_frame:base_linkchild_frame:livox1_linkxyz:[x,y,z]rpy:[roll,pitch,yaw]publish_static_tf:false含义与 URDF 一致:
<jointtype="fixed"><parentlink="base_link"/><childlink="livox1_link"/><originxyz="x y z"rpy="roll pitch yaw"/></joint>所有角度单位均为弧度。
在标定未通过前保持:
publish_static_tf:false避免样例值或粗测值覆盖机器人现有 TF。
4.2 相机内参
建议只保存一次最高分辨率、未裁剪传感器模式的主内参:
intrinsics:frame_id:rgbd1_color_optical_framewidth:1280height:720distortion_model:plumb_bobd:[...]k:[...]r:[...]p:[...]推荐流程:
- 原始高分辨率完成一次完整内参和外参标定;
- 保存为
master_calibration.yaml; - 每种运行分辨率生成派生
CameraInfo; - 分辨率变化时外参保持不变;
- 每种分辨率运行投影验证;
- 误差明显变大时,检查裁剪、binning、ISP sensor mode,再决定是否重标内参。
5. 编译
cdsensor_driver_factory_wssource/opt/ros/humble/setup.bash rosdepinstall\--from-paths src\--ignore-src\-r-ycolcon build\--symlink-install\--cmake-args-DCMAKE_BUILD_TYPE=Releasesourceinstall/setup.bash6. 标定前检查
6.1 坐标系定义
建议固定:
base_link: x 前 y 左 z 上 camera optical frame: x 右 y 下 z 前明确以下 frame:
base_link laser_link livox1_link rgbd1_color_optical_frame front_radar_link imu_link检查 TF 树:
ros2 run tf2_tools view_frames ros2 run tf2_ros tf2_echo base_link laser_link ros2 run tf2_ros tf2_echo laser_link livox1_link6.2 时间戳来源
逐个确认:
- LiDAR:设备时钟、PTP 或主机接收时间;
- 相机:曝光开始、曝光结束或主机回调时间;
- IMU:设备采样时钟;
- Radar:CAN/以太网帧时间;
- GNSS:PPS/GNSS time;
header.stamp是否在驱动中被二次覆盖。
先运行时间监测:
ros2 run multi_sensor_calibration timestamp_monitor_node\--ros-args\--params-file install/multi_sensor_calibration/share/\multi_sensor_calibration/config/timestamp_monitor.yaml标定数据建议:
- 静态 bag:验证时间戳稳定、TF、噪声和静态点云;
- 动态 bag:验证时间偏移对投影和点云重合的影响;
- 相机–IMU 标定必须有充分的旋转与平移激励;
- LiDAR–IMU 标定必须包含多方向运动,避免仅直线行驶。
录制示例:
ros2 bag record\/rslidar_points\/livox/lidar\/rgbd_filter_1\/sensing/camera/front/image_raw\/sensing/camera/front/camera_info\/imu/data\/tf\/tf_static7. LiDAR–LiDAR / RGB-D 点云标定
7.1 人工 CloudCompare 路线
安装:
sudoaptinstallcloudcompare操作顺序:
- 使用最近时间戳配对工具找帧;
- 将参考点云和目标点云导入 CloudCompare;
- 设置不同颜色;
- 使用 TF/URDF 外参作为初值;
- 应用目标点云到参考坐标系的变换;
- 从正视、侧视和俯视三个方向检查;
- 在近、中、远以及左右区域检查地面高度;
- 微调 roll/pitch,再调整 z;最后调整 yaw/x/y;
- 至少使用多个时刻重复检查;
- 将结果写回主标定文件,而非直接覆盖生产 URDF。
最近时间戳配对:
python3\src/multi_sensor_calibration/tools/select_nearest_timestamp_pairs.py\/data/lidar\/data/livox_front\--max-diff0.1\--output/data/lidar_livox_pairs.csv7.2 自动地面网格验证
启动:
ros2 run multi_sensor_calibration\pointcloud_alignment_validator_node\--ros-args\--params-file install/multi_sensor_calibration/share/\multi_sensor_calibration/config/pointcloud_alignment.yaml算法:
参考点云 ───────────────┐ ├─ ROI 过滤 目标点云 ─ TF到参考系 ──┘ ↓ XY 划分网格 ↓ 每个网格取最小 z 近似地面 ↓ 比较相同网格的高度差 ↓ MAE / RMSE / P95 / matched_cells默认验收:
timestamp skew <= 0.1 s matched_cells >= 20 P95(|Δz|) <= 0.05 mP95 <= 5 cm是对参考文档“多个位置地面 z 差控制在 5 cm 内”的工程化实现。实际阈值应根据雷达量程、地面平整度、时间同步和业务需求重新标定。
7.3 不能只看地面
只看地面主要约束:
- roll;
- pitch;
- z。
还必须利用立柱、墙角、路缘、标定板等非共面结构约束:
- yaw;
- x;
- y。
推荐最终场景同时包含:
平整地面 + 垂直墙面 + 立柱/墙角 + 远近目标8. LiDAR–Camera / RGB-D–LiDAR 标定
8.1 标定方法
可选:
- 标定板/AprilTag/ChArUco + LiDAR 平面或边缘;
- 专用 LiDAR–Camera 标定工具;
- 初值来自机械测量与 URDF;
- 最后用在线投影节点验证。
8.2 投影验证
投影节点假设输入为去畸变后的图像:
ros2 run multi_sensor_calibration\lidar_camera_projection_validator_node\--ros-args\--params-file install/multi_sensor_calibration/share/\multi_sensor_calibration/config/lidar_camera_projection.yaml输出:
/lidar_camera_projection_validator/overlay /lidar_camera_projection_validator/diagnostics在 RViz2 或图像查看器中检查:
- 墙面点是否落在墙面;
- 门框、柱子、路缘等边缘是否重合;
- 近、中、远距离是否都一致;
- 图像中心正确但边缘偏移:优先检查内参和畸变;
- 全图同方向平移:优先检查外参平移;
- 一侧对齐另一侧不齐:优先检查旋转;
- 运动物体或转弯时错位:优先检查时间同步。
注意:节点中的in_bounds_ratio只用于判断数据链路是否可投影,不代表外参精度。最终精度必须通过叠加图和目标边缘误差判断。
9. 相机分辨率变化与 CameraInfo 派生
对于主图像裁剪区域:
crop = (crop_x, crop_y, crop_width, crop_height) target = (target_width, target_height)缩放系数:
sx = target_width / crop_width sy = target_height / crop_height派生内参:
fx' = fx × sx fy' = fy × sy cx' = (cx - crop_x) × sx cy' = (cy - crop_y) × sy启动:
ros2 launch multi_sensor_calibration\camera_info_deriver.launch.py\calibration_file:=/path/master_calibration.yaml\camera_name:=rgbd1\target_width:=640\target_height:=480保存派生文件:
ros2servicecall\/camera_info_deriver/save\std_srvs/srv/Trigger{}以下情况不能只做比例换算:
- 运行模式使用不同传感器读出区域;
- ISP 做了数字防抖或几何矫正;
- binning 与 skipping 规则未知;
- 图像经过非等比例缩放;
- 驱动自动裁剪但未提供 ROI;
- 深度和彩色图使用不同的对齐模式。
10. Camera–IMU 与 LiDAR–IMU
10.1 Camera–IMU
推荐流程:
- 相机内参先标定;
- IMU 采样率和噪声参数先确认;
- 录制有充分六自由度激励的数据;
- 使用 Kalibr 类工具估计外参和时间偏移;
- 将结果转换为 ROS frame 定义;
- 写入主标定文件;
- 在 VIO 中检查静止漂移和快速转动稳定性。
常见失败原因:
- 运动只有平移、没有旋转;
- 画面模糊;
- rolling shutter 未建模;
- IMU 时间戳由主机到达时间代替;
- IMU 单位或轴向错误;
- 相机光学 frame 与机身 frame 混用。
10.2 LiDAR–IMU
推荐:
- 静态数据估计 IMU 偏置和噪声;
- 动态数据包含绕三个轴旋转;
- 使用 LiDAR–IMU 标定工具或 LIO 联合优化;
- 通过 FAST-LIO2 等系统检查地图重影;
- 检查急转弯和颠簸场景。
验收重点:
- 静止点云不出现周期摆动;
- 快速转弯时墙面不分层;
- 长直线与回环后地图不明显错层;
- 重力方向与
base_linkz 轴一致。
11. Radar–LiDAR / Radar–Camera
毫米波雷达角分辨率较低,建议使用:
- 角反射器;
- 大型金属平面;
- 多个已知位置静态目标;
- 低速直线运动目标用于速度方向检查。
Radar–LiDAR 流程:
选择稳定雷达目标 ↓ LiDAR 中提取对应反射器/车辆 ↓ 初值来自机械安装 ↓ 优化 yaw + translation ↓ 多距离、多方位重复验证验收:
- 近、中、远距离目标位置一致;
- 左右目标无系统偏差;
- 速度正负方向正确;
- Radar frame 到
base_link的轴向定义正确; - 不能用单个正前方目标同时确定所有自由度。
12. 标定结果管理
12.1 修改与发布
- 编辑
master_calibration.yaml; - 校验:
python3\src/multi_sensor_calibration/tools/validate_master_calibration.py\src/multi_sensor_calibration/config/master_calibration.yaml- 标定通过后,将对应项设为:
publish_static_tf:true- 启动:
ros2 launch multi_sensor_calibration\calibration_manager.launch.py\calibration_file:=/path/master_calibration.yaml- 热加载:
ros2servicecall\/calibration_manager/reload\std_srvs/srv/Trigger{}静态 TF 在生产系统中不应频繁更新。热加载主要用于调试,最终仍建议重启相关节点并完整验证。
12.2 导出 URDF
ros2servicecall\/calibration_manager/export_urdf\std_srvs/srv/Trigger{}默认输出:
/tmp/calibration_fixed_joints.urdf.xacro确认后再合并到机器人描述包。不要让标定管理器和robot_state_publisher同时发布同名 child frame 的不同静态 TF。
13. 验收清单
13.1 数据与时间
- 每个话题
header.stamp来源明确; - 静态 rosbag 无时间戳倒退;
- 动态 rosbag 的配对偏差满足要求;
- PTP/PPS/硬件同步状态已记录;
- 运动场景中无明显时间错位。
13.2 相机
- 每种原始 sensor mode 完成内参验证;
- 重投影误差满足项目要求;
- 派生分辨率投影验证通过;
camera_info.frame_id为光学坐标系;- RGB 与深度对齐模式明确。
13.3 LiDAR–LiDAR / RGB-D 点云
- 时间差不超过配置阈值;
- 多帧 P95 地面高度差通过;
- 地面、墙面和立柱同时对齐;
- 近、中、远区域均检查;
- 点云无由运动畸变造成的假错位。
13.4 LiDAR–Camera
- 点云可稳定投影到图像;
- 中心与边缘都对齐;
- 静态和动态场景均检查;
- 分辨率变化后重新验证;
- 相机畸变模型与图像类型一致。
13.5 IMU 与系统闭环
- IMU 轴向、单位和重力方向正确;
- VIO/LIO 无明显地图重影;
- 急转弯时融合稳定;
- URDF 与主标定文件一致;
- 导航、障碍物投影、语义融合闭环通过。
14. 建议的版本记录
每次标定保存:
calibration_id 机器人序列号 传感器型号与序列号 安装状态/是否拆装 标定日期 数据包路径和 SHA256 使用工具及版本 内参和外参 时间偏移 验证指标 操作人和审核人标定文件建议纳入 Git,但原始 rosbag/PCD 使用对象存储或数据版本管理系统保存。
15. 当前实现限制
- 点云验证采用最低 z 网格近似地面,不是完整 ICP;
- 投影节点假设图像已去畸变,不在节点内应用畸变模型;
timestamp_monitor_node比较的是各话题最新时间戳,不替代离线相关性时间偏移估计;- Radar 只提供流程和统一外参管理,未实现特定厂商目标关联优化;
- IMU 外参求解仍需外部标定器;
- 示例
master_calibration.yaml为安全占位值,默认不发布 TF; - 当前环境未执行 ROS 2
colcon build,上线前必须在 Humble 环境编译和 rosbag 回放验证。
16. 快速启动顺序
# 1. 校验主文件python3 src/multi_sensor_calibration/tools/validate_master_calibration.py\src/multi_sensor_calibration/config/master_calibration.yaml# 2. 启动主标定与验证节点ros2 launch multi_sensor_calibration full_sensor_calibration.launch.py\calibration_file:=/path/master_calibration.yaml# 3. 查看诊断ros2 topicecho/calibration_manager/diagnostics ros2 topicecho/timestamp_monitor/diagnostics ros2 topicecho/pointcloud_alignment_validator/diagnostics ros2 topicecho/lidar_camera_projection_validator/diagnostics# 4. 查看投影图ros2 run rqt_image_view rqt_image_view\/lidar_camera_projection_validator/overlay# 5. 验证后导出 URDFros2servicecall /calibration_manager/export_urdf\std_srvs/srv/Trigger{}