跳到主要内容

ROS 2 教程

开发者补充:仓库与 ROS 2 功能包

ugv_jetson 偏 Web 控制端、JupyterLab 教学、上位机视觉示例、下位机 JSON 控制和媒体功能。入门教程、上位机教程、下位机教程主要围绕这一套流程展开。

ugv_ws 偏 ROS 2 工作空间,包含机器人模型、TF、雷达、里程计、建图、导航、视觉接口、行为控制、仿真和 ROS 2 Web 可视化。

功能包作用
ugv_base_node根据原始里程计和 IMU 发布 /odom,可发布 odom → base_footprint TF
ugv_bringup底盘、雷达、驱动和基础数据启动入口
ugv_descriptionURDF 模型、机器人结构 TF、RViz 模型显示
ugv_interface产品封装的 Action / 消息接口
ugv_nav2D 定位、Navigation、Nav2 参数和地图
ugv_slamGmapping、Cartographer、RTAB-Map 等建图流程
ugv_tools键盘控制、手柄控制、行为控制
ugv_visionUSB 相机、OAK-D Lite、AprilTag、视觉交互入口
ugv_gazeboGazebo 仿真模型、世界、仿真建图和导航入口
ugv_web_app基于 Vizanti 的 ROS 2 Web 可视化或控制入口
ugv_chat_aiWeb AI 交互入口,依赖额外 AI 服务配置

依赖包中常见名称还包括 apriltag_roscartographeremcl2explore_liteslam_gmappingldlidarrf2o_laser_odometryrobot_pose_publisherteb_local_plannervizanti

环境准备

先完成入门教程中的 开机网络连接Web 页面访问。确认 UGV 电量UGV 预检 已完成,雷达、相机无遮挡,底盘周围有足够空间。

后续 ROS 2 操作需要多个 Docker ROS 2 容器终端同时运行。JupyterLab Terminal 只适合临时检查,Notebook 代码单元格不适合运行持续占用终端的 ROS 2 命令。SSH 工具用于通过网络进入远程命令行环境。 使用 SSH 工具登录 Docker ROS 2 容器,并按需要多开终端标签页。MobaXterm 不是必须工具,但适合 Windows 环境按示例填写地址、端口、用户名和密码,也方便保存会话和复制标签页。

从 JupyterLab 启动 Docker 并配置 MobaXterm

入门教程:打开 Web 控制页面上位机教程:JupyterLab 界面速览 打开 JupyterLab,并在 Launcher 中进入 Terminal。这里用 Terminal 启动 Docker 远程 SSH 服务,不要在 Notebook 代码单元格中运行持续占用终端的 ROS 2 命令。

  1. 在 JupyterLab Terminal 中进入 ROS 2 工作空间目录。

    cd ~/ugv_ws
  2. 启动 ROS 2 Docker 远程服务。

    sudo chmod +x ros2_humble.sh remotessh.sh
    ./ros2_humble.sh

    终端输出示例:

    jetson@ubuntu:~/ugv_ws$ sudo chmod +x ros2_humble.sh remotessh.sh
    ./ros2_humble.sh
    [sudo] password for jetson:
    Entering the container...
    ugv_jetson_ros_humble
    Container started successfully.
    Executing docker exec command to open a bash shell in the container...
    * Starting OpenBSD Secure Shell server sshd [ OK ]
    Opened bash shell in the container.
  3. 打开 MobaXterm,点击左上角 Session

    MobaXterm Session 按钮位置

  4. 在弹出的会话类型窗口中选择 SSH

    MobaXterm SSH 类型位置

  5. Basic SSH settings 中填写 Docker ROS 2 容器连接信息。

    • Remote host 填写 OLED 或 Web 页面中看到的 UGV IP。
    • Port 填写 23。端口 23 用于进入 ROS 2 Docker 镜像,不要改成普通 SSH 端口。
    • 其余选项保持默认。

    新建 MobaXterm SSH 会话

  6. 点击 OK 连接。出现 login as: 时输入 root

    输入 Docker ROS 2 容器用户名

  7. 按提示输入密码。当前镜像密码为jetson,输入时终端不会显示明文字符。

    登录 Docker ROS 2 容器

  8. 第一次保存密码提示选择 No

    MobaXterm 保存密码提示

  9. 第一次选择鼠标右键动作时,选择 Paste,这样 Windows 复制命令后可在 MobaXterm 中右键粘贴。

    MobaXterm 右键动作设置

  10. 登录后会进入 Docker ROS 2 容器终端。看到 root@ubuntu 这类提示符时,表示已经进入容器环境。

    Docker ROS 2 容器登录完成

备注

从Windows往终端复制粘贴代码的方法

在 Windows 中先选中并按 Ctrl + C 复制代码,再切回 MobaXterm,把光标放到终端窗口内并右键粘贴。后续复制多行命令时,MobaXterm 会提示即将粘贴多行内容。确认内容无误后点击 OK

MobaXterm 多行粘贴提示

  1. ROS 2 后续经常需要多个终端同时运行。在 MobaXterm 左侧 User sessions 中再次双击刚创建的 UGV IP 会话,每双击一次会新开一个 Docker ROS 2 容器 SSH 标签页。也能点击顶部标签栏旁边的 +,再重新打开同一个 SSH 会话。

    多个终端

提示

sudo chmod +x ros2_humble.sh remotessh.sh 一般仅需执行一次。脚本文件未替换、系统未重新刷写时,后续不需要重复执行该命令。

./ros2_humble.sh 用于启动 ROS 2 Docker 远程 SSH 服务。UGV 重启后,先在 Windows PowerShell 中检查 23 端口是否可连接:

Test-NetConnection <UGV_IP> -Port 23

如果显示 TcpTestSucceeded : True,表示 MobaXterm 可登录 ROS 2 Docker。如果显示 TcpTestSucceeded : False,返回 JupyterLab Terminal 执行:

cd ~/ugv_ws
./ros2_humble.sh

更换 Wi-Fi、热点或路由器后,先确认 UGV 的当前 IP 地址。为避免左侧会话名称与实际连接 IP 不一致,新建一个使用当前 IP 的 MobaXterm SSH Session。

新建 Session 时仍然使用相同配置:

  • Remote host:UGV 当前 IP;
  • Specify usernameroot
  • Port23

新建 Session 后,可让 Session 名称使用当前 IP,例如 192.168.9.197,方便识别。旧 Session 不会自动更新 IP。如果旧 IP 不再使用,右键旧 Session,选择 Delete session 删除,避免后续误点。

首次调试时先保持 JupyterLab 页面不关闭,便于回到 Terminal 检查或重新启动服务;确认端口 23 仍可连接后,关闭 JupyterLab 页面不影响已启动的 Docker SSH 服务。

开发者补充:修改已有 MobaXterm Session

如果需要修改已有 Session,右键旧 Session,选择 Edit session,在 SSHBasic SSH settings 中修改 Remote hostRemote host 才是 MobaXterm 实际连接的 UGV IP。Rename session 只修改左侧显示名称,不修改实际连接 IP。

ip选项位置

修改 MobaXterm 会话中的 UGV IP

加载 ROS 2 环境

每个新开的 Docker ROS 2 终端都先加载 ROS 2 环境。后文功能步骤中出现“加载 ROS 2 环境”时,均指执行以下命令。

cd /home/ws/ugv_ws
source /opt/ros/humble/setup.bash
source install/setup.bash

如果当前镜像工作空间路径不同,请以当前容器中的实际路径为准。source 命令没有输出。

ROS 2 最小检查

  1. 先在当前 Docker ROS 2 终端加载 ROS 2 环境。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash
  2. 确认 ROS 2 能找到 UGV 相关包:

ros2 pkg list | grep ugv
ros2 node list
ros2 topic list
提示

刚加载环境但未启动任何 launch 时,ros2 node list 没有输出不代表错误。ros2 topic list 只看到 /parameter_events/rosout 这类基础 topic,不属于异常。后文功能会在相关节点启动后检查相关 topic。

启动机器人基础功能

启动基础功能时,先连接 ROS 2、真实底盘和雷达,再进行第一次低速遥控。bringup_lidar.launch.py 本身不主动让底盘运动,但会占用底盘串口和雷达;键盘控制、手柄控制和行为控制会驱动实体底盘。

危险

执行底盘运动前,先确认电量、场地和停止方式。首次测试可架空底盘,或放在空旷平整地面低速短时移动。

启动底盘与雷达

bringup_lidar.launch.py 会访问实体底盘和雷达。启动前如果不确定底盘串口是否被占用,先检查并释放目标串口。

检查并释放底盘串口

以下命令在 Jetson 主机终端或 JupyterLab Terminal 中执行,不要求进入固定目录。命令访问 /dev/ttyTHS* 串口设备,与当前所在目录无关。

Jetson Orin Nano 底盘串口为 /dev/ttyTHS0,Jetson Orin NX 底盘串口为 /dev/ttyTHS1,以系统实际列出的串口为准。更多说明见 下位机教程:串口占用检查与释放

ls /dev/ttyTHS*
PORT=/dev/ttyTHS1
sudo fuser -v $PORT

ls /dev/ttyTHS* 会列出当前系统中的串口,例如:

/dev/ttyTHS1 /dev/ttyTHS2

sudo fuser -v $PORT 有输出时,目标串口正在被某个进程占用。输出中的数字是 PID,也就是进程编号。例如:

USER PID ACCESS COMMAND
/dev/ttyTHS1: jetson 1805 F.... python

其中 1805 是占用 /dev/ttyTHS1 的进程编号。后面的 fuser 释放命令会按 $PORT 处理占用该串口的进程,不需要手动输入 PID。

备注

如需确认占用来源,将输出中的 PID 代入下面命令查看进程信息:

ps -fp <PID>

例如输出中显示 1805,就执行:

ps -fp 1805

这一步不是必须执行。

发送 TERM 信号,让占用目标串口的进程正常退出:

sudo fuser -TERM -k $PORT
sleep 2
sudo fuser -v $PORT

如果 sudo fuser -v $PORT 仍然有输出,说明目标串口仍未释放。确认当前 $PORT 是要释放的目标串口后,再只针对该目标串口执行强制释放:

sudo fuser -KILL -k $PORT
sleep 1
sudo fuser -v $PORT

最后一条 sudo fuser -v $PORT 没有输出,目标串口已经释放。sudo fuser -KILL -k $PORT 只结束占用当前 $PORT 设备文件的进程,不是结束所有 Python 进程。不要使用结束所有 Python 进程的方式释放资源;只释放目标串口或目标摄像头的占用进程。

注意

如果 ps -fp <PID> 显示进程路径中包含 ugv_jetson/app.py,说明占用来源为 Web 主程序。

