rviz 源码详细分析

rviz 源码详细分析

工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros2/rviz
版本:11.2.26(Humble),子包 8 个,许可证 BSD 3-Clause(Willow Garage / OSRF)。

rviz 仓库实现 ROS 2 的 3D 可视化工具 rviz2:Qt 图形界面 + Ogre3D 渲染引擎 + pluginlib 插件体系(Display / Tool / Panel / ViewController / FrameTransformer)。用户通过订阅 ROS 话题、查询坐标变换,在 3D 场景中绘制激光、点云、机器人模型、TF 等。

范围说明:本仓库为 rviz2 核心;ROS 1 版 rviz 在 ros-visualization/rviz。Humble 尚未移植 Stereo 等少数 ROS 1 功能(见 README)。


1. 总体认识

1.1 核心职责

能力 实现位置 说明
可执行入口 rviz2 main.cppVisualizerApp → Qt 事件循环
应用框架 rviz_common 管理器、Display/Tool/Panel、属性树、配置
3D 渲染 rviz_rendering Ogre 封装、点云/网格/形状等可视对象
内置插件 rviz_default_plugins LaserScan、TF、RobotModel、Marker 等
Ogre 依赖 rviz_ogre_vendor 打包 Ogre3D + CMake 模块
Assimp 依赖 rviz_assimp_vendor 网格/DAE/STL 加载
渲染测试 rviz_rendering_tests mesh loader 等单元测试
视觉回归测试 rviz_visual_testing_framework 截图对比式 GUI 测试

1.2 在 ROS 2 栈中的位置

用户rviz2rviz_commonrviz_default_pluginsrviz_renderingROS 2ros2 run rviz2 rviz2main.cpp\nQApplication + VisualizerAppVisualizationFrame\n(Qt 主窗口)VisualizationManager\n(核心调度)DisplayFactory\npluginlibToolManager / ViewManagerTransformationManager\n(可插拔 TF)LaserScanDisplayTFDisplayRobotModelDisplayRenderSystem\n(Ogre::Root)PointCloud / Shape / ...rclcpp\nsubscription / clocktf2 / tf2_ros
对比项 ROS 1 rviz ROS 2 rviz(本仓库)
节点 API roscpp rclcpp + 独立 RosClientAbstraction
TF 硬编码 tf 可插拔 FrameTransformer(默认 tf2)
QoS 有限 QosProfileProperty per Display
时间 /clock 支持 rclcpp::Clock + time jump 处理
插件基类包 librviz rviz_common

1.3 主循环(一帧)

VisualizationManager::onUpdate()(定时器驱动,约 30Hz):

1
2
3
4
5
6
7
1. executor_->spin_some(10ms)     // 处理 ROS 回调
2. frame_manager_->update() // 坐标系
3. root_display_group_->update() // 各 Display 更新 Ogre 对象
4. view_manager_->update() // 相机/视角
5. selection_manager_->update()
6. current_tool_->update()
7. ogre_root_->renderOneFrame() // Ogre 渲染(限帧 ≥10ms)

2. 仓库结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
rviz/
├── README.md
├── docs/
│ ├── FEATURES.md
│ ├── plugin_development.md # ★ 插件开发指南
│ └── migration_guide.md
├── rviz2/ # 可执行 (~180 行)
│ └── src/main.cpp
├── rviz_common/ # ★ 框架核心 (~34100 行 C++)
│ ├── include/rviz_common/
│ │ ├── visualization_manager.hpp
│ │ ├── display.hpp / ros_topic_display.hpp
│ │ ├── message_filter_display.hpp
│ │ ├── tool.hpp / panel.hpp / view_controller.hpp
│ │ ├── display_context.hpp
│ │ ├── properties/ # Qt 属性树
│ │ ├── factory/pluginlib_factory.hpp
│ │ ├── transformation/
│ │ └── ros_integration/
│ └── src/rviz_common/
├── rviz_rendering/ # Ogre 封装 (~13400 行)
│ ├── render_system.hpp
│ └── objects/ point_cloud, shape, grid, ...
├── rviz_default_plugins/ # 内置插件 (~51300 行)
│ ├── displays/ laser_scan, image, marker, ...
│ ├── tools/ move_camera, select, ...
│ ├── view_controllers/
│ ├── panels/
│ └── plugins_description.xml
├── rviz_ogre_vendor/
├── rviz_assimp_vendor/
├── rviz_rendering_tests/
└── rviz_visual_testing_framework/

2.1 子包一览

包名 规模 职责
rviz2 极小 可执行文件、文档
rviz_common Qt UI 框架、管理器、插件 API
rviz_rendering Ogre 场景对象、RenderSystem 单例
rviz_default_plugins 最大 默认 Display/Tool/Panel/View/Transformer
rviz_ogre_vendor vendor Ogre3D 源码/预编译 + FindOGRE
rviz_assimp_vendor vendor Assimp 模型加载
rviz_rendering_tests 测试 rendering 层测试
rviz_visual_testing_framework 测试 端到端视觉测试框架

