RViz2可视化传感器数据系列教程之一:在RViz2中显示相机视频图像数据

【编者注】本文由深圳季连科技有限公司kenny wang创作,知识产权归深圳季连科技有限公司所有。如需转载,敬请注明原文出处。

在RViz2中,可以对相机的视频图像数据进行可视化显示,但前提是必须以话题这种ROS的通信方式对相机视频图像数据进行了发布。因此,首先需要通过ROS 2节点发布相机视频图像数据。

可以使用ROS 2的内建相机视频图像发布节点,即v4l2_camera软件包的v4l2_camera_node节点,具体运行命令如下:

ros2 run v4l2_camera v4l2_camera_node

运行上述命令后,会发布以下几个与相机视频图像相关的话题:

/camera_info

/image_raw

/image_raw/compressed

/image_raw/compressedDepth

/image_raw/theora

从中可以看出,v4l2_camera_node节点不仅可以发布一般的视频图像数据(即/image_raw话题),还可以发布压缩的视频数据和压缩的深度相机视频数据(发布压缩视频数据需要安装ros-perception/vision_opencv和image_transport_plugins等依赖包)。在无人机、无人船、远程控制机器人等应用场景下,压缩视频数据传输对于减轻网络负荷非常有用,但在显示视频图像数据前,需要对这类话题发布的视频图像数据进行解压缩。此时,需要通过运行ROS 2的软件包image_transport的republish节点对视频数据解压缩并进行重新发布,解压缩后视频数据话题名称为image_raw/uncompressed:

ros2 run image_transport republish compressed in/compressed:=image_raw/compressed out:=image_raw/uncompressed

当然,也可以使用用户自定义的软件包及其节点进行相机视频图像发布,例如可以使用spreader_camera_pubsub软件包(该软件包用于进行集装箱吊具相机视频图像数据发布、订阅、角件/锁孔目标检测、目标特征点吊具参考坐标系转换计算与发布)的images_publisher节点进行吊具相机视频图像数据发布,具体命令为:

ros2 run spreader_camera_pubsub images_publisher

这样,会发布以下两个与吊具相机视频图像相关的话题:

/camera_info

/image_raw

注意:如果使用用户自定义的软件包及其节点进行相机视频图像发布,必须在发布的类型为sensor_msgs/msgs/Image消息变量(如image_msg)中填充其header字段的相关信息,包括时间戳(stamp)和坐标系名称(frame_id),如下Python代码所示:

image_msg.header.stamp = self. get_clock().now().to_msg()
image_msg.header.frame_id = 'camera'

如果不填充这两个头信息,由于发布的消息其frame_id为空,也没有时间戳,就不能正确配置其固定坐标系,也就不能在RViz2中显示该话题发布的视频图像数据了。

其次,运行RViz2并进行相关配置。可以使用以下命令来运行RViz2:

ros2 run rviz2 rviz2

或者直接运行命令:

rviz2

这样就会打开RViz2图形用户界面。点击该界面左下角的“Add”按钮,可以添加需要进行可视化显示的话题。在选中“Image”话题并点击“Ok”按钮后,会在左侧“Displays”面板底部的“Image”话题对话框中出现的“No Image”字样,如下图所示。

为解决这个问题,需要对该视频图像话题的固定坐标系(Fixed Frame)和话题名称(Topic Name)进行配置。对于该话题的固定坐标系,可以通过两种方式进行配置:一是直接将“Displays”面板顶部固定坐标系(Fixed Frame)的值由默认的“map”修改为“camera”。注意,对于使用自定义软件包节点发布的视频图像话题来说,坐标系名称有可能并不是“camera”,而是其它frame_id,这就需要运行以下命令来查看该视频图像话题的frame_id:

ros2 topic echo /image_raw --no-arr

这样就会看到如下所示的消息:

header:

stamp:

sec: 1639564328

nanosec: 59671685

frame_id: camera

height: 480

width: 640

encoding: rgb8

is_bigendian: 0

step: 1920

