5.5.4 数据可视化

Foxglove 可视化

使用 Foxglove Studio 对机器人话题数据进行实时可视化。

机器人端运行 foxglove_bridge,将话题数据通过 WebSocket 发布;上位机(PC)运行 Foxglove 客户端订阅并可视化。两者需处于同一网络中。

安装 Foxglove

上位机安装 Foxglove 桌面版或使用网页版,均可用任意账号登录:

备注

使用网页版连接机器人(ws:// 明文连接)时,浏览器可能因安全策略拦截而无法连接;此时在网页右上角选择启用不安全脚本即可。

启用不安全脚本

启动 foxglove_bridge

在开发计算单元(PC2,10.0.1.41)启动 foxglove_bridge(环境中已预装):

# source 环境变量
source /opt/ros/humble/setup.bash
source ~/aimdk/install/setup.bash

# 启动 foxglove_bridge(默认配置文件)
ros2 launch foxglove_bridge foxglove_bridge_launch.xml

连接到机器人

在上位机的 Foxglove 桌面版 / 网页版中进入控制台,选择 打开连接。

打开连接

选择 Foxglove WebSocket 协议(可连接 ROS 1、ROS 2 或自定义系统),将默认地址 ws://localhost:8765 中的 localhost 替换为目标机器 IP,点击 打开 建立连接。

WebSocket 连接

添加面板

完成连接后,在右上角选择添加面板,根据目标 topic 选择相应面板——例如雷达点云选3D面板,TF 关系选变换树。

添加面板

话题设置

单击对应面板,即可在左侧选择可视化所需的 topic、参考系等面板配置。

话题可视化

导入布局

除手动添加面板外,也可以直接导入预设布局(URDF 显示仅限桌面版)。在 Foxglove 右上角点击布局图标:

布局图标

导入下面的配置文件(使用前需将配置文件中的 $URDF_DIR 改为本地 urdf 实际路径):foxglove_layout.json

foxglove_layout.json(点击展开查看)
{
  "configById": {
    "Image!3uaqfl": {
      "cameraState": {
        "distance": 20,
        "perspective": true,
        "phi": 60,
        "target": [
          0,
          0,
          0
        ],
        "targetOffset": [
          0,
          0,
          0
        ],
        "targetOrientation": [
          0,
          0,
          0,
          1
        ],
        "thetaOffset": 60,
        "fovy": 45,
        "near": 0.5,
        "far": 5000
      },
      "followMode": "follow-pose",
      "scene": {},
      "transforms": {},
      "topics": {},
      "layers": {},
      "publish": {
        "type": "point",
        "poseTopic": "/move_base_simple/goal",
        "pointTopic": "/clicked_point",
        "poseEstimateTopic": "/initialpose",
        "poseEstimateXDeviation": 0.5,
        "poseEstimateYDeviation": 0.5,
        "poseEstimateThetaDeviation": 0.26179939
      },
      "synchronize": false,
      "imageMode": {
        "imageTopic": "/aima/hal/sensor/rgb_head_front_center/rgb_image/compressed",
        "calibrationTopic": "/aima/hal/sensor/rgb_head_front_center/camera_info"
      }
    },
    "TransformTree!1x6xlz3": {},
    "3D!4h2shil": {
      "cameraState": {
        "distance": 7.169718448166827,
        "perspective": true,
        "phi": 51.81818181819366,
        "target": [
          0,
          0,
          0
        ],
        "targetOffset": [
          1.925555282550379,
          -0.29000302203140244,
          -3.7627255213825705e-17
        ],
        "targetOrientation": [
          0,
          0,
          0,
          1
        ],
        "thetaOffset": 111.27272727272965,
        "fovy": 45,
        "near": 0.5,
        "far": 5000
      },
      "followMode": "follow-pose",
      "scene": {
        "transforms": {
          "visible": false
        },
        "ignoreColladaUpAxis": true,
        "meshUpAxis": "z_up"
      },
      "transforms": {},
      "topics": {
        "/aima/hal/sensor/lidar_chest_front/lidar_pointcloud": {
          "visible": true,
          "colorField": "intensity",
          "colorMode": "colormap",
          "colorMap": "turbo"
        },
        "/perception/debug/obstacle_cluster": {
          "visible": false
        },
        "/aima/hal/sensor/rgb_head_rear/camera_info": {
          "visible": false
        },
        "/aima/hal/sensor/rgb_head_front_center/rgb_image/compressed": {
          "visible": false,
          "cameraInfoTopic": "/aima/hal/sensor/rgb_head_front_center/camera_info"
        }
      },
      "layers": {
        "grid": {
          "visible": true,
          "drawBehind": false,
          "instanceId": "d3834cc3-4c3e-40a9-8929-a318e6037634",
          "layerId": "foxglove.Grid",
          "size": 10,
          "divisions": 10,
          "lineWidth": 1,
          "color": "#248eff",
          "position": [
            0,
            0,
            0
          ],
          "rotation": [
            0,
            0,
            0
          ]
        },
        "44530a1b-44f8-4bf8-beb2-0a9e2df95e96": {
          "controlMode": "transforms",
          "jointStatesTopic": "",
          "displayMode": "auto",
          "fallbackColor": "#ffffff",
          "showAxis": false,
          "axisScale": 1,
          "showOutlines": true,
          "opacity": 1,
          "visible": true,
          "instanceId": "44530a1b-44f8-4bf8-beb2-0a9e2df95e96",
          "layerId": "foxglove.Urdf",
          "label": "URDF",
          "sourceType": "filePath",
          "url": "",
          "filePath": "$URDF_DIR/x2_ultra.urdf",
          "parameter": "",
          "topic": "",
          "framePrefix": "",
          "order": 1
        }
      },
      "publish": {
        "type": "point",
        "poseTopic": "/move_base_simple/goal",
        "pointTopic": "/clicked_point",
        "poseEstimateTopic": "/initialpose",
        "poseEstimateXDeviation": 0.5,
        "poseEstimateYDeviation": 0.5,
        "poseEstimateThetaDeviation": 0.26179939
      },
      "synchronize": false,
      "imageMode": {},
      "followTf": "base_link"
    }
  },
  "globalVariables": {
    "globalVariable": 3
  },
  "userNodes": {},
  "playbackConfig": {
    "speed": 1
  },
  "layout": {
    "direction": "row",
    "first": {
      "first": "Image!3uaqfl",
      "second": "TransformTree!1x6xlz3",
      "direction": "column"
    },
    "second": "3D!4h2shil"
  }
}

该布局同时显示雷达点云、URDF、交互相机、TF 实时数据:

导入布局后的显示效果

更多配置可参考 Foxglove 官方文档。

建图与重定位

高程图、动态障碍物等感知话题依赖建图与重定位就绪。前置条件:

  1. 机器人为稳定站立模式

  2. 以下 app 已启动(通常随系统自动启动,可通过 ros2 node list 确认):

    • slam —— 建图与重定位

    • map_manager —— 地图管理

    • perception —— 感知

    • pnc —— 规划与控制

    • auto_integrated —— 导航集成

警告

请勿进入开发者模式中的自主开发模式,否则上述 app 可能不会自动启动,导致感知话题无数据。

若前置未就绪,相关话题将无数据发布。可通过以下方式确认:

# 检查节点是否启动
ros2 node list | grep -E "slam|perception|pnc"

# 检查话题是否有数据(应有持续输出)
ros2 topic hz /perception/grid_map

感知可视化话题说明

话题名称

数据类型

描述

QoS

频率

/perception/debug/left_fisheye_detection_viz

sensor_msgs/Image

左目图像实例分割可视化结果(调试用,理解机器人识别到了什么)

RELIABLE+VOLATILE

10Hz

/perception/debug/obstacle_semantic

sensor_msgs/PointCloud2

语义点云(避障输入,区分可通行/障碍/行人)

RELIABLE+VOLATILE

10Hz

/perception/grid_map

grid_map_msgs/GridMap

高程图(地形可通行性,判断台阶/坑/斜坡)

RELIABLE+VOLATILE

10Hz

/perception/debug/obstacle_polygon

visualization_msgs/MarkerArray

动态障碍物(动态避障,追踪移动的人/物)

RELIABLE+VOLATILE

10Hz

所有话题 QoS 详细配置:history=keep_last, depth=10, reliability=reliable, durability=volatile

坐标系说明

机器人各部件坐标系遵循 ROS 标准的 FLU 约定(X 轴向前、Y 轴向左、Z 轴向上),详见 坐标系。本章话题涉及两个关键坐标系:

坐标系

含义

特点

map

全局世界坐标系,由 SLAM 建图时确定

固定不动,Z 轴朝上,X/Y 在水平面;高程图、动态障碍物使用此坐标系

gravity_aligned_base_link

机器人本体坐标系(重力对齐)

随机器人移动而平移、转向而旋转;X 轴指向机器人正前方,Y 轴指向左侧,Z 轴指向上方;语义点云使用此坐标系

两者的关系:map 是固定的世界参考系,gravity_aligned_base_link 随机器人运动。机器人在 map 中的位置和朝向,通过 TF 变换 map → gravity_aligned_base_link 查询,例如:

# 查询机器人在全局地图中的位姿(平移=位置,旋转=朝向)
ros2 run tf2_ros tf2_echo map gravity_aligned_base_link

对可视化数据的影响:

  • 高程图、动态障碍物等话题的坐标值是相对 map 的,方向固定于世界,不随机器人转向而变。

  • 语义点云的坐标值是相对 gravity_aligned_base_link 的,随机器人转向而变化(X 轴始终指向机器人正前方)。

  • 若需在两个坐标系间转换坐标,使用 TF(tf2_ros / tf2_geometry_msgs)。

图像实例分割可视化结果

绘制了左目图像的实例分割结果,包含类别名称、置信度、边界框、分割图等可视化信息。

图像实例分割可视化结果

语义点云

当前传感器 FOV 内的点云,frame_id 为 gravity_aligned_base_link(机器人本体坐标系,X 轴指向正前方,见上方坐标系说明)。使用颜色区分不同语义,各类别颜色(BGR)如下:

语义类别

BGR

颜色

可通行区域

[237, 177, 32]

蓝色

静态障碍物

[126, 47, 142]

紫色

行人

[119, 172, 48]

绿色

颜色值存储在 PointCloud2 的 rgb 字段中(float32 位域打包为 uint32,格式为 0x00RRGGBB),解包方式参考 ROS2 sensor_msgs_py/point_cloud2.py。

语义点云

高程图

基于当前传感器输入生成的栅格高程图,frame_id 为 map(坐标系原点与方向固定,见上方坐标系说明),分辨率 0.05m,尺寸 8m×8m(160×160 栅格)。地图是一个跟随机器人移动的滑动窗口:每帧将窗口中心对准机器人位置(info.pose.position 即机器人在 map 下的位置),机器人始终位于图中心,实际覆盖范围为机器人周围约 ±4m;超出窗口的旧栅格数据会被丢弃。需要注意的是,平移的是地图窗口(可见范围),而非坐标系本身——各栅格的坐标值始终是 map 绝对坐标,不随机器人移动而变化。包含三个层:

层名

数据类型

内容

elevation_map

float

栅格高度值(map 坐标系下的 z,单位 m),由传感器点云逐帧融合;未观测栅格为 NaN

variance_map

float

高程方差,反映该栅格高度估计的不确定度;未观测为 NaN

semantic_map

float

语义类型,取 SemanticType 枚举值:可通行区域=1,静态障碍物=2,行人=3,其他障碍物=4;未观测为 NaN

每个栅格的语义由多帧观测累积得到:新观测会更新该栅格的语义置信度,长时间未观测的栅格置信度会逐渐降低。消息解析使用 GridMapRosConverter::fromMessage,参考 grid_map_ros 官方文档。

高程图

动态障碍物

每个障碍物对应 4 个 marker,按命名空间(ns)与类型(type)区分:

marker 命名空间(ns)

类型(type)

内容

cylinder

CYLINDER(3)

障碍物中心点位置,保存在 marker.pose.position

arrow

ARROW(0)

障碍物速度:方向保存在 marker.pose.orientation,速率大小保存在 marker.scale.x

text

TEXT_VIEW_FACING(9)

障碍物 ID 与速率大小,保存在 marker.text

polygon

LINE_STRIP(4)

二维包围多边形顶点,保存在 marker.points(末尾重复首顶点以闭合)

每个障碍物的 4 个 marker 共享同一种颜色,颜色按障碍物 ID 自动分配,与语义点云按类型着色的方式不同。

每条 MarkerArray 消息的 markers 数组按以下顺序构造:

  1. 先追加 4 个 action = DELETEALL(3)的 marker,分属上述 4 个命名空间,清空上一帧画面

  2. 再为每个障碍物依次追加 4 个 action = ADD(0)的 marker(cylinder → arrow → text → polygon)重绘当前帧

因此每条消息自包含完整画面,订阅者直接使用最新一帧替换即可,无需跨帧累积;话题以 10Hz 整帧刷新。所有 marker 的 header.frame_id 为 map(全局世界坐标系,见上方坐标系说明),header.stamp 为当前帧时间戳。与高程图不同,动态障碍物的坐标是 map 绝对坐标,不随机器人移动而平移或裁剪——只要传感器观测到的障碍物都会在 map 中标出,机器人位置不编码在消息中(需通过 TF 查询)。

图中细圆柱表示障碍物中心点(cylinder marker),外圈多边形表示障碍物轮廓(polygon marker)。

动态障碍物

RSView 可视化

RSView 是 RoboSense 提供的激光雷达数据可视化工具,可实时显示 E1R 点云数据,也支持从 .pcap 文件离线回放。

软件功能

  1. 通过以太网实时显示数据

  2. 将实时数据记录保存为 PCAP 文件

  3. 从记录的 PCAP 文件中回放(不支持 .pcapng 格式)

  4. 多种可视化模式:距离、时间、水平角度、反射强度、激光线束序号等

  5. 用表格显示点的数据

  6. 将点云数据导出为 CSV 格式文件

  7. 测量距离工具

  8. 将回放数据的连续多帧同时显示

  9. 裁剪显示

安装 RSView

RSView 支持在 Windows 64 位、Ubuntu 18.04 及以上操作系统上运行。

从 RoboSense 官网下载最新版本:https://www.robosense.cn/resources-160

软件无需安装,解压后运行可执行文件即可使用。解压路径请勿出现中文字符。

在线模式

注意

在集成的 X2 机器人上,RSView 在线模式不可用——请改用 Foxglove / RViz 实时查看点云。原因有二:

  • 单播:机器人上的 E1R 点云为单播,只发往机器人本机(如 10.11.1.1),同网段的其他电脑收不到这份数据;

  • 端口被占:MSOP / DIFOP 端口(6699 / 7788)已被机器人 HAL 驱动独占,RSView 无法再 bind。

实时查看点云的正确方式:HAL 驱动已把雷达 UDP 实时转成 ROS topic,直接在 Foxglove 3D 面板(或 RViz)订阅即可——同源数据、同样实时、零改动、不打断机器人:

/aima/hal/sensor/lidar_chest_front/lidar_pointcloud   # sensor_msgs/PointCloud2

用法见上文「Foxglove 可视化」的添加面板 / 导入布局。若只想离线分析录制数据,见下文「离线回放(pcap 文件)」。

离线回放

  1. 通过 dumpcap 指定雷达网口抓包:

dumpcap -i sensor0 -f "udp port 6699 or udp port 7788" \
    -w ~/lidar_capture.pcap
  1. 在 RSView 中选择左上角 File → Open PCAP File,选择已录制的 .pcap 文件,并且选择对应的sensor type进行回放。

RSView 离线回放

RViz 可视化

RViz 通过 DDS 直接订阅机器人话题,可实时观测机器人模型、运动行为、TF、雷达点云、地图等信息。

注意

RViz 依赖 DDS 网络直连,目前仅支持有线直连方式(无法跨 VPN / 无线),网口配置详见网络连接。

按网络连接完成有线直连、并设 ROS_DOMAIN_ID 与机器人一致后,启动 RViz2 即可:

source /opt/ros/humble/setup.bash
rviz2

TF、雷达点云、地图、运动行为等均通过 DDS 直接订阅,无需额外发布。当想查看 URDF(机器人模型)时,需另开一个终端发布 robot_description:

source install/local_setup.bash
ros2 run robot_state_publisher robot_state_publisher \
    --ros-args -p robot_description:="$(
      sed "s#filename=\"\\./meshes/#filename=\"file://$URDF_DIR/meshes/#g" \
      "$URDF_DIR/x2_ultra.urdf"
    )"  # $URDF_DIR 换成 urdf 文件实际路径

