先把结论放这儿这套流程我前前后后调过几次实际走通整个“ROS2 巡检小车从零连起来”的链路要比你想的费时间但每一步都有清晰的验证方法只要不跳步基本都能顺利跑通。这篇文章不聊理论空话直接按我实操时的顺序把六个关键步骤拆开讲清楚包括每步的命令、参数、波形截图怎么看、报错怎么查以及我踩过的几个坑希望对正在搞 ROS2 小车但卡在某一步的朋友有帮助。先说下背景。我手里这块板子是某个国产工控板主控是 AMD 架构的四核处理器底盘是四轮差速驱动板走 CAN 总线传感器搭配是单线激光雷达加一个 USB 摄像头系统盘烧的是 Ubuntu 22.04ROS2 用的是 Humble。这套组合不算新鲜但遍历所有常见坑之后能覆盖绝大多数入门和中阶需求。标题说“6 步”其实我是在保证每步都有一眼能看懂的验证手段才把这套流程收敛到六段的。适合谁看已经搭过 ROS2 基础环境、但对“小车整体连通”还没有完整概念的人也适合学生做课程设计、开发者搞安防巡检 Demo、甚至极客自己组一套室内巡检样机。1. 整体思路先通底层再谈感知与导航很多人拿到小车第一反应是把激光雷达和相机都挂上然后直接跑 SLAM 建图结果一跑就是里程计数据乱跳、点云方向不对、地图打不开。我的建议是分层次来优先级是系统层确认主控、底盘驱动板、传感器能分别上电工作通信层确认 CAN/串口/USB 每个通道都在 ROS2 里发布稳定数据控制层能通过 ROS2 消息控制电机正反转和转速感知层激光、图像数据正确进入可视化工具坐标系无偏移应用层建图、定位、导航、巡检点巡航最后串成一个任务脚本。那这 6 步怎么划分我实际操作的时候是按“从通电到独立巡检”的自然顺序拆的每一段都在前一段基础上做验证第 1 步硬件组装与系统确认第 2 步底层电机驱动连通跑通差速控制第 3 步里程计与底盘状态接入 ROS2第 4 步激光雷达、摄像头接入和标定检查第 5 步SLAM 建图与自动导航第 6 步巡检任务编排与联调收尾。这个结构看起来简单但每一步背后都有一个核心问题要回答我如何确认这一步真的通了下面逐个来说每个环节我会把验证方法、命令行、关键报错一并写出来。2. 硬件组装与系统确认别小看供电和总线时序2.1 硬件清单与连接要点小车硬件上最容易被忽略的其实是电源分配。电机驱动板和主控板如果共用一个电源电机启动瞬间的大电流会让主控电压跌落严重时直接重启这个现象在 ROS2 里表现为节点随机失联非常难查。我的做法是动力电源单独给驱动板主控板用独立 DC-DC 降压模块供电两路电源在总开关处并联但主控电源前加一级 LC 滤波。这样即使电机堵转主控也不会掉电。然后是总线拓扑。我板子的驱动板 CAN 接口通过 USB-CAN 模块连到工控板激光雷达用的串口 TTL摄像头是 USB3.0。建议把 USB-CAN、雷达串口、摄像头分别插在主控的不同 USB 控制器上避免抢占带宽。你可以用lsusb -t查看当前 USB 总线分配确保三个设备不在同一控制器下。2.2 系统与 ROS2 环境检查系统层面我用的固定流程是这几条命令lsb_release -a # 确认 Ubuntu 22.04.3 LTS uname -m # x86_64 sudo apt update sudo apt upgrade -yROS2 Humble 安装完成后建议先做一次环境变量验证printenv | grep ROS # ROS_DISTRO humble # ROS_VERSION 2这里容易踩坑的是 bashrc 里重复 source。如果你在多个文件里都写了source /opt/ros/humble/setup.bash后面新开的终端可能环境重叠表现为 package 版本错乱。我建议只保留~/.bashrc里一处 source。然后要验证 DDS 通信是否正常。ROS2 默认用的 DDS 实现是 Fast DDS多机或同一主控不同进程之间通信出问题多半是共享内存配置不对。可以先跑最简测试ros2 run demo_nodes_cpp talker # 新开终端 ros2 run demo_nodes_cpp listener这个能通说明 DDS 基础没问题不能通先检查/etc/hosts把主控的 hostname 对应到 127.0.1.1再检查防火墙sudo ufw disable还有一种情况是 Cyclone DDS 或 Fast DDS 的网卡选择错了多网卡机器上需要指定ROS_AUTOMATIC_DISCOVERY_RANGE或CYCLONEDDS_URI这个我们后面在巡检任务远程联调时再说。3. 第 1 步实操组装、接线与基础环境验证这一步的核心是“通电后所有设备在系统层面可见”。先把电机驱动板、激光雷达、摄像头按上面的电源原则接好然后插上主控开机。开机后依次检查lsusb # 能看到 USB-CAN、雷达串口芯片、摄像头 sudo dmesg | grep -i can\|serial\|video激光雷达如果走串口确认设备节点是不是/dev/ttyUSB0还是ttyACM0因为后面驱动配置里要绑定具体串口。我最开始就是没注意雷达插上后系统枚举成了ttyACM0而驱动默认读ttyUSB0折腾了好久才发现。然后检查 CAN 总线设备sudo ip link set can0 up type can bitrate 500000 sudo ip link show can0如果这里报“Cannot find device”大概率是 USB-CAN 驱动没加载或没插好需要重新插拔或看dmesg是否有can0注册信息。CAN 起来之后用candump can0看看有没有数据流如果底盘驱动板一上电就不停发心跳帧那说明链路已经通了。底层硬件都系统可见之后再安装 ROS2 基础工具链这一步我统一装齐了后面全流程要用的包sudo apt install ros-humble-desktop ros-humble-nav2* ros-humble-slam-toolbox ros-humble-teleop-twist-keyboard ros-humble-robot-localization ros-humble-cartographer这个安装体积比较大建议预留至少 15GB 磁盘空间。装完后别急着往下走先验证所有包都能被解析ros2 pkg list | grep nav2\|slam_toolbox\|teleop如果 grep 出来为空多数是包的版本名不对可以用ros2 pkg list | grep逐步搜。这里也顺便把工作空间建好mkdir -p ~/robot_ws/src cd ~/robot_ws colcon build到这一步你的小车已经完成了“通电后在系统与 ROS2 层面都可见”的第一阶段。有个小技巧把ll /dev/ttyUSB*、ip link show can0的结果固定记下来后面排查驱动问题时能快速判断是设备没上电还是驱动没起来。4. 第 2 步实操电机驱动与差速控制4.1 驱动板通信协议与 CAN 报文解析底盘驱动板多数是厂家提供 CAN 协议一般包括控制帧速度指令和反馈帧当前转速、转向角等。不同厂商协议可能差异很大这里我以“控制帧 ID 0x1238 字节”这种常见面板为例说明实际操作时一定要对照自己驱动板的协议手册。假设控制指令格式是Byte 0: 0x01 表示速度控制模式Byte 1-2: 左轮速度int16单位 mm/sByte 3-4: 右轮速度int16单位 mm/sByte 5-7: 保留反馈帧格式可能是Byte 0: 0x02 表示状态帧Byte 1-2: 左轮编码器值Byte 3-4: 右轮编码器值Byte 5: 电池电压百分比通常情况下你不需要自己造 CAN 协议底盘厂家会给 ROS 驱动包或 SDK。但驱动包未必适配 ROS2所以要么自己封装一个 ROS2 节点要么用厂家提供的 ROS2 版驱动。这里推荐自己封装原因后面细说。一个极简的 ROS2 发布者节点骨架如下import rclpy from rclpy.node import Node from std_msgs.msg import Float32MultiArray import can class ChassisDriver(Node): def __init__(self): super().__init__(chassis_driver) self.pub self.create_publisher(Float32MultiArray, wheel_speed, 10) self.sub self.create_subscription(Float32MultiArray, cmd_speed, 10, self.cmd_callback) self.bus can.interface.Bus(channelcan0, bustypesocketcan) def cmd_callback(self, msg): left int(msg.data[0]) right int(msg.data[1]) data [0x01, left 0xFF, (left 8) 0xFF, right 0xFF, (right 8) 0xFF, 0, 0, 0] self.bus.send(can.Message(arbitration_id0x123, datadata, is_extended_idFalse)) def timer_callback(self): pass def main(argsNone): rclpy.init(argsargs) node ChassisDriver() rclpy.spin(node) node.destroy_node() rclpy.shutdown()注意 CAN 报文数据都是小端还是大端必须和协议严格一致我遇到过厂家的协议手册写的“高位在前”实际是小端发送导致同样数据跑出来的轮速完全不对。你可以在电机悬空的情况下发一个固定速度指令观察电机方向与转速是否和预期一致。4.2 用键盘控制验证电机逻辑有了驱动节点接下来用一个最简单的控制源验证整个闭环键盘控制。我常用的方式是ros2 run teleop_twist_keyboard teleop_twist_keyboard这个节点会发布cmd_vel小车的驱动包订阅cmd_vel后转换成左右轮速发送到 CAN 总线。如果键盘按前进两个轮子同时正转按左转左轮减速、右轮加速这个逻辑对了说明差速模型基本正确。这里有一个非常常见的坑cmd_vel的坐标系方向。ROS2 中线性速度 x 正方向为车头前方z 轴逆时针为正。很多驱动板定义的是“左轮正转代表前进”但在 CAN 协议里某些厂商用的是反逻辑导致后退时小车反而前进。建议在正式跑导航前把遥控器测试完整做一遍前进双轮正转后退双轮反转左转左轮反转、右轮正转右转左轮正转、右轮反转用一个表格记录每个方向的 CAN 原始数据、轮子实际方向、期望方向快速排查协议映射问题。这个阶段还要检查一个关键指标左右轮在相同指令下是否速度一致。实测中我遇到过左轮 100 mm/s 时实际转了 10 秒走了 98cm右轮只有 92cm这种差异可能是编码器分辨率不一致或电机机械阻力不同后期 SLAM 地图会明显偏斜。可以用ros2 topic echo /odom对比左右轮速度的均值和方差差异超过 3% 就要注意了。5. 第 3 步实操里程计接入与坐标变换5.1 编码器数据到里程计的换算电机编码器通常输出脉冲数需要转换成轮子转速和位移。以常见 500 线增量式编码器为例如果驱动板反馈的是“单位时间脉冲数”那么换算关系是轮子周长 π × 轮径转速 (rad/s) 脉冲数 ÷ 编码器分辨率 ÷ 减速比 × 2π线速度 (m/s) 转速 × 轮半径例如轮径 0.1m减速比 30编码器 500 线实测 1 秒内脉冲 3000轮子实际转速 3000 / 500 / 30 0.2 转/秒 角速度 0.2 × 2π 1.256 rad/s 线速度 1.256 × 0.05 0.0628 m/s这个换算关系不要靠猜一定要用尺子实测车轮在地面的实际移动距离来校准。我的做法是把小车悬空给固定速度指令跑 10 秒用记号笔在轮子上做标记数圈数再把小车放地面跑 10 米看里程计累计位移是否吻合。两者偏差超过 5% 就要调整轮径或减速比参数。5.2 发布 tf 坐标树导航框架中常见的 tf 树是odom - base_link - laser_link - camera_linkodom 是里程计原点通常由底盘驱动节点发布base_link 是车体中心连接里程计和底盘物理中心laser_link 是激光雷达中心需要相对 base_link 的固定安装偏移camera_link 是相机光心。ROS2 中这两个坐标系的发布可以用robot_state_publisher配合 URDF 文件也可以用static_transform_publisher手动发布。对于固定安装的传感器推荐直接写在 URDF 里因为导航和 SLAM 都依赖完整 tf 树。简单做法是ros2 run tf2_ros static_transform_publisher 0 0 0.2 0 0 0 base_link laser_link但这种方式后期维护不方便还是建议建一个最简单 URDF哪怕只有三个 link 两个 joint。里程计数据发布到odomtopic同时也要发布odom - base_link的变换很多初学者把这两者搞混。odomtopic 是数据tf 是坐标系之间的关系Nav2 定位时订阅/odom获取位置估计同时需要 tf 才能把传感器数据投影到车体坐标系。底盘驱动的 ROS2 节点里这两块都要做。5.3 用轮式里程计数据检验电机响应发布里程计之后可以用ros2 topic echo /odom看实时数据。跑直线时x 应该稳定增长y 基本不变原地转 90 度时theta 应该从 0 增加到约 1.57如果偏差明显要么是差速模型没配对要么是轮距参数不准确。我建议在正式建图前先做“里程计标定”方法很简单直线跑 2 米记录 odom 中 x 增量算出比例修正原地转 360 度记录 theta 增量修正轮距。这个标定流程花不了半小时但能让后续 SLAM 地图的畸变大幅减少。实测中未标定的轮距参数误差 1cm跑 5 米的地图转角就可能偏 15 度非常明显。6. 第 4 步实操雷达与摄像头接入点云和图像可视化6.1 激光雷达驱动与坐标系校准单线激光雷达驱动一般以 ROS2 包的形式提供或者你也可以用sllidar_ros2这类通用包。核心配置有两个串口设备和 frame_id。串口我前面说了确认是ttyUSB0还是ttyACM0在 launch 文件里指定。frame_id 要和你 URDF 里的 laser_link 名称一致否则后面点云在 Rviz2 里会显示不出来。启动雷达后用三个命令验证ros2 topic hz /scan # 应该稳定在 10Hz 或 20Hz 左右 ros2 topic echo /scan --once | head -50 # 查看 range_min/range_max 和角度增量 ros2 run rviz2 rviz2 # 添加 LaserScan 显示选择 /scan topic这里涉及两个问题雷达的扫描角度旋转方向和车头方向是否一致以及安装高度。我用的雷达扫描范围是 0~360 度正前方在 0 度。但有些雷达默认正方向是 90 度或 180 度需要配置驱动里的角度补偿。你可以把小车放正前方放一个纸板查看 Rviz2 里点云哪个角度有遮挡就知道当前角度偏移是多少然后通过参数修正。雷达安装高度建议在 10~30cm 之间太低会扫到地面噪声太高可能扫不到桌椅等障碍物的下半部分。我实际标定的结果是离地 18cm 比较平衡既保留了低矮障碍物检测又不会大量扫到地面。如果你用的是 360 度雷达还要注意雷达底部线缆不要遮挡扫描窗口。6.2 相机接入与内参确认USB 摄像头相对简单ROS2 下可以用usb_cam包ros2 run usb_cam usb_cam_node_exe --ros-args -p video_device:/dev/video0注意这里设备节点通常与雷达不同是/dev/video0多摄像头机器上要确认选的是哪一路可以用v4l2-ctl --list-devices启动后ros2 topic hz /image_raw # 应该稳定在 30fps至少不低于 15fps ros2 run rqt_image_view rqt_image_view相机内参标定我强烈建议做一次因为后续做任何视觉识别或目标跟踪畸变太大会导致坐标投影完全不准。用camera_calibration功能包加棋盘格标定板ros2 run camera_calibration cameracalibrator --size 8x6 --square 0.024 image:/image_raw camera:/camera_info采集 20 张左右不同角度的棋盘格标定后生成camera_info的 yaml 文件更新到相机驱动配置里。相机的 frame_id 对应 URDF 里的camera_link安装位置要尽量保证激光雷达和相机能覆盖相近的区域如果有时间同步要求要用硬件触发或在软件上用message_filters做时间近似同步。6.3 Rviz2 分布式显示与坐标系检查在 Rviz2 里全局 Fixed Frame 设为odom然后添加 LaserScan 和 Image 显示如果点云和图像位置完全对不上先检查 tf 树是否完整ros2 run tf2_ros tf2_echo base_link laser_link ros2 run tf2_ros tf2_echo base_link camera_link如果 tf 报找不到优先看 URDF 里 joint 的原点 offset 是否是传感器实际安装位置。我用尺子量了激光雷达和相机相对 base_link 的位置写进 URDF 后Rviz2 中看起来就很整齐。7. 第 5 步实操SLAM 建图、地图保存与 Nav2 导航7.1 手动遥控建图与保存建图方法多我推荐用 slam_toolbox 而不是 Cartographer原因是对参数敏感度低、地图质量足够室内巡检找路时也好调。启动建图前先把底盘驱动和雷达驱动都跑起来然后ros2 launch slam_toolbox online_async_launch.py同时打开 Rviz2确认地图慢慢展开。然后手动遥控小车跑一圈覆盖整个巡检区域。建图时注意几点速度放慢尤其在转弯处太快会照成地图重影经过同一个位置时尽量走不同的方向帮助回环检测不要只在房间中央转圈要靠边走把墙和障碍物轮廓完整描出来。地图建完后保存ros2 run nav2_map_server map_saver_cli -f ~/robot_ws/maps/indoor_map会生成indoor_map.pgm和indoor_map.yamlyaml 里记录了分辨率、原点、占用概率阈值Nav2 加载地图时就用这个文件。7.2 Nav2 导航配置与初始位姿设置导航用 Nav2启动方式取决于地图文件路径和机器人模型。我用的启动方式大概是ros2 launch nav2_bringup bringup_launch.py map:/home/xxx/robot_ws/maps/indoor_map.yamlNav2 启动后最关键的一步是“给定初始位姿”。在 Rviz2 中用 2D Pose Estimate 在地图上大致指一下小车的实际位置和朝向。如果不给AMCL 的粒子可能发散表现为地图坐标和实际偏差很大小车导航直接抽风。此后你可以用 2D Goal Pose 指定一个目标点Nav2 会规划路径。第一次跑大概率会遇到“规划失败”或“膨胀区域塞满”这时要检查代价地图参数inflation_radius是否太大导致小车认为离墙非常远路径难以生成建议先设 0.3m 左右。robot_radius或 footprint 是否和实际车体尺寸一致我刚开始用的圆形半径比车体半宽大 0.05m结果穿过门时总被卡住。7.3 定位与路径跟踪精度验证导航能跑起来后多测几个点验证定位精度。一个最简单的办法给一个目标点让小车走到达后手动量下车体实际中心与目标点的距离。偏差在 10cm 以内算正常超过 20cm 就要检查里程计标定和 AMCL 参数。Nav2 里还要关注几个导航参数max_vel_x、max_vel_theta巡检场景下不需要跑太快建议 max_vel_x 设 0.3 m/s避障反应更从容min_vel_x设为 0.0避免走走停停的抖振acc_lim_x、acc_lim_theta加减速限制不要设太高不然电机响应跟不上实际轨迹会“内切”。如果你观察到小车在导航中走“Z”字形那多半是控制频率和里程计反馈频率不匹配或者base_local_planner里sim_time设置得太短。可以调高控制循环频率到 20Hz并在controller_frequency对应设置。8. 第 6 步实操巡检任务编排、多机联调与远程监控8.1 巡检流程设计与任务脚本最后一步是把导航能力变成“巡检逻辑”。我实际用的方案是预设巡检点列表按顺序导航到达后停留数秒做拍照和传感器信息记录全部走完再回到起点或充电桩。巡检点可以用坐标数组写在 YAML 配置文件里例如patrol_points: - point1: {x: 1.0, y: 0.5, theta: 0.0} - point2: {x: 3.2, y: 1.8, theta: 90.0} - point3: {x: 2.5, y: 4.2, theta: 180.0}写一个 ROS2 Python 节点调用 Nav2 的 action 接口发送目标点import rclpy from rclpy.node import Node from nav2_msgs.action import NavigateToPose from action_msgs.msg import GoalStatus from rclpy.action import ActionClient class PatrolNode(Node): def __init__(self): super().__init__(patrol_node) self.client ActionClient(self, NavigateToPose, navigate_to_pose) self.goals [(1.0, 0.5, 0.0), (3.2, 1.8, 1.5708), (2.5, 4.2, 3.14159)] self._send_goal_index 0 def send_goal(self): goal_msg NavigateToPose.Goal() goal_msg.pose.header.frame_id map goal_msg.pose.pose.position.x self.goals[self._send_goal_index][0] goal_msg.pose.pose.position.y self.goals[self._send_goal_index][1] goal_msg.pose.pose.orientation.z 0.0 goal_msg.pose.pose.orientation.w 1.0 self.client.send_goal_async(goal_msg).add_done_callback(self.goal_response_callback) def goal_response_callback(self, future): goal_handle future.result() if goal_handle.accepted: goal_handle.get_result_async().add_done_callback(self.goal_result_callback) else: self.get_logger().warn(Goal rejected, trying next) self._send_goal_index (self._send_goal_index 1) % len(self.goals) self.send_goal() def goal_result_callback(self, future): status future.result().status if status GoalStatus.STATUS_SUCCEEDED: self.get_logger().info(Reached point) self._send_goal_index (self._send_goal_index 1) % len(self.goals) self.send_goal() def main(argsNone): rclpy.init(argsargs) node PatrolNode() node.send_goal() rclpy.spin(node) rclpy.shutdown()这个逻辑很粗糙但已经能跑。实际部署时我会加上到达检测、拍照保存、异常重试等逻辑。重点在于如果在导航过程中 Nav2 取消/拒绝目标巡检节点要能处理异常而不是死循环或者卡在等结果上。8.2 多机联调与 DDS 通信配置如果你和我一样喜欢在电脑上远程看地图和点云那就需要多机 ROS2 通信。核心是让两台机器的 DDS 发现协议能互通最简单的方法是用同一个局域网然后在一台机器上指定 ROS 发现服务器。用 fastdds 的 discovery server 模式会更稳# 在电脑上启动发现服务 fastdds discovery --server-id 0 --port 11811 # 小车主控设置环境变量 export ROS_DISCOVERY_SERVER192.168.1.100:11811这样主控上的所有 ROS2 节点都会注册到发现服务器电脑上只要也设置同样的发现服务器地址就能看到主控上的 topic。这个比广播模式稳得多特别是在办公室/实验室 WiFi 环境下广播包经常丢帧或者跨网段不通。除了发现服务还需要设置ROS_DOMAIN_ID同一个域才能互相通信。建议设一个固定值比如 42不要默认 0防止和别人的机器人冲突export ROS_DOMAIN_ID428.3 远程监控与数据记录巡检过程中可以固定录制 rosbagros2 bag record /scan /odom /image_raw /map /amcl_pose这样巡检结束后可以回放数据复现问题时不需要重新跑车。远程监控画面我常用的组合是 Rviz2 订阅点云和地图再加一个web_video_server把图像流通过网页查看。也可以直接跑rviz2在电脑上看如果网络不好可以降低图像 topic 的分辨率或者只订阅 /map。总之一句话先让 ROS2 通信层面稳定远程监控才有意义。9. 常见问题与排查技巧实录我把自己和周围人在这套流程中遇过的高频问题整理成了一张速查表你在操作时如果遇到相似场景可以按表格快速定位现象可能原因快速排查方法解决措施电机不动但 CAN 数据正常控制帧 ID 或数据格式不对candump can0对比协议按协议手册修正驱动节点发送帧车轮方向反了差速模型符号定义不一致键盘遥控前进看真实转向在驱动节点中翻转左右轮符号里程计漂移明显轮径/减速比或轮距参数不准标定直线 2m 和原地 360 度修改 URDF/驱动参数重新校准激光点云在 Rviz2 中看不到frame_id 不一致tf2_echo base_link laser_link统一 laser_link 名称和 urdf地图扭曲或重影建图速度太快或回环不足慢速重复扫描同一区域重新建图控制车速和路径导航规划失败膨胀半径/足迹设置不合理查看代价地图显示调小 inflation_radius 或修改 footprint小车导航中反复横跳控制频率低或 DWA 参数不稳查看 cmd_vel 发布频率提高 controller_frequency 到 20Hz多机看不到 topic域 ID 不一致或发现服务未配两端ROS_DOMAIN_ID是否一致统一下配置必要时用 discovery server巡检点执行卡住Action 目标被取消但节点未处理查看节点日志增加目标状态回调超时处理逐条说几个细的。CAN 数据正常但电机不动这是最磨人的。之前一次排查candump 能看到底盘反馈轮速但电机就是不转后来发现是驱动板此时处于急停状态需要先发送一个“使能”帧。检查步骤是看手册里是否有“运行使能”帧若有在驱动节点启动时先发送使能再发速度指令。车轮方向反了我把两轮都当成“正转前进”结果驱动板实际定义右轮正转是反向导致原地旋转方向完全搞反。处理方式是修改驱动节点里左右轮换算符号不要改 CAN 协议字段。地图扭曲有一种隐蔽原因是雷达安装角度有微小倾斜导致扫描数据整体偏低或偏高表现为走廊墙体向某一侧偏。解决方法是检查雷达支架水平度用水平尺量一下重新调平固定。Nav2 规划失败遇到过最离谱的一次是地图没问题、AMCL 定位正确但就是规划不出来最后发现是footprint里写的车体外框和车头方向跟 URDF 里 base_link 方向差了 90 度。虽然看起来只是数据但因为膨胀层计算时用的是 footprint 坐标方向错了整个路径都被堵住。这个容易栽建议在地图上手动添加一个薄墙测试看代价地图的足迹朝向和车体实际朝向是否一致。远程监控看不到 topic最常见原因是电脑和主控连接的不是同一个 AP或路由器开了 AP 隔离。如果是双网卡还要检查 ROS 环境变量里的ROS_IP/网卡绑定。最省心的方式是接同一台交换机不用 WiFi。另外有两个经验和大家分享。第一小车跑导航之前一定要检查“急停按钮”有没有被触发很多驱动板上有硬件急停上层 ROS2 怎么发都不转第二尽量在代码里把雷达、里程计、图像的频率都打印出来巡检场景下这几种数据频率的稳定直接决定任务成功率。10. 巡检功能扩展与个人体会这套 6 步走完之后小车已经有了最基础的“自主移动 环境感知 定点巡航”能力。如果你后续要把它做成真正的巡检系统还可以从这几个方向扩展增加目标检测模型比如 YOLO处理巡检图像检测特定表计读数或异常物体在巡检点加入更复杂的任务比如机械臂操作、环境传感器数据采集接入自动充电功能在电池电压低时返回充电桩通过红外或二维码定位对接把定位结果和云端地图服务对接实现远程多车调度。我个人在实际操作中最深的体会是这台小车能否稳定跑起来不在于某一个传感器多高级而在于每一步连通后你是否真的做了肉眼可见的验证。不要跳步不要相信“系统看起来没问题”ROS2 的魅力在于所有状态都能用 topic、tf、日志暴露出来只要你愿意看数据问题往往很快就能定位。最后再分享一个小技巧在你每完成一步时把当前所有节点的状态和话题频率记录到一个本地文件里比如checkpoints.md。以后升级系统或更换设备直接用这份记录做基准对比能省下大量重复排查时间。这个习惯我保持到了所有小车项目里非常管用。如果你也在组类似的巡检小车可以按这 6 步顺序慢慢走遇到具体报错不妨把日志和命令贴出来交流很多 ROS2 的问题别人可能早就踩过。希望这篇内容能让你少走一点弯路顺利把小车的轮子转起来。