ROS仿真揭秘——七自由度机器人(二)


这一篇我们来了解一下组成七自由度机器人的文件系统,并对launch文件做一个解读。

0 前言

如果你对ROS的基础知识有所欠缺,可以参考:

如果你不清楚七自由度机器人机器人仿真的运行方式,可以参考:

1 组成模型的文件

关于工作空间建立好后有的文件,因为都是统一的格式,这里不予过多介绍。我们关注的是,每一个仿真模型的文件组成。依据上一篇的介绍,我们在下载模型时从github上下载了两个文件,一个是机器人3D模型,另一个是机器人控制器。

1.1 3D模型的文件组成

3D模型mastering_ros_robot_description_pkg的组成如下图所示。
01

1.2 控制器的文件组成

七自由度机器人控制器seven_dof_arm_gazebo的组成如下图所示。
02

2 launch文件解读

从上面的两组文件以及在上一篇第3节运行程序的过程中,可以看出,二者从文件组成方面似乎没有一个直观的交集。莫急,我们顺着它程序运行的流程做一个探究。从上一篇博客中,我们知道通过两条roslaunch的命令分别启动了Rviz和Gazebo,我们就从这里开始研究吧!

2.1 launch文件的使用方法

launch文件的使用格式是:

1
$ roslaunch package_name launch_file_name

launch文件是XML文件,所有内容都包含在<launch>....</launch>这一对标签之内。对于七自由度机器人,首先是只在Rviz下显示的程序,我们在上节直接通过执行roslaunch mastering_ros_robot_description_pkg view_arm.launch这一条指令将程序跑了起来。从这条指令中可以看到,它运行的是mastering_ros_robot_descripion_pkg中的launch/view_arm.launch文件。其次,在上节通过roslaunch seven_dof_arm_gazebo seven_dof_arm_gazebo_control.launch这一条指令在Gazebo中将模型显示了出来。这条指令运行的是seven_dof_arm_gazebo中的launch/seven_dof_arm_gazebo_control.launch文件。

说明:对于上一篇中将机器人运动的那条指令在这里先不介绍,后面到控制机器人运动的时候再做介绍。

2.2 view_arm.launch文件解读

2.2.1 源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<launch>
<arg name="model" />
<!-- Parsing xacro and setting robot_description parameter -->

<param name="robot_description" command="$(find xacro)/xacro --inorder $(find mastering_ros_robot_description_pkg)/urdf/seven_dof_arm.xacro" />


<!-- Setting gui parameter to true for display joint slider -->
<param name="use_gui" value="true"/>

<!-- Starting Joint state publisher node which will publish the joint values -->
<node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" />

<!-- Starting robot state publish which will publish tf -->
<node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher" />

<!-- Launch visualization in rviz -->
<node name="rviz" pkg="rviz" type="rviz" args="-d $(find mastering_ros_robot_description_pkg)/urdf.rviz" required="true" />

</launch>

2.2.2 view_arm.launch文件释义

2.2.2.1 arg标签

argument类似于launch文件内部的局部变量,仅限于launch文件使用,便于launch文件的重构,和ROS节点内部的实现没有关系。

设置argument使用<arg>标签元素,语法如下:

1
<arg name=”arg-name” default=”arg-value”/>

name是参数的名称,还可以在后面添加参数value或者default。value是参数的值,default设定预设值,命令行方式可以覆盖default的值,但是不能覆盖value。

launch文件中需要使用到argument时,可以使用如下方式调用:

1
2
<paramname="foo" value="$(argarg-name)" />
<node name="node" pkg="package" type="type "args="$(arg arg-name)" />

回到view_arm.launch文件中:

1
<arg name="model" />

这里定义了一个名为model的参数。

2.2.2.2 param标签

parameter是ROS系统运行中的参数,存储在参数服务器中。在launch文件中通过<param>元素加载parameter;launch文件执行后,parameter就加载到ROS的参数服务器上了。每个活跃的节点都可以通过 ros::param::get()接口来获取parameter的值,用户也可以在终端中通过rosparam命令获得parameter的值。

<param>的使用方法如下:

1
<param name="output_frame" value="odom"/>

运行launch文件后,output_frame这个parameter的值就设置为odom,并且加载到ROS参数服务器上了。但是在很多复杂的系统中,参数的数量很多,如果这样一个一个的设置会非常麻烦,ROS也为我们提供了另外一种类似的参数加载方式——<rosparam>
1
<rosparamfile="$(find 2dnav_pr2)/config/costmap_common_params.yaml" command="load" ns="local_costmap" />