系统状态、日志、通信可视化

机器人调试 TUI 工具。SSH 友好,不需要图形界面。一个统一菜单驱动的 Textual app,把开发现场最常看的几类信息聚到一起,不依赖 rviz / Foxglove,纯终端可用。

主要功能

  • 系统状态仪表盘(dash)

    五格实时面板,集中展示五大子系统状态:

    • MC Action:当前运动行为(如 STAND_DEFAULT / LOCOMOTION_DEFAULT)及运行/切换状态

    • System:SystemState 业务状态(如 IN_READY / IN_MOVE)

    • Faults / HDS:活跃诊断码、告警码、post-proc 信号计数及最近一条码

    • PMU / Battery:PMU 温度、Orin 电压电流、电池容量、错误位

    • Orin:Jetson Orin CPU/GPU/MEM/温度,由独立 orin-monitor 进程发布

  • 日志聚合(logs)

    将多路日志统一到一个 TUI 页面,支持实时查看、历史查询和过滤:

    • ROS2 节点日志:~/.ros/log/<latest_session>/(默认源,可 --no-ros 跳过)

    • 其他日志文件:--add-path FILE 添加

    • 可选订阅:/rosout(--rosout,仅实时模式)

    • 自动解析多种时间戳格式并统一时区

    • 支持 --since 10m、"yesterday 18:00"、绝对时间等查询方式

    • 支持过滤 DSL,如 level>=WARN node~=slam connect

  • 通信可视化(comm)

    展示当前 ROS graph 中的 topic / service;默认过滤隐藏 topic,行为与 ros2 topic list 一致。支持对选中 topic 按需测频,启动时零订阅负载,QoS 自动匹配发布者。

  • 故障 / HDS(faults)

    健康诊断系统专屏:

    • Live:实时展示 Diag / Alert / PostProc 三类数据

    • History:通过 GetHdsEventHistory 查询历史故障,支持按时间窗、状态、诊断码过滤和分页

  • Orin 监控发布器(orin-monitor)

    独立 ROS2 节点,采集 Jetson Orin 的 CPU/GPU/MEM/温度等资源信息,并以 diagnostic_msgs/DiagnosticArray 发布到 /orin/stats。采集基于 psutil + tegrastats ,可供 dash、bag 记录或其他节点订阅。

