NavForge is a ROS2 navigation deployment framework built around Nav2, providing reusable configurations, dependencies and launch workflows for mobile robots.
NavForge 面向仿真与真实移动机器人提供可复用的 ROS 2 Humble 2D 导航部署能力。框架通过 profile、mode 和 params 组合构建导航系统;自研节点负责话题与 frame 规范化,点云转激光、SLAM、AMCL 和 Nav2 等能力通过依赖包按需启用。
ROS 2 包名遵循小写命名约定:
navforge:话题与 TF 规范化节点navforge_bringup:统一入口和模块化 launch 工作流navforge_config:机器人 profile、部署 mode、算法参数、地图与 RViz 配置
| mode | 用途 | 定位 | 模块 |
|---|---|---|---|
mapping |
SLAM 建图与地图保存 | slam_toolbox | static_tf + laserscan + interface + slam |
nav |
已保存地图上的全局导航 | AMCL | static_tf + laserscan + interface + amcl + nav2 |
local_nav |
不维护全局地图的短程导航 | 连续 odom | static_tf + laserscan + nav2 |
每种模式在 modes/templates/ 中提供组合模板,实际启动文件按机器人添加尾缀:
mapping_l1、nav_l1、local_nav_l1,以及
mapping_quadrover、nav_quadrover、local_nav_quadrover。
./scripts/install_deps.sh
./scripts/build.sh
source install/setup.bash先启动仿真或真机驱动,再 launch 对应 mode。
外部环境(仿真 / 真机)
├── 传感器 / 定位话题
│ ↓ params 中写明的 input_topic
│ laserscan + interface
│ ↓ /scan、/odom 与动态 TF
│ slam / amcl / nav2
│
└── static_tf(按配置补齐真机缺失的固定坐标变换)
| 模式 | odom→base_link | map→odom |
|---|---|---|
mapping |
interface 或机器人里程计 | slam_toolbox |
nav |
interface 或机器人里程计 | AMCL |
local_nav |
interface 或机器人里程计 | 无(全链路使用 odom) |
map→odom 只能由一个模块发布:slam_toolbox 与 AMCL 不可同时运行。
传感器等固定 frame 由机器人驱动、robot_state_publisher 或 static_tf 发布,
同一变换只能保留一个发布源。
| 层级 | 职责 |
|---|---|
profiles/*.yaml |
footprint、frames |
modes/templates/*.yaml |
mapping、nav、local_nav 三种组合模板 |
modes/*_{l1,quadrover}.yaml |
机器人实际使用的模块、params、RViz 与地图路径 |
params/*.yaml |
话题、frame、算法参数 |
launch use_sim_time |
可覆盖 mode 默认时钟;Quadrover 为 true,L1 为 false |
| 文件 | 用于 |
|---|---|
laserscan_{l1,quadrover}.yaml |
点云→/scan |
cloud_filter_l1.yaml |
L1 机身与上装点云剔除 |
interface_mapping_{l1,quadrover}.yaml |
建图里程计接口 |
interface_nav_{l1,quadrover}.yaml |
全局/局部导航里程计接口 |
slam_mapping_{l1,quadrover}.yaml |
slam_toolbox建图 |
amcl_nav_{l1,quadrover}.yaml |
nav的AMCL + map_server |
nav2_nav_{l1,quadrover}.yaml |
已保存地图导航 |
nav2_local_nav_{l1,quadrover}.yaml |
滚动局部导航 |
static_tf_{l1,quadrover}.yaml |
各机器人缺失的静态TF |
robot_localization_l1.yaml |
L1 Local/Global EKF 完整示例 |
rtk_adapter_l1.yaml |
L1 Unicore 双天线标准消息适配 |
L1 使用 _l1 参数文件。其中 nav2_nav_l1.yaml 使用 MPPI Omni 控制器、
SmacPlanner2D、全向 AMCL 运动模型配套的室外 costmap;slam_mapping_l1.yaml
为实时建图调整扫描节流、队列与回环检测。
nav2_local_nav_l1.yaml 不订阅静态地图,使用 odom 下固定 20 m × 20 m
滚动全局 costmap,供长时间运行的短程避障导航使用。
params/templates/ 按 launch 模块提供完整、带注释的配置基线。模板不会被 mode
自动加载;可以复制到 params/ 后修改,也可以直接使用相对路径,例如
params_file:=templates/laserscan.yaml。完整清单和使用说明见
params/templates/README.md。
仿真与真机差异:修改 interface_*.yaml 中的 input_topic。
L1 mode 默认在生成 /scan 前启动 cloud_filter:
/livox/lidar → cloud_filter → /livox/lidar_filtered
→ pointcloud_to_laserscan → /scan
节点按点云时间戳查询 filter_frame(默认 base_link)的 TF,并剔除落在任一
三维包围盒内的点。输出仍保留输入点云的 frame 和全部字段,后续
pointcloud_to_laserscan 会按自身配置转换到 base_link。TF 不可用或点云
缺少有效的 x/y/z 字段时整帧丢弃并输出节流告警,避免使用坐标系错误的数据。
编辑 params/cloud_filter_l1.yaml 的 box_names,并为每个名称配置
boxes.<name>.min_x/max_x/min_y/max_y/min_z/max_z。ROS 2 参数不支持配置示例
中那种字典数组,因此采用名称列表加嵌套参数:
box_names: [robot_body, rear_payload]
boxes:
rear_payload:
min_x: -0.65
max_x: -0.25
min_y: -0.30
max_y: 0.30
min_z: 0.25
max_z: 0.90包围盒边界均以 filter_frame 为准,真机部署前应根据实际机身、腿部运动包络和
上装尺寸测量,并用 RViz 同时观察原始点云与 /livox/lidar_filtered,确认不会
误删紧邻机器狗的真实障碍物。模块也可单独启动:
ros2 launch navforge_bringup cloud_filter.launch.py \
params_file:=cloud_filter_l1.yamlrobot_localization.launch.py 可以从一个 params 文件启动 Local EKF 和
Global EKF。当前没有 mode 引用该模块,需单独启动:
ros2 launch navforge_bringup robot_localization.launch.py \
params_file:=robot_localization_l1.yaml示例数据流如下:
L1 LIO → /slam_odom + odom→base_link
/slam_odom + 绝对全局观测 → ekf_global → /odometry/global + map→odom
编辑 params/robot_localization_l1.yaml 中各节点的 enabled、remappings 和
ros__parameters 即可选择节点、替换真机话题并调整融合维度。配置数组顺序为
x, y, z, roll, pitch, yaw, vx, vy, vz, vroll, vpitch, vyaw, ax, ay, az;
L1 示例保留全向运动所需的 vx 和 vy。
L1 的 LIO 已完成 LiDAR/IMU 紧耦合并负责 odom→base_link,因此示例默认关闭
ekf_local。由于暂未选择绝对全局观测,ekf_global 也默认关闭。配置全局
观测后,仍需确保它与 SLAM、AMCL 不会同时发布 map→odom。RTK/NavSat
数据转换与融合策略将在确定接收机输出、航向定义和质量状态后单独设计。
rtk_adapter 将 L1 的 Unicore 派生自定义消息转换为 ROS 标准类型:
/uni_best_nav → /rtk/fix sensor_msgs/msg/NavSatFix
/uni_heading → /rtk/heading sensor_msgs/msg/Imu(仅绝对 ENU yaw)
状态与拒绝原因 → /rtk/status diagnostic_msgs/msg/DiagnosticArray
节点会从 ROS graph 自动发现两个输入话题的实际消息类型,因此 NavForge 不需要
编译依赖厂商接口包;运行环境仍必须安装并 source 对应消息接口包。也可在
params 中填写 package/msg/Type 显式指定。当前没有 mode 自动启动该模块,
可独立验证:
ros2 launch navforge_bringup rtk_adapter.launch.py \
params_file:=rtk_adapter_l1.yamlL1 默认只接受 solution_status=0 且类型为 50 的 RTK Fixed 位置和航向。
无效解只写入 /rtk/status,不会把零经纬度或零航向送入定位链路。航向从
“真北为零、顺时针为正”转换为 ROS ENU,并通过
heading_mounting_yaw_deg 补偿双天线基线相对 base_link +X 的安装角。
rtk_primary_link 必须通过静态 TF 正确连接到 base_link。RTK 与 Global
EKF 的融合仍保持独立,待确定门限和 TF 所有权后再接入 mode。hor_spd、
trk_gnd 和 ver_spd 暂不输出:其 ENU 速度还需要随全局 frame 对齐,且当前
厂商消息未提供可用速度标准差,避免在此阶段生成看似完整但协方差不可信的速度。
所有 mode 默认都会启动 static_tf 模块。编辑对应机器人的
params/static_tf_<robot>.yaml 中
transforms 数组即可补齐任意多个固定变换;未配置 transforms 时节点不会发布 TF。
static_tf_publisher:
ros__parameters:
# parent child x y z roll pitch yaw
# 平移单位:米;旋转单位:弧度
transforms:
- "base_link lidar_link 0.20 0.0 0.30 0.0 0.0 0.0"
- "base_link imu_link 0.00 0.0 0.10 0.0 0.0 1.5708"每个 child frame 只能配置一次。不要配置已由机器人驱动、
robot_state_publisher、SLAM 或 AMCL 发布的变换,否则 TF 会产生冲突。
src/navforge_config/maps/ 已 gitignore,本地存放,不入库。
./scripts/save_map.sh
# 默认保存:src/navforge_config/maps/<当前 profile>/map.yaml + map.pgm| 用途 | 格式 | 默认路径 |
|---|---|---|
| 建图保存 | .yaml + .pgm |
maps/<profile>/map |
| 已知地图导航 | .yaml + .pgm |
maps/example/map |
| L1 建图 / 已知地图 | 同上 | maps/l1/map |
建图类 mode 不配置保存路径。save_map.sh 会从正在运行的 topic_relay_odom
读取 profile;无法读取时回退为 quadrover。也可以显式指定 profile、目录和文件名:
./scripts/save_map.sh --profile l1
./scripts/save_map.sh --output-dir /data/maps --map-name campus
# 兼容旧位置参数:MAP_NAME OUTPUT_DIR
./scripts/save_map.sh campus /data/maps# Quadrover 建图
ros2 launch navforge_bringup navforge.launch.py mode:=mapping_quadrover
# Quadrover 已保存地图导航
ros2 launch navforge_bringup navforge.launch.py mode:=nav_quadrover
# Quadrover 局部导航
ros2 launch navforge_bringup navforge.launch.py mode:=local_nav_quadrover
# 覆盖 mode 中的地图路径
ros2 launch navforge_bringup navforge.launch.py \
mode:=nav_quadrover map:=maps/quadrover/map可选参数:use_rviz:=false、profile:=...、use_sim_time:=...。未显式指定时
使用 mode 中的默认值;Quadrover mode 使用仿真时钟,L1 mode 使用
l1/系统时钟。
默认配置假设机器狗驱动提供:
| 接口 | 默认值 | 要求 |
|---|---|---|
| Livox 原始点云 | /livox/lidar |
sensor_msgs/PointCloud2,有效时间戳和 frame,包含 x/y/z |
| LIO 里程计 | /slam_odom |
nav_msgs/Odometry,并提供连续的 odom→base_link |
| 速度指令 | /cmd_vel |
支持 linear.x、linear.y、angular.z,停止时三个分量必须为 0 |
| TF | SLAM/AMCL 为 map→odom→base_link;局部导航为 odom→base_link |
只能有一个发布源;传感器固定 TF 必须完整 |
按实际驱动修改 cloud_filter_l1.yaml、laserscan_l1.yaml、
interface_*_l1.yaml,测量包含载荷后的机身外廓并同步修改
profiles/l1.yaml 与 nav2_nav_l1.yaml、nav2_local_nav_l1.yaml
中的 footprint。
在 static_tf_l1.yaml 中只补充驱动或 URDF 未发布的固定变换。
当前L1 LIO自身发布odom→base_link,因此interface_*_l1.yaml已将
publish_tf设为none;只有更换为不发布TF的里程计源时才应调整所有权。
本仓库使用 2D costmap。室外坡地、台阶和负障碍不能仅凭投影后的
/scan判断可通行性;送入本工具前必须完成地面分割/地形过滤,并保留独立急停与真机限速。
# 室外建图
ros2 launch navforge_bringup navforge.launch.py \
mode:=mapping_l1 use_sim_time:=false
# 保存地图上的AMCL导航
ros2 launch navforge_bringup navforge.launch.py \
mode:=nav_l1 use_sim_time:=false map:=maps/l1/map
# 不维护全局地图的长时间短程导航
# 目标使用 odom frame,推荐单段距离 5-8 m
ros2 launch navforge_bringup navforge.launch.py \
mode:=local_nav_l1 use_sim_time:=false
首次上车应架空或低速测试横移方向和急停,再从空旷区域开始。默认速度上限为
前进 0.4 m/s、后退 0.2 m/s、横移 0.2 m/s、转动 0.6 rad/s;
目标容差为 0.10 m / 0.10 rad。L1 驱动接受的最小非零速度为前后
0.05 m/s、横移 0.10 m/s、转动 0.02 rad/s,对应配置在
nav2_nav_l1.yaml或nav2_local_nav_l1.yaml中的
VelocityDeadbandCritic.deadband_velocities和
velocity_smoother.deadband_velocity。
整体导航速度主要由 FollowPath 下的 vx_max、vx_min、vy_max、wz_max
决定;velocity_smoother 的 max_velocity、min_velocity 是发布到真机前的最终
硬限制。调整速度时应同步修改这两组值,并优先保持两处限制一致。
先校准里程计、TF、footprint 和时间同步,再调整 MPPI critic 权重;不要通过继续
缩小目标容差掩盖定位漂移。
- 启动
mapping,遥控覆盖环境 ./scripts/save_map.sh保存栅格地图;默认输出到当前 profile 的map.yaml与map.pgm
- 使用
mapping_<robot>生成地图并保存 - 启动
nav_<robot> - RViz 2D Pose Estimate 设置初始位姿
- 发送 Nav2 Goal
- 启动
local_nav_<robot> - 确认机器人提供连续的
odom→base_link - 在
odom坐标系内发送短程目标;长任务由上层拆分为多个局部目标
| 脚本 | 作用 |
|---|---|
install_deps.sh |
安装 apt 依赖 |
build.sh |
colcon 编译 |
save_map.sh |
保存栅格地图,支持自定义 profile、目录和文件名 |
pointcloud_to_laserscan 懒订阅:打开 RViz 或 ros2 topic hz /scan 触发。
/scan 为 Best Effort。使用 mode 对应的 rviz_config,或手动将 Reliability 改为 Best Effort。
修改 launch 后需重新 colcon build 并 source install/setup.bash。
必须先 2D Pose Estimate,等待 AMCL 发布 map→odom 后再发 Goal。
在对应的nav2_nav_<robot>.yaml或nav2_local_nav_<robot>.yaml中调整
xy_goal_tolerance(米)与yaw_goal_tolerance(弧度)。
L1 使用precise_goal_checker;实际精度上限首先取决于传感器标定、
时间同步、里程计和全局定位质量。
/loc/gazebo是map→base_link真值,不能作为AMCL的odom输入;Quadrover导航
应使用/odom/wheel(见interface_nav_quadrover.yaml)。
NavForge/
├── scripts/ # install / build / save_map
├── src/
│ ├── navforge/ # C++ nodes and shared headers
│ ├── navforge_bringup/ # launch entrypoint and module launch files
│ └── navforge_config/ # profiles / modes / params / maps / rviz
├── AGENTS.md
└── README.md