释放该串口后,Web 控制页面的底盘控制会暂时不可用。完成 ROS 2 测试后,重启或运行程序

sudo reboot

恢复Web端页面。

开发者补充:让 Web 主程序开机后不自动恢复

Web 主程序自启由 autorun.sh 写入当前账号 crontab 的 @reboot 项。JupyterLab 使用 ugv_jupyter.service,两者不是同一个启动入口。不同镜像的自启方式以当前设备为准。

如果只是临时切换到 ROS 2 或 Notebook 独占底盘串口,不要禁用 Web 主程序开机自启。停止 Web 主程序后,完成测试并重启设备,默认启动项会恢复 Web 控制页面。

先查看当前 crontab:

crontab -l

如果看到包含 ~/ugv_jetson/app.py@reboot 行,先保存该行,再编辑 crontab:

crontab -e

删除或注释包含 ~/ugv_jetson/app.py@reboot 行后保存。再次查看:

crontab -l

恢复自启时,把记录的 @reboot ... ~/ugv_jetson/app.py ... 行加回 crontab。

如果当前镜像改用 systemd,先查实际服务名:

systemctl list-unit-files | grep -Ei "ugv|jetson|web|app|access"
systemctl list-units --type=service | grep -Ei "ugv|jetson|web|app|access"

确认服务名后查看:

systemctl status <SERVICE_NAME>

禁用并停止该服务:

sudo systemctl disable --now <SERVICE_NAME>
systemctl is-enabled <SERVICE_NAME>

恢复自启时执行:

sudo systemctl enable --now <SERVICE_NAME>
systemctl status <SERVICE_NAME>

禁用 Web 主程序自启后,设备重启后 Web 控制页面、视频流或依赖 Web 主程序的功能不会自动恢复。记录服务名或 crontab 原始行和恢复命令,避免后续无法回到默认状态。

  1. 在 Docker ROS 2 终端中加载 ROS 2 环境。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash
  2. 设置型号并启动底盘与雷达:

export UGV_MODEL=ugv_rover
export LDLIDAR_MODEL=ld19
ros2 launch ugv_bringup bringup_lidar.launch.py use_rviz:=false

Docker ROS 2 终端 1

启动该命令的终端也可称为 bringup 终端。后续检查 topic、手动控制底盘、手柄控制、RViz 查看雷达扫描等需要实体底盘和雷达数据的步骤,都需要保持该终端继续运行,不要关闭。

提示

该命令会启动实体底盘、雷达和相关 ROS 2 节点,不打开 RViz。命令会启动 robot_state_publisherjoint_state_publisherugv_bringupugv_driverldlidar_noderf2o_laser_odometry_nodebase_node 等进程。雷达数据是否进入 ROS 2,后面通过 /scan topic 检查;需要图形化查看模型和雷达扫描时,再进入“RViz 模型与传感器可视化”章节。

启动后查看终端输出,看到以下日志时,bringup 正在运行:

  • ldlidar communication is normal
  • Publish topic message:ldlidar scan data
  • Got first Laser Scan
  • 终端停留在 launch 输出中,没有回到命令提示符。

如需停止,回到启动 bringup_lidar.launch.py 的 MobaXterm 终端,按 Ctrl + C。停止后,底盘节点、雷达节点和相关里程计节点会随 launch 一起退出。

检查 ROS 2 节点和 topic 状态

保持上一步启动 bringup_lidar.launch.pyDocker ROS 2 终端 1 继续运行,不要关闭。另开一个 Docker ROS 2 终端 2,用于查看当前正在运行的 node 和 topic。

node 和 topic 检查只读取 ROS 2 状态,不主动驱动底盘,也不需要打开 RViz。

如果 Docker ROS 2 终端 1 已经关闭,请先回到 启动底盘与雷达 重新启动。

  1. 在 Docker ROS 2 终端 2 中加载 ROS 2 环境。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash
  2. 查看当前节点和 topic:

ros2 node list
ros2 topic list

Docker ROS 2 终端 1 中的 bringup_lidar.launch.py 运行时,ros2 node list 会列出 /LD19/base_node/rf2o_laser_odometry/ugv_bringup/ugv_driver/ugv/robot_state_publisher 等节点。ros2 topic list 会列出 /scan/tf/tf_static/odom/odom/odom_raw/odom_rf2o/imu/data/imu/data_raw/imu/mag/voltage 等 topic。终端中出现同名节点提示时,继续按 topic 列表检查,不影响 topic 读取。

  1. 按关键词检查常见 topic:
ros2 topic list | grep scan
ros2 topic list | grep tf
ros2 topic list | grep odom
ros2 topic list | grep imu
ros2 topic list | grep voltage

ros2 topic list 的实际输出为准。如果某个 topic 没有出现,不要直接对它执行 echo

  1. /scan 检查雷达扫描数据。

检查雷达是否发布数据,不需要打开 RViz。/scan topic 存在并持续发布数据,表示雷达扫描数据已经进入 ROS 2。

确认 /scan 存在后,查看发布频率:

ros2 topic hz /scan

看到频率输出后,按 Ctrl + C 结束频率查看。再查看一条雷达扫描消息:

ros2 topic echo /scan --once

如果 /scan 不存在,不要继续执行 echo,先检查 Docker ROS 2 终端 1 是否仍在运行。

  1. 只在 topic 已存在时查看其它数据。

确认 /odom 存在后,查看一条里程计数据:

ros2 topic echo /odom --once
提示

如果终端提示:

WARNING: topic [...] does not appear to be published yet
Could not determine the type for the passed topic

表示当前没有节点正在发布该 topic,或该 topic 不适用于当前启动配置。先确认 Docker ROS 2 终端 1 中的 bringup_lidar.launch.py 仍在运行,再根据 ros2 topic list 的实际输出选择要查看的 topic。

ros2 topic hz /scan 刚启动时会先等待数据,随后持续输出 average rate。频率稳定在约 10 Hz 时,/scan 正在持续发布。

/scan 执行 ros2 topic echo /scan --once 后,终端会输出一条 LaserScan 消息,其中包含 frame_id: base_lidar_linkangle_minangle_maxrange_minrange_maxrangesintensitiesranges 中的数字是雷达到障碍物的距离,.nan 表示该角度没有有效距离读数。

/odom 执行 ros2 topic echo /odom --once 后,终端会输出一条里程计消息,其中包含 frame_id: odomchild_frame_id: base_footprintposetwist。这表示里程计 topic 已经发布。

手动控制底盘

键盘控制和手柄控制都是手动输入方式。控制节点会把按键或手柄摇杆转换成 /cmd_vel 速度指令,Docker ROS 2 终端 1 中的底盘驱动接收指令后,让实体底盘移动。测试前保持 Docker ROS 2 终端 1 中的 bringup_lidar.launch.py 继续运行;如果 Docker ROS 2 终端 1 已经关闭,先回到 启动底盘与雷达 重新启动。

危险

键盘控制和手柄控制会主动驱动实体底盘。首次测试前让机器人悬空,或放在空旷地面,并远离人、宠物、桌脚和线缆。测试结束前先发送停止指令,再退出控制节点。

使用键盘低速移动

保持 Docker ROS 2 终端 1 中的 bringup_lidar.launch.py 继续运行,不要关闭。另开一个 Docker ROS 2 终端 2 运行键盘控制命令。

  1. 在 Docker ROS 2 终端 2 中加载 ROS 2 环境。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash
  2. 确认 /cmd_vel topic。

ros2 topic info /cmd_vel
  1. 启动键盘控制。
ros2 run ugv_tools keyboard_ctrl

常用按键如下:

按键动作
I前进
,后退
J左转
L右转
K 或空格停止
Ctrl + C退出键盘控制

键盘控制终端必须保持输入焦点。每次短按方向键或控制键后,立即按 K 或空格停止。不要长时间持续按方向键。测试结束前先按 K 或空格停止,确认机器人停止后,再按 Ctrl + C 退出键盘控制节点。

使用手柄控制

手柄控制会把手柄输入转换为 /cmd_vel,从而控制底盘运动。先完成键盘低速移动测试,并确认停止方式后,再使用手柄控制。

先将手柄的 2.4G USB 接收器插入 Jetson 主板 USB 接口。打开手柄背面的电源开关,手柄指示灯闪红灯后,再启动 ROS 2 手柄控制节点。

手柄接口连接

保持 Docker ROS 2 终端 1 中的 bringup_lidar.launch.py 继续运行,不要关闭。另开新的 Docker ROS 2 终端运行手柄控制命令。

  1. 在新的 Docker ROS 2 终端中加载 ROS 2 环境。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash
  2. 启动手柄控制:

ros2 launch ugv_tools teleop_twist_joy.launch.py

手柄控制会主动驱动底盘。测试时先小幅移动摇杆,按下面对应关系检查底盘动作:

手柄动作底盘动作
左摇杆向上前进
左摇杆向下后退
右摇杆向左左转
右摇杆向右右转

测试结束前先松开摇杆并确认底盘停止,再按 Ctrl + C 退出手柄控制 launch。

检查 TF 坐标关系

TF 用来描述机器人不同坐标系之间的位置和方向关系,可以理解为 ROS 2 中的坐标换算关系。它不是控制命令,也不是传感器数据,而是让 ROS 2 知道车体、雷达、IMU、相机、轮子、里程计坐标之间如何对齐。

在 UGV 上,雷达扫描来自雷达坐标系,底盘运动与车体坐标系和里程计坐标系有关。TF 关系正常时,RViz、SLAM 和 Navigation 才能把机器人模型、雷达扫描、地图和机器人位置放到同一个空间中显示和计算。TF 正常不等于建图或导航完成。

保持前面启动 bringup_lidar.launch.pyDocker ROS 2 终端 1 继续运行。另开一个 Docker ROS 2 终端,用于检查 TF。

  1. 在新的 Docker ROS 2 终端中加载 ROS 2 环境。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash
  2. 查看 TF topic。

ros2 topic list | grep tf

输出中会看到 /tf/tf_static/tf_static 保存固定安装关系,例如车体到雷达、IMU、相机和轮子的关系。/tf 保存运行中会变化的关系,例如 odombase_footprintbase_link 的关系。

  1. 查看固定安装关系。

查看雷达相对车体的位置和方向:

ros2 run tf2_ros tf2_echo base_link base_lidar_link

查看 IMU 相对车体的位置和方向:

ros2 run tf2_ros tf2_echo base_link base_imu_link

如果终端持续输出 TranslationRotationMatrix,表示 ROS 2 可以查询到对应的坐标关系。执行 tf2_echo 后,前几秒会出现 Waiting for transformframe does not exist。随后开始持续输出 TranslationRotationMatrix 时,该 TF 关系已经可以查询到。

  1. 查看动态坐标关系。
