如何在ROS 2中使用URDF创建仿真移动机器人(二)

八、添加依赖项

现在来添加一些我们项目要依赖的软件包。使用以下命令进入到 package.xml 文件中:

cd ~/dev_ws/src/basic_mobile_robot
gedit package.xml

在<buildtool_depend>标签之后添加以下几行:

<exec_depend>joint_state_publisher</exec_depend>
<exec_depend>robot_state_publisher</exec_depend>
<exec_depend>rviz</exec_depend>
<exec_depend>xacro</exec_depend>

保存并关闭该文件。

我们会用到Joint State Publisher和Robot State Publisher。此外还会用到RViz来可视化机器人模型。

九、创建启动文件

现在来创建启动文件。ROS 2会使用这个启动文件来加载该软件包所需的各个节点

colcon_cd basic_mobile_robot
cd launch
gedit basic_mobile_bot_v1.launch.py

将下面这段代码复制并粘贴到该文件中:

# Author: Addison Sears-Collins
# Date: August 27, 2021
# Description: Launch a basic mobile robot URDF file using Rviz.
# https://automaticaddison.com
 
import os
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.conditions import IfCondition, UnlessCondition
from launch.substitutions import Command, LaunchConfiguration
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare
 
def generate_launch_description():
 
  # Set the path to different files and folders.
  pkg_share = FindPackageShare(package='basic_mobile_robot').find('basic_mobile_robot')
  default_launch_dir = os.path.join(pkg_share, 'launch')
  default_model_path = os.path.join(pkg_share, 'models/basic_mobile_bot_v1.urdf')
  robot_name_in_urdf = 'basic_mobile_bot'
  default_rviz_config_path = os.path.join(pkg_share, 'rviz/urdf_config.rviz')
 
  # Launch configuration variables specific to simulation
  gui = LaunchConfiguration('gui')
  model = LaunchConfiguration('model')
  rviz_config_file = LaunchConfiguration('rviz_config_file')
  use_robot_state_pub = LaunchConfiguration('use_robot_state_pub')
  use_rviz = LaunchConfiguration('use_rviz')
  use_sim_time = LaunchConfiguration('use_sim_time')
 
  # Declare the launch arguments  
  declare_model_path_cmd = DeclareLaunchArgument(
    name='model', 
    default_value=default_model_path, 
    description='Absolute path to robot urdf file')
 
  declare_rviz_config_file_cmd = DeclareLaunchArgument(
    name='rviz_config_file',
    default_value=default_rviz_config_path,
    description='Full path to the RVIZ config file to use')
 
  declare_use_joint_state_publisher_cmd = DeclareLaunchArgument(
    name='gui',
    default_value='True',
    description='Flag to enable joint_state_publisher_gui')
 
  declare_use_robot_state_pub_cmd = DeclareLaunchArgument(
    name='use_robot_state_pub',
    default_value='True',
    description='Whether to start the robot state publisher')
 
  declare_use_rviz_cmd = DeclareLaunchArgument(
    name='use_rviz',
    default_value='True',
    description='Whether to start RVIZ')
 
  declare_use_sim_time_cmd = DeclareLaunchArgument(
    name='use_sim_time',
    default_value='True',
    description='Use simulation (Gazebo) clock if true')
 
  # Specify the actions
 
  # Publish the joint state values for the non-fixed joints in the URDF file.
  start_joint_state_publisher_cmd = Node(
    condition=UnlessCondition(gui),
    package='joint_state_publisher',
    executable='joint_state_publisher',
    name='joint_state_publisher')
 
  # A GUI to manipulate the joint state values
  start_joint_state_publisher_gui_node = Node(
    condition=IfCondition(gui),
    package='joint_state_publisher_gui',
    executable='joint_state_publisher_gui',
    name='joint_state_publisher_gui')
 
  # Subscribe to the joint states of the robot, and publish the 3D pose of each link.
  start_robot_state_publisher_cmd = Node(
    condition=IfCondition(use_robot_state_pub),
    package='robot_state_publisher',
    executable='robot_state_publisher',
    parameters=[{'use_sim_time': use_sim_time, 
    'robot_description': Command(['xacro ', model])}],
    arguments=[default_model_path])
 
  # Launch RViz
  start_rviz_cmd = Node(
    condition=IfCondition(use_rviz),
    package='rviz2',
    executable='rviz2',
    name='rviz2',
    output='screen',
    arguments=['-d', rviz_config_file])
 
  # Create the launch description and populate
  ld = LaunchDescription()
 
  # Declare the launch options
  ld.add_action(declare_model_path_cmd)
  ld.add_action(declare_rviz_config_file_cmd)
  ld.add_action(declare_use_joint_state_publisher_cmd)
  ld.add_action(declare_use_robot_state_pub_cmd)  
  ld.add_action(declare_use_rviz_cmd) 
  ld.add_action(declare_use_sim_time_cmd)
 
  # Add any actions
  ld.add_action(start_joint_state_publisher_cmd)
  ld.add_action(start_joint_state_publisher_gui_node)
  ld.add_action(start_robot_state_publisher_cmd)
  ld.add_action(start_rviz_cmd)
 
  return ld

保存并关闭该文件。

如果您从头到尾浏览该启动文件,您就会看到,我们首先设置了机器人正确启动所需的重要文件和目录的路径。

设置好文件和目录路径后,我们设置了LaunchConfiguration变量。您可以将其视为我们想要如何启动机器人仿真的各个选项。

例如,假设我们想使用ROS 2启动文件来启动机器人,但想要确保RViz(ROS 提供的机器人可视化工具)保持关闭状态。这样可以创建一个名为“use_rviz”的标志。当将“use_rviz”设置为True时,RViz会启动,而当将其设置为False时,RViz则不会启动。