统一入口:ros2 run robotop robotop 不带参数进菜单,方向键选屏,Enter 进入,子屏 q 回菜单,菜单 q 退出,Ctrl+C 任意位置强退。也可以走直接子命令:robotop dash / robotop logs --since 1h 等。

安装

依赖工作区已包含 aimdk_msgs、rclpy、diagnostic_msgs。运行时 Python 包装在用户目录:

pip3 install --user typer textual dateparser

构建:

cd ~/aimdk
colcon build --packages-select robotop
source ~/aimdk/install/setup.bash

注意:dash 的 Orin 卡片需要独立的 orin_monitor_node 后台发布 /orin/stats,否则会显示 stale:

ros2 run robotop orin_monitor_node

命令总览

命令

功能

robotop 或 robotop menu

顶层菜单,统一入口

robotop dash

系统状态面板(MC / System / Faults / PMU / Orin)

robotop logs

聚合 ROS / app 日志,实时或历史

robotop comm

topic/service 列表 + 按需测频

robotop faults

HDS 诊断/告警实时表;--history 走 GetHdsEventHistory

robotop orin-monitor

发布 /orin/stats(diagnostic_msgs/DiagnosticArray)

ros2 run robotop robotop                          # 进菜单(推荐)
ros2 run robotop robotop <subcmd> [options]       # 直接进某屏
ros2 run robotop orin_monitor_node                # 独立 node,适合 systemd