ros2 run tf2_ros tf2_echo odom base_footprint

移动或转动 UGV 时,输出中的 TranslationRotation 会变化。如果只是原地转向,主要关注 Rotation 或 yaw 变化。

如果 odom -> base_footprint 没有输出,尝试:

ros2 run tf2_ros tf2_echo odom base_link

不同工作空间或模型配置中,动态 TF 的 child frame 不完全相同,以终端输出为准。

  1. 生成 TF 树 PDF。
ros2 run tf2_tools view_frames

该命令会监听几秒 TF 数据,并在当前终端目录生成 frames_*.pdf。这个 PDF 用于检查当前系统的完整 TF 树,不作为理解 TF 的唯一材料。

在 MobaXterm 左侧文件栏勾选 Follow terminal folder,可以在当前目录找到并下载生成的 frames_*.pdf

在 MobaXterm 中找到 frames.pdf

TF 树 PDF 示例

UGV Rover 的简化 TF 树如下:

base_footprint
└── base_link
├── base_lidar_link
├── base_imu_link
├── 3d_camera_link
├── left_up_wheel_link
├── left_down_wheel_link
├── right_up_wheel_link
├── right_down_wheel_link
└── pt_base_link
└── pt_link1
└── pt_link2
└── pt_camera_link
坐标系含义
base_footprint底盘在地面上的参考点
base_link车身主体
base_lidar_link雷达安装位置
base_imu_linkIMU 安装位置
3d_camera_link3D 相机安装位置
四个 wheel link轮子模型位置,主要用于模型显示和关节状态
pt_base_link → pt_link1 → pt_link2 → pt_camera_link云台和云台相机链路

只启动 Docker ROS 2 终端 1 中的 bringup_lidar.launch.py 时,重点关注 odom → base_footprintodom → base_link。启动 SLAM / Navigation 后,重点关注 map → odom → base_footprint。没有启动 SLAM 或定位时,看不到 map 不属于错误。

开发者补充:查看 /tf 原始消息

/tf 原始消息会输出大量 frame_idchild_frame_id、translation 和 rotation 数据,适合排查具体 transform。普通检查优先使用 view_frames 查看 TF 树,或使用 tf2_echo 查看一对坐标关系。

ros2 topic echo /tf --once

RViz 模型与传感器可视化

RViz 用于图形化查看模型、TF、雷达扫描和常见显示项,只负责显示,不会主动驱动底盘。

注意

下面的命令会启动底盘与雷达 bringup,并同时打开 RViz,会占用底盘串口和雷达。 如果前面已经运行 bringup_lidar.launch.py use_rviz:=false,请先在 Docker ROS 2 终端 1Ctrl + C 停止,再运行下面命令,避免重复占用底盘串口和雷达。

export UGV_MODEL=ugv_rover
export LDLIDAR_MODEL=ld19
ros2 launch ugv_bringup bringup_lidar.launch.py use_rviz:=true
备注

如果 Docker ROS 2 终端 1 已经启动 bringup_lidar.launch.py use_rviz:=false,也可以另开终端单独打开 RViz。打开后需要在左侧 Displays 面板中检查或添加 RobotModelTFLaserScan 等显示项。

cd /home/ws/ugv_ws
source /opt/ros/humble/setup.bash
source install/setup.bash
ros2 run rviz2 rviz2

查看优先使用前面的 use_rviz:=true 命令,避免空 RViz 配置带来的困惑。

RViz 界面出现后,左侧是 Displays 面板,中间区域是 3D 视图,右侧是 Views 面板。左侧 Displays 面板用于检查当前任务需要的显示项;TF 的命令行检查见前面的“检查 TF 坐标关系”。

显示项常见 topic / 设置作用
RobotModel/ugv/robot_description显示机器人模型
TF/tf/tf_static显示坐标系
LaserScan/scan显示 2D 雷达扫描
GridRViz 网格显示显示参考网格
Map/map显示 2D 地图
Path以当前 Nav2 输出为准显示规划路径

先看左侧状态:Global Status 显示 Ok,表示当前 RViz 配置整体正常;RobotModel 显示 Status: Ok,表示机器人模型已经加载。

RViz 查看 UGV 模型

查看雷达扫描时,在左侧 Displays 面板中找到或添加 LaserScan,并将 Topic 设置为:

/scan

Topic 为空时,RViz 会显示 Error subscribing: Empty topic name。这表示 LaserScan 显示项还没有订阅雷达 topic,不代表雷达硬件损坏。设置为 /scan 后,LaserScan 变为 Status: Ok,并在 3D 视图中显示彩色点段。

添加雷达 topic

雷达监控

这些彩色点段是 2D 雷达扫描数据,表示雷达检测到的周围障碍物边缘,不是摄像头画面,也不是地图。点段的位置和形状会随周围环境变化。默认显示的网格 Grid 和机器人模型 RobotModel 用于查看机器人在 3D 视图中的位置和朝向。

RViz 界面

移动实体 UGV 后,RViz 中的模型位置、朝向和雷达扫描画面会随之变化。如果 TF 关系正常,RobotModelLaserScan 等显示项才能对齐到同一个空间。这只表示模型显示和雷达可视化正常,不等于建图或导航完成。

开发者补充:只查看 URDF 模型

如果只想查看机器人 URDF 模型和基础 TF,不连接实体底盘和雷达,可以使用:

cd /home/ws/ugv_ws
source /opt/ros/humble/setup.bash
source install/setup.bash

export UGV_MODEL=ugv_rover
ros2 launch ugv_description display.launch.py use_rviz:=true

该命令主要用于查看模型,不主动驱动底盘,也不读取实体雷达,不会发布真实 /scan 数据。查看实体底盘和雷达时,优先使用前面的 bringup_lidar.launch.py use_rviz:=true

2D 地图与导航总览

2D 地图与导航分成两个阶段:

  1. 建图:使用 Gmapping 或 Cartographer,让 UGV 在环境中移动,并生成 2D 占据栅格地图。
  2. 导航:加载已经保存的地图,让 UGV 在地图中定位、规划路径并移动到目标点。

建图阶段不会自动完成导航;Navigation 阶段也不是重新建图。保存后的 map.pgmmap.yaml 会在 Navigation 中被加载,用于定位和路径规划。

建图 launch
-> 键盘控制低速移动
-> RViz 中地图逐步出现
-> 保存 map.pgm + map.yaml
-> 打开 map.pgm 快速检查
-> 启动 Navigation
-> 设置 2D Pose Estimate
-> 设置 Nav2 Goal

2D 建图

Gmapping 和 Cartographer 都用于生成 2D 地图。第一轮实车 2D 建图优先使用 Gmapping,先把雷达、里程计、TF、RViz 地图显示和地图保存流程跑完整;熟悉 Gmapping 后,再测试 Cartographer。

建图质量要求与保存前检查

2D 地图会被后续 Navigation、保存导航点和自动探索继续使用。地图文件能保存成功,不代表地图适合导航。如果地图存在重影、错位、断墙、大片未知区域,或与当前测试场地不一致,后续会影响 AMCL、costmap、Nav2 Goal 和导航点任务。

建图时保持测试环境稳定:

  • 不要在人、宠物、椅子、箱子频繁移动的环境中建图;
  • 建图路线中的门、桌椅、障碍物位置应和后续 Navigation 测试时一致;
  • 透明玻璃、镜面、黑色吸光物体、细桌脚和低矮线缆可能影响雷达扫描;
  • 雷达前方不要被外壳、手、线缆或桌边遮挡;
  • 地面应平整,避免轮子打滑;
  • 建图过程中不要手动搬起或拖动 UGV。

建图路线不要只原地转圈,也不要快速移动。建议按下面方式采集:

  1. 启动 SLAM 后先原地小幅左右转动,让地图开始出现;
  2. 沿主要通道低速前进;
  3. 在每个拐角处慢速转向;
  4. 尽量走一个小闭环,最后回到起点附近;
  5. 对后续要导航的区域至少扫描一遍;
  6. 保存地图前让 UGV 停止,观察地图是否稳定。
注意

移动太快、频繁急转或轮子打滑时,地图容易出现重影、错位或墙体弯曲。这样的地图即使能保存,也不适合直接用于 Navigation。

保存地图前,先在 RViz 中检查:

  • Map 显示项不再持续提示 No map received
  • 地图中主要墙体、桌边、通道边界轮廓连续;
  • 没有明显双层墙、重影、弯曲走廊或整体错位;
  • 后续要导航的区域不是大片灰色未知区域;
  • LaserScan 彩色点段大致落在地图墙体或障碍物边缘附近;
  • UGV 停止不动时,地图不会明显抖动或跳变。

如果以上观察点不满足,不要急着保存地图。先继续低速补扫,或停止当前 SLAM 后重新建图。

Gmapping 建图

  1. 启动前准备。

    gmapping.launch.py 会启动底盘、雷达、里程计、robot_pose_publisherslam_gmapping 和 RViz。使用 use_rviz:=true 时,会加载 2D 建图视图。

    启动前停止其它 ROS 2 终端

    启动 Gmapping 前,先停止之前用于底盘 bringup、RViz、键盘控制、手柄控制或其它 SLAM 测试的终端。Gmapping launch 会重新启动底盘、雷达、里程计、slam_gmapping 和 RViz。

    如果前面已经运行 bringup_lidar.launch.py use_rviz:=false,请先在对应终端按 Ctrl + C 停止,再启动 Gmapping,避免重复占用底盘串口、雷达或产生重复节点。

    启动 SLAM 前按 检查并释放底盘串口 检查底盘串口,并确认雷达连接和供电正常。

  2. 启动 Gmapping。

    在新的 Docker ROS 2 终端中执行。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash

    export UGV_MODEL=ugv_rover
    export LDLIDAR_MODEL=ld19
    ros2 launch ugv_slam gmapping.launch.py use_rviz:=true

    该命令会启动底盘、雷达、里程计、Gmapping 和 RViz。启动后不要关闭这个 Gmapping 终端。

    Gmapping

    终端会输出 ldlidar communication is normalGot first Laser Scanslam_gmappingInitialization completeRegistering Scans:Done。这些日志表示雷达数据已经进入 Gmapping,建图节点正在处理扫描数据。

    提示

    RViz 刚打开时,Map 可能显示 Status: WarnNo map received。这表示 RViz 还没有收到 Gmapping 发布的 /map 地图数据,不代表雷达或 Gmapping 已经停止。

    启动后先等待 10 ~ 30 秒。Gmapping 完成初始化后,地图有时会自动出现。如果等待后仍然只看到 LaserScan 彩色点段,没有灰、白、黑的栅格地图,再使用键盘控制让 UGV 低速转向或短距离移动。

    map加载不出来

  3. 用键盘控制低速建图。

    保持 Gmapping 终端运行。另开一个 Docker ROS 2 终端运行键盘控制。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash

    ros2 run ugv_tools keyboard_ctrl

    按键如下。

    按键动作
    I前进
    ,后退
    J左转
    L右转
    K 或空格停止
    Ctrl + C退出键盘控制

    先原地小幅转向,再短距离前进或后退,让雷达扫描覆盖周围环境。每次移动后按 K 或空格停止。不要长时间持续按方向键。

  4. 保存地图。

    保存地图前,先按 建图质量要求与保存前检查 检查 RViz 中的地图质量,并确认已经出现灰、白、黑的栅格地图,Map 显示项不再提示 No map received

    1. 保持 Gmapping launch 继续运行。
    2. 在键盘控制终端按 K 或空格停止底盘,再按 Ctrl + C 退出键盘控制。
    3. 新开一个保存终端,执行保存脚本。
    cd /home/ws/ugv_ws
    chmod +x ./save_2d_gmapping_map.sh
    ./save_2d_gmapping_map.sh

    如果提示 Permission denied,表示脚本没有执行权限,不表示 Gmapping 建图失败,也不表示地图数据错误。已经执行过 chmod +x 后仍无法运行时,可以改用下面命令。

    bash ./save_2d_gmapping_map.sh
    1. 看到 Writing map occupancy dataWriting map metadataMap saved successfully 后,确认 map.pgmmap.yaml 已生成。
    2. 回到 Gmapping 终端按 Ctrl + C 退出。

    保存地图

    日志中出现 Received a 384 X 384 map @ 0.05 m/pix 时,表示保存到的地图尺寸为 384 x 384 像素,分辨率为 0.05 m/pix,也就是每个像素约 5 cm。地图像素尺寸不等于实际场地尺寸。