<rosparam>可以帮助我们将一个yaml格式文件中的参数全部加载到ROS参数服务器中,需要设置command属性为“load”,还可以选择设置命名空间“ns”。

回到view_arm.launch文件中:

1
2
3
<param name="robot_description" command="$(find xacro)/xacro --inorder $(find mastering_ros_robot_description_pkg)/urdf/seven_dof_arm.xacro" />

<param name="use_gui" value="true"/>

依据上述原理,我们知道第一句话的意思是robot_description这个参数,会加载在mastering_ros_robot_description中的urdf/seven_dof_arm_.xacro文件,该文件即为七自由度机器人的模型描述文件。第二句话的意思是设置一个GUI参数,从效果上来说就是会出现上一篇博客3.1节中的GUI界面。

2.2.2.3 node标签

启动文件的核心是启动ROS节点,采用<node>标签定义,语法如下:

1
<node pkg="package-name"type="executable-name" name="node-name" />

从上边的定义规则可以看出,在启动文件中启动一个节点需要三个属性:pkg、type和name。其中pkg定义节点所在的功能包名称,type定义节点的可执行文件名称,这两个属性等同于在终端中使用rosrun命令执行节点时的输入参数。name属性用来定义节点运行的名称,将覆盖节点中init()赋予节点的名称。这是三个最常用的属性,在某些情况下,我们还有可能用到以下属性:

  • output = “screen”:将节点的标准输出打印到终端屏幕,默认输出为日志文档;
  • respawn = “true”:复位属性,该节点停止时,会自动重启,默认为false;
  • required = “true”:必要节点,当该节点终止时,launch文件中的其他节点也被终止;
  • ns = “namespace”:命名空间,为节点内的相对名称添加命名空间前缀;
  • args = “arguments”:节点需要的输入参数。

view_arm.launch文件中:

1
2
3
4
5
<node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" />

<node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher" />

<node name="rviz" pkg="rviz" type="rviz" args="-d $(find mastering_ros_robot_description_pkg)/urdf.rviz" required="true" />

最后的这三个node:第一个node是joint_state_publisher,它和GUI关联在一起;第二个是robot_state_publisher;第三个是rviz,它来启动Rviz,并且输入urdf.rviz这个参数,以使模型正常显示。

在一个新终端中输入

1
rosrun rqt_graph rqt_graph

可以看到:
05

2.3 seven_dof_arm_gazebo_control.launch文件解读

2.3.1 源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" ?>
<launch>
<!-- Launch Gazebo -->
<include file="$(find seven_dof_arm_gazebo)/launch/seven_dof_arm_world.launch" />

<!-- Load joint controller configurations from YAML file to parameter server -->
<rosparam file="$(find seven_dof_arm_gazebo)/config/seven_dof_arm_gazebo_control.yaml" command="load"/>

<!-- load the controllers -->
<node name="controller_spawner" pkg="controller_manager" type="spawner" respawn="false"
output="screen" ns="/seven_dof_arm" args="joint_state_controller
joint1_position_controller
joint2_position_controller
joint3_position_controller
joint4_position_controller
joint5_position_controller
joint6_position_controller
joint7_position_controller"/>


<!-- convert joint states to TF transforms for rviz, etc -->
<node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher"
respawn="false" output="screen">
<remap from="/joint_states" to="/seven_dof_arm/joint_states" />
</node>

</launch>

2.3.2 解读

2.3.2.1 include标签

复杂系统有很多launch文件,它们之间存在依赖关系,可以复用已经有的launch文件。
大多数时候,include 使用find命令来搜索程序包,代替一个明确的完整路径:

1
<include file="$(find package-name)/launch-file-name" />

include 也支持 ns 属性,可以让这个文件里的内容推送到一个命名空间里面:
1
<include file="..." ns="namespace" />

example:

1
2
3
4
<include file="$(find realsense2_camera)/launch/rs_camera.launch">
<arg name="align_depth" value="true"/>
<arg name="unite_imu_method" value="linear_interpolation"/>
</include>

seven_dof_arm_gazebo_control.launch文件中:

1
<include file="$(find seven_dof_arm_gazebo)/launch/seven_dof_arm_world.launch" />

通过加载seven_dof_arm_world.launch文件,启动了Gazebo。

2.3.2.2 控制器节点

中间的两句:

1
2
3
4
5
6
7
8
9
10
11
 <rosparam file="$(find seven_dof_arm_gazebo)/config/seven_dof_arm_gazebo_control.yaml" command="load"/>

<node name="controller_spawner" pkg="controller_manager" type="spawner" respawn="false"
output="screen" ns="/seven_dof_arm" args="joint_state_controller
joint1_position_controller
joint2_position_controller
joint3_position_controller
joint4_position_controller
joint5_position_controller
joint6_position_controller
joint7_position_controller"/>

第一句加载了seven_dof_arm_gazebo_control.yaml文件,该文件包含了七自由度机器人控制器的一些配置,我们之后再做介绍。第二句是控制器的节点,对应的pkg是controller_manager,type是spawner,还包含了joint_state_controller与7个关节对应的位置控制器,这些名字是怎么来的,我们以后再讲。

2.3.2.3 remap标签

ROS的设计目标是提高代码的复用率,所以ROS社区中的很多功能包我们都可以拿来直接使用,而不需要关注功能包的内部实现。那么问题就来了,别人功能包的接口不一定和我们的系统兼容呀?

ROS提供一种重映射的机制,简单来说就是取别名,类似于C++中的别名机制,我们不需要修改别人功能包的接口,只需要将接口名称重映射一下,取个别名,我们的系统就认识了(接口的数据类型必须相同)。launch文件中的<remap>标签可以帮我们实现这个重映射的功能。

seven_dof_arm_gazebo_control.launch文件中的最后一个节点:

1
2
3
4
 <node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher"
respawn="false" output="screen">
<remap from="/joint_states" to="/seven_dof_arm/joint_states" />
</node>

上述robot_state_publisher节点中,将ROS自带的joint_states映射到了/seven_dof_arm/joint_states。
06

2.4 seven_dof_arm_world.launch解读

2.4.1 源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<launch>

<!-- Part 1: -->
<!-- these are the arguments you can pass this launch file, for example paused:=true -->
<arg name="paused" default="false"/>
<arg name="use_sim_time" default="true"/>
<arg name="gui" default="true"/>
<arg name="headless" default="false"/>
<arg name="debug" default="false"/>

<!-- Part 2: -->
<!-- We resume the logic in empty_world.launch -->
<include file="$(find gazebo_ros)/launch/empty_world.launch">
<arg name="debug" value="$(arg debug)" />
<arg name="gui" value="$(arg gui)" />
<arg name="paused" value="$(arg paused)"/>
<arg name="use_sim_time" value="$(arg use_sim_time)"/>
<arg name="headless" value="$(arg headless)"/>
</include>

<!-- Part 3: -->
<!-- Load the URDF into the ROS Parameter Server -->
<param name="robot_description" command="$(find xacro)/xacro --inorder '$(find mastering_ros_robot_description_pkg)/urdf/seven_dof_arm.xacro'" />

<!-- Part 4: -->
<!-- Run a python script to the send a service call to gazebo_ros to spawn a URDF robot -->
<node name="urdf_spawner" pkg="gazebo_ros" type="spawn_model" respawn="false" output="screen"
args="-urdf -model seven_dof_arm -param robot_description"/>

</launch>

2.4.2 解读

从源码中可以看出,可以将该文件分为四块来解读。Part1是一部分针对仿真环境的默认参数设置;Part2是启动Gazebo的world模型及相关参数设置;Part3是设置一个名为robot_description的参数,对应的正是mastering_ros_robot_description_pkg/urdf/seven_dof_arm.xacro文件,因此这里将mastering_ros_robot_description_pkgseven_dof_arm_gazebo两个ROS Package联系了起来;Part4则是通过节点urdf_spawner加载Part3中的参数robot_description。

2.5 launch文件的其他语法格式

因为我们主要是依据七自由度机器人来介绍,所以这里只介绍了目前用到launch文件中的已有标签,但是launch文件的语法格式除了这些外,还有其他的标签,比如remap、group等标签,请自行查阅相关的参考链接。

3 七自由度机器人文件流程图

在第2节我们从roslaunch命令入手,一窥七自由度机器人的第一步运行过程。我将两个ROS Package的运行过程分别做了一个简单的流程图,如下所示:

03
04

参考链接

  1. ROS Launch 文件语法和例子讲解
  2. ROS探索总结(五十六)—— launch文件
  3. joint_state_publisher
  4. robot_state_publisher
  5. controller_manager
------ 本文结束感谢您的阅读------
Donate a cup of cola?