3. rviz2:程序入口

rviz2/src/main.cpp 流程:

  1. rclcpp::remove_ros_arguments() — 剥离 ROS 参数,剩余给 QApplication
  2. rviz_common::set_logging_handlers() — Qt/Ogre 日志转发到 RCLCPP_*
  3. 构造 VisualizerApp(std::make_unique<RosClientAbstraction>())
  4. vapp.init(argc, argv) → 创建 VisualizationFrame、初始化 ROS
  5. qapp.exec() — Qt 主循环

VisualizerAppvisualizer_app.cpp)负责:

  • 解析命令行(-d 加载 .rviz 配置等)
  • 创建 VisualizationFrame
  • 协调 ROS shutdown 与 Qt 退出

4. rviz_common:应用框架

4.1 核心类关系

1
2
3
4
5
6
7
8
9
10
11
VisualizationFrame (QMainWindow)
└── VisualizationManager (extends DisplayContext)
├── DisplayGroup (root_display_group_)
├── DisplayFactory (PluginlibFactory<Display>)
├── ToolManager
├── ViewManager (ViewController 插件)
├── TransformationManager (FrameTransformer 插件)
├── FrameManager (fixed frame / 坐标变换查询)
├── SelectionManager / HandlerManager
├── RenderPanel (嵌入 Ogre RenderWindow)
└── rclcpp::executors (spin_some)

DisplayContextdisplay_context.hpp)是 Display/Tool 插件看到的 窄接口:提供 getSceneManager()getFrameManager()getRosNodeAbstraction()getTransformationManager() 等,便于单元测试 mock。

4.2 Display 插件体系

基类层次

1
2
3
4
5
properties::BoolProperty
└── Display # 所有可视插件基类
└── _RosTopicDisplay # 带 Topic/QoS 属性
└── RosTopicDisplay<MessageType> # 模板订阅
└── MessageFilterDisplay<MessageType> # + tf2::MessageFilter
基类 适用场景
Display 无 ROS 订阅(Grid、Axes)
RosTopicDisplay<T> 普通话题订阅 + processMessage()
MessageFilterDisplay<T> 需要 TF 同步 的消息(LaserScan、PointCloud2)

Display 生命周期

  1. initialize(context) — 获得 DisplayContext、创建 Ogre::SceneNode
  2. onInitialize() — 子类设置订阅/属性
  3. setEnabled(true) → 订阅话题、显示场景节点
  4. 每帧 update(wall_dt, ros_dt) — 动画、衰减、状态更新
  5. reset() / onDisable() — 清数据、退订

属性树:Display 继承 BoolProperty,子属性(Topic、Color、Size 等)挂到 Qt Model,由 Displays 面板 编辑;load()/save() 写入 .rviz YAML。

4.3 MessageFilterDisplay 与 TF

MessageFilterDisplay 组合:

  • message_filters::Subscriber + rclcpp::Subscription
  • tf2_ros::MessageFilter — 仅当变换可用时投递消息
  • Fixed Frame 来自 FrameManager

典型回调链(LaserScan):

1
2
3
4
sensor_msgs/LaserScan 到达
→ MessageFilter (等待 transform 到 fixed frame)
→ LaserScanDisplay::processMessage()
→ laser_geometry 投影 / PointCloudCommon 更新 Ogre 点云

4.4 可插拔坐标变换(ROS 2 新特性)

TransformationManager 通过 PluginlibFactory<FrameTransformer> 加载:

插件 位置 行为
TFFrameTransformer rviz_default_plugins 标准 tf2(默认)
IdentityFrameTransformer rviz_common 恒等变换(无 TF 时 fallback)

GUI Transformation 面板 切换插件。依赖 TF 的 Display 应使用 TransformerGuardrviz_default_plugins),在错误 transformer 下自动禁用。

FrameManager 封装对当前 FrameTransformer 的查询,供 Display 将数据变换到 Fixed Frame

4.5 Tool / Panel / ViewController

类型 基类 示例
Tool rviz_common::Tool Move Camera、Select、2D Nav Goal、Publish Point
Panel rviz_common::Panel Displays、Selection、Time、Tool Properties、Views
ViewController rviz_common::ViewController Orbit、XY Orbit、First Person、TopDownOrtho

Tool 处理 RenderPanel 鼠标/键盘事件;ViewController 控制 Ogre::Camera 位姿。

4.6 插件加载:PluginlibFactory

1
2
3
4
// pluginlib_factory.hpp
class_loader_ = new pluginlib::ClassLoader<Type>(
package.toStdString(), base_class_type.toStdString());
// 例如 package="rviz_common", base="rviz_common::Display"

