rviz2是ROS2的3D可视化工具,它不执行任何计算,只订阅ROS2话题并将数据以图形化方式展示。它是调试机器人系统的核心工具。
# 直接启动
rviz2
# 加载配置文件启动
rviz2 -d /path/to/config.rviz
# 在Launch中启动
ros2 run rviz2 rviz2 -d $(ros2 pkg prefix my_pkg)/share/my_pkg/rviz/nav.rviz
| Display类型 | 订阅话题 | 可视化内容 |
|---|---|---|
| LaserScan | sensor_msgs/LaserScan | 激光雷达扫描点 |
| PointCloud2 | sensor_msgs/PointCloud2 | 3D点云数据 |
| Map | nav_msgs/OccupancyGrid | 2D栅格地图 |
| Path | nav_msgs/Path | 规划路径 |
| TF | /tf, /tf_static | 坐标系变换树 |
| RobotModel | /robot_description | URDF机器人模型 |
| Odometry | nav_msgs/Odometry | 里程计位姿 |
| Image | sensor_msgs/Image | 摄像头图像 |
| Marker | visualization_msgs/Marker | 自定义标记 |
| MarkerArray | visualization_msgs/MarkerArray | 标记数组 |
| PoseArray | geometry_msgs/PoseArray | 位姿粒子集 |
| Grid | - | 参考网格 |
| Axes | - | 坐标轴 |
#!/usr/bin/env python3
"""rviz2可视化标记发布节点"""
import rclpy
from rclpy.node import Node
from visualization_msgs.msg import Marker, MarkerArray
from geometry_msgs.msg import Point
from std_msgs.msg import ColorRGBA
class RvizMarkerPublisher(Node):
"""发布各种可视化标记供rviz2显示"""
def __init__(self):
super().__init__('rviz_marker_publisher')
self.marker_pub = self.create_publisher(Marker, 'visualization_marker', 10)
self.array_pub = self.create_publisher(MarkerArray, 'visualization_marker_array', 10)
self.timer = self.create_timer(0.5, self.publish_markers)
self.count = 0
self.get_logger().info('RViz标记发布节点启动')
def publish_markers(self):
"""发布各种类型的标记"""
self.count += 1
# 1. 箭头标记 - 表示方向
arrow = Marker()
arrow.header.frame_id = 'map'
arrow.header.stamp = self.get_clock().now().to_msg()
arrow.ns = 'arrow'
arrow.id = 0
arrow.type = Marker.ARROW
arrow.action = Marker.ADD
arrow.pose.position.x = 0.0
arrow.pose.position.y = 0.0
arrow.pose.orientation.w = 1.0
arrow.scale.x = 1.0 # 箭头长度
arrow.scale.y = 0.1 # 箭头宽度
arrow.scale.z = 0.1 # 箭头高度
arrow.color = ColorRGBA(r=1.0, g=0.0, b=0.0, a=1.0) # 红色
arrow.lifetime.sec = 0 # 永久
self.marker_pub.publish(arrow)
# 2. 球体标记 - 表示目标点
sphere = Marker()
sphere.header.frame_id = 'map'
sphere.header.stamp = self.get_clock().now().to_msg()
sphere.ns = 'target'
sphere.id = 1
sphere.type = Marker.SPHERE
sphere.action = Marker.ADD
sphere.pose.position.x = 2.0
sphere.pose.position.y = 1.0
sphere.pose.position.z = 0.5
sphere.pose.orientation.w = 1.0
sphere.scale.x = 0.3
sphere.scale.y = 0.3
sphere.scale.z = 0.3
sphere.color = ColorRGBA(r=0.0, g=1.0, b=0.0, a=0.8) # 绿色半透明
self.marker_pub.publish(sphere)
# 3. 文字标记
text = Marker()
text.header.frame_id = 'map'
text.header.stamp = self.get_clock()->now().to_msg()
text.ns = 'label'
text.id = 2
text.type = Marker.TEXT_VIEW_FACING
text.action = Marker.ADD
text.pose.position.x = 2.0
text.pose.position.y = 1.0
text.pose.position.z = 1.0
text.pose.orientation.w = 1.0
text.scale.z = 0.3 # 文字大小
text.color = ColorRGBA(r=1.0, g=1.0, b=1.0, a=1.0) # 白色
text.text = f'目标点 (帧: {self.count})'
self.marker_pub.publish(text)
# 4. 路径线标记(LINE_STRIP)
path_marker = Marker()
path_marker.header.frame_id = 'map'
path_marker.header.stamp = self.get_clock().now().to_msg()
path_marker.ns = 'path'
path_marker.id = 3
path_marker.type = Marker.LINE_STRIP
path_marker.action = Marker.ADD
path_marker.pose.orientation.w = 1.0
path_marker.scale.x = 0.05 # 线宽
path_marker.color = ColorRGBA(r=0.0, g=0.5, b=1.0, a=1.0) # 蓝色
import math
for i in range(100):
p = Point()
t = i * 0.05
p.x = t * math.cos(t * 0.5)
p.y = t * math.sin(t * 0.5)
p.z = 0.0
path_marker.points.append(p)
self.marker_pub.publish(path_marker)
# 5. MarkerArray - 多个圆柱标记(障碍物)
marker_array = MarkerArray()
obstacles = [(1.0, 0.5), (2.0, -1.0), (-1.0, 2.0), (3.0, 1.5)]
for i, (ox, oy) in enumerate(obstacles):
m = Marker()
m.header.frame_id = 'map'
m.header.stamp = self.get_clock().now().to_msg()
m.ns = 'obstacles'
m.id = i
m.type = Marker.CYLINDER
m.action = Marker.ADD
m.pose.position.x = ox
m.pose.position.y = oy
m.pose.position.z = 0.25
m.pose.orientation.w = 1.0
m.scale.x = 0.4 # 直径
m.scale.y = 0.4
m.scale.z = 0.5 # 高度
m.color = ColorRGBA(r=1.0, g=0.5, b=0.0, a=0.7)
marker_array.markers.append(m)
self.array_pub.publish(marker_array)
def main(args=None):
rclpy.init(args=args)
node = RvizMarkerPublisher()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
# nav_config.rviz - 导航可视化配置
Panels:
- class: rviz_common/Displays
- class: rviz_common/Views
Visualization Manager:
Displays:
- Class: rviz_default_plugins/Grid
Name: Grid
Enabled: true
Line Style:
Line Width: 0.03
Value: Lines
Normal Cell Count: 0
Reference Frame: <Fixed Frame>
Plane: XY
Plane Cell Count: 50
Color: 100; 100; 100
- Class: rviz_default_plugins/TF
Name: TF
Enabled: true
Show Arrows: true
Show Axes: true
Show Names: true
- Class: rviz_default_plugins/Map
Name: Map
Enabled: true
Topic: /map
Color Scheme: map
Alpha: 0.7
- Class: rviz_default_plugins/LaserScan
Name: LaserScan
Enabled: true
Topic: /scan
Size (m): 0.05
Color: 255; 0; 0
Use Fixed Frame: true
- Class: rviz_default_plugins/Path
Name: GlobalPath
Enabled: true
Topic: /plan
Color: 0; 255; 0
- Class: rviz_default_plugins/Path
Name: LocalPath
Enabled: true
Topic: /local_plan
Color: 255; 255; 0
- Class: rviz_default_plugins/RobotModel
Name: RobotModel
Enabled: true
Description Topic: /robot_description
Global Options:
Background Color: 48; 48; 48
Fixed Frame: map
Frame Rate: 30
Name: root
Tools:
- Class: rviz_default_plugins/MoveCamera
- Class: rviz_default_plugins/SetInitialPose
Topic: /initialpose
- Class: rviz_default_plugins/SetGoal
Topic: /goal_pose
Window Geometry:
Width: 1920
Height: 1080
发布Marker创建一个虚拟停车场:矩形框表示车位,圆柱表示柱子,箭头表示行驶方向。
配置一个包含Grid、TF、LaserScan、Map的rviz界面,保存为YAML,在Launch中加载。
创建一个节点,根据时间动态更新标记位置(如移动的机器人图标)。
经验值:+150 XP