dash — 系统状态

ros2 run robotop robotop dash

五个卡片 :

卡片

数据来源

说明

MC Action

/aimdk_5Fmsgs/srv/GetMcAction

当前 MC 模式 + 状态(RUNNING / TRANSITION / IDLE)

System

/aimdk_5Fmsgs/srv/GetSystemState

业务状态名(IN_READY / IN_MOVE 等)

Faults / HDS

/aima/hds/{diag_code_list,alert_code_list,post_proc/signal}

活跃 diag/alert 数 + 最近一条码

PMU / Battery

/aima/hal/pmu/state

PMU 温度、电压电流、电池容量、错误位

Orin

/orin/stats

需先启 orin_monitor_node

键位:q 回菜单,r 立即刷新,Ctrl+C 强退。卡片显示 [stale] 表示对应数据源没接通。

logs — 日志聚合

实时(默认)

ros2 run robotop robotop logs --follow
ros2 run robotop robotop logs -f --rosout                          # 加 /rosout 订阅
ros2 run robotop robotop logs -f --add-path ~/.aima/log/aima.log   # 加入 AIMA 日志
ros2 run robotop robotop logs -f --no-ros                          # 跳过 ROS session 日志源

默认源:最新 ROS session 的最新 .log 文件(~/.ros/log/<latest_session>/<最新>.log)。AIMA、AIMRT 等其他日志需用 --add-path 显式加入。