data: '<sequence type: uint8, length: 921600>'

其中在header下的frame_id的值就是所需的坐标系名称。

二是可以通过运行ROS 2的坐标变换软件包tf2_ros的静态坐标变换发布者节点,发布RViz2中默认固定坐标系map到该视频图像话题坐标系名称(frame_id)的静态坐标变换。具体命令为:

ros2 run tf2_ros static_transform_publisher 0 0 0 0 0 0 map camera

也要注意,如果该视频图像话题的坐标系名称不是默认的camera,则需将camera替换成正确的坐标系名称。

这样,点击“Displays”面板顶部固定坐标系(Fixed Frame)字段的值(map所在之处),就会出现下拉列表,可以从中选择正确的坐标系名称。此时,如果话题名称配置正确的话,就会开始显示相机视频图像数据了。

但有的时候,话题名称可能也不能直接配置好,需要手动对其进行配置。如果“Image”话题下面的“Topic”字段的值为空,则需要手动输入话题的正确名称,如/image_raw或者其它名称。

当固定坐标系和话题名称被正确配置后,就可以在“Displays”面板底部的“Image”窗口中正常显示相机视频图像了,如下图所示。

除了上述在RViz2图形用户界面中进行手动配置的方式外,还可以直接在RViz2的配置文件*.rviz中进行配置,以便可以在启动RViz2后直接显示相机视频图像数据。下面就是一个这样的示例.rviz文件:

Panels:

- Class: rviz_common/Displays

Help Height: 78

Name: Displays

Property Tree Widget:

Expanded:

- /Global Options1

- /Status1

- /Grid1

- /Image1

- /Image1/Topic1

Splitter Ratio: 0.5

Tree Height: 478

- Class: rviz_common/Selection

Name: Selection

- Class: rviz_common/Tool Properties

Expanded:

- /2D Goal Pose1

- /Publish Point1

Name: Tool Properties

Splitter Ratio: 0.5886790156364441

- Class: rviz_common/Views

Expanded:

- /Current View1

Name: Views

Splitter Ratio: 0.5

- Class: rviz_common/Time

Experimental: false

Name: Time

SyncMode: 0

SyncSource: ""

Visualization Manager:

Class: ""

Displays:

- Alpha: 0.5

Cell Size: 1

Class: rviz_default_plugins/Grid

Color: 160; 160; 164

Enabled: true

Line Style:

Line Width: 0.029999999329447746

Value: Lines

Name: Grid

Normal Cell Count: 0

Offset:

X: 0

Y: 0

Z: 0

Plane: XY

Plane Cell Count: 10

Reference Frame: <Fixed Frame>

Value: true

- Class: rviz_default_plugins/Image

Enabled: true

Max Value: 1

Median window: 5

Min Value: 0

Name: Image

Normalize Range: true

Topic:

Depth: 5

Durability Policy: Volatile

Filter size: 10

History Policy: Keep Last

Reliability Policy: Reliable

Value: /image_raw/ur_camera

Value: true

Enabled: true

Global Options:

Background Color: 48; 48; 48

Fixed Frame: camera

Frame Rate: 30

Name: root

Tools:

- Class: rviz_default_plugins/Interact

Hide Inactive Objects: true

- Class: rviz_default_plugins/MoveCamera

- Class: rviz_default_plugins/Select

- Class: rviz_default_plugins/FocusCamera

- Class: rviz_default_plugins/Measure

Line color: 128; 128; 0

- Class: rviz_default_plugins/SetInitialPose

Covariance x: 0.25

Covariance y: 0.25

Covariance yaw: 0.06853891909122467

Topic:

Depth: 5

Durability Policy: Volatile

History Policy: Keep Last

Reliability Policy: Reliable

Value: /initialpose

- Class: rviz_default_plugins/SetGoal

Topic:

Depth: 5

Durability Policy: Volatile

History Policy: Keep Last

Reliability Policy: Reliable

Value: /goal_pose

- Class: rviz_default_plugins/PublishPoint

Single click: true

Topic:

Depth: 5

Durability Policy: Volatile

History Policy: Keep Last

Reliability Policy: Reliable

Value: /clicked_point

Transformation:

Current:

Class: rviz_default_plugins/TF

Value: true

Views:

Current:

Class: rviz_default_plugins/Orbit

Distance: 10

Enable Stereo Rendering:

Stereo Eye Separation: 0.05999999865889549

Stereo Focal Distance: 1

Swap Stereo Eyes: false

Value: false

Focal Point:

X: 0

Y: 0

Z: 0

Focal Shape Fixed Size: true

Focal Shape Size: 0.05000000074505806

Invert Z Axis: false

Name: Current View

Near Clip Distance: 0.009999999776482582

Pitch: 0.785398006439209

Target Frame: <Fixed Frame>

Value: Orbit (rviz)

Yaw: 0.785398006439209

Saved: ~

Window Geometry:

Displays:

collapsed: false

Height: 1016

Hide Left Dock: false

Hide Right Dock: false

Image:

collapsed: false

QMainWindow State: 000000ff00000000fd0000000400000000000001560000035afc0200000009fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d00000269000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d00610067006501000002ac000000eb0000002800ffffff000000010000010f0000035afc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d0000035a000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000007380000003efc0100000002fb0000000800540069006d0065010000000000000738000002eb00fffffffb0000000800540069006d00650100000000000004500000000000000000000004c70000035a00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000

Selection:

collapsed: false

Time:

collapsed: false

Tool Properties:

collapsed: false

Views:

collapsed: false

Width: 1848

X: 72

Y: 27

与没有添加任何话题时RViz2提供的默认default.rviz文件相比,上面这个.rviz文件中添加的内容用字体加粗进行了标注。从中可以看出,由于要添加Image类型的话题,该.rviz文件在4个地方进行了内容添加或修改:

首先,在“Property Tree Widget”元素的“Expended”子元素中添加了以下两行,用于实例化Image及其Topic:

- /Image1

- /Image1/Topic1

其次,在“Visualization Manager”元素的子元素“displays”中添加了Image插件类及其所有相关属性,包括其话题名称(这里的话题名称为/image_raw/ur_camera),如下所示:

- Class: rviz_default_plugins/Image

Enabled: true

Max Value: 1

Median window: 5

Min Value: 0

Name: Image

Normalize Range: true

Topic:

Depth: 5

Durability Policy: Volatile

Filter size: 10

History Policy: Keep Last

Reliability Policy: Reliable

Value: /image_raw/ur_camera

Value: true

第三,将“Displays”元素的“Global Options”子元素中的固定坐标系“Fixed Frame”子元素值由默认的“map”修改成“camera”。

最后,在“Window Geometry”元素中、“QMainWindow State”子元素之前添加了以下两行:

Image:

collapsed: false

这样,就可以将这个.rviz文件保存到包含相机视频图像话题发布者节点的软件包中源代码子目录下的rviz子目录中,并创建一个launch文件(保存在软件包中源代码子目录下的launch子目录中)来同时启动该发布者节点和rviz2节点,以便发布相机视频图像话题并打开RViz2以直接显示该视频图像话题的数据。下面是一个这样的示例launch文件:

import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.conditions import IfCondition
from launch_ros.actions import Node

def generate_launch_description():
	pkg_camera_pubsub = get_package_share_directory('spreader_camera_pubsub')
	return LaunchDescription([
		DeclareLaunchArgument('rviz', default_value='true',
                              description='Open RViz.'),
		Node(
			package='spreader_camera_pubsub',
			executable='images_publisher',
			name='spreader_camera_images_publisher',
			emulate_tty = True,
			parameters=[
				{'video_source': 0}
			],
			output='screen'
		),
		# RViz
		Node(
			package='rviz2',
			executable='rviz2',
			arguments=['-d', os.path.join(pkg_camera_pubsub, 'rviz', 'my_image.rviz')],
			condition=IfCondition(LaunchConfiguration('rviz'))
		),
	])