2025年7月3日 星期四

ROS2 URDF load to Gazebo spawn_entity.py 的基本概念

 spawn_entity.py 是一個在 ROS 2 中非常實用的工具,

它允許您將機器人模型或任何其他 SDF/URDF 格式的實體動態地載入到正在運行的 Gazebo 模擬環境中。

這對於在已經啟動的世界中添加物體或多個機器人非常有用。

spawn_entity.py 的基本概念

spawn_entity.py 實際上是一個 ROS 2 節點,它作為一個服務客戶端,向 Gazebo 提供的 /spawn_entity 服務發送請求。這個服務會接收您提供的模型定義(通常是 URDF 或 SDF 格式的 XML 字串),然後將模型添加到 Gazebo 世界中。

spawn_entity.py 的常用參數和使用範例

以下是 spawn_entity.py 腳本的一些常用參數及其使用範例:


常用參數

  • -entity <name>--entity <name>必填。要載入的模型在 Gazebo 中的名稱。這個名稱在 Gazebo 世界中必須是唯一的。

  • -file <path_to_model_file>--file <path_to_model_file>:指定要載入的 URDF 或 SDF 模型的檔案路徑。

  • -topic <topic_name>--topic <topic_name>:如果模型定義是通過 ROS 2 話題發布的,則指定該話題的名稱。通常用於 robot_description 話題,由 robot_state_publisher 發布。

  • -x <x_pos>-y <y_pos>-z <z_pos>:模型在世界中的初始 XYZ 座標(米)。

  • -R <roll>-P <pitch>-Y <yaw>:模型在世界中的初始滾轉 (Roll)、俯仰 (Pitch)、偏航 (Yaw) 角度(弧度)。

  • -Y <yaw_deg>--yaw <yaw_deg>:偏航角,可以使用角度值(度)。(注意:有 -Y--yaw 兩種格式,-Y 是弧度,--yaw 是角度,要小心區分。)

  • -timeout <seconds>:等待 /spawn_entity 服務可用的超時時間(秒)。預設通常是 30 秒。

  • -robot_namespace <namespace>--robot-namespace <namespace>:為模型中的所有 ROS 接口添加一個命名空間。這在多機器人模擬中非常有用,可以避免話題或服務名稱衝突。

  • -b--bond:維持與 Gazebo 的連接。如果 spawn_entity.py 退出,Gazebo 中的模型也會被移除。


使用範例

範例 1:從 URDF 檔案載入機器人模型

這個範例假設您有一個名為 my_robot.urdf 的機器人模型檔案。

  1. 確保 Gazebo 正在運行: 您可以先啟動一個空白世界:

    Bash
    ros2 launch gazebo_ros gazebo.launch.py # 或您自定義的 empty_world.launch.py
    
  2. 在新的終端機中執行 spawn_entity.py 假設 my_robot.urdf 位於 my_robot_description 套件的 urdf 目錄下。

    Bash
    # 獲取套件路徑
    MY_ROBOT_DESCRIPTION_DIR=$(ros2 pkg prefix my_robot_description)/share/my_robot_description
    
    ros2 run gazebo_ros spawn_entity.py \
        -entity my_robot \
        -file $MY_ROBOT_DESCRIPTION_DIR/urdf/my_robot.urdf \
        -x 0 -y 0 -z 0.5 \
        -R 0 -P 0 -Y 0
    
    • -entity my_robot:將模型命名為 my_robot

    • -file .../my_robot.urdf:指定 URDF 檔案的路徑。

    • -x 0 -y 0 -z 0.5:將機器人放置在 (0, 0, 0.5) 的位置。

    • -R 0 -P 0 -Y 0:設置初始姿態為零角度。

範例 2:從 ROS 2 話題載入機器人模型 (robot_description topic)