为了能够从终端窗口(即在启动文件之外)设置此标志的值,必须做以下两件事:

· 将字符串 ‘use_rviz’ 转变为一个LaunchConfiguration变量,因为我们无法将True和False等布尔值分配给‘use_rviz’ 这样的字符串。通过使用LaunchConfiguration命令use_rviz == ‘use_rviz’来实现这点。

· 还需要为此标志设置默认值,以便让系统知道如果在启动该启动文件时没有在终端窗口中对此标志参数的值进行设置时该怎么办。DeclareLaunchArgument命令可以设置字符串“use_rviz”的默认值,在本示例启动文件中为True。因此,LaunchConfiguration变量use_rviz的默认值也为 True。

在设置并声明了这些启动参数后,接下来就是启动相关ROS 2节点(例如robot state publisher、RViz 等)。

十、添加RViz配置文件

现在来添加一个配置文件,该配置文件将会使用适当的设置初始化RViz,以便可以在RViz启动后立即浏览该机器人。

打开一个新的终端窗口,并键入以下命令:

colcon_cd basic_mobile_robot
cd rviz
gedit urdf_config.rviz

这段代码复制粘贴到该文件中,然后保存并关闭该文件。

十一、编译构建软件包

现在就可以编译构建该软件包了。打开一个新的终端窗口并输入以下命令:

cd ~/dev_ws/src/basic_mobile_robot/
gedit CMakeLists.txt

将下面这个代码段添加到CMakeLists.txt文件中的if(BUILD_TESTING)代码行前面:

install(
  DIRECTORY config launch maps meshes models params rviz src worlds
  DESTINATION share/${PROJECT_NAME}
)

保存并关闭该文件。

编译该软件包:

cd ~/dev_ws/
colcon build

如果在您的工作空间中还有其它软件包,而且您只想构建basic_mobile_robot软件包,则需要键入以下命令:

colcon build --packages-select basic_mobile_robot

十二、在RViz中启动机器人

在成功地编译构建好该软件包后,就可以启动该机器人模型了。打开一个新的终端窗口,并输入以下命令:

cd ~/dev_ws/
ros2 launch basic_mobile_robot basic_mobile_bot_v1.launch.py

在RViz中启动该机器人后的输出结果应该如下图所示:

关节状态发布者 GUI 具有让您可以移动车轮的滑块(下图)。当向右滑动滑块(正值)时车轮会逆时针转动;而当向左滑动滑块(负值)时车轮会顺时针转动。

如果启动出现问题,请关闭所有程序,重新启动Linux,然后再次启动。

检查您是否正确设置了碰撞属性,这可以通过启用RViz左侧面板中RobotModel下的“启用碰撞(Collision Enabled)”来完成。

您可以在上图中看到,已经将base_link建模为一个用于碰撞的方盒。可以简单地使用STL模型本身(用于可视化)作为碰撞几何体,但本示例中使用更简单的方盒表示,以便在必要时更灵活地更改碰撞区域。而且,Gazebo(一款流行的机器人仿真软件)建议使用更简单的碰撞几何体来提高性能。

顺便提一下,如果您想查看能从终端窗口传递给启动文件的可用参数,请输入以下命令:

ros2 launch -s basic_mobile_robot basic_mobile_bot_v1.launch.py

而如果你想设置一个参数的值(例如在不启动RViz的情况下启动机器人),则可以像下面这样做(这是单条的命令):

ros2 launch basic_mobile_robot basic_mobile_bot_v1.launch.py use_rviz:='False'

十三、查看坐标系

现在来查看该机器人的坐标系。首先需要安装必要的软件:

sudo apt install ros-foxy-tf2-tools

同样地,如果您的 ROS 2发行版不同,则需要将上面命令中的“foxy”替换为您的发行版名称。

使用以下命令来查看该机器人模型的坐标系(*注意此处英语原文中有坑,最后多了.py,会出错):

ros2 run tf2_tools view_frames

在当前工作目录中,将会生成一个名为frames.pdf的文件。使用以下命令来打开该文件:

evince frames.pdf

其结果应该是如下图所示的坐标变换(即 tf)树:

如果想查看从一个链接到另一个链接的坐标变换,则可以输入以下命令。例如,相对于机器人base_link,前脚轮的位置和方位是什么?

该命令的语法为:

ros2 run tf2_ros tf2_echo <parent frame> <child frame>

打开一个新的终端窗口,并输入以下命令:

ros2 run tf2_ros tf2_echo base_link front_caster

输出结果如下图所示。相对于base_link参考坐标系,前脚轮位于 (x=0.217 米,y=0 米,z=-0.1 米)的位置。

旋转角度值采用四元数格式。可以看到旋转角度值都是0,说明两个坐标系的方位是等价的(即两个坐标系的x、y、z轴指向同一个方向)。

请记住,按照惯例:

· x 轴为红色

· y 轴为绿色

· z 轴为蓝色

要查看该ROS系统的架构图,请打开一个新的终端窗口,并键入以下命令:

rqt_graph

选择屏幕左上角的“节点/话题(全部)”(Nodes/Topics(all))选项,然后单击左侧的刷新箭头。

从下图中您可以看出关节状态发布者 GUI 是如何操纵关节状态的(即每个车轮关节的旋转角度)。关节状态数据馈入机器人状态发布者节点。机器人状态发布者节点将各个坐标系的关系发布到tf话题上,且更新后的机器人模型馈入机器人模型可视化程序RViz中。

本教程到此结束。

完成所有任务后,请在所有终端窗口中按 CTRL+C 组合键关闭所有程序。

*英语原文地址:automaticaddison.com/ho