构建机器人


预计完成时间:7 分钟

2.3   创建移动机器人

要创建一个新的机器人模型,你需要按照特定的目录结构来组织机器人的定义文件和文件夹。

除了机器人的定义文件,也就是 URDF 文件之外,你还需要使用 launch 文件来调试机器人的创建过程。

创建一个功能包,并在其中新建以下文件夹:

  在 Web Shell 1 中执行

In [ ]:
cd /workspace/src
In [ ]:
ros2 pkg create robot_description --build-type ament_python --dependencies rclpy urdf xacro
In [ ]:
mkdir -p robot_description/urdf
In [ ]:
touch robot_description/urdf/robot.urdf

URDF 文件夹将用于存放机器人的描述文件。

将下面的内容粘贴到 robot.urdf 文件中:

  robot.urdf

In [ ]:
<?xml version="1.0"?>

<robot name="robot">
    <!-- Link - chassis -->
    <link name="link_chassis">
        <inertial>
            <mass value="10" />
            <origin xyz="0 0 0.3" rpy="0 0 0" />
            <inertia ixx="1.5417" ixy="0" ixz="0" iyy="3.467" iyz="0" izz="4.742" />
        </inertial>

        <collision>
            <geometry>
                <box size="2 1.3 0.4" />
            </geometry>
        </collision>

        <visual>
            <geometry>
                <box size="2 1.3 0.4" />
            </geometry>
            <material name="Red">
                <color rgba="1 0 0 1" />
            </material>
        </visual>
    </link>
</robot>

使用下面的命令在该功能包中创建一个新的 launch 文件:

  在 Web Shell 1 中执行

In [ ]:
cd /workspace/src
In [ ]:
mkdir -p robot_description/launch
In [ ]:
touch robot_description/launch/rviz.launch.py

launch 文件夹将用于存放使用 RVIZ2 调试该机器人模型所需的 launch 文件。

复制并粘贴以下内容:

  rviz.launch.py

In [ ]:
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.substitutions import Command
from launch_ros.actions import Node

# ROS2 Launch System will look for this function definition #
def generate_launch_description():

    # Get Package Description and Directory #
    package_description = "robot_description"
    package_directory = get_package_share_directory(package_description)

    # Load URDF File #
    urdf_file = 'robot.urdf'
    robot_desc_path = os.path.join(package_directory, "urdf", urdf_file)
    print("URDF Loaded !")

    # Robot State Publisher (RSP) #
    robot_state_publisher_node = Node(
        package='robot_state_publisher',
        executable='robot_state_publisher',
        name='robot_state_publisher_node',
        output="screen",
        emulate_tty=True,
        parameters=[{'use_sim_time': True, 
                     'robot_description': Command(['xacro ', robot_desc_path])}]
    )

    # RViz2 Launch Configuration (RViz) #
    rviz_node = Node(
        package='rviz2',
        executable='rviz2',
        name='rviz2_node',
        output='screen',
        emulate_tty=True,
        parameters=[{'use_sim_time': True}],
    )

    # Create and Return the Launch Description Object #
    return LaunchDescription(
        [
            robot_state_publisher_node,
            rviz_node,
        ]
    )

修改 setup.py 文件,确保 ROS2 Launch 系统能够检测并执行你在上面步骤中创建的 launch 文件。

将下面的内容复制到 setup.py 文件中,直接替换原有内容。

  setup.py

In [ ]:
import os
from glob import glob
from setuptools import setup

package_name = 'robot_description'

setup(
    name=package_name,
    version='0.0.0',
    packages=[package_name],
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
        (os.path.join('share', package_name, 'urdf'), glob('urdf/*.urdf')),
        (os.path.join('share', package_name, 'launch'), glob('launch/*.launch.*')),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='user',
    maintainer_email='user@todo.todo',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
        ],
    },
)

新旧文件之间真正不同的只有下面这几行:

# imports
import os
from glob import glob
...

# paths to urdf and launch files that must be copied to 'share' folder
(os.path.join('share', package_name, 'urdf'), glob('urdf/*.urdf')),
(os.path.join('share', package_name, 'launch'), glob('launch/*.launch.*')),

按照下面的命令来启动你刚刚创建的文件:

  在 Web Shell 1 中执行

In [ ]:
cd /workspace
In [ ]:
colcon build --packages-select robot_description && source install/setup.bash
In [ ]:
ros2 launch robot_description rviz.launch.py

启动该文件后,等待图形化工具打开。你应该会看到下图所示的结果。然后,在显示面板中添加 RobotModel,并将 /robot_description 设为描述主题。接着,把 RVIZ2 中的固定坐标系改为 link_chassis。这样就会把机器人的底盘连杆设为 RVIZ2 模型可视化的参考坐标系。把这个配置保存在你的工作区中,这样下次启动 RVIZ2 时,机器人模型就会被正确显示。

将当前的 RVIZ2 配置保存为 config.rviz,并放到你的 ROS2 robot_description 功能包中新建的 rviz 目录里。

"RViz URDF First Launch"

rviz 文件夹添加到 setup.py 中,这样下次编译时就能检测到 config.rviz

此时你的 setup.py 文件应该包含下面这三行:

(os.path.join('share', package_name, 'urdf'), glob('urdf/*.urdf')),
(os.path.join('share', package_name, 'rviz'), glob('rviz/*.rviz')),
(os.path.join('share', package_name, 'launch'), glob('launch/*.launch.*')),

最后,把 config.rviz 文件作为参数传给 launch 文件中的 RVIZ 节点。

# Load RViz Configuration File #
    rviz_config_file = "config.rviz"
    rviz_config_path = os.path.join(package_directory, "rviz", rviz_config_file)
    print("RViz Config Loaded !")

    # Rviz2 Launch Configuration (RViz)#
    rviz_node = Node(
        package='rviz2',
        executable='rviz2',
        name='rviz2_node',
        output='screen',
        emulate_tty=True,
        parameters=[{'use_sim_time': True}],
        arguments=['-d', rviz_config_path],
    )

现在你的 launch 文件应该如下所示:

  rviz.launch.py

In [ ]:
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.substitutions import Command
from launch_ros.actions import Node

# ROS2 Launch System will look for this function definition #
def generate_launch_description():

    # Get Package Description and Directory #
    package_description = "robot_description"
    package_directory = get_package_share_directory(package_description)

    # Load URDF File #
    urdf_file = 'robot.urdf'
    robot_desc_path = os.path.join(package_directory, "urdf", urdf_file)
    print("URDF Loaded !")

    # Robot State Publisher (RSP) #
    robot_state_publisher_node = Node(
        package='robot_state_publisher',
        executable='robot_state_publisher',
        name='robot_state_publisher_node',
        output="screen",
        emulate_tty=True,
        parameters=[{'use_sim_time': True, 
                     'robot_description': Command(['xacro ', robot_desc_path])}]
    )

    # Load RViz Configuration File #
    rviz_config_file = "config.rviz"
    rviz_config_path = os.path.join(package_directory, "rviz", rviz_config_file)
    print("RViz Config Loaded !")

    # Rviz2 Launch Configuration (RViz)#
    rviz_node = Node(
        package='rviz2',
        executable='rviz2',
        name='rviz2_node',
        output='screen',
        emulate_tty=True,
        parameters=[{'use_sim_time': True}],
        arguments=['-d', rviz_config_path],
    )

    # Create and Return the Launch Description Object #
    return LaunchDescription(
        [
            robot_state_publisher_node,
            rviz_node,
        ]
    )