ros2 humble的 urdf模型及在rviz中显示的一个简单例子
https://docs.ros.org/en/humble/

核心原理:
|
使用urdf建模机器人。 发布关节状态 ( 调用ROS2包 robot_state_publisher,发布(/tf /tf_static 和 RViz2根据robot_state_publisher发布的话题,仿真显示机器人运动。 |
工作空间文件树:
ros2-urdf-tutorial-master$ tree -L 4 . ├── build │ ├── COLCON_IGNORE │ └── urdf_tutorial │ ├── build │ │ └── lib │ ├── colcon_build.rc │ ├── colcon_command_prefix_setup_py.sh │ ├── colcon_command_prefix_setup_py.sh.env │ ├── install.log │ ├── prefix_override │ │ ├── __pycache__ │ │ └── sitecustomize.py │ └── urdf_tutorial.egg-info │ ├── dependency_links.txt │ ├── entry_points.txt │ ├── PKG-INFO │ ├── requires.txt │ ├── SOURCES.txt │ ├── top_level.txt │ └── zip-safe ├── install │ ├── COLCON_IGNORE │ ├── local_setup.bash │ ├── local_setup.ps1 │ ├── local_setup.sh │ ├── _local_setup_util_ps1.py │ ├── _local_setup_util_sh.py │ ├── local_setup.zsh │ ├── setup.bash │ ├── setup.ps1 │ ├── setup.sh │ ├── setup.zsh │ └── urdf_tutorial │ ├── lib │ │ ├── python3.10 │ │ └── urdf_tutorial │ └── share │ ├── ament_index │ ├── colcon-core │ └── urdf_tutorial ├── LICENSE ├── log │ ├── build_2026-04-07_22-59-13 │ │ ├── events.log │ │ ├── logger_all.log │ │ └── urdf_tutorial │ │ ├── command.log │ │ ├── stderr.log │ │ ├── stdout.log │ │ ├── stdout_stderr.log │ │ └── streams.log │ ├── COLCON_IGNORE │ ├── latest -> latest_build │ └── latest_build -> build_2026-04-07_22-59-13 ├── README.md └── urdf_tutorial ├── demo │ └── r2d2_rviz_demo.gif ├── launch │ ├── demo.launch.py │ └── rviz.launch.py ├── package.xml ├── README.md ├── resource │ └── urdf_tutorial ├── setup.cfg ├── setup.py ├── test │ ├── test_copyright.py │ ├── test_flake8.py │ └── test_pep257.py ├── urdf │ ├── r2d2.rviz │ └── r2d2.urdf.xml └── urdf_tutorial ├── __init__.py └── state_publisher.py
源代码:
#! /usr/bin/env python from math import sin, cos, pi import threading import rclpy from rclpy.node import Node from rclpy.qos import QoSProfile from geometry_msgs.msg import Quaternion from sensor_msgs.msg import JointState from tf2_ros import TransformBroadcaster, TransformStamped class StatePublisher(Node): def __init__(self): rclpy.init() super().__init__('state_publisher') qos_profile = QoSProfile(depth=10) self.joint_pub = self.create_publisher(JointState, 'joint_states', qos_profile) # JointStates self.broadcaster = TransformBroadcaster(self, qos=qos_profile) self.nodeName = self.get_name() self.get_logger().info("{0} started".format(self.nodeName)) degree = pi / 180.0 loop_rate = self.create_rate(30) # robot state tilt = 0. tinc = degree swivel = 0. angle = 0. height = 0. hinc = 0.005 # message declarations odom_trans = TransformStamped() odom_trans.header.frame_id = 'odom' odom_trans.child_frame_id = 'axis' joint_state = JointState() try: while rclpy.ok(): rclpy.spin_once(self) # update joint_state now = self.get_clock().now() joint_state.header.stamp = now.to_msg() joint_state.name = ['swivel', 'tilt', 'periscope'] joint_state.position = [swivel, tilt, height] # update transform # (moving in a circle with radius=2) odom_trans.header.stamp = now.to_msg() odom_trans.transform.translation.x = cos(angle)*2 odom_trans.transform.translation.y = sin(angle)*2 odom_trans.transform.translation.z = 0.7 odom_trans.transform.rotation = \ euler_to_quaternion(0, 0, angle + pi/2) # roll,pitch,yaw # send the joint state and transform self.joint_pub.publish(joint_state) self.broadcaster.sendTransform(odom_trans) # Create new robot state tilt += tinc if tilt < -0.5 or tilt > 0.0: tinc *= -1 height += hinc if height > 0.2 or height < 0.0: hinc *= -1 swivel += degree angle += degree/4 # This will adjust as needed per iteration loop_rate.sleep() except KeyboardInterrupt: pass def euler_to_quaternion(roll, pitch, yaw): qx = sin(roll/2) * cos(pitch/2) * cos(yaw/2) - cos(roll/2) * sin(pitch/2) * sin(yaw/2) qy = cos(roll/2) * sin(pitch/2) * cos(yaw/2) + sin(roll/2) * cos(pitch/2) * sin(yaw/2) qz = cos(roll/2) * cos(pitch/2) * sin(yaw/2) - sin(roll/2) * sin(pitch/2) * cos(yaw/2) qw = cos(roll/2) * cos(pitch/2) * cos(yaw/2) + sin(roll/2) * sin(pitch/2) * sin(yaw/2) return Quaternion(x=qx, y=qy, z=qz, w=qw) def main(): node = StatePublisher() if __name__ == '__main__': main()
import os from glob import glob from setuptools import setup from setuptools import find_packages package_name = 'urdf_tutorial' setup( name=package_name, version='1.0.0', packages=[package_name], data_files=[ ('share/ament_index/resource_index/packages', ['resource/' + package_name]), ('share/' + package_name, ['package.xml']), # Include all launch files (os.path.join('share', package_name), glob('launch/*.py')), # Include model and simulation files (os.path.join('share', package_name), glob('urdf/*')) ], install_requires=['setuptools'], zip_safe=True, maintainer='Ben Bongalon', maintainer_email='ben.bongalon@gmail.com', description='ROS 2 tutorial: Using URDF with robot_state_publisher', license='BSD', tests_require=['pytest'], entry_points={ 'console_scripts': [ 'state_publisher = urdf_tutorial.state_publisher:main' ], }, )
<?xml version="1.0"?> <?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?> <package format="3"> <name>urdf_tutorial</name> <version>0.0.0</version> <description>TODO: Package description</description> <maintainer email="ben.bongalon@gmail.com">benb</maintainer> <license>TODO: License declaration</license> <depend>rclpy</depend> <test_depend>ament_copyright</test_depend> <test_depend>ament_flake8</test_depend> <test_depend>ament_pep257</test_depend> <test_depend>python3-pytest</test_depend> <export> <build_type>ament_python</build_type> </export> </package>
#!/usr/bin/env python3 # # Copyright 2020, Ben Bongalon # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Authors: Ben Bongalon (ben.bongalon@gmail.com) import os from ament_index_python.packages import get_package_share_directory from launch import LaunchDescription from launch.actions import DeclareLaunchArgument from launch.substitutions import LaunchConfiguration from launch_ros.actions import Node def generate_launch_description(): use_sim_time = LaunchConfiguration('use_sim_time', default='false') urdf_file_name = 'r2d2.urdf.xml' print("urdf_file_name : {}".format(urdf_file_name)) urdf = os.path.join( get_package_share_directory('urdf_tutorial'), urdf_file_name) # 读取 URDF 内容 with open(urdf, 'r') as f: robot_description = f.read() return LaunchDescription([ DeclareLaunchArgument( 'use_sim_time', default_value='false', description='Use simulation (Gazebo) clock if true'), Node( package='robot_state_publisher', executable='robot_state_publisher', name='robot_state_publisher', output='screen', parameters=[ {'use_sim_time': use_sim_time, 'robot_description': robot_description}, # 添加这个 ], #arguments=[urdf]# 移除 arguments,使用 parameters 方式 ), Node( package='urdf_tutorial', executable='state_publisher', name='state_publisher', output='screen'), ])
#!/usr/bin/env python3 # # Copyright 2020, Ben Bongalon # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Authors: Ben Bongalon (ben.bongalon@gmail.com) import os from ament_index_python.packages import get_package_share_directory from launch import LaunchDescription from launch.actions import DeclareLaunchArgument from launch.substitutions import LaunchConfiguration from launch_ros.actions import Node def generate_launch_description(): config_file_name = 'r2d2.rviz' rviz_config = os.path.join( get_package_share_directory('urdf_tutorial'), config_file_name) return LaunchDescription([ DeclareLaunchArgument( 'use_sim_time', default_value='false', description='Use simulation (Gazebo) clock if true'), Node( package='rviz2', executable='rviz2', name='rviz2', output='screen', arguments=['-d', rviz_config]), ])
<robot name="r2d2"> <link name="axis"> <inertial> <mass value="1"/> <inertia ixx="100" ixy="0" ixz="0" iyy="100" iyz="0" izz="100" /> <origin/> </inertial> <visual> <origin xyz="0 0 0" rpy="1.57 0 0" /> <geometry> <cylinder radius="0.01" length=".5" /> </geometry> <material name="gray"> <color rgba=".2 .2 .2 1" /> </material> </visual> <collision> <origin xyz="0 0 0" rpy="1.57 0 0" /> <geometry> <cylinder radius="0.01" length=".5" /> </geometry> <contact_coefficients mu="0" kp="1000.0" kd="1.0"/> </collision> </link> <link name="leg1"> <inertial> <mass value="1"/> <inertia ixx="100" ixy="0" ixz="0" iyy="100" iyz="0" izz="100" /> <origin/> </inertial> <visual> <origin xyz="0 0 -.3" /> <geometry> <box size=".20 .10 .8" /> </geometry> <material name="white"> <color rgba="1 1 1 1"/> </material> </visual> <collision> <origin xyz="0 0 -.3" /> <geometry> <box size=".20 .10 .8" /> </geometry> <contact_coefficients mu="0" kp="1000.0" kd="1.0"/> </collision> </link> <joint name="leg1connect" type="fixed"> <origin xyz="0 .30 0" /> <parent link="axis"/> <child link="leg1"/> </joint> <link name="leg2"> <inertial> <mass value="1"/> <inertia ixx="100" ixy="0" ixz="0" iyy="100" iyz="0" izz="100" /> <origin/> </inertial> <visual> <origin xyz="0 0 -.3" /> <geometry> <box size=".20 .10 .8" /> </geometry> <material name="white"> <color rgba="1 1 1 1"/> </material> </visual> <collision> <origin xyz="0 0 -.3" /> <geometry> <box size=".20 .10 .8" /> </geometry> <contact_coefficients mu="0" kp="1000.0" kd="1.0"/> </collision> </link> <joint name="leg2connect" type="fixed"> <origin xyz="0 -.30 0" /> <parent link="axis"/> <child link="leg2"/> </joint> <link name="body"> <inertial> <mass value="1"/> <inertia ixx="100" ixy="0" ixz="0" iyy="100" iyz="0" izz="100" /> <origin/> </inertial> <visual> <origin xyz="0 0 -0.2" /> <geometry> <cylinder radius=".20" length=".6"/> </geometry> <material name="white"/> </visual> <collision> <origin xyz="0 0 0.2" /> <geometry> <cylinder radius=".20" length=".6"/> </geometry> <contact_coefficients mu="0" kp="1000.0" kd="1.0"/> </collision> </link> <joint name="tilt" type="revolute"> <parent link="axis"/> <child link="body"/> <origin xyz="0 0 0" rpy="0 0 0" /> <axis xyz="0 1 0" /> <limit upper="0" lower="-.5" effort="10" velocity="10" /> </joint> <link name="head"> <inertial> <mass value="1"/> <inertia ixx="100" ixy="0" ixz="0" iyy="100" iyz="0" izz="100" /> <origin/> </inertial> <visual> <geometry> <sphere radius=".4" /> </geometry> <material name="white" /> </visual> <collision> <origin/> <geometry> <sphere radius=".4" /> </geometry> <contact_coefficients mu="0" kp="1000.0" kd="1.0"/> </collision> </link> <joint name="swivel" type="continuous"> <origin xyz="0 0 0.1" /> <axis xyz="0 0 1" /> <parent link="body"/> <child link="head"/> </joint> <link name="rod"> <inertial> <mass value="1"/> <inertia ixx="100" ixy="0" ixz="0" iyy="100" iyz="0" izz="100" /> <origin/> </inertial> <visual> <origin xyz="0 0 -.1" /> <geometry> <cylinder radius=".02" length=".2" /> </geometry> <material name="gray" /> </visual> <collision> <origin/> <geometry> <cylinder radius=".02" length=".2" /> </geometry> <contact_coefficients mu="0" kp="1000.0" kd="1.0"/> </collision> </link> <joint name="periscope" type="prismatic"> <origin xyz=".12 0 .15" /> <axis xyz="0 0 1" /> <limit upper="0" lower="-.5" effort="10" velocity="10" /> <parent link="head"/> <child link="rod"/> </joint> <link name="box"> <inertial> <mass value="1"/> <inertia ixx="100" ixy="0" ixz="0" iyy="100" iyz="0" izz="100" /> <origin/> </inertial> <visual> <geometry> <box size=".05 .05 .05" /> </geometry> <material name="blue" > <color rgba="0 0 1 1" /> </material> </visual> <collision> <origin/> <geometry> <box size=".05 .05 .05" /> </geometry> <contact_coefficients mu="0" kp="1000.0" kd="1.0"/> </collision> </link> <joint name="boxconnect" type="fixed"> <origin xyz="0 0 0" /> <parent link="rod"/> <child link="box"/> </joint> </robot>
Panels: - Class: rviz_common/Displays Help Height: 78 Name: Displays Property Tree Widget: Expanded: - /Global Options1 - /Status1 - /TF1 - /RobotModel1 - /RobotModel1/Description Topic1 Splitter Ratio: 0.5 Tree Height: 617 - 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 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/TF Enabled: true Frame Timeout: 15 Frames: All Enabled: true axis: Value: true body: Value: true box: Value: true head: Value: true leg1: Value: true leg2: Value: true odom: Value: true rod: Value: true Marker Scale: 1 Name: TF Show Arrows: true Show Axes: true Show Names: false Tree: odom: axis: body: head: rod: box: {} leg1: {} leg2: {} Update Interval: 0 Value: true - Alpha: 1 Class: rviz_default_plugins/RobotModel Collision Enabled: false Description File: "" Description Source: Topic Description Topic: Depth: 5 Durability Policy: Volatile History Policy: Keep Last Reliability Policy: Reliable Value: /robot_description Enabled: true Links: All Links Enabled: true Expand Joint Details: false Expand Link Details: false Expand Tree: false Link Tree Style: Links in Alphabetic Order axis: Alpha: 1 Show Axes: false Show Trail: false Value: true body: Alpha: 1 Show Axes: false Show Trail: false Value: true box: Alpha: 1 Show Axes: false Show Trail: false Value: true head: Alpha: 1 Show Axes: false Show Trail: false Value: true leg1: Alpha: 1 Show Axes: false Show Trail: false Value: true leg2: Alpha: 1 Show Axes: false Show Trail: false Value: true rod: Alpha: 1 Show Axes: false Show Trail: false Value: true Name: RobotModel TF Prefix: "" Update Interval: 0 Value: true Visual Enabled: true Enabled: true Global Options: Background Color: 48; 48; 48 Fixed Frame: odom 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 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.459797203540802 Target Frame: <Fixed Frame> Value: Orbit (rviz) Yaw: 4.618575572967529 Saved: ~ Window Geometry: Displays: collapsed: false Height: 846 Hide Left Dock: false Hide Right Dock: false QMainWindow State: 000000ff00000000fd000000040000000000000156000002f4fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000002f4000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f000002f4fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d000002f4000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004420000003efc0100000002fb0000000800540069006d00650100000000000004420000000000000000fb0000000800540069006d00650100000000000004500000000000000000000002cb000002f400000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 Selection: collapsed: false Tool Properties: collapsed: false Views: collapsed: false Width: 1340 X: 72 Y: 60
可用的部分命令行:
mkdir -p ros2-urdf-tutorial-master
ros2 pkg create --build-type ament_python --license Apache-2.0 urdf_tutorial --dependencies rclpy
ros2 pkg create --build-type ament_cmake --license Apache-2.0 urdf_tutorial
cd urdf_tutorial
mkdir -p urdf
mkdir -p launch
rm -rf install build log
colcon build --packages-select urdf_tutorial
source install/setup.bash
ros2 launch urdf_tutorial demo.launch.py
新开一个terminal,执行如下命令行
source install/setup.bash
ros2 launch urdf_tutorial rviz.launch.py
|
ROS2包 robot_state_publisher
简单来说,它将抽象的关节角度数据,转换成了直观的、可被其他模块(如可视化工具 RViz、导航模块 Nav2)使用的坐标变换(TF)。 核心功能与工作流程
重要参数
典型协作模式
|

浙公网安备 33010602011771号