這是最常見且推薦的方式,特別是當您的機器人模型是 Xacro 格式,或需要由 robot_state_publisher 處理時。

  1. 啟動 Gazebo 和 robot_state_publisher 您需要一個 Launch 檔案來同時啟動這兩者。

    Python
    # my_robot_launch_package/launch/spawn_robot_in_gazebo.launch.py
    import os
    from ament_index_python.packages import get_package_share_directory
    from launch import LaunchDescription
    from launch.actions import ExecuteProcess, IncludeLaunchDescription
    from launch.launch_description_sources import PythonLaunchDescriptionSource
    from launch.substitutions import Command
    from launch_ros.actions import Node
    from launch_ros.parameter_descriptions import ParameterValue
    
    def generate_launch_description():
        # 獲取機器人描述套件的路徑
        robot_description_package_dir = get_package_share_directory('ur5_description') # 請替換為您的機器人描述套件名稱
    
        # 組合 Xacro 檔案的路徑
        xacro_file = os.path.join(robot_description_package_dir, 'urdf', 'ur5.urdf.xacro') # 請替換為您的 Xacro/URDF 檔案名稱
    
        # 使用 Command 替換 Xacro 預處理,並將結果傳遞給 robot_description
        robot_description_content = ParameterValue(
            Command(['xacro ', xacro_file]),
            value_type=str
        )
    
        # 啟動 Gazebo 伺服器
        gazebo_server_cmd = IncludeLaunchDescription(
            PythonLaunchDescriptionSource([os.path.join(
                get_package_share_directory('gazebo_ros'), 'launch', 'gazebo.launch.py'
            )]),
            launch_arguments={'world': os.path.join(get_package_share_directory('gazebo_ros'), 'worlds', 'empty.world')}.items()
        )
    
        # 啟動 Gazebo 客戶端 (圖形界面)
        gazebo_client_cmd = ExecuteProcess(
            cmd=['gzclient'],
            output='screen'
        )
    
        # 啟動 robot_state_publisher 節點,發布機器人描述
        robot_state_publisher_node = Node(
            package='robot_state_publisher',
            executable='robot_state_publisher',
            name='robot_state_publisher',
            output='screen',
            parameters=[{'robot_description': robot_description_content}],
        )
    
        # 使用 spawn_entity.py 將機器人模型載入 Gazebo
        spawn_entity_cmd = Node(
            package='gazebo_ros',
            executable='spawn_entity.py',
            arguments=[
                '-entity', 'my_spawned_robot', # 模型名稱
                '-topic', 'robot_description', # 從 robot_description 話題讀取模型
                '-x', '0', '-y', '0', '-z', '0.1', # 初始位置
                # '-R', '0', '-P', '0', '-Y', '1.5708', # 初始姿態 (弧度)
                # '--yaw', '90' # 初始姿態 (角度)
            ],
            output='screen'
        )
    
        return LaunchDescription([
            gazebo_server_cmd,
            gazebo_client_cmd,
            robot_state_publisher_node,
            spawn_entity_cmd,
        ])
    
    
  2. 執行 Launch 檔案:

    [Bash]
    ros2 launch my_robot_launch_package spawn_robot_in_gazebo.launch.py
    
    • -entity my_spawned_robot:指定在 Gazebo 中顯示的模型名稱。

    • -topic robot_description:告訴 spawn_entity.py 去訂閱 /robot_description 話題來獲取機器人模型數據。

範例 3:在特定命名空間載入多個機器人

如果您想在同一個 Gazebo 世界中載入多個機器人,

並且希望它們的 ROS 接口(話題、服務等)是獨立的,可以使用 robot_namespace 參數。


[ Python ]
# 假設您想載入兩個相同的機器人,分別位於不同的命名空間

# robot_1 的啟動器
robot_1_state_publisher = Node(
    package='robot_state_publisher',
    executable='robot_state_publisher',
    namespace='robot_1', # 注意這裡的命名空間
    parameters=[{'robot_description': robot_description_content}],
    output='screen',
)

spawn_robot_1 = Node(
    package='gazebo_ros',
    executable='spawn_entity.py',
    arguments=[
        '-entity', 'robot_1_model',
        '-topic', 'robot_1/robot_description', # 這裡的話題名也要包含命名空間
        '-x', '0', '-y', '0', '-z', '0.1',
        '-robot_namespace', 'robot_1' # 為 Gazebo 內的 ROS 接口應用命名空間
    ],
    output='screen'
)

# robot_2 的啟動器 (類似於 robot_1,只是改變名稱和位置)
robot_2_state_publisher = Node(
    package='robot_state_publisher',
    executable='robot_state_publisher',
    namespace='robot_2',
    parameters=[{'robot_description': robot_description_content}],
    output='screen',
)

spawn_robot_2 = Node(
    package='gazebo_ros',
    executable='spawn_entity.py',
    arguments=[
        '-entity', 'robot_2_model',
        '-topic', 'robot_2/robot_description',
        '-x', '1', '-y', '0', '-z', '0.1',
        '-robot_namespace', 'robot_2'
    ],
    output='screen'
)

return LaunchDescription([
    gazebo_server_cmd,
    gazebo_client_cmd,
    robot_1_state_publisher,
    spawn_robot_1,
    robot_2_state_publisher,
    spawn_robot_2,
])


在這個範例中,robot_1 的所有 ROS 接口都會在 /robot_1/ 命名空間下,

robot_2 的會在 /robot_2/ 命名空間下,這對於多機器人協同工作非常重要。

總結

spawn_entity.py 是一個非常靈活的工具,讓您能夠在 Gazebo 模擬中動態地控制模型的載入。最常見的用法是搭配 robot_state_publisher 來載入 URDF/Xacro 定義的機器人模型。

沒有留言:

張貼留言