Cartographer 建图

  1. Cartographer 和 Gmapping 的区别。

    Gmapping 和 Cartographer 都是 2D 建图方法,目的都是让 UGV 利用雷达、里程计和 TF 生成环境地图。对常规使用流程来说,最终重点是得到可用于 Navigation 的 map.pgmmap.yaml

    先使用 Gmapping 完成第一轮实车建图。Gmapping 流程更直接,适合先确认雷达、里程计、TF、地图保存和 Navigation 是否能跑通。Cartographer 可作为进阶建图方案;它除了导出 map.pgmmap.yaml,保存脚本还会生成 Cartographer 内部状态文件,例如 map.pbstream

    对比项GmappingCartographer
    主要用途快速完成第一张 2D 地图,验证雷达、里程计、TF 和地图保存流程作为另一种 2D 建图方案,适合在熟悉基础流程后进一步测试
    建图结果主要得到 map.pgmmap.yaml通常也会得到 map.pgmmap.yaml,并生成 map.pbstream
    文件怎么理解map.pgm 是地图图片,map.yaml 是地图参数;后续 Navigation 主要加载这两个文件map.pgmmap.yaml 同样可用于 Navigation;map.pbstream 是 Cartographer 的内部状态文件,用于 Cartographer 相关流程
    学习顺序第一轮实车建图优先使用跑通 Gmapping 和 Navigation 后再测试
    使用建议适合写成主线流程适合作为进阶建图方案,不需要一开始就使用
  2. 启动 Cartographer。 Cartographer launch 也会占用底盘、雷达和相关 SLAM 资源。启动前先停止其它 bringup、Gmapping、Navigation 或键盘控制终端。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash

    export UGV_MODEL=ugv_rover
    export LDLIDAR_MODEL=ld19
    ros2 launch ugv_slam cartographer.launch.py use_rviz:=true

    启动后不要关闭当前 Cartographer 终端。

  3. 用键盘控制低速建图。 保持 Cartographer 终端运行。另开 Docker ROS 2 终端运行键盘控制。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash

    ros2 run ugv_tools keyboard_ctrl

    J / L 小幅转向,再用 I / , 短距离移动。每次动作后按 K 或空格停止。

  4. 保存地图。 保存前先按 建图质量要求与保存前检查 检查 RViz 中的地图质量,再停止键盘控制底盘运动,但保持 Cartographer launch 运行。

    cd /home/ws/ugv_ws
    chmod +x ./save_2d_cartographer_map.sh
    ./save_2d_cartographer_map.sh

    如果提示 Permission denied,可以用 bash 执行。

    cd /home/ws/ugv_ws
    bash ./save_2d_cartographer_map.sh

    地图会保存到以下目录。

    /home/ws/ugv_ws/src/ugv_main/ugv_nav/maps/

    保存完成后再回到 Cartographer 终端按 Ctrl + C 退出。

    RViz 中可以看到地图逐步更新。使用键盘控制移动时,地图范围会逐步扩展。保存后会生成或更新 map.pgmmap.yamlmap.pbstream。Cartographer 建图完成后仍需进入 Navigation 才能使用地图导航。

查看保存的地图文件

Gmapping 和 Cartographer 保存后的地图默认位于以下路径:

/home/ws/ugv_ws/src/ugv_main/ugv_nav/maps/map.pgm
/home/ws/ugv_ws/src/ugv_main/ugv_nav/maps/map.yaml

在 Docker ROS 2 终端中执行下面命令检查文件。

cd /home/ws/ugv_ws

ls -lh src/ugv_main/ugv_nav/maps/map.pgm \
src/ugv_main/ugv_nav/maps/map.yaml

cat src/ugv_main/ugv_nav/maps/map.yaml

map.pgm 是地图图片,map.yaml 是地图参数文件。后续 Navigation 会根据 map.yaml 找到 map.pgm,并按照其中的分辨率、原点和阈值解释地图。如果 ls 能看到 map.pgmmap.yaml,且文件大小不是 0,说明地图文件已经生成。cat map.yaml 用于确认地图图片路径、分辨率和原点等信息是否存在。

保存地图后,不只检查 map.pgmmap.yaml 是否存在,还要打开 map.pgm 快速查看地图质量。文件能保存成功,不等于地图可以用于 Navigation。

在 MobaXterm 左侧文件栏中进入以下目录。

/home/ws/ugv_ws/src/ugv_main/ugv_nav/maps/

下载 map.pgmmap.yaml。如果左侧文件栏没有立即显示新文件,点击刷新按钮,或重新勾选 Follow terminal folder 后进入上述目录。

打开 map.pgm 快速检查。 map.pgm 可以作为图片直接打开,用于快速检查地图轮廓。

pgm

PGM 地图不是照片,也不是 RViz 截图,而是 2D 占据栅格地图。它通常由灰、白、黑三类颜色组成。

颜色含义
灰色未知区域,机器人还没有可靠扫描到
白色可通行区域,雷达认为这里没有障碍
黑色障碍物边缘,例如墙、桌脚、箱子或其它物体轮廓

判断地图是否适合进入 Navigation 时,重点看以下内容:

检查项可用于 Navigation 的表现不建议用于 Navigation 的表现
墙体轮廓连续、清楚双层墙、断裂、明显重影
通道区域白色区域连贯通道中间大片黑色或灰色
未知区域不影响计划导航路线目标点附近大片灰色未知区域
地图比例与实际场地大致一致房间、走廊或障碍轮廓被拉伸、旋转或错位
起点附近清楚可辨认起点附近混乱或被障碍覆盖

如果更换了测试房间、桌椅位置变化较大、门的开闭状态不同,或地图与当前环境明显不一致,应重新建图。

2D Navigation:使用地图导航

Navigation 使用已经保存的地图进行定位、路径规划和底盘控制。它不会重新建图。启动 Navigation 后,系统会加载 map.pgm / map.yaml,启动定位模块、路径规划模块、控制器、代价地图和 RViz。Gmapping / Cartographer 负责生成地图;Navigation 负责使用地图。

Navigation / Localization 显示 active 只表示相关 Nav2 lifecycle 节点已经激活,不表示地图一定正确、AMCL 一定收敛、LaserScan 一定对齐,也不表示当前目标一定可达。发出目标前必须先确认地图、定位、雷达扫描和 costmap 状态。

注意

Navigation 使用前面保存的 map.pgmmap.yaml 进行定位和路径规划。启动 Navigation 前,请先打开 map.pgm 检查地图轮廓是否清楚,并确认当前测试环境与建图时基本一致。

如果地图有明显重影、断墙、错位、大片未知区域,或当前场地已经发生较大变化,不要继续测试 Navigation。应先重新建图或重新保存地图。

前置条件

启动 Navigation 前,先完成以下检查:

  1. 已按 查看保存的地图文件 确认 map.pgmmap.yaml 存在,并检查 map.pgm 轮廓质量;
  2. 已按 检查并释放底盘串口 确认底盘串口没有被其它程序占用;
  3. 已停止 Gmapping、Cartographer、基础 bringup、键盘控制或其它会占用底盘、雷达的终端;
  4. 测试场地空旷,UGV 旁边没有人、宠物、线缆、桌脚或其它障碍;
  5. 已确认 停止 Navigation 的流程。

nav.launch.py 会包含底盘与雷达 bringup,不要同时单独启动另一个 bringup_lidar.launch.py

危险

Navigation 会发布速度指令并驱动实体底盘。关闭终端不应作为唯一停止手段,测试时始终保留物理停止方式。

启动 Navigation

cd /home/ws/ugv_ws
source /opt/ros/humble/setup.bash
source install/setup.bash

export UGV_MODEL=ugv_rover
ros2 launch ugv_nav nav.launch.py use_rviz:=true use_localization:=amcl use_localplan:=teb

该命令会启动 Navigation、定位、局部规划、底盘与雷达相关节点,并打开 RViz。启动后不要关闭该 Navigation 终端。

本节使用推荐组合 amcl + teb。其中 amcl 用来判断机器人在地图中的位置,teb 用来根据目标点和障碍物规划局部移动路线。首次导航时先不要修改 use_localizationuse_localplan 参数,先完成地图加载、初始位姿设置、目标点发送和底盘移动测试。

开发者补充:Navigation 参数说明

nav.launch.py 中的两个参数分别决定:

  • use_localization:机器人使用哪种方式在地图中定位,也就是判断“我现在在地图哪里”;
  • use_localplan:机器人使用哪种局部规划方式移动到目标点,也就是判断“我怎么绕开附近障碍物走过去”。
参数可选值作用建议
use_localizationamcl常用 2D 定位方式。启动后需要在 RViz 中用 2D Pose Estimate 设置初始位姿首次实车导航优先使用
use_localizationemcl另一种 2D 定位方式,也需要设置初始位姿跑通 AMCL 后再测试
use_localizationcartographer使用 Cartographer 相关定位流程前面使用 Cartographer 建图流程时再测试
use_localplanteb局部规划方式之一,用于根据目标点、障碍物和机器人运动约束生成局部运动路径当前主线使用
use_localplandwa另一种局部规划方式跑通 TEB 后再对比测试

