保姆级教程:在ROS2 Humble上为TurtleBot4仿真环境手动编译Cartographer(含源码修改输出轨迹)
从零构建ROS2 Humble下的CartographerTurtleBot4仿真环境深度定制指南在机器人领域实时定位与地图构建(SLAM)一直是核心技术难题。对于使用TurtleBot4进行研究的开发者而言官方提供的Cartographer二进制包往往无法满足特定需求比如轨迹记录、算法深度定制等场景。本文将带你从源码开始在ROS2 Humble环境下为TurtleBot4仿真环境手动编译Cartographer并实现轨迹输出功能。1. 环境准备与源码获取在开始前请确保已具备以下基础环境已安装ROS2 Humble完整版配置好TurtleBot4仿真环境具备基本的Linux命令行操作能力源码获取步骤# 创建工作空间 mkdir -p ~/turtlebot4_ws/src cd ~/turtlebot4_ws/src # 克隆Cartographer核心库 git clone https://github.com/cartographer-project/cartographer.git -b ros2 # 克隆ROS接口包 git clone https://github.com/cartographer-project/cartographer_ros.git -b ros2 # 克隆ROS2专用工具包 git clone https://github.com/ros2/ros2_cartographer.git提示如果GitHub访问缓慢可以使用国内镜像源替换原始URL2. 依赖安装与编译配置Cartographer的依赖项较多推荐使用鱼香ROS工具简化安装过程# 安装鱼香ROS工具 wget http://fishros.com/install -O fishros . fishros # 使用工具安装依赖 rosdepc install -r --from-paths src --ignore-src --rosdistro $ROS_DISTRO -y常见依赖问题解决方案依赖项解决方案备注abseil-cppsudo apt install libabsl-dev必须版本匹配protobufsudo apt install libprotobuf-dev需要3.0版本ceres-solversudo apt install libceres-dev非线性优化库编译配置建议cd ~/turtlebot4_ws colcon build --packages-up-to cartographer_ros --cmake-args \ -DCMAKE_BUILD_TYPERelease \ -DABSL_PROPAGATE_CXX_STDON3. 源码修改实现轨迹输出功能Cartographer默认不提供轨迹输出接口我们需要修改源码添加此功能。核心修改点在node.cpp文件中定位到cartographer_ros/src/node.cpp文件找到HandleTrajectoryQuery函数约199行在return true前添加以下代码// 轨迹记录代码 std::string trajectory_file /tmp/estimated_trajectory.txt; std::ofstream out_file(trajectory_file, std::ios::app); for (const auto pose : response-trajectory) { const auto stamp pose.header.stamp; const auto position pose.pose.position; const auto orientation pose.pose.orientation; out_file std::setprecision(15) stamp.sec . std::setw(9) std::setfill(0) stamp.nanosec position.x position.y position.z orientation.x orientation.y orientation.z orientation.w \n; }修改后重新编译colcon build --packages-select cartographer_ros4. 配置与启动SLAM系统创建专用功能包管理Cartographer配置ros2 pkg create turtlebot4_cartographer --build-type ament_cmake关键配置文件结构turtlebot4_cartographer/ ├── config/ │ └── turtlebot_2d.lua ├── launch/ │ └── cartographer.launch.py └── rviz/ └── slam.rviz典型2D配置示例turtlebot_2d.luainclude map_builder.lua include trajectory_builder.lua options { map_builder MAP_BUILDER, trajectory_builder TRAJECTORY_BUILDER, publish_frame_projected_to_2d true, use_odometry true, use_nav_sat false, use_landmarks false, num_laser_scans 1, num_multi_echo_laser_scans 0, num_subdivisions_per_laser_scan 1, num_point_clouds 0, lookup_transform_timeout_sec 0.2, submap_publish_period_sec 0.3, pose_publish_period_sec 5e-3, trajectory_publish_period_sec 30e-3, rangefinder_sampling_ratio 1., odometry_sampling_ratio 1., fixed_frame_pose_sampling_ratio 1., imu_sampling_ratio 1., landmarks_sampling_ratio 1., } MAP_BUILDER.use_trajectory_builder_2d true TRAJECTORY_BUILDER_2D.submaps.num_range_data 90 TRAJECTORY_BUILDER_2D.min_range 0.1 TRAJECTORY_BUILDER_2D.max_range 3.5 TRAJECTORY_BUILDER_2D.missing_data_ray_length 3. TRAJECTORY_BUILDER_2D.use_imu_data false TRAJECTORY_BUILDER_2D.motion_filter.max_angle_radians math.rad(0.1) return options启动文件配置要点from launch import LaunchDescription from launch_ros.actions import Node def generate_launch_description(): return LaunchDescription([ Node( packagecartographer_ros, executablecartographer_node, namecartographer_node, outputscreen, parameters[{use_sim_time: True}], arguments[ -configuration_directory, launch.substitutions.ThisLaunchFileDir() /../config, -configuration_basename, turtlebot_2d.lua ] ), Node( packagecartographer_ros, executableoccupancy_grid_node, nameoccupancy_grid_node, outputscreen, parameters[{use_sim_time: True}], arguments[-resolution, 0.05] ) ])5. 完整工作流程与验证启动完整SLAM系统的步骤启动Gazebo仿真环境ros2 launch turtlebot4_ignition_bringup turtlebot4_ignition.launch.py启动Cartographer节点ros2 launch turtlebot4_cartographer cartographer.launch.py启动键盘控制节点ros2 run teleop_twist_keyboard teleop_twist_keyboard触发轨迹记录服务ros2 service call /trajectory_query std_srvs/srv/Trigger使用RViz可视化ros2 run rviz2 rviz2 -d $(ros2 pkg prefix turtlebot4_cartographer)/share/turtlebot4_cartographer/rviz/slam.rviz验证轨迹记录是否成功less /tmp/estimated_trajectory.txt # 预期输出格式 # 1698765432.123456789 1.234 -0.567 0.000 0.0 0.0 0.123 0.987 # 1698765432.223456789 1.245 -0.578 0.000 0.0 0.0 0.124 0.9866. 高级调试与性能优化当系统运行不如预期时可参考以下调试方法常见问题排查表问题现象可能原因解决方案无激光数据传感器配置错误检查Gazebo传感器插件地图漂移里程计不准调整use_odometry参数轨迹断裂运动过滤过严修改motion_filter参数建图模糊子图更新过快增加num_range_data值性能优化参数建议-- 前端参数实时性 TRAJECTORY_BUILDER_2D.ceres_scan_matcher.translation_weight 10 TRAJECTORY_BUILDER_2D.ceres_scan_matcher.rotation_weight 40 TRAJECTORY_BUILDER_2D.submaps.num_range_data 60 -- 后端参数全局一致性 POSE_GRAPH.constraint_builder.sampling_ratio 0.3 POSE_GRAPH.constraint_builder.max_constraint_distance 15.0 POSE_GRAPH.optimize_every_n_nodes 907. 扩展应用多机器人协同建图基于此基础架构可以进一步实现多机器人协同建图。关键修改点为每个机器人创建独立的轨迹-- 在配置文件中添加 MAP_BUILDER.num_background_threads 4 POSE_GRAPH.optimize_every_n_nodes 20修改启动文件支持多机# 为每个机器人创建独立的cartographer节点 for robot_id in [robot1, robot2]: ld.add_action(Node( packagecartographer_ros, executablecartographer_node, namefcartographer_node_{robot_id}, namespacerobot_id, parameters[{use_sim_time: True}], arguments[ -configuration_directory, config_dir, -configuration_basename, turtlebot_2d.lua ] ))合并轨迹数据# 使用Python脚本合并多个轨迹文件 import pandas as pd trajectories [] for robot in [robot1, robot2]: df pd.read_csv(f/tmp/{robot}_trajectory.txt, delim_whitespaceTrue, headerNone) df[robot] robot trajectories.append(df) combined pd.concat(trajectories) combined.to_csv(/tmp/combined_trajectory.txt, indexFalse)在实际项目中这套定制化的Cartographer解决方案已经帮助多个研究团队实现了精确的室内建图与定位。特别是在需要量化评估算法性能的场景中自主添加的轨迹输出功能成为了不可或缺的工具。