VisualizationManager::createDisplay(class_lookup_name)DisplayFactory::make() → pluginlib 实例化。

插件注册(rviz_default_plugins/CMakeLists.txt):

1
pluginlib_export_plugin_description_file(rviz_common plugins_description.xml)

4.7 ROS 集成层

RosClientAbstraction / RosNodeAbstraction

  • 封装 rclcpp::initrclcpp::Node 创建
  • Display 通过 context_->getRosNodeAbstraction() 获取节点,在同一 executor 上 spin_some
  • 支持 --ros-args、匿名节点名等

与纯 rclcpp 节点不同,rviz 在 GUI 线程定时 spin,而非单独 rclcpp::spin 线程(部分 Display 仍可能用异步回调)。

4.8 配置持久化

  • 格式:YAML(.rviz 文件)
  • 读写YamlConfigReader / YamlConfigWriter + Config 树形结构
  • 内容:Display 列表、属性值、ViewController、Transformation 插件、窗口布局等

启动:ros2 run rviz2 rviz2 -d my_config.rviz


5. rviz_rendering:Ogre 渲染层

5.1 RenderSystem 单例

RenderSystem::get() 管理:

  • Ogre::Root 初始化
  • OpenGL 渲染插件加载(OgreGLPlugin
  • Ogre::OverlaySystem
  • makeRenderWindow(window_id, w, h) — 绑定 Qt 窗口 X11/Win/Cocoa ID

Qt 集成RenderPanel 将原生窗口 handle 交给 Ogre 创建 RenderWindow

5.2 可视对象(objects/)

用途
PointCloud / PointCloudRenderable 点云(激光、DepthCloud)
Shape 立方体、球、箭头等
Grid 地面网格
BillboardLine 路径、多边形线
MovableText 3D 文本
WrenchVisual / EffortVisual 力/力矩可视化
CovarianceVisual 协方差椭圆

Mesh 加载mesh_loader + Assimprviz_assimp_vendor)加载 DAE/STL/OGRE mesh。

5.3 与 Display 的分工

  • Display(rviz_common/plugins):ROS 消息、TF、属性、何时更新
  • rendering 对象:如何在 Ogre 里画(材质、顶点缓冲、LOD)

例:LaserScanDisplay 使用 rviz_default_plugins::PointCloudCommon,内部持有 rviz_rendering::PointCloud


6. rviz_default_plugins:内置插件

6.1 plugins_description.xml

单文件注册 Display / Tool / Panel / ViewController / FrameTransformer(节选):

1
2
3
4
5
<class name="rviz_default_plugins/LaserScan"
type="rviz_default_plugins::displays::LaserScanDisplay"
base_class_type="rviz_common::Display">
<message_type>sensor_msgs/msg/LaserScan</message_type>
</class>

class name 即 GUI 中 Add Display 对话框里的 lookup name。

6.2 Display 分类(README 已移植列表)

类别 代表 Display 消息类型
传感器 LaserScan, PointCloud2, Image, Camera sensor_msgs
机器人 RobotModel, TF, Odometry urdf, tf2, nav_msgs
地图 Map, GridCells nav_msgs
标注 Marker, MarkerArray, InteractiveMarker visualization_msgs
几何 Pose, Path, Polygon, Wrench geometry_msgs
其他 Grid, Axes, DepthCloud, Effort

6.3 LaserScanDisplay 实现要点

1
2
class LaserScanDisplay : public
rviz_common::MessageFilterDisplay<sensor_msgs::msg::LaserScan>
  • TransformerGuard 确保 tf2 transformer 生效
  • laser_geometry::LaserProjection 将 scan 转为点云
  • PointCloudCommon 管理颜色、衰减、Ogre 点云对象
  • update() 中处理 transformer 切换 与点云衰减

6.4 RobotModelDisplay

  • 订阅 robot_description(参数或 topic)
  • urdf + kdl / tf2 更新关节角
  • Ogre 场景图挂载 link mesh(Assimp 加载)

6.5 工具与面板

Tools:MoveCamera、FocusCamera、Measure、Select、Interact、InitialPose、Goal、PublishPoint

Panels:Displays、Help、Selection、Time、Tool Properties、Views、Transformation

ViewControllers:Orbit、XYOrbit、FirstPerson、FrameAligned、ThirdPersonFollower、TopDownOrtho


7. Vendor 包

7.1 rviz_ogre_vendor

  • 提供固定版本 Ogre3D(Humble 为 1.x 分支)
  • 导出 CMake:find_package(rviz_ogre_vendor)Ogre 目标
  • rviz_rendering / rviz_common 链接 Ogre

7.2 rviz_assimp_vendor

  • 包装 Assimp
  • rviz_rendering mesh_loader 用于 COLLADA/STL 等