简单理解:

  • amcl / emcl / cartographer 解决“机器人在哪里”;
  • teb / dwa 解决“机器人怎么走过去”;
  • 首次导航不要改参数,先使用 amcl + teb 跑通地图加载、初始位姿、目标点和底盘移动流程。

确认 Navigation 实际加载的地图

启动 Navigation 后,另开 Docker ROS 2 终端确认 /map_server 实际加载的地图路径。

cd /home/ws/ugv_ws
source /opt/ros/humble/setup.bash
source install/setup.bash

ros2 param get /map_server yaml_filename

再检查源码目录和安装目录中的地图文件。

ls -lh src/ugv_main/ugv_nav/maps/map.pgm \
src/ugv_main/ugv_nav/maps/map.yaml

ls -lh install/ugv_nav/share/ugv_nav/maps/map.pgm \
install/ugv_nav/share/ugv_nav/maps/map.yaml

查看两个 map.yaml 的内容。

echo "===== src map.yaml ====="
cat src/ugv_main/ugv_nav/maps/map.yaml

echo "===== install map.yaml ====="
cat install/ugv_nav/share/ugv_nav/maps/map.yaml

Navigation 实际使用哪张地图,以 /map_serveryaml_filename 为准。如果 srcinstall 中的地图文件时间、大小或内容不一致,可能出现保存的是新地图、Navigation 加载的却是旧地图的情况。

地图不匹配时,LaserScan 与地图轮廓会错开,AMCL 定位和 costmap 会异常,近距离目标也可能无法到达。如果发现 Navigation 加载的不是预期地图,请先停止 Navigation,确认当前工作空间的地图保存脚本和 launch 加载路径,再重新启动 Navigation。

认识 Navigation RViz 界面

Navigation RViz 不是只看地图,而是把静态地图、实时雷达扫描、定位粒子、全局路径、局部控制和 costmap 叠加显示。颜色较多属于正常界面状态。

Navigation RViz 中常见显示项如下。

显示项含义
Map保存后加载的 2D 占据栅格地图
RobotModel当前机器人模型和朝向
LaserScan实时雷达扫描,用来和地图边缘对齐
Amcl Particle SwarmAMCL 定位粒子,用来估计机器人在地图中的位置
Global Planner全局规划路径
Controller局部控制器或局部代价相关显示
costmap 红色 / 紫色 / 粉色 / 青色区域导航认为靠近障碍物、有碰撞风险或需要避开的区域
Navigation / Localization显示 active 时,表示 Nav2 导航和定位模块已激活

需要区分:Map 是原始 2D 地图,LaserScan 是实时雷达扫描,costmap 是导航代价图。costmap 会把障碍物、安全距离和风险区域叠加显示,不等于原始地图。

Navigation 初始界面

查看地图时,先选择顶部 Move Camera。鼠标滚轮用于缩放,按住鼠标中键拖动可以平移地图视图。如果视图被左侧 Displays 面板挡住,可以拖动面板边界缩小左侧区域,或折叠暂时不需要查看的显示项。设置初始位姿或目标点前,可以先用 Move Camera 调整视角,再点击 2D Pose EstimateNav2 Goal

如果路径、机器人模型或地图被过多显示项遮挡,可在左侧 Displays 面板中临时取消勾选部分 costmap、粒子或调试显示项,只保留查看路径需要的 MapRobotModelLaserScanGlobal Planner 等显示项。该操作只改变 RViz 显示,不会修改导航参数,也不会停止 Navigation。

Navigation 只保留关键显示项

设置初始位姿

  1. 确认地图已经加载。
  2. 确认实时 LaserScan 大致落在地图墙体或障碍物边缘附近。
  3. 点击顶部 2D Pose Estimate
  4. 在地图上点击 UGV 实际位置,按住鼠标左键拖出车头方向箭头。
  5. 松开鼠标完成设置。

松开鼠标后,RViz 工具栏回到 Move Camera 时属于正常显示状态。判断初始位姿是否生效,不看工具栏当前按钮,而是看 Amcl Particle Swarm 是否收敛到机器人附近,以及实时 LaserScan 是否大致贴合地图中的墙体或障碍物边缘。

设置初始位姿后,先等待 10 ~ 20 秒,不要立即发送目标。观察 LaserScan 是否与地图轮廓对齐,观察 Amcl Particle Swarm 是否集中。如果粒子分散、机器人模型跳动、LaserScan 与墙体轮廓明显错开,需要重新设置 2D Pose Estimate

判断 LaserScan、AMCL 和 costmap 是否正常

设置目标点前,必须完成四项检查:

  1. 地图是当前场地的地图;
  2. LaserScan 与地图墙体或障碍物轮廓大致对齐;
  3. Amcl Particle Swarm 集中在机器人真实位置附近;
  4. 机器人自身和目标点附近没有被 costmap 高风险区域覆盖。

如果以上任意一项不满足,不要点击 Nav2 Goal

costmap 中出现红色、紫色、粉色或青色区域不一定是错误,它表示 Navigation 根据静态地图、实时雷达、障碍层和膨胀层计算出的风险区域。需要重点看机器人中心附近和目标点附近是否被高风险区域覆盖。

如果机器人中心附近被大面积高风险区域覆盖,或者目标点位于高风险区域内,或者 RViz 中无法生成路径、Feedback 显示 canceledRecoveries 持续增加,不要继续发送目标。先回到地图、LaserScan、AMCL 和传感器状态检查。

清理 costmap

需要排查代价地图缓存时,先查看当前是否存在清理服务。

cd /home/ws/ugv_ws
source /opt/ros/humble/setup.bash
source install/setup.bash

ros2 service list | grep clear

如果存在以下服务,再执行清理命令。

ros2 service call /global_costmap/clear_entirely_global_costmap nav2_msgs/srv/ClearEntireCostmap {}
ros2 service call /local_costmap/clear_entirely_local_costmap nav2_msgs/srv/ClearEntireCostmap {}

清理 costmap 只清除当前代价地图缓存,不会修复错误地图、错误初始位姿或传感器异常。如果清理后机器人周围很快再次被大面积高风险区域覆盖,应回到地图、LaserScan、AMCL 和传感器状态检查。

设置极近距离 Nav2 Goal

危险

第一次只设置 20 ~ 30 cm 的极近距离目标。目标点必须在白色可通行区域,且不靠墙、不靠障碍、不在线缆附近。极近目标可达后,再测试 0.5 ~ 1 m 目标。不要第一次就跨房间、穿过狭窄区域或贴近障碍物。

  1. 完成 判断 LaserScan、AMCL 和 costmap 是否正常 中的四项检查。
  2. 点击顶部 Nav2 Goal
  3. 在地图上选择机器人附近 20 ~ 30 cm 的白色可通行区域。
  4. 按住鼠标左键拖出目标点到达后的车头方向箭头。
  5. 松开鼠标后,观察 RViz 中是否生成路径,以及 Navigation 面板状态是否变化。
提示

Nav2 Goal 会让导航系统发布速度指令并驱动底盘。拖出的箭头表示“到达目标点后的车头朝向”,不表示机器人一定会沿箭头方向直线行驶。实际路径由 Nav2 根据地图、定位、实时雷达扫描和 costmap 自动规划。

目标太近时,Navigation 面板可能很快显示 reached,机器人不会有明显移动;这不代表远距离目标已经可用。先用极近目标确认基础导航状态,再逐步增加距离。

执行目标后,可以在 RViz 左侧 Navigation 2 面板中查看状态:

  • Navigation: active:导航模块已激活;
  • Localization: active:定位模块已激活;
  • Feedback: active:Nav2 正在执行目标;
  • Feedback: reached:Nav2 认为目标已经到达;
  • ETA:预计剩余时间;
  • Distance remaining:距离目标的剩余距离;
  • Recoveries:恢复行为触发次数。

Recoveries 不是普通计时器。它表示导航过程中触发恢复行为的次数。如果该数值持续增加,通常说明目标点附近代价过高、路线被阻挡、定位不够准,或目标选择不合适。首次测试时可以取消当前目标,重新选择更近、更空旷的白色区域。

Navigation 执行中界面

图中 NavigationLocalization 均为 activeFeedbackactive,表示 Nav2 已接收目标并正在执行。Distance remaining 表示距离目标的剩余距离,Recoveries 表示恢复行为触发次数。若 Recoveries 持续增加,可取消当前目标,重新选择更近、更空旷的目标点。到达目标后,Feedback 会显示 reached。这些状态表示 Navigation 正在使用地图,不代表正在重新建图。

停止 Navigation

需要停止 Navigation 时,按下面顺序处理:

  1. 在 RViz 左侧 Navigation 2 面板中点击 CancelPause

  2. 另开 Docker ROS 2 终端发送一次 /cmd_vel 零速度。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash

    ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.0}, angular: {z: 0.0}}"
  3. 回到 Navigation launch 终端,按 Ctrl + C 停止 nav.launch.py

  4. 如果 UGV 仍在移动,立即断电。

发送一次零速度只能作为辅助停止动作。Navigation 控制器仍在运行时,可能继续发布新的 /cmd_vel,因此不要把该命令作为唯一停止方式。

常见异常排查

如果近距离 Nav2 Goal 也无法移动、Feedback 显示 canceled、costmap 大面积异常或 UGV 有碰撞风险,先停止 Navigation,再按下面顺序排查:

  1. Navigation 实际加载的 map.yaml 是否是当前场地地图;
  2. srcinstall 中地图文件是否不一致;
  3. LaserScan 是否与地图轮廓对齐;
  4. AMCL 粒子是否收敛;
  5. 机器人中心或目标点是否被 costmap 高风险区域覆盖;
  6. /scan 是否有稳定频率;
  7. /odom/robot_pose 是否持续输出;
  8. ugv_bringupugv_driverbase_node 是否仍在运行;
  9. Navigation launch 终端是否出现 process has diedmultiple accessKeyError、TF extrapolation、Message Filter dropping message 等日志;
  10. 目标点是否太远、靠墙、靠障碍或在未知区域。

检查关键节点:

ros2 node list | grep -E "ugv_bringup|ugv_driver|base_node|amcl|map_server|planner_server|controller_server|bt_navigator"

检查关键数据:

timeout 8 ros2 topic hz /scan
timeout 8 ros2 topic echo /odom
timeout 8 ros2 topic echo /robot_pose

如果 /scan/odom/robot_pose 没有输出,先不要继续发目标。回到 bringup、定位和地图加载检查。

