Skip to content

Wiki

机械臂物理仿真

About 5 min read

Existing English translations were reused. 32 current source blocks remain in Simplified Chinese, without triggering paid translation.

本节使用 Gazebo Fortress 与 gz_ros2_control 0.7.21。前面的接口与控制器继续使用,反馈改由物理仿真提供。

1.环境与版本约定

先完成环境准备与源码地图的 Humble 安装与 ROS 2 软件源配置。下面安装 Fortress 仿真器及 ROS 集成包;已经安装对应组件时,可以直接检查版本与包路径。

1.1.安装 Fortress 仿真器

按 Fortress 官方 Ubuntu 安装说明 配置 OSRF 软件源。以下命令用于 Ubuntu 22.04(jammy);若系统已配置同一软件源,无需重复添加。

sudo apt-get update
sudo apt-get install -y curl ca-certificates gnupg lsb-release
curl -fsSL https://packages.osrfoundation.org/gazebo.gpg -o /tmp/gazebo-key.gpg
sudo install -m 644 /tmp/gazebo-key.gpg /usr/share/keyrings/pkgs-osrf-archive-keyring.gpg
printf 'deb [arch=%s signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] https://packages.osrfoundation.org/gazebo/ubuntu-stable jammy main\n' \
  "$(dpkg --print-architecture)" | sudo tee /etc/apt/sources.list.d/gazebo-stable.list > /dev/null
sudo apt-get update
sudo apt-get install -y ignition-fortress

1.2.安装 ROS 集成包并检查

sudo apt-get install -y ros-humble-ros-gz ros-humble-gz-ros2-control
source /opt/ros/humble/setup.bash
ign gazebo --versions
ros2 pkg prefix ros_gz_sim
ros2 pkg prefix ros_gz_bridge
ros2 pkg prefix gz_ros2_control

ign gazebo --versions 应能找到 Fortress 对应的 Gazebo Sim 6,三个 ROS 包都应返回有效安装路径。插件安装方式参考 Humble gz_ros2_control 文档。

本章不使用 Gazebo Classic 的 libgazebo_ros2_control.so。Fortress 环境中启动命令仍可能是 ign gazebo;ROS 包名采用 ros_gz_sim、gz_ros2_control,按实际安装接口运行。

在 package.xml 中补充以下运行依赖,放在 export 之前:

<exec_depend>gz_ros2_control</exec_depend>
<exec_depend>ros_gz_sim</exec_depend>
<exec_depend>ros_gz_bridge</exec_depend>

2.示例 1:编写完整仿真模型

在自己的包中创建 urdf/mini_gz.urdf.xacro:

<?xml version="1.0"?>
<robot name="mini" xmlns:xacro="http://www.ros.org/wiki/xacro">

  <link name="world"/>
  <link name="base_link">
    <inertial><mass value="1"/><inertia ixx="0.005" ixy="0" ixz="0" iyy="0.005" iyz="0" izz="0.007"/></inertial>
    <visual><geometry><box size="0.2 0.2 0.1"/></geometry></visual>
  </link>
  <joint name="world_fixed" type="fixed"><parent link="world"/><child link="base_link"/></joint>

  <link name="link1">
    <inertial><origin xyz="0 0 0.2"/><mass value="1"/>
      <inertia ixx="0.0135" ixy="0" ixz="0" iyy="0.0135" iyz="0" izz="0.00042"/>
    </inertial>
    <visual><origin xyz="0 0 0.2"/><geometry><box size="0.05 0.05 0.4"/></geometry></visual>
    <collision><origin xyz="0 0 0.2"/><geometry><box size="0.05 0.05 0.4"/></geometry></collision>
  </link>
  <joint name="joint1" type="revolute">
    <parent link="base_link"/><child link="link1"/>
    <origin xyz="0 0 0.05"/><axis xyz="0 1 0"/>
    <limit lower="-1" upper="1" effort="20" velocity="2"/>
  </joint>

  <link name="link2">
    <inertial><origin xyz="0 0 0.2"/><mass value="1"/>
      <inertia ixx="0.0135" ixy="0" ixz="0" iyy="0.0135" iyz="0" izz="0.00042"/>
    </inertial>
    <visual><origin xyz="0 0 0.2"/><geometry><box size="0.05 0.05 0.4"/></geometry></visual>
    <collision><origin xyz="0 0 0.2"/><geometry><box size="0.05 0.05 0.4"/></geometry></collision>
  </link>
  <joint name="joint2" type="revolute">
    <parent link="link1"/><child link="link2"/>
    <origin xyz="0 0 0.4"/><axis xyz="0 1 0"/>
    <limit lower="-1" upper="1" effort="20" velocity="2"/>
  </joint>

  <ros2_control name="MiniSystem" type="system">
    <hardware><plugin>gz_ros2_control/GazeboSimSystem</plugin></hardware>
    <joint name="joint1">
      <command_interface name="position"/>
      <state_interface name="position"><param name="initial_value">0.0</param></state_interface>
      <state_interface name="velocity"/>
    </joint>
    <joint name="joint2">
      <command_interface name="position"/>
      <state_interface name="position"><param name="initial_value">0.0</param></state_interface>
      <state_interface name="velocity"/>
    </joint>
  </ros2_control>

  <gazebo>
    <plugin filename="gz_ros2_control-system" name="gz_ros2_control::GazeboSimROS2ControlPlugin">
      <parameters>$(find learn_control_hardware)/config/controllers.yaml</parameters>
    </plugin>
  </gazebo>