7.3 fastrtps_cmake_module 类比

rviz 不依赖 DDS 消息栈做渲染;vendor 仅图形栈。与 rosidl_typesupport_fastrtps 无直接关系。


8. 插件开发概要

详见 docs/plugin_development.md

8.1 五步开发 Display

  1. 继承 DisplayRosTopicDisplay<T> / MessageFilterDisplay<T>
  2. PLUGINLIB_EXPORT_CLASS(my_plugin::MyDisplay, rviz_common::Display)
  3. 编写 plugins_description.xml
  4. pluginlib_export_plugin_description_file(rviz_common ...)
  5. target_link_libraries(... rviz_common::rviz_common rviz_rendering::rviz_rendering)

8.2 扩展点汇总

插件类型 基类
Display rviz_common::Display
Panel rviz_common::Panel
Tool rviz_common::Tool
ViewController rviz_common::ViewController
FrameTransformer rviz_common::transformation::FrameTransformer

9. 测试体系

9.1 rviz_rendering_tests

  • Ogre 环境 fixture
  • mesh_loader、ogre media 导出测试

9.2 rviz_visual_testing_framework

  • Page Object 模式驱动 Qt GUI
  • 发布测试 TF/消息 → 截图 → 与 golden image 对比
  • 用于回归 Display 渲染结果

10. 端到端序列图:LaserScan 显示

RenderSystemPointCloud (Ogre)FrameManagertf2::MessageFilterLaserScanDisplayVisualizationManagerrclcpp激光节点RenderSystemPointCloud (Ogre)FrameManagertf2::MessageFilterLaserScanDisplayVisualizationManagerrclcpp激光节点publish /scanspin_some()LaserScan callback查询 fixed frame 变换okprocessMessage()更新顶点update()renderOneFrame()

11. 与 ROS 2 其他组件关系

组件 关系
tf2 / tf2_ros 默认 FrameTransformer;MessageFilter 同步
robot_state_publisher RobotModel 显示 link 姿态
urdf 解析 robot_description
interactive_markers InteractiveMarkerDisplay
rclcpp QoS Display 可配置 reliability/durability
ros2 bag play rviz 订阅回放话题 + /clock

12. 调试与常见问题

现象 排查
Fixed Frame 红字 TF 树不完整;检查 ros2 run tf2_tools view_frames
话题无显示 QoS 不兼容;Display 属性中改 QoS
插件找不到 pluginlib 描述文件是否 export;AMENT_PREFIX_PATH
Ogre 黑屏 显卡/OpenGL;LIBGL_* 环境变量
sim time 不动 Time 面板是否 Pause;是否订阅 /clock
RobotModel 空白 robot_description 参数;mesh 路径 package://

常用命令

1
2
3
4
ros2 run rviz2 rviz2
ros2 run rviz2 rviz2 -d install/share/nav2_bringup/rviz/nav2_default_view.rviz
# 调试 TF
ros2 run tf2_tools view_frames

13. 与 ROS 1 rviz 架构对比

维度 ROS 1 ROS 2(本仓库)
GUI Qt Qt(相同范式)
渲染 Ogre Ogre(rviz_rendering 拆分)
插件库 librviz rviz_common + pluginlib
TF tf 内置 可插拔 Transformer
配置 .rviz YAML 兼容思路,格式演进
代码组织 单包为主 8 包分层

14. 源码阅读顺序

  1. 启动链rviz2/main.cppvisualizer_app.cppvisualization_frame.cpp
  2. 主循环visualization_manager.cpponUpdate()
  3. 插件 APIdisplay.hppros_topic_display.hppmessage_filter_display.hpp
  4. 工厂pluginlib_factory.hppadd_display_dialog.cpp
  5. 示例 Displaylaser_scan_display.cpp + point_cloud_common.cpp
  6. 渲染render_system.cppobjects/point_cloud.cpp
  7. TF 插件transformation_manager.cpptf_frame_transformer.cpp
  8. 插件清单plugins_description.xml
  9. 插件开发docs/plugin_development.md

15. 小结

rviz 仓库以 rviz_common 为 Qt+ROS 调度核心,rviz_rendering 为 Ogre 绘图引擎,rviz_default_plugins 提供开箱即用的 Display/Tool/Panel,rviz2 仅为薄入口。数据路径是:rclcpp 订阅 →(可选 TF 滤波)→ Display 处理消息 → rendering 对象更新 → VisualizationManager 驱动 Ogre 渲染

自定义可视化应优先继承 RosTopicDisplayMessageFilterDisplay,并理解 Fixed FrameTransformationManager;性能问题则关注 PointCloud 更新频率与 renderOneFrame 限帧逻辑。

文章互动

阅读 --

留言

0 条留言

正在加载留言…