行为控制与导航点任务

行为控制是产品封装的任务接口,用来把常见底盘动作和导航点操作封装成 Action 任务。它不是 Nav2 标准功能,也不是建图功能。

开发者补充:behavior_ctrl 的 topic 和 Action

behavior_ctrl 创建 /behavior Action Server,订阅 /odom/robot_pose,发布 /cmd_vel/goal_pose。短距离动作通过 /cmd_vel 驱动底盘;导航点任务通过 /robot_pose 获取当前位置,并通过 /goal_pose 发布目标点。

短距离动作与停止

短距离动作适合先验证行为控制接口,例如让 UGV 前进一小段、后退一小段或原地旋转一定角度。它依赖 /odom 判断动作进度,并通过 /cmd_vel 驱动底盘,不依赖地图。

危险

短距离动作、前往导航点和 Navigation 目标都会驱动实体底盘。测试前确认场地空旷,远离人、宠物、线缆和桌脚。测试时先使用低速、短距离任务。

  1. 确认 Docker ROS 2 终端 1 中的 bringup_lidar.launch.py 正在运行。若该终端已经关闭,先回到 启动底盘与雷达 重新启动。

启动 behavior Action Server

  1. 另开新的 Docker ROS 2 终端启动行为控制。启动后保持该终端运行,不要关闭。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash

    ros2 run ugv_tools behavior_ctrl
  2. 另开 Docker ROS 2 终端发送短距离动作。新终端也先加载 ROS 2 环境。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash

前进一小段:

ros2 action send_goal /behavior ugv_interface/action/Behavior "{command: '[{\"T\": 1, \"type\": \"drive_on_heading\", \"data\": 0.1}]'}"

后退一小段:

ros2 action send_goal /behavior ugv_interface/action/Behavior "{command: '[{\"T\": 1, \"type\": \"back_up\", \"data\": 0.1}]'}"

原地旋转:

ros2 action send_goal /behavior ugv_interface/action/Behavior "{command: '[{\"T\": 1, \"type\": \"spin\", \"data\": -1}]'}"

停止当前行为:

ros2 action send_goal /behavior ugv_interface/action/Behavior "{command: '[{\"T\": 1, \"type\": \"stop\", \"data\": 0}]'}"

如果行为动作仍在执行,优先发送停止动作;如果仍无法停止,回到行为控制终端或底盘 bringup 终端按 Ctrl + C,必要时直接关闭机器人电源。

可选验证:保存并发布导航点

保存并发布导航点用于验证 /behavior/robot_pose/goal_pose 的接口链路。它不是完整的航点管理系统,也不替代基础 Navigation 检查。

save_map_point 会把当前 /robot_pose 记录为命名点。pub_nav_point 会把已保存点发布到 /goal_pose。这相当于把“手动点 Nav2 Goal”替换为“程序发布目标点”。真正路径规划和底盘运动仍由 Navigation / Nav2 完成。

能否实际到达,仍取决于 Navigation 的地图、AMCL、TF、costmap、底盘反馈和目标容差。如果 RViz 手动 Nav2 Goal 的极近距离目标都不能稳定到达,不要继续测试保存导航点后的实际移动。

方式目标点来源
RViz Nav2 Goal在地图上手动点击目标
pub_nav_point程序把之前保存的导航点发布成目标

测试 A:接口链路验证。

  1. 先按 2D Navigation:使用地图导航 启动 Navigation,再按 启动 behavior Action Server 启动行为控制节点,并保持两个终端运行。

  2. 另开 Docker ROS 2 终端发送导航点任务命令。新终端也先加载 ROS 2 环境。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash
  3. 先确认 /behavior Action Server 已经可用。

    ros2 action list | grep behavior
    ros2 action info /behavior

    如果发送行为命令时一直停在 Waiting for an action server to become available...,说明当前没有找到 /behavior Action Server。先回到第 1 步的行为控制终端,确认 ros2 run ugv_tools behavior_ctrl 仍在运行;如果该终端已经退出,重新启动行为控制节点后再发送命令。

  4. 检查 /robot_pose

    ros2 topic echo /robot_pose --once

    如果 /robot_pose 有输出,表示系统已经能得到机器人在地图中的位姿。若没有输出,先检查 Navigation 是否启动、初始位姿是否设置、定位是否正常。

  5. 保存当前位置为 A 点。

    ros2 action send_goal /behavior ugv_interface/action/Behavior "{command: '[{\"T\": 1, \"type\": \"save_map_point\", \"data\": \"a\"}]'}"

    该命令会把当前 /robot_pose 记录为一个导航点。保存时 UGV 不会因为“保存点”这个动作而主动移动。

  6. 在另一个 Docker ROS 2 终端监听 /goal_pose

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash

    ros2 topic echo /goal_pose
  7. 回到发送导航点任务命令的终端,发布 A 点为 Navigation 目标。

    ros2 action send_goal /behavior ugv_interface/action/Behavior "{command: '[{\"T\": 1, \"type\": \"pub_nav_point\", \"data\": \"a\"}]'}"

    该命令会向 /goal_pose 发布已保存的 A 点。后续是否能规划路径、是否移动、是否到达,取决于 Navigation 的定位、地图、costmap 和路径规划状态。

接口链路验证的观察点:

  • /behavior Action Server 可用;
  • /robot_pose 有输出;
  • save_map_point 返回;
  • pub_nav_point/goal_pose 有输出;
  • /goal_poseframe_idmap
  • Navigation 面板是否收到目标。

测试 B:实际移动验证。

只有在基础 Navigation 已经通过 设置极近距离 Nav2 Goal 后,才继续测试实际移动。不要把精确回到物理 A 点作为通过标准。

  1. 保存当前位置为 A 点。
  2. 可以按 设置极近距离 Nav2 Goal 使用 RViz Nav2 Goal 设置一个近距离目标,也可以按 使用键盘低速移动 用键盘控制短距离移动。
  3. 移动后确认 UGV 已经离开 A 点,并保持 Navigation 正常运行。使用键盘控制时,不要同时保留其它会持续发布速度指令的程序。
  4. 发布 A 点为 Navigation 目标。

发布后只观察 Navigation 状态,不把到达某个物理点作为唯一判断:

  • 是否生成路径;
  • 是否开始执行;
  • 是否 canceled
  • Recoveries 是否增加;
  • 是否被 costmap 阻挡;
  • 是否接近目标容差范围。

如果手动 Nav2 Goal 的近距离目标都不能稳定到达,先回到 常见异常排查,不要继续测试保存导航点的实际移动。

自动探索

自动探索是进阶功能,不作为首次导航。先完成 Gmapping 建图查看保存的地图文件 和近距离 2D Navigation:使用地图导航,再测试自动探索。

  1. 先在当前 Docker ROS 2 终端加载 ROS 2 环境。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash
  2. 启动建图导航。

ros2 launch ugv_nav slam_nav.launch.py use_rviz:=true

继续启动探索入口时,不要结束当前建图导航终端。

  1. 另开终端启动探索入口。新终端也先加载 ROS 2 环境。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash
ros2 launch explore_lite explore.launch.py

自动探索会让机器人自主选择目标点并移动。使用前确认场地封闭、规则、空旷,并全程准备 停止 Navigation 的方式。保存探索地图时,可按 Gmapping 建图 中的保存方式处理。停止时先停止探索节点,再按 停止 Navigation 中的方式停止底盘运动,最后停止 SLAM / Navigation 相关 launch。

rosbag 数据记录、复现与离线分析

rosbag 是调试和复现工具,不是基础启动流程的一部分。建议先完成底盘、雷达、TF、RViz、2D 建图和 Navigation 的基本流程后,再使用 rosbag 记录问题现场或保存可复现数据。

rosbag 用于把 ROS 2 中已经发布的 topic 数据保存成文件,例如雷达扫描、TF、里程计和 IMU 数据。记录后的数据可以回放,用于复现问题、离线查看和对比调试。rosbag 不是启动功能的命令,只负责保存和回放已经发布出来的数据。

记录底盘、雷达、TF 和里程计数据

该任务用于记录 UGV 低速转向时的雷达、TF 和里程计数据,便于后续检查雷达扫描、车体姿态和坐标关系是否同步变化。

保持 Docker ROS 2 终端 1 继续运行。记录任务本身不驱动底盘;为了让记录内容有观察价值,需要在记录过程中让 UGV 低速原地转向。

另开 Docker ROS 2 终端,加载 ROS 2 环境。

cd /home/ws/ugv_ws
source /opt/ros/humble/setup.bash
source install/setup.bash

检查当前 topic。

ros2 topic list | grep -E "scan|tf|odom|imu|voltage|cmd"

本任务默认只记录传感器和状态 topic,不记录 /cmd_vel/cmd_vel 是底盘速度指令;如果把它录进 rosbag,回放时也会重新发布该控制指令。只有在明确需要复现控制输入时,才记录 /cmd_vel,并在回放前确认真实底盘不会响应该 topic。

开发者补充:记录控制 topic 的风险

如果需要分析控制输入,可以把 /cmd_vel 加入记录命令。但回放包含 /cmd_vel 的 bag 前,先确认真实底盘驱动不会接收该 topic,或在安全环境中测试。

创建保存目录并记录数据。

mkdir -p /home/ws/ugv_ws/rosbag_records

ros2 bag record -o /home/ws/ugv_ws/rosbag_records/ugv_lidar_tf_odom \
/scan \
/tf \
/tf_static \
/odom \
/imu/data_raw \
/voltage

这里使用完整路径 /home/ws/ugv_ws/rosbag_records,便于在 MobaXterm 左侧文件栏中直接找到。

如果某个 topic 在当前 topic 列表中不存在,请从记录命令中删除该 topic 后再执行。

记录期间,另开一个 Docker ROS 2 终端,让记录数据产生变化。

cd /home/ws/ugv_ws
source /opt/ros/humble/setup.bash
source install/setup.bash

timeout 5 ros2 topic pub -r 10 /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.0}, angular: {z: 0.50}}"
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.0}, angular: {z: 0.0}}"

该命令会让 UGV 低速原地转向约 5 秒。请将 UGV 放在地面,周围留出安全空间。动作结束后会发送一次 0 速度命令。

也可以在记录期间使用 键盘控制 短按 J / L 让 UGV 原地转向,随后按 K 或空格停止。

回到 ros2 bag record 终端,按 Ctrl + C 停止记录,并等待数据写入完成。

记录完成后会看到以下内容:

  • ros2 bag record 终端会显示正在记录的 topic;
  • UGV 低速转向时,/tf/odom/scan 等数据会随时间变化;
  • 停止记录后,/home/ws/ugv_ws/rosbag_records/ugv_lidar_tf_odom 目录中会生成 metadata.yaml.db3 数据文件;
  • 使用 ros2 bag info 可以查看记录时长、topic 列表和消息数量。