历史查询

ros2 run robotop robotop logs --since 1h
ros2 run robotop robotop logs --since 7d --level WARN
ros2 run robotop robotop logs --since "2026-06-04 14:00" --until "2026-06-04 16:00"
ros2 run robotop robotop logs --since 30m --grep "fall"

--since / --until 支持:相对(30s / 10m / 1h / 2d)、now、自然语言("yesterday 18:00")、绝对时间("2026-06-04 14:00")。

TUI 键位

键

动作

/

进顶部过滤输入框

Esc

退出过滤框回 log 区

Enter

应用当前过滤

Ctrl+L

清屏

q

退出

过滤 DSL:顶部输入框,空格分隔,全部命中才显示。

表达

含义

error connect

消息同时含 “error” 和 “connect”(大小写不敏感)

level=ERROR

等级正好等于 ERROR

level>=WARN

WARN 及以上(DEBUG < INFO < WARN < ERROR < FATAL)

node~=slam

来源含 “slam”

source~=aima

同上(node~= 的别名)

faults — HDS 故障

Live(默认)

ros2 run robotop robotop faults

三个 tab 分别对应:Diag(/aima/hds/diag_code_list)、Alert(/aima/hds/alert_code_list)、PostProc(/aima/hds/post_proc/signal)。

History