</robot>

2.1.两种插件分别负责什么

硬件标签中的 gz_ros2_control/GazeboSimSystem 连接关节状态与命令接口;gazebo 标签中的系统插件负责把控制框架接入仿真过程,并按 parameters 路径读取 YAML。

仿真插件会在仿真进程中创建管理器,因此本章不另外启动 mini.launch.py 中的独立 ros2_control_node。同一机器人启动两个管理器会造成资源与名称冲突。

2.2.模型为什么需要惯量

物理引擎需要质量与惯量;只有 visual 的 link 可能在模型转换时被丢弃。例如,base_link 缺少有效惯量时,模型转换可能导致其子关节找不到父坐标系。应补齐有效的质量与惯量,并重新检查模型转换日志。即使 RViz 能显示模型,也不能保证它已经是有效的物理模型。

3.示例 2:编写仿真启动文件

保存为 launch/sim.launch.py:

from pathlib import Path
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import Command, LaunchConfiguration, PythonExpression
from launch_ros.actions import Node
from launch_ros.parameter_descriptions import ParameterValue


def generate_launch_description():
    share = Path(get_package_share_directory('learn_control_hardware'))
    gui = LaunchConfiguration('gui')
    model = LaunchConfiguration('model')
    controller = LaunchConfiguration('controller')
    description = ParameterValue(Command(['xacro ', model]), value_type=str)
    args = PythonExpression(["'-r -v 2 empty.sdf' if '", gui,
                             "' == 'true' else '-s -r -v 2 empty.sdf'"])
    return LaunchDescription([
        DeclareLaunchArgument('gui', default_value='false'),
        DeclareLaunchArgument('model', default_value=str(share / 'urdf' / 'mini_gz.urdf.xacro')),
        DeclareLaunchArgument('controller', default_value='forward_position_controller'),
        IncludeLaunchDescription(PythonLaunchDescriptionSource(
            str(Path(get_package_share_directory('ros_gz_sim')) / 'launch' / 'gz_sim.launch.py')),
            launch_arguments={'gz_args': args}.items()),
        Node(package='ros_gz_bridge', executable='parameter_bridge',
             arguments=['/clock@rosgraph_msgs/msg/Clock[gz.msgs.Clock'], output='screen'),
        Node(package='robot_state_publisher', executable='robot_state_publisher',
             parameters=[{'robot_description': description, 'use_sim_time': True}]),
        Node(package='ros_gz_sim', executable='create',
             arguments=['-topic', '/robot_description', '-name', 'learning_robot', '-z', '0.02']),
        Node(package='controller_manager', executable='spawner',
             arguments=['joint_state_broadcaster', '-c', '/controller_manager',
                        '--controller-manager-timeout', '60']),
        Node(package='controller_manager', executable='spawner',
             arguments=[controller, '-c', '/controller_manager',
                        '--controller-manager-timeout', '60']),
    ])

3.1.完整启动过程

启动 Fortress world
  → robot_state_publisher 发布展开的机器人描述
  → ros_gz_sim/create 请求生成机器人
  → 模型中的系统插件加载控制管理器
  → spawner 等待服务并激活控制器
  → /clock 桥接到 ROS,使用仿真时间

create 返回请求成功,还应继续检查模型是否真正生成、插件是否加载、控制器是否 active。不能只凭一个 OK 判断整条链路成功。

4.编译运行

停止前面的独立 mini 实例:

cd ~/ros2_control_humble_ws
colcon build --symlink-install --packages-select learn_control_hardware
source install/setup.bash
ros2 launch learn_control_hardware sim.launch.py gui:=false

上面的 gui:=false 只运行仿真服务,适合先检查后台链路。在 Ubuntu 图形桌面中需要 Gazebo 窗口时,将启动参数改为 gui:=true。两种方式使用相同的模型和控制器;启动前先停止上一套实例。

另一个终端:

source /opt/ros/humble/setup.bash
source ~/ros2_control_humble_ws/install/setup.bash
ros2 control list_controllers
ros2 topic echo /clock --once
ros2 topic pub --once /forward_position_controller/commands \
  std_msgs/msg/Float64MultiArray "{data: [0.3, -0.2]}"
ros2 topic echo /joint_states

4.1.运行结果与检查方法

两个关节的反馈应逐渐接近 0.3、-0.2 rad。检查控制器是否 active、/clock 是否前进,以及 /joint_states 中的名称和数值是否对应。使用 GUI 时,还应观察模型关节是否按目标运动。

position 接口在仿真后端中具有自己的执行方式。即使模型位于物理引擎中,也不能把默认位置执行器响应直接当成真实电机的性能。

5.对应底层源码

阅读 gz_ros2_control/gz_ros2_control/src/gz_ros2_control_plugin.cpp、gz_ros2_control/gz_ros2_control/src/gz_system.cpp,寻找仿真更新回调、管理器调用和关节数据访问。再与独立 ros2_control_node 对照。

参考:Humble gz_ros2_control。


返回 Humble 总览 · 上一篇:Joint-Trajectory-Controller · 下一篇:DiffBot与差速底盘控制