在 MobaXterm 左侧文件栏勾选 Follow terminal folder,进入 /home/ws/ugv_ws/rosbag_records/ugv_lidar_tf_odom,可以看到本次记录生成的文件。

metadata.yaml 是 rosbag 的索引说明文件,可以用文本编辑器打开。里面会记录 bag 版本、存储格式、记录时长、起始时间、总消息数量、每个 topic 的名称、消息类型和消息数量。例如 /scan 对应 sensor_msgs/msg/LaserScan/odom 对应 nav_msgs/msg/Odometry

.db3 文件是 SQLite 数据库文件,保存实际消息数据。它不适合直接用文本编辑器阅读。日常查看优先使用 ros2 bag inforos2 bag play;需要查看数据库表时,可用 SQLite 工具打开 .db3 文件,里面主要包含 topic 信息和按时间保存的 ROS 2 消息数据。

查看与回放 rosbag

先加载 ROS 2 环境。

cd /home/ws/ugv_ws
source /opt/ros/humble/setup.bash
source install/setup.bash

查看 rosbag 信息。

ros2 bag info /home/ws/ugv_ws/rosbag_records/ugv_lidar_tf_odom

回放 rosbag。

ros2 bag play /home/ws/ugv_ws/rosbag_records/ugv_lidar_tf_odom

终端显示 Opened database ... for READ_ONLYSet rate to 1 和暂停 / 调速快捷键提示时,表示 rosbag 已经打开 .db3 数据库并开始回放。回放时终端不会逐条打印 /scan/tf/odom 等消息,而是在后台重新发布 bag 中保存的 topic。需要停止回放时,在该终端按 Ctrl + C

如需确认回放数据正在发布,保持 ros2 bag play 终端继续运行,不要停止。另开 Docker ROS 2 终端执行下面命令。

cd /home/ws/ugv_ws
source /opt/ros/humble/setup.bash
source install/setup.bash

ros2 topic list | grep -E "scan|tf|odom|imu|voltage"
ros2 topic echo /scan --once

ros2 topic list 会输出本次回放中的 topic,例如 /scan/tf/tf_static/odom/imu/data_raw/voltageros2 topic echo /scan --once 会输出一条 LaserScan 消息,其中包含 frame_id: base_lidar_link、角度范围、距离范围和 ranges 数组。ranges 中的数字是雷达到周围物体的距离,.nan 表示该角度没有有效距离读数。

提示

如果 ros2 bag play 已经结束,或在回放完成后才执行 ros2 topic echo /scan --once,终端会提示 WARNING: topic [/scan] does not appear to be published yetCould not determine the type for the passed topic。这表示当前没有节点正在发布 /scan,不是 rosbag 文件损坏。重新启动 ros2 bag play 后,再另开终端检查。

AprilTag 视觉标签识别

AprilTag 是一种视觉基准标签,由已知编码的黑白图案组成。摄像头拍到标签后,程序可以识别标签 ID,并根据图案角点计算标签在图像中的位置。它常用于视觉识别测试、目标标记、简单交互控制、定位辅助和自动对接等场景。

基础测试只使用 tag36h11 ID 0。先用手机或平板显示标签,确认摄像头画面中能看到完整黑白图案和外侧白边。

标签用途屏幕显示版打印版
tag36h11 ID 0基础识别测试打开 SVG打开 SVG

手机或平板显示标签时,调高亮度,避免反光,不要裁掉外侧白边。从 30 cm ~ 80 cm 距离开始测试。进行姿态估计、跟踪或控制测试时,使用打印版,按 100% 比例打印并固定在平整纸板上。

提示

有效黑白图案区域才是 AprilTag 姿态估计使用的 tag size;外侧白边和 A4 纸张空白不计入 tag size。如果参数使用 tag size 0.08,打印时让标签有效黑白图案区域接近 80 mm

  1. 启动摄像头。在 Docker ROS 2 终端中执行:
cd /home/ws/ugv_ws
source /opt/ros/humble/setup.bash
source install/setup.bash

ros2 launch ugv_vision camera.launch.py

保持摄像头终端运行,不要关闭。

  1. 确认图像。另开 Docker ROS 2 终端检查图像 topic:
cd /home/ws/ugv_ws
source /opt/ros/humble/setup.bash
source install/setup.bash

ros2 topic list | grep -E "image|camera|rect"
timeout 5 ros2 topic hz /image_raw

timeout 5 ros2 topic hz /image_raw 会等待约 5 秒。如果看到 average rate,说明 /image_raw 正在发布图像。若一直没有频率输出,说明摄像头节点没有发布图像或摄像头 launch 已退出;先回到摄像头终端检查日志,必要时按 Ctrl + C 停止后重新运行 ros2 launch ugv_vision camera.launch.py

确认 /image_raw 有频率输出后,继续打开图像查看器:

ros2 run rqt_image_view rqt_image_view

rqt_image_view 中选择 /image_raw。能看到 USB 摄像头画面后,再继续下一步启动识别节点。

imageview

imageview2

  1. 添加安全识别节点。apriltag_detect_only 是本教程用于基础识别的安全节点。该节点只订阅图像、识别 ID、发布结果图像,不调用 behavior,也不控制底盘。

另开一个 Docker ROS 2 终端,在设备端工作区执行下面命令。该命令会写入安全识别节点,并把 apriltag_detect_only 加入 ugv_vision 的可执行入口。

cd /home/ws/ugv_ws
mkdir -p src/ugv_main/ugv_vision/ugv_vision

cat > src/ugv_main/ugv_vision/ugv_vision/apriltag_detect_only.py <<'PYFILE'
import cv2
import rclpy
from apriltag import apriltag
from cv_bridge import CvBridge
from rclpy.node import Node
from sensor_msgs.msg import Image


class ApriltagDetectOnly(Node):
def __init__(self):
super().__init__('apriltag_detect_only')

self.declare_parameter('image_topic', '/image_raw')
self.declare_parameter('result_topic', '/apriltag_detect_only/result')
self.declare_parameter('tag_family', 'tag36h11')
self.declare_parameter('draw_result', True)
self.declare_parameter('print_detection', True)
self.declare_parameter('print_interval_sec', 0.5)

image_topic = self.get_parameter('image_topic').value
result_topic = self.get_parameter('result_topic').value
tag_family = self.get_parameter('tag_family').value

self.draw_result = self.get_parameter('draw_result').value
self.print_detection = self.get_parameter('print_detection').value
self.print_interval_sec = float(self.get_parameter('print_interval_sec').value)
self.last_print_time = 0.0

self.bridge = CvBridge()
self.detector = apriltag(tag_family)
self.image_sub = self.create_subscription(Image, image_topic, self.image_callback, 10)
self.result_pub = self.create_publisher(Image, result_topic, 10)

self.get_logger().info(f'Input image topic: {image_topic}')
self.get_logger().info(f'Result image topic: {result_topic}')
self.get_logger().info(f'AprilTag family: {tag_family}')

def image_callback(self, msg):
frame = self.bridge.imgmsg_to_cv2(msg, 'bgr8')
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
results = self.detector.detect(gray)

now_sec = self.get_clock().now().nanoseconds / 1e9

for result in results:
tag_id = int(result['id'])
center_x = int(result['center'][0])
center_y = int(result['center'][1])

if self.draw_result:
corners = result['lb-rb-rt-lt'].astype(int)
cv2.polylines(frame, [corners], isClosed=True, color=(0, 255, 0), thickness=2)
cv2.circle(frame, (center_x, center_y), 5, (0, 0, 255), -1)
cv2.putText(
frame,
f'ID {tag_id} ({center_x}, {center_y})',
(center_x + 8, center_y - 8),
cv2.FONT_HERSHEY_SIMPLEX,
0.5,
(0, 255, 0),
2,
)

if self.print_detection and now_sec - self.last_print_time >= self.print_interval_sec:
print(f'Tag ID: {tag_id}, Center: ({center_x}, {center_y})')
self.last_print_time = now_sec

result_msg = self.bridge.cv2_to_imgmsg(frame, encoding='bgr8')
self.result_pub.publish(result_msg)


def main(args=None):
rclpy.init(args=args)
node = ApriltagDetectOnly()
try:
rclpy.spin(node)
finally:
node.destroy_node()
rclpy.shutdown()


if __name__ == '__main__':
main()
PYFILE

python3 - <<'PYSETUP'
from pathlib import Path

p = Path("src/ugv_main/ugv_vision/setup.py")
text = p.read_text()
entry = "apriltag_detect_only = ugv_vision.apriltag_detect_only:main"

if entry not in text:
old = " 'apriltag_track_2 = ugv_vision.apriltag_track_2:main'\n"
new = (
" 'apriltag_track_2 = ugv_vision.apriltag_track_2:main',\n"
" 'apriltag_detect_only = ugv_vision.apriltag_detect_only:main'\n"
)
if old not in text:
raise SystemExit("未找到 apriltag_track_2 入口,请手动检查 setup.py")
p.write_text(text.replace(old, new))

print("apriltag_detect_only entry is ready")
PYSETUP
  1. 重新构建 ugv_vision 并检查入口:
cd /home/ws/ugv_ws
source /opt/ros/humble/setup.bash
source install/setup.bash

colcon build --packages-select ugv_vision --symlink-install
source install/setup.bash
ros2 pkg executables ugv_vision | grep apriltag

重新检查时应能看到 ugv_vision apriltag_detect_only

apriltag

启动安全识别节点

  1. 启动安全识别节点。保持摄像头终端继续运行,另开一个 Docker ROS 2 终端执行下面命令:

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash

    ros2 run ugv_vision apriltag_detect_only

检查 AprilTag ID 0 识别结果

  1. 查看识别结果。将 tag36h11 ID 0 标签完整对准摄像头,保持黑白图案和外侧白边都在画面中。先从 30 cm ~ 80 cm 距离开始,避免反光、遮挡和过度倾斜。

    识别运行后,终端会先输出输入图像、结果图像和标签家族信息,随后持续打印识别到的标签 ID 和中心坐标:

    [INFO] [apriltag_detect_only]: Input image topic: /image_raw
    [INFO] [apriltag_detect_only]: Result image topic: /apriltag_detect_only/result
    [INFO] [apriltag_detect_only]: AprilTag family: tag36h11
    Tag ID: 0, Center: (369, 291)
    Tag ID: 0, Center: (318, 289)
    Tag ID: 0, Center: (322, 278)
    Tag ID: 0, Center: (335, 280)
    Tag ID: 0, Center: (369, 296)
    Tag ID: 0, Center: (371, 295)
    Tag ID: 0, Center: (382, 294)
    Tag ID: 0, Center: (388, 294)
    Tag ID: 0, Center: (380, 270)
    Tag ID: 0, Center: (448, 190)

    识别结果中会看到:

    • 终端输出 Tag ID: 0
    • 移动标签时,Center 坐标变化;
    • 节点会发布 /apriltag_detect_only/result 结果图像 topic;
    • 遮挡、反光、距离过远或图案过小时,识别可能中断;
    • 不出现 Action server not available!
    • UGV 不运动。
