ros2 humble的 urdf模型及在rviz中显示的一个简单例子

https://docs.ros.org/en/humble/

 r2d2_rviz_demo

 

核心原理:

使用urdf建模机器人。

发布关节状态 (/joint_states话题,  消息类型sensor_msgs/msg/JointState)

调用ROS2包 robot_state_publisher,发布(/tf  /tf_static /robot_description话题)。

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()
state_publisher.py
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'
        ],
    },
)
setup.py
<?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>
package.xml
#!/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'),
    ])
demo.launch.py
#!/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]),
    ])
rviz.launch.py
<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>
r2d2.urdf.xml
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
r2d2.rviz

可用的部分命令行:

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

robot_state_publisher 是 ROS 2 中一个非常核心且基础的功能包。它的职责可以概括为:根据机器人的模型(URDF)和当前的关节状态,计算出机器人每个部件在三维空间中的位置和姿态,并将这些信息广播出去,让整个 ROS 2 系统都能“看见”机器人的实时形态。

简单来说,它将抽象的关节角度数据,转换成了直观的、可被其他模块(如可视化工具 RViz、导航模块 Nav2)使用的坐标变换(TF)。

核心功能与工作流程

  1. 输入:

    • 机器人模型 (robot_description):一个必须通过参数传入的URDF(统一机器人描述格式)字符串。这个模型描述了机器人的连杆(link)和关节(joint)的树状结构。

    • 关节状态 (/joint_states 话题):订阅 sensor_msgs/msg/JointState 类型的消息,获取每个关节当前的角度、速度等数据。这些数据通常由硬件驱动、仿真器或 joint_state_publisher 节点提供。

  2. 处理: 节点内部维护着一个运动学树。每当收到新的关节状态消息,它就会更新这个树,并执行正向运动学计算,实时推算出每个子连杆相对于其父连杆的位姿。

  3. 输出:

    • 动态变换 (/tf 话题):发布所有可移动关节(如转动关节、滑动关节)对应的坐标变换,频率由 publish_frequency 参数控制(默认为 20Hz)。

    • 静态变换 (/tf_static 话题):在启动时发布一次所有固定关节type="fixed")对应的坐标变换。使用 transient_local 服务质量,确保新订阅的节点也能立即获取到这些静态信息。

    • 机器人描述 (/robot_description 话题):重新发布其加载的 URDF 模型,方便网络中的其他节点获取。

重要参数

  • robot_description必需。URDF 模型字符串,是工作的基础。

  • publish_frequency:动态变换的最大发布频率,默认 20.0 Hz。

  • ignore_timestamp:是否忽略 joint_states 消息的时间戳。默认 false,只处理更新的状态。

  • frame_prefix:为所有发布的 TF 坐标系名称添加前缀,用于在同一个网络中使用多个机器人模型。

典型协作模式

robot_state_publisher 通常与 joint_state_publisher 或其 GUI 版本搭配使用,尤其是在开发调试阶段。joint_state_publisher 提供了一个简单的关节角度数据源(例如通过GUI滑杆手动调节),填补了真实硬件或仿真器尚未提供数据时的空白,让你可以快速检查 URDF 模型是否正确。

posted @ 2026-04-08 14:26  辛河  阅读(21)  评论(0)    收藏  举报