
简介面向ROS 2与Navigation 2的自动巡检机器人完整项目适合高校学生毕业设计、期末大作业及课程设计使用也适合希望快速上手导航机器人开发的初学者。资源围绕自动巡检场景提供基于Nav2的导航配置、机器人描述模型、应用层控制逻辑与交互接口包括launch启动、rviz可视化、world仿真环境等配套文件部署后可快速体验完整巡检流程。资源包共71个文件以Python脚本23个为核心控制逻辑另含URDF/Xacro模型文件、YAML参数与地图文件、XML配置、rviz视图及world仿真文件等整体仅80KB结构清晰。代码内附详细注释关键模块如fishbot_navigation2、fishbot_application_cpp、autopatrol_robot等均有独立目录。目前已有1037人学习下载项目经严格调试可运行可直接作为高分毕设或课程设计提交实用价值高。1. ROS 2 Navigation 2 自动巡检机器人源码包拆解与复现路径「自动巡检机器人」听起来离普通开发者很远但拆开看核心就四件事建图、定位、巡航、返航。这套基于 ROS 2 和 Navigation 2 的源码包价值在于把 Nav2 的参数、行为树结构、巡检任务循环组织成了可以直接跑的工程而不是官网 demo 的拼盘。拿到手你能看到的是一条完整的任务流水线Cartographer 建图、AMCL 定位、BT Navigator 做导航决策、独立巡检节点做多目标点巡航。它适合三类人要做轮式巡检 Demo 的在校生、正在从单点导航往多任务导航过渡的工程师、以及想抄一套可用 Nav2 参数模板的开发者。下面我把建图选型、巡检任务循环、常见翻车点和离线验证方法挨个拆开讲。2. 建图与导航选型Cartographer 还是 SLAM ToolboxNav2 参数怎么给2.1 建图方案怎么选两种管线的适用场景巡检机器人的第一步是建图。这个源码包按照最常见的室内场景做了选型Cartographer 做 2D 栅格建图。原因很直接室内走廊和厂房环境有大量直角结构Cartographer 的闭环检测在直线走廊里表现稳定不像纯里程计累积误差那样越走越飘。如果你手里已经有建好的 pgm/yaml 地图文件也可以直接跳到 Nav2 加载地图不一定非要重新跑一遍建图。另一种方案是 SLAM Toolbox它和 Nav2 的 map_server 集成更顺但实话说在环境特征少的长走廊里SLAM Toolbox 的闭环不如 Cartographer 激进容易出现地图错位。我的建议是厂房、仓库这种有明显几何结构的环境优先 Cartographer如果你只是实验室里一块小场地、地图半小时能建完SLAM Toolbox 更省事。源码包里两种配置都放了但默认走的是 Cartographer。建图的启动方式各家机器人底盘差异很大核心是保证激光雷达的 frame 和底盘 odom 的 frame 能对得上。下面这段是典型流程# 第一步启动底盘和激光雷达驱动确保 /odom 和 /scan 在发数据 ros2 launch your_robot robot_base.launch.py # 第二步启动 Cartographer 建图 ros2 launch cartographer_ros cartographer.launch.py \ configuration_directory:/path/to/your_config \ configuration_basename:robot_2d.lua # 第三步建完图后保存地图 ros2 run nav2_map_server map_saver_cli -f ~/maps/factory_map这里有几个参数值得注意。configuration_basename指向的 lua 文件里tracking_frame要设成激光雷达的 frame 名常见是laser或base_lasermap_frame统一用map。map_saver_cli的-f参数决定地图文件名前缀生成的是factory_map.pgm和factory_map.yaml两个文件后者是 Nav2 加载地图时的入口。很多人第一次建图翻车就是 laser frame 名字写错导致 Cartographer 的机器人轨迹全偏。2.2 Nav2 行为树与核心参数模板地图有了之后导航是第二个大头。Nav2 的架构是三个服务各管一段planner_server算全局路径controller_server做局部路径跟踪bt_navigator负责把整个导航过程编排成行为树。源码包里的参数文件建议直接抄这三段# nav2_params.yaml 关键片段 planner_server: ros__parameters: expected_planner_frequency: 0.5 planner_plugins: [GridBased] GridBased: plugin: nav2_navfn_plugin/NavfnPlanner tolerance: 0.3 controller_server: ros__parameters: controller_plugins: [FollowPath] FollowPath: plugin: nav2_regulated_pure_pursuit_controller/RegulatedPurePursuitController max_angular_accel: 0.3 min_turning_radius: 0.2 bt_navigator: ros__parameters: default_nav_to_pose_bt_xml: /path/to/navigate_to_pose_w_replanning.xml参数说明分三层。expected_planner_frequency是规划频率下限如果规划超时低于这个值Nav2 会在日志里警告巡检时长期低于 0.5 说明地图太大、规划器太重要考虑降地图分辨率或换规划插件。RegulatedPurePursuitController是 Nav2 里对差速和全向底盘都比较稳的控制器max_angular_accel控制转向加速度设太小会让机器人转弯像蜗牛设太大容易在窄走廊里撞墙0.3 是我在多数室内底盘上验证过的折中值。行为树的 XML 决定了导航失败时做什么后面避坑章节会专门讲。2.3 从加载地图到把 Nav2 跑起来源码包里已经把 map_server、AMCL、Nav2 的启动写成了一个 launch 文件你只需要把地图路径指对。关键点在于 AMCL 的initial_pose要和机器人实际起始位置对齐否则导航一开就默认你已经定位错了。# 加载地图并启动 Nav2 全套导航栈 ros2 launch patrol_bot navigation_launch.py \ map_yaml:/home/user/maps/factory_map.yaml # 手动检查 TF 树是否完整 ros2 run tf2_ros tf2_echo map odom第二种做法的价值是快速定位问题如果tf2_echo map odom几秒内没有输出说明 AMCL 没拿到粒子收敛多半是初始位姿给错了或者激光数据没进 AMCL。这个检查我几乎每次跑新环境都会做一遍比盯着 RViz 看快得多。3. 巡检任务实现多目标点巡航与失败重试的代码结构3.1 把「走过去」变成「巡一遍」巡检点与路线模型导航栈解决的是单点导航巡检要解决的是多点串联。源码包里定义了一个PatrolRoute结构一个路线由若干个巡检点组成每个点包含位置、停留时间、允许重试次数。这个设计比单纯在 launch 文件里塞一串目标点要合理得多因为真机运行时你是要能动态增删巡检点的。点位数据放在 YAML 里维护格式是这样route: name: factory_north_line points: - id: P1 pose: [2.5, 1.8, 0.0] # x, y, yaw dwell_time: 5.0 # 到达后停留秒数 max_retries: 2 - id: P2 pose: [5.0, 3.2, 1.57] dwell_time: 8.0 max_retries: 1yaw是机器人到点后的朝向单位弧度。巡检对朝向其实很敏感很多设备读取要求机器人正对设备如果点位只存 x、y 不存 yaw到了现场还得人工转一下这个字段不能省。dwell_time看场景需求读取仪表数据给 35 秒拍照识别建议给 8 秒以上。3.2 巡航循环导航结果怎么处理巡检节点的核心循环不复杂依次取点、导航、等待、记录结果、处理失败。但失败处理是拉开差距的地方。源码包里的逻辑是三级递进先重试当前点重试耗尽就跳过继续下一个点最后把失败点汇总成一份报告。这样设计的考虑是巡检任务不应该被一个顽固故障点阻塞整条产线记录在案比死磕更重要。# patrol_node.py 核心循环节选 for point in route.points: goal PoseStamped() goal.header.frame_id map goal.header.stamp self.get_clock().now().to_msg() goal.pose.position.x point[pose][0] goal.pose.position.y point[pose][1] goal.pose.orientation.z math.sin(point[pose][2] / 2.0) goal.pose.orientation.w math.cos(point[pose][2] / 2.0) status self.navigate_to_pose(goal, timeout60.0) if status succeeded: self.record_point(point[id], ok) self.wait_for_dwell(point[dwell_time]) else: self.handle_failure(point, status)这段代码有三个细节值得说。goal.header.frame_id必须显式设成map不能沿用上一个 goal 的坐标系Nav2 对跨帧目标处理起来很容易出问题。orientation的 z 和 w 分量用半角公式把 yaw 转成四元数手算容易错直接复制这段公式是稳的。navigate_to_pose是封装了NavigateToPoseaction 的同步调用超时时间 60 秒的意思是如果 60 秒内导航没结束直接判定失败并触发重试避免机器人卡在某个地方干等。3.3 返回充电点与任务收尾巡检结束后的动作源码包默认是返航到充电点。实现上很简单把充电点当作 route 的最后一个固定点处理。但这里有个常见的疏忽如果最后一段返航失败整个任务会被标记为异常。实际处理时我会把返航单独剥离出来和巡检点走不同的重试逻辑返航允许的重试次数更多因为电量不足时盲目重试很危险。def return_to_dock(self): for attempt in range(3): status self.navigate_to_pose(self.dock_pose, timeout90.0) if status succeeded: self.shutdown_cleanly() return True self.log.warn(fdock attempt {attempt 1} failed: {status}) self.log.error(return-to-dock failed, robot needs manual recovery) return False这里的shutdown_cleanly不只是关节点还会把当前巡检记录落盘避免机器人断电后丢失本次任务数据。返航设置 90 秒超时是因为返航路径一般较长而且可能存在遇到障碍物绕行的情况60 秒会偏紧。4. 避坑排查Nav2 巡检机器人最常见的五个问题4.1 问题一地图加载了但 RViz 里机器人不在地图上现象Nav2 启动后 RViz 里能看到地图但机器人模型要么在地图外要么 TF 树报map到odom的变换一直没输出。原因AMCL 的initial_pose没设或者设的位置和地图原点对不上。AMCL 起不来map - odom的变换就不会发布导航自然无法开始。解决在navigation_launch.py里给 AMCL 传初始位姿或者先手动用 RViz 的 2D Pose Estimate 给一个粗略位姿等粒子收敛后再开始导航。另外确认robot_state_publisher正常odom - base_link的变换没断。4.2 问题二机器人导航中撞墙或贴边擦行现象路径规划出来了但实际走的时候机器人贴着障碍物走或者走到一半卡住。原因inflation_radius设得太小。代价地图的膨胀半径小于机器人实际半径时规划器会认为贴墙路径是可行的但真机一转向就蹭墙。解决检查local_costmap和global_costmap的robot_radius和inflation_radius。差速底盘建议inflation_radius至少是robot_radius的 1.5 倍。源码包里给的是robot_radius: 0.25、inflation_radius: 0.4这个组合在多数室内环境够用。4.3 问题三导航失败后行为树无限循环重试现象机器人撞到一个障碍物后开始不停转圈、后退重试日志里同一个 recovery 动作刷了几十次任务卡死。原因默认的navigate_to_pose_w_replanning.xml行为树里Recovery 节点没有限制重试次数只要没有彻底失败行为树就会反复执行旋转和后退恢复。解决改行为树 XML给 Recovery 节点加number_of_retries限制或者换用带超时控制的版本。源码包里的patrol_bt.xml已经把重试次数限制在 2 次超过后直接返回失败交给巡检节点做跳过处理。这是巡检场景和普通导航最大的区别普通导航希望无限重试巡检希望快速失败。4.4 问题四多目标点巡航时部分点一直 aborted现象第一个点导航正常第二个点开始偶发aborted同一批点有时候能到有时候不能。原因两个点之间的距离太短机器人刚完成减速还没来得及重新规划路径目标点就失效了或者目标点落在代价地图边界外。短距离巡检点之间还会出现 planner 来不及在expected_planner_frequency内刷新全局路径的情况。解决巡检点之间的直线距离至少保留 0.8 米太近的巡检点合并成一个停留点。同时把NavigateToPose的action_server_timeout调大一点给规划器更多缓冲时间。4.5 问题五仿真里没问题真机上定位慢慢漂移现象仿真环境里巡检一圈没毛病真机上跑两圈后机器人在地图上的位置和实际位置偏差越来越大最后导航到错误位置。原因默认情况下 Nav2 只依赖轮式里程计做 odom长时间运行累计漂移是必然的不是代码写错了。解决确认 AMCL 正常参与定位检查amcl节点是否输出map - odom变换。其次看激光数据质量雷达扫描频率太低或反光表面太多都会让 AMCL 粒子收敛变差。真机巡检建议每跑一圈触发一次重定位校准源码包在巡检循环里加了一个可选的重定位接口跑完一圈确认到起点附近就调一次。5. 把巡检接入业务监控、日志与告警的闭环设计5.1 巡检记录的数据结构别只存「成功/失败」很多巡检 Demo 跑完就完事了但真做项目巡检数据的留存比巡检本身重要。源码包里定义了一份巡检记录结构每个点每次巡检生成一条记录字段包含时间戳、点位 ID、导航结果、到达时机器人坐标、停留时长。别小看这个结构等你想复盘「昨天下午 3 点这台设备有没有被检查到」的时候没有结构化数据就只能翻日志。记录落盘用 SQLite 最省事不需要额外起服务巡检测节点在嵌入式设备上跑资源紧张SQLite 比 MySQL 之类轻一个量级。字段设计大致如下字段类型说明idINTEGER PRIMARY KEY自增主键point_idTEXT巡检点编号对应 YAML 中的 idstatusTEXTok / failed / skippedrobot_xREAL导航结束时的机器人 x 坐标robot_yREAL导航结束时的机器人 y 坐标dwell_actualREAL实际停留时长秒timestampTEXT完成时间ISO 8601 格式robot_x和robot_y很多人不存我建议存。后续排查「机器人是不是真到了那个点」光看 status 不够坐标数据能还原现场。5.2 状态上报与告警分发记录是事后复盘用的告警是实时响应用的。两者要分开。源码包里做了一个简单的告警分发器巡检节点把异常点位发给告警模块告警模块按配置把消息推到钉钉群或 MQTT这样值班的人不用一直盯着 RViz。def handle_failure(self, point, status): # 先落库 self.db.insert({ point_id: point[id], status: failed, robot_x: self.current_x, robot_y: self.current_y, dwell_actual: 0.0, timestamp: self.now_iso() }) # 连续失败才告警避免偶发故障刷屏 self.failure_count[point[id]] self.failure_count.get(point[id], 0) 1 if self.failure_count[point[id]] 2: self.alert_client.send({ event: patrol_point_failed, point_id: point[id], retries: self.failure_count[point[id]] })这个逻辑里有个关键设计连续失败两次才触发告警。巡检现场的偶发失败很多路径被临时堆放的箱子挡一下、雷达扫到反光物体都是单次失败不值得惊动值班人员。但同一个点连续失败基本能确认现场有持续性问题这时候才发告警。这个阈值在源码包里是可配置的别写死。5.3 巡检结果如何回流到业务系统如果巡检机器人只是自己跑价值有限真正有用的数据是要回流到上层业务系统的。源码包的接口层预留了一个 ROS 2 topic巡检节点把每次巡检结果发布到patrol_recordtopic外部系统可以直接订阅。如果你要把数据推到 Web 系统另外起一个桥接节点订阅同一个 topic转成 JSON 通过 HTTP 或 MQTT 推送巡检节点本身不掺和网络协议。数据回流有一个容易被忽视的点时间同步。机器人端的时间戳如果用 ROS 的now()是仿真时钟还是系统时钟要明确。真机场景用系统时钟否则时间戳和业务系统的记录对不上排查问题时会很痛苦。源码包里默认use_sim_time: false真机部署务必保持这个值不变。6. 先用仿真把闭环跑通一个不花钱的验证技巧Nav2 自带了一套仿真环境nav2_bringup包里可以直接启动 TurtleBot 3 的仿真世界。你不需要先有真机就能把巡检节点和 Nav2 参数完整跑一遍。这套验证方法价值很大因为巡检机器人真机调试成本高场地、安全、充电都是问题而仿真环境能先把 80% 的逻辑问题暴露掉。# 启动仿真世界 ros2 launch nav2_bringup tb3_simulation_launch.py \ world:/path/to/factory.world \ map:/path/to/factory_map.yaml # 另开终端启动巡检节点 ros2 run patrol_bot patrol_node.py \ --ros-args -p route_file:/config/factory_route.yaml仿真跑通的意义在于如果行为树、参数、巡检循环在仿真里都正常那真机出问题大概率集中在传感器噪声、里程计漂移、底盘响应这三块排查范围一下子就缩小了。如果仿真里就翻车那一定是参数或代码问题别急着上真机烧电机。从那以后我每次动 Nav2 参数或改巡检逻辑都强制先在仿真里把完整路线跑一遍确认没有 aborted、没有无限重试、记录数据正确才允许把代码部署到真机上。这套习惯帮我挡掉了至少一半的现场返工希望帮到你。本文还有配套的精品资源点击获取