提示

需要更多 ID 或不同尺寸时,使用标准 AprilTag 生成器或 AprilTag 官方软件页面 提供的资源生成 tag36h11 家族标签。

OAK-D Lite 基础体验

OAK-D Lite 是一款 RGB-D 深度相机,可以同时提供彩色图像和深度图像。RTAB-Map 3D 建图会使用 OAK-D Lite 的 RGB 图像、深度图像和相机参数,再结合雷达、里程计和 TF 生成地图数据库。

本节只用于确认 OAK-D Lite 数据是否正常。后续 RTAB-Map 会自动启动 OAK-D Lite,不需要再单独启动相机。

  1. 在 Docker ROS 2 终端中启动 OAK-D Lite。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash

    ros2 launch ugv_vision oak_d_lite.launch.py
  2. 保持 OAK-D Lite 终端运行,另开 Docker ROS 2 终端检查 topic。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash

    ros2 topic list | grep -Ei "oak|rgb|depth|image|camera_info"
  3. 按实际 topic 检查频率。

    timeout 8 ros2 topic hz /oak/rgb/image_rect
    timeout 8 ros2 topic hz /oak/stereo/image_raw
    timeout 8 ros2 topic hz /oak/rgb/camera_info

    如果 topic 名称不同,以 ros2 topic list 的实际输出为准。

  4. 需要查看 RGB 图像时,可以打开 rqt_image_view

    ros2 run rqt_image_view rqt_image_view

    在窗口中选择 /oak/rgb/image_rect,或选择当前实际存在的 RGB 图像 topic。

可以看到 OAK-D Lite 相关 topic;RGB 图像 topic 和 Depth 图像 topic 有频率输出;camera_info topic 存在;rqt_image_view 中可以选择 RGB 图像 topic;终端没有持续出现设备找不到、DepthAI 启动失败或 USB 错误。

OAK-D Lite 单独体验结束后,在 launch 终端按 Ctrl + C 停止。后续启动 RTAB-Map 时,会由 rtabmap_rgbd.launch.py 自动启动 OAK-D Lite,避免重复启动。

RTAB-Map 三维建图与定位

RTAB-Map 在这里用于 RGB-D 建图和定位。它会结合 OAK-D Lite 的 RGB-D 图像、雷达 /scan、底盘 /odom 和 TF,生成 RTAB-Map 数据库。建图结果默认保存在 ~/.ros/rtabmap.db

RTAB-Map 建图不是自动导航。建图阶段负责采集环境并生成数据库;导航目标点和路径规划属于 Nav2 等导航栈的功能。

RTAB-Map RGB-D 建图

启动 RTAB-Map 后,可能同时看到 RViz 和 RTAB-Map 自带窗口。RViz 适合观察机器人模型、LaserScan、二维地图、点云地图和图结构;RTAB-Map 自带窗口适合观察 RGB 图像、Odometry 图像、3D Map、关键帧 ID 和回环检测状态。

rtabmap_rgbd.launch.py 会启动底盘与雷达 bringup、OAK-D Lite、robot_pose_publisherrtabmap_slam/rtabmaprtabmap_viz。RTAB-Map 输入 topic 会映射到 OAK-D Lite 的 RGB 图像、深度图像和相机参数。

RTAB-Map 输入OAK-D Lite topic
rgb/imageoak/rgb/image_rect
rgb/camera_infooak/rgb/camera_info
depth/imageoak/stereo/image_raw
  1. 在 Docker ROS 2 终端加载 ROS 2 环境。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash
  2. 建图前备份旧数据库。

    ls -lh ~/.ros/rtabmap.db
    cp ~/.ros/rtabmap.db ~/.ros/rtabmap_backup_$(date +%Y%m%d_%H%M%S).db 2>/dev/null || true

    当前建图模式会重新生成默认数据库。需要保留旧地图时,先备份 ~/.ros/rtabmap.db

  3. 设置型号并启动 RTAB-Map RGB-D 建图。

    export UGV_MODEL=ugv_rover
    export LDLIDAR_MODEL=ld19

    ros2 launch ugv_slam rtabmap_rgbd.launch.py use_rviz:=true

    本教程统一显式使用 use_rviz:=true。实际运行时可能同时打开 RViz 和 RTAB-Map 自带窗口,可以保留两个窗口进行对比观察。

  4. 保持 RTAB-Map 终端运行,另开 Docker ROS 2 终端检查输入数据。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash

    ros2 node list | grep -Ei "rtab|oak|depth|rgb|camera|ugv|driver|base|lidar|pose"
    ros2 topic list | grep -Ei "rtab|oak|rgb|depth|image|camera_info|odom|scan|tf|cloud|map"

    检查 RTAB-Map 使用的输入 topic。

    timeout 8 ros2 topic hz /oak/rgb/image_rect
    timeout 8 ros2 topic hz /oak/stereo/image_raw
    timeout 8 ros2 topic hz /oak/rgb/camera_info
    timeout 8 ros2 topic hz /scan

    如果 topic 名称不同,以当前 ros2 topic list 的实际输出为准。

  5. 另开 Docker ROS 2 终端,用键盘低速移动采集数据。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash

    ros2 run ugv_tools keyboard_ctrl

    短按 J / L 小幅转向;短按 I / , 小幅前后移动;每次动作后按 K 或空格停止。不要快速原地旋转,不要长时间连续运动。建议先采集 12 分钟的小范围地图。

RViz 中 Fixed FramemapGlobal StatusOkRobotModel 正常显示;LaserScan 随环境变化;MapMapCloud 随机器人移动逐渐增长;MapGraph 或轨迹随移动增加;终端没有持续 TF、No data、RGB-D sync 报错。

RTAB-Map 自带窗口中 RGB 图像刷新;Odometry 图像刷新;右侧 3D Map 出现点云;New ID 数字随着采集增加;下方日志持续出现地图更新信息;回到旧区域附近时,可以观察 Loop closure 相关变化。

结束建图时,先在 keyboard_ctrl 终端按 K 或空格停止底盘,再按 Ctrl + C 停止键盘控制。回到 RTAB-Map launch 终端按 Ctrl + C,等待数据库写入完成。

ls -lh ~/.ros/rtabmap.db

~/.ros/rtabmap.db 存在且文件大小不是 0 时,表示数据库已经写入。重新定位前不要随意删除该文件。

RTAB-Map 定位

RTAB-Map 定位用于加载已有数据库,不是重新建图。启动前确认已经完成 RTAB-Map 建图,~/.ros/rtabmap.db 存在,并尽量在与建图时相近的环境中测试。

cd /home/ws/ugv_ws
source /opt/ros/humble/setup.bash
source install/setup.bash

export UGV_MODEL=ugv_rover
export LDLIDAR_MODEL=ld19

ls -lh ~/.ros/rtabmap.db

ros2 launch ugv_slam rtabmap_rgbd.launch.py localization:=true use_rviz:=true

定位模式同样显式使用 use_rviz:=true。启动日志不应持续提示找不到数据库、No data received、TF 报错或 RGB-D synchronization failed。机器人小幅移动后应仍在已有地图范围内保持定位,地图不应完全从零重新生成。

需要小幅移动验证时,另开 Docker ROS 2 终端运行键盘控制。

cd /home/ws/ugv_ws
source /opt/ros/humble/setup.bash
source install/setup.bash

ros2 run ugv_tools keyboard_ctrl

只做小幅转向或短距离移动,测试 30 秒到 1 分钟即可。测试结束前先按 K 或空格停止底盘,再退出键盘控制和 RTAB-Map 定位终端。

ROS 2 Web App / Vizanti 可视化面板

ROS 2 Web App / Vizanti 是浏览器端 ROS 2 可视化面板,不是上位机 Web 主页面,也不是 JupyterLab。首次学习 SLAM 和 Navigation 时,优先使用 RViz;Vizanti 适合远程浏览器查看、教学演示和轻量调试。

工具默认入口作用
上位机 Web 主页面http://<UGV_IP>:5000整机 Web 控制、视频、按钮、基础功能
JupyterLabhttp://<UGV_IP>:8888Notebook、文件管理、短脚本和教学示例
ROS 2 Web App / Vizantihttp://<UGV_IP>:5100浏览器端 ROS 2 可视化面板,查看模型、TF、雷达、地图和导航状态

ROS 2 Web App 启动后显示空网格不代表错误。它只是浏览器端可视化面板。只有 ROS 2 中已经存在 /scan/map/odom/tf 等数据,并且在页面中添加对应组件后,才会显示雷达、地图、里程计或导航信息。

  1. 启动 ROS 2 Web App。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash

    ros2 launch ugv_web_app bringup.launch.py host:=0.0.0.0

    浏览器访问:

    http://<UGV_IP>:5100

    不要把占位 IP 当作参数值直接输入。如果使用 host:=<UGV_IP>,需要把 <UGV_IP> 替换成 OLED 或当前网络中显示的机器人实际 IP。测试时也可以使用 host:=0.0.0.0,浏览器仍访问 http://<UGV_IP>:5100

  2. 启动一个要显示的数据源,例如底盘和雷达。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash

    export UGV_MODEL=ugv_rover
    export LDLIDAR_MODEL=ld19
    ros2 launch ugv_bringup bringup_lidar.launch.py use_rviz:=false
  3. 另开 Docker ROS 2 终端检查 topic。

    cd /home/ws/ugv_ws
    source /opt/ros/humble/setup.bash
    source install/setup.bash

    ros2 topic list | grep -E "scan|map|tf|odom|cmd_vel"
    timeout 8 ros2 topic hz /scan
  4. 在 Vizanti 页面中添加显示组件。

    点击页面左上角 + 添加显示组件。常见选择如下:

    要查看的内容添加组件topic
    机器人模型RobotModelrobot_description
    TFTF/tf/tf_static
    雷达LaserScan/scan
    地图Map/map
    里程计Odometry/odom
    路径PathNavigation 发布的路径 topic

如果没有启动 bringup、SLAM 或 Navigation,页面不会自动显示雷达、地图或路径。

Web AI 交互属于可选二次开发功能,依赖额外 AI 服务配置。相关说明见 二次开发