ros2 run robotop robotop faults --history --since 1h
ros2 run robotop robotop faults --history --since 1d --state active
ros2 run robotop robotop faults --history --diag-code 0x6300000001
ros2 run robotop robotop faults --history --alert-code battery_low --asc

参数

含义

--since / --until

时间窗,同 logs 解析规则

--state all/active/recovered

状态过滤

--diag-code N

按诊断码过滤(可重复)

--alert-code STR

按告警字符串过滤(可重复)

--limit N

最多返回条数(默认 200)

--asc

时间正序(默认倒序)

comm — 通信可视化

ros2 run robotop robotop comm                    # 默认与 ros2 topic list 一致
ros2 run robotop robotop comm --include-hidden   # 显示所有,含 _* 隐藏路径

两个 tab:Topics(列 M / topic / type / pub / sub / hz / count)和 Services(列 service / type)。

键

动作

m

toggle 测频:开始 subscribe + 计数 / 停止并销毁订阅

M

一键停掉所有正在测频的 topic

/

进顶部过滤栏(子串匹配 name 或 type)

Esc

退出过滤回表格

r

立即重新枚举

q

退出

orin-monitor — Orin 资源 publisher

ros2 run robotop orin_monitor_node               # 1 Hz,默认
ros2 run robotop orin_monitor_node --rate 2      # 2 Hz

采集基于 psutil + tegrastats。发布到 /orin/stats,类型 diagnostic_msgs/msg/DiagnosticArray。Key 包含:cpu_pct_avg、cpu_cores、cpu_pct_core_0..N、gpu_pct、mem_used_mb、mem_total_mb、mem_pct、temp_cpu、temp_gpu、temp_thermal、power_total_mw。

持久化运行(tmux):

tmux new -d -s orin 'source /opt/ros/humble/setup.bash && source ~/aimdk/install/setup.bash && ros2 run robotop orin_monitor_node'
# 看输出:tmux attach -t orin  (Ctrl-b d 退出 attach,不杀进程)
# 停掉:tmux kill-session -t orin