
使用 Rerun 可视化 ROS 2 机器人数据从 ROS Topic 订阅到 Rerun 日志转换的完整实践指南【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun导读Rerun 目前还没有对 ROS 的原生支持但 ROS 与 Rerun 的许多核心概念时间线、坐标变换、图像与点云能够很好地相互对应。本指南基于仓库中的 ROS node 示例 及其配套文档 Use Rerun with ROS 2讲解如何编写一个最小的 ROS 2 Python 节点订阅常见的 ROS TopicTF、里程计、激光扫描、代价地图、相机图像与深度图、URDF 等逐类转换为 Rerun 日志调用并实时可视化。读完本文你将掌握 ROS 2 消息与 Rerun archetype 之间的映射套路、ros_time时间线同步方法以及如何在 Nav2 Turtlebot 4 仿真环境中直接运行该示例。背景为什么需要自己写一个 ROS 节点Robot Operating SystemROS通过软件库和工具帮助开发者构建机器人应用。Rerun 本身没有 ROS 的原生支持但你可以很容易地创建一个基础的 ROS 2 Python 节点订阅常见 Topic 并将它们记录到 Rerun 中。本示例的定位是玩具级toy example它的目的不是做一个完整的 ROS 桥接方案而是展示ROS 概念如何映射到 Rerun。在示例中Rerun 可视化的是仿真数据包括机器人位姿、图像、相机位置、激光扫描、点云和速度覆盖了机器人导航过程中的典型多模态数据。官方对更完善 ROS 支持的追踪 issue 见 re_importer 相关的 ROS 支持讨论对应 issue #1537LaserScan 原生投影原语的支持跟踪见 docs/content/concepts/logging-and-ingestion/mcap.md 所引用的 issue #1534。用到的 Rerun 类型本示例涉及以下 Rerun archetype全部可以在 crates/store/re_log_types 的类型体系与 rerun_py/rerun_sdk/rerun 的 Python SDK 中找到对应实现ROS 消息Rerun archetype用途sensor_msgs/ImageRGBrr.Image彩色图像sensor_msgs/Image深度rr.DepthImage深度图sensor_msgs/CameraInforr.Pinhole针孔相机内参、视锥可视化tf2_msgs/TFMessagerr.Transform3D坐标变换树nav_msgs/OccupancyGridrr.GridMap地图与代价地图栅格sensor_msgs/PointCloud2由 LaserScan 投影rr.LineStrips3D激光扫描线束nav_msgs/Odometryrr.Scalars线速度/角速度时间序列std_msgs/String/robot_descriptionrr.log_file_from_contentsURDF 机器人模型环境与依赖准备系统级依赖ROS 2 与 Nav2与仓库中大多数示例不同本示例除了requirements.txt中的 Python 包之外还需要系统级安装 ROS。README 中的命令基于 Ubuntu 发行版上 ROS 的官方安装路径编写如果你通过 Robostack 安装 ROS命令可以类推使用。示例开发和测试基于ROS 2 Kilted Kaiju即 ROS 2 的 Kilted 发行版搭配Nav2以及nav2_bringup包中的Turtlebot 4 仿真示例。如果你使用其他版本的 ROS例如 Jazzy只需把下面命令中的kilted替换为jazzy即可。ROS 的安装本身不在示例范围内但你需要安装等价于以下命令的软件包sudo apt install ros-kilted-desktop ros-kilted-nav2-bringup其中ros-kilted-nav2-bringup会作为依赖拉入我们所需的所有导航与仿真包如果尚未安装的话。克隆仓库并准备 Python 环境git clone https://github.com/rerun-io/rerun.git # Clone the repository cd rerun git checkout latest # Check out the commit matching the latest SDK release提示本仓库对应分支路径为GitHub_Trending/re/rerun其中的示例源码位于 examples/python/ros_node/main.py。务必使用 Python 虚拟环境这里以venv为例sudo apt install python3-venvpython3 -m venv --system-site-packages rerun-ros-example source rerun-ros-example/bin/activate使用--system-site-packages创建虚拟环境是关键一步这样 venv 才能看到系统安装的 ROS 2 Python 包rclpy、cv_bridge、laser_geometry等。然后安装最新版 Rerun SDK 以及本示例requirements.txt中指定的库pip install --upgrade rerun-sdk pip install -r examples/python/ros_node/requirements.txtrequirements.txt 内容解析examples/python/ros_node/requirements.txt 内容如下# NOTE: numpy has to be downgraded to be compatible with ROS packages # that were built against the 1.x version of python3-numpy in Ubuntu 24.04. numpy2.0 opencv-python pycollada pyyaml rerun-sdk scipy yourdfpy两点值得注意numpy2.0的版本约束文件头部注释明确说明Ubuntu 24.04 中 ROS 包是针对 python3-numpy 1.x 构建的因此 numpy 必须降级到 2.0 以下才能与 ROS 包兼容yourdfpy与pycollada用于 URDF/COLLADA 网格解析与 docs/content/howto/logging-and-ingestion/urdf.md 中提到的 URDF 加载管线相关opencv-python提供cv_bridge底层所需的图像编码支持。激活 ROS 环境在安装requirements.txt中的依赖之外你还需要 source ROS 的 setup 脚本source /opt/ros/kilted/setup.bash运行示例连接 Nav2 Turtlebot 仿真第一步启动 Turtlebot 4 仿真在一个终端中启动 Nav2 turtlebot demosource /opt/ros/kilted/setup.bash ros2 launch nav2_bringup tb4_simulation_launch.py headless:False这会打开 Gazebo 和 RViz 两个窗口。使用 RViz 窗口初始化位姿估计把机器人放到地图上然后设置一个导航目标让机器人移动起来。第二步运行 ROS 节点在另一个终端中连接正在运行的 ROS 系统source /opt/ros/kilted/setup.bash python examples/python/ros_node/main.py # run the example运行后Rerun Viewer 会自动打开由rr.script_setup的默认行为触发实时展示从 ROS 订阅到的机器人数据。第三步自定义与探索 CLI如果你想自定义或探索更多功能使用--help选项查看 CLI 指南python examples/python/ros_node/main.py --helpmain.py中通过rr.script_add_args(parser)注册 Rerun 的通用参数如--headless、--connect、--serve等通过rr.script_setup(args, rerun_example_ros_node)完成 SDK 初始化剩余的未知参数则传给rclpy.init(argsunknownargs)因此你可以同时透传rclpy的启动参数。参见 examples/python/ros_node/main.py。核心架构订阅 → 转换 → 日志从源码结构看main.py 的实现模式非常统一对每个感兴趣的 ROS Topic 创建一个订阅者配一个回调函数回调内部做数据转换然后调用 Rerun 日志 API。核心类为TurtleSubscriber(Node)它继承自rclpy.node.Node节点名为rr_turtlebot见 main.py。订阅列表与 QoS 处理在构造函数中示例通过自定义的subscribe辅助方法订阅了一组 Topic见 main.pyTopic消息类型回调说明/tfTFMessagetf_callback动态变换/tf_staticTFMessagetf_callback静态变换latching/odomOdometryodom_callback里程计/scanLaserScanscan_callback激光扫描/rgbd_camera/camera_infoCameraInfocam_info_callback相机内参/rgbd_camera/imageImageimage_callbackRGB 图像/rgbd_camera/depth_imageImagedepth_callback深度图/robot_descriptionStringurdf_callbackURDF 描述latching/mapOccupancyGridoccupancy_grid_callback静态地图latching/global_costmap/costmapOccupancyGridoccupancy_grid_callback全局代价地图/local_costmap/costmapOccupancyGridoccupancy_grid_callback局部代价地图subscribe方法内部见 main.py对 QoS 做了区分对需要latching静态数据如/tf_static、/map、/robot_description的订阅使用QoSProfile(depth1, durabilityQoSDurabilityPolicy.TRANSIENT_LOCAL)其余动态数据使用整数10即 history depth 为 10 的默认 QoS所有订阅都挂在ReentrantCallbackGroup()上允许回调并发执行。执行模型MultiThreadedExecutor入口处使用rclpy.spin(turtle_subscriber, executorrclpy.executors.MultiThreadedExecutor())见 main.py。文档 Use Rerun with ROS 2 对此的解释是ReentrantCallbackGroup配合MultiThreadedExecutor可以让每个回调线程独立处理消息避免某个回调做 TF 查询等耗时操作时阻塞其他消息的接收。更高级的 ROS 执行模型与异步 TF 查询不在本指南范围内。时间同步用ros_time时间线对齐所有数据所有传感器数据能否在 Rerun 时间轴上正确对齐是可视化的关键。示例的每个回调都遵循同一模式从消息头的 stamped 时间更新 Rerun 的ros_time时间线而不是使用消息被接收或转发到 Rerun 的时间def some_msg_callback(self, msg: Msg): time Time.from_msg(msg.header.stamp) rr.set_time(ros_time, timestampnp.datetime64(time.nanoseconds, ns))这段代码使用rclpy.time.Time.from_msg解析msg.header.stamp得到纳秒精度的时间戳再以np.datetime64设置到名为ros_time的 Rerun 时间线上。该时间戳会持续作用于该回调线程后续的所有日志调用直到再次更新时间见 docs/content/howto/integrations/ros2-nav-turtlebot.md 的 Updating time 一节。这也与 Rerun 的数据模型一致——时间线是 Rerun 检索与回放数据的核心维度。各类 ROS 消息到 Rerun 的映射详解TF 变换 →rr.Transform3DROS 的 tf2 变换直接对应 Rerun 的Transform3Darchetype。自 Rerun 0.28 起Transform3D支持 parent/child frame 关系使转换变得直接注意TFMessage是多个独立时间戳的变换的容器所以需要为每个 transform 单独设置时间。通过指定 parent/child frame所有变换都可以记录到同一个实体路径transforms下与 ROS 的 TF topic 行为类似。def tf_callback(self, tf_msg: TFMessage) - None: for transform in tf_msg.transforms: time Time.from_msg(transform.header.stamp) rr.set_time(ros_time, timestampnp.datetime64(time.nanoseconds, ns)) rr.log( transforms, rr.Transform3D( translation[ transform.transform.translation.x, transform.transform.translation.y, transform.transform.translation.z, ], rotationrr.Quaternion( xyzw[ transform.transform.rotation.x, transform.transform.rotation.y, transform.transform.rotation.z, transform.transform.rotation.w, ] ), parent_frametransform.header.frame_id, child_frametransform.child_frame_id, ), )为了让这些变换在其余日志数据中发挥作用还需要用rr.CoordinateFrame将实体关联到对应的命名 frame。这正是 Rerun 的命名变换帧named transform frames机制每个实体先用CoordinateFrame关联到某个命名 frame两个 frame 之间的几何关系由带child_frame/parent_frame的Transform3D确定。这与 ROS tf2 的约定一致——每个 frame 在同一时刻只有一个 parent见 docs/content/concepts/logging-and-ingestion/transforms.md。命名 frame 相比实体路径层级变换的优势在于拓扑可以随时间变化、多个实体可以关联到同一 frame、实体路径可以留给语义组织。URDF 机器人模型 → 内置 URDF importerRerun 内置了 URDF importer详见 docs/content/howto/logging-and-ingestion/urdf.md所以/robot_descriptiontopic 上收到的字符串可以直接转发给它def urdf_callback(self, urdf_msg: String) - None: # NOTE: file_path is not known here, robot.urdf is just a placeholder to let # Rerun know the file type. Since we run this example in a ROS environment, # Rerun can use AMENT_PREFIX_PATH etc to resolve asset paths of the URDF. rr.log_file_from_contents( file_pathrobot.urdf, file_contentsurdf_msg.data.encode(utf-8), entity_path_prefixurdf, staticTrue, )这里robot.urdf只是一个占位文件名用于让 Rerun 识别文件类型由于运行在 ROS 环境中Rerun 可以通过AMENT_PREFIX_PATH等环境变量解析 URDF 引用的资源网格等路径。importer 会自动完成网格路径解析、网格与形状加载为 Rerun 实体、关节变换与 link 的 frame ID 关联。此后可以通过发送带parent_frame/child_frame的Transform3D来更新关节位姿。LaserScan →rr.LineStrips3DRerun 目前还没有原生的LaserScan类型原语跟踪 issue #1534所以需要额外做一步转换见 docs/content/howto/integrations/ros2-nav-turtlebot.md 的 LaserScan 一节def scan_callback(self, scan: LaserScan) - None: time Time.from_msg(scan.header.stamp) rr.set_time(ros_time, timestampnp.datetime64(time.nanoseconds, ns)) # Project the laser scan to a collection of points points self.laser_proj.projectLaser(scan) pts point_cloud2.read_points(points, field_names[x, y, z], skip_nansTrue) pts structured_to_unstructured(pts) # Turn every pt into a line-segment from the origin to the point. origin (pts / np.linalg.norm(pts, axis1).reshape(-1, 1)) * 0.3 segs np.hstack([origin, pts]).reshape(pts.shape[0] * 2, 3) rr.log(scan, rr.LineStrips3D(segs, radii0.0025, colors[255, 165, 0])) rr.log(scan, rr.CoordinateFrame(framescan.header.frame_id))转换思路分三步用 ROS 生态的laser_geometry.LaserProjection().projectLaser()将扫描投影成点云再用sensor_msgs_py.point_cloud2.read_points读取 x/y/z 坐标并转换为非结构化 NumPy 数组对每个点生成一个从原点沿射线方向伸出 0.3m 的起点然后用np.hstack与reshape把两组点交错排列成起点-终点-起点-终点…的线段序列——这正是rr.LineStrips3D期望的输入格式可以想象成 Gazebo 中激光线的显示方式同时把scan.header.frame_id作为CoordinateFrame记录确保 Rerun 在正确的变换层级位置可视化这些线段。OccupancyGrid →rr.GridMapROS 的nav_msgs/OccupancyGrid消息直接对应 Rerun 的GridMaparchetype。示例订阅了静态地图以及全局/局部代价地图分别使用 Rerun 的 RViz 兼容配色RvizMap与RvizCostmap并通过draw_order定义图层顺序见 main.py。大部分字段是 1:1 映射占用数据成为GridMap的图像数据info.resolution成为 cell sizeinfo.origin定义地图位姿。唯一的坑是行顺序ROS 栅格地图从地图左下角单元格开始而 RerunGridMap使用的常规图像缓冲区是首行在顶部因此示例在记录前要np.flipud翻转行见 main.py# ROS maps start at the bottom-left cell; Rerun image buffers are top-row first. data np.asarray(grid.data, dtypenp.int8).reshape((grid.info.height, grid.info.width)) image_data np.flipud(data).astype(np.uint8, copyFalse) rr.log( entity_path, rr.GridMap( dataimage_data.tobytes(), formatrr.components.ImageFormat( widthgrid.info.width, heightgrid.info.height, color_modelL, channel_datatypeU8, ), cell_sizegrid.info.resolution, translation[grid.info.origin.position.x, grid.info.origin.position.y, grid.info.origin.position.z], quaternionrr.Quaternion( xyzw[ grid.info.origin.orientation.x, grid.info.origin.orientation.y, grid.info.origin.orientation.z, grid.info.origin.orientation.w, ] ), colormapcolormap, draw_orderdraw_order, opacityopacity, ), )同时记录地图的CoordinateFrame(framegrid.header.frame_id)让地图在变换树中与机器人、传感器数据对齐。相机内参与图像 →rr.Pinholerr.ImageROS 图像可以用cv_bridge包轻松映射cv_bridge.imgmsg_to_cv2的输出可以直接喂给rr.Image。相机内参 topic 则用image_geometry包PinholeCameraModel.from_camera_info()暴露的相机内参矩阵结构与 Rerunrr.Pinhole一致见 main.pydef cam_info_callback(self, info: CameraInfo) - None: time Time.from_msg(info.header.stamp) self.pinhole_model.from_camera_info(info) rr.set_time(ros_time, timestampnp.datetime64(time.nanoseconds, ns)) rr.log( rgbd_camera/camera_info, rr.Pinhole( resolution[info.width, info.height], image_from_cameraself.pinhole_model.intrinsic_matrix(), image_plane_distance1.0, parent_frameinfo.header.frame_id, # Specifying a child_frame for the 2D image plane allows Rerun to # visualize the pinhole frustum together with the image in 3D views. # This has to match the coordinate frames used when logging images, # see image_callback below. child_frameinfo.header.frame_id _image_plane, ), )这里的关键技巧是为 2D 图像平面建立一个独立的 frameROS 概念里没有图像平面这个坐标系为了让 Rerun 在 3D 视图中把图像投影到针孔视锥pinhole frustum里需要建立 3D 外参相机坐标系即消息的frame_id与 2D 图像平面之间的关系。示例的做法是用_image_plane后缀命名图像平面 frame并确保 Pinhole 与图像日志都使用它def image_callback(self, img: Image) - None: time Time.from_msg(img.header.stamp) rr.set_time(ros_time, timestampnp.datetime64(time.nanoseconds, ns)) rr.log(rgbd_camera/image, rr.Image(self.cv_bridge.imgmsg_to_cv2(img))) # Make sure the image plane frame matches what we set in cam_info_callback. rr.log(rgbd_camera/image, rr.CoordinateFrame(frameimg.header.frame_id _image_plane))深度图处理方式类似只是用rr.DepthImage并指定meter1.0、colormapviridis编码通过cv_bridge指定为32FC1见 main.py。从 Rerun 的变换系统看Pinhole 不只是可可视化的 archetype它本身就是一种3D 空间到 2D 子空间的投影关系配合正确的 frame 设置即可实现 2D 图像嵌入 3D 场景、3D 对象投影到 2D 相机视图等强大效果见 docs/content/concepts/logging-and-ingestion/transforms.md 的 Pinhole projections 一节。Odometry →rr.Scalars里程计数据被拆成时间序列标量记录def odom_callback(self, odom: Odometry) - None: time Time.from_msg(odom.header.stamp) rr.set_time(ros_time, timestampnp.datetime64(time.nanoseconds, ns)) # Capture time-series data for the linear and angular velocities rr.log(odom/twist/linear/x, rr.Scalars(odom.twist.twist.linear.x)) rr.log(odom/twist/angular/z, rr.Scalars(odom.twist.twist.angular.z))线速度x 方向与角速度z 方向分别记录到独立实体路径在 Rerun 中以时间序列曲线呈现便于观察机器人运动状态。总结与扩展方向虽然示例代码量不小但没有一段是复杂逻辑每个消息回调独立运作——接收消息、适配为 Rerun 数据、再记录。这套订阅 → 转换 → 日志的模式可以很容易推广到本文未覆盖的其他 ROS 消息类型例如更多相机位姿、点云、IMU 数据等。几个可以继续深入的仓库资源完整代码examples/python/ros_node/main.py依赖清单examples/python/ros_node/requirements.txt配套图文教程docs/content/howto/integrations/ros2-nav-turtlebot.mdURDF 加载原理docs/content/howto/logging-and-ingestion/urdf.md变换与坐标帧概念docs/content/concepts/logging-and-ingestion/transforms.mdMCAP 记录数据回放docs/content/howto/logging-and-ingestion/mcap.md如果你已经录制了 ROS 数据例如 .mcap 文件也可以直接参考 MCAP 文档让 Rerun 读取录制的数据而无需运行实时节点。Rerun 对激光扫描等原语的原生支持仍在演进中issue #1534随着新功能落地这套桥接代码会进一步简化。【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考