首页 关于
树枝想去撕裂天空 / 却只戳了几个微小的窟窿 / 它透出天外的光亮 / 人们把它叫做月亮和星星
目录

召唤Turtlebot

我们已经在Gazebo的环境中与Turtlebot打了一个招呼, 并且讨论了它的描述模型。本文中将详细分析召唤Turtlebot的那条指令:

        $ roslaunch turtlebot_gazebo turtlebot_world.launch
这条指令中,我们通过工具roslaunch启动了turtlebot_gazebo包中的turtlebot_world.launch文件,那么我们到turtlebot_gazebo包的launch目录下看一下都有什么:
        $ roscd turtlebot_gazebo/launch
        $ ls
        amcl_demo.launch  gmapping_demo.launch  includes  turtlebot_world.launch
在这个目录下,有三个launch文件和一个includes的子目录。其中amcl_demo.launch是Turtlebot你好一文中自主导航的demo, gmapping_demo.launch用于地图建模。turtlebot_world.launch则是本文关注的对象,用于打开Gazebo并召唤Turtlebot。

1. launch文件

turtlebot_world.launch

在turtlebot_world.launch文件的一开始定义了各种参数。第一个参数world_file用于指示Gazebo的世界文件,默认参数是系统变量TURTLEBOT_GAZEBO_WORLD_FILE提供。 这个系统变量的值可以通过$ env | grep TURTLEBOT_WORLD_FILE来查询,在我这里是"/opt/ros/kinetic/share/turtlebot_gazebo/worlds/playground.world"。

        <launch>
            <arg name="world_file"  default="$(env TURTLEBOT_GAZEBO_WORLD_FILE)"/>
接着定义了一系列与Turtlebot相关的参数。在Turtlebot的系统中底盘、3D传感器等都是可以替换的。参数base指示了Turtlebot的底盘,battery指示了电池,stacks则是Turtlebot上各层的托盘, 3d_sensor则是其3D传感器。这些参数都使用optenv赋予了环境变量TURTLEBOT_×××的值,如果没有定义环境变量,则使用其后的值代替之,同样的我们可以通过env组合grep查询到这些系统变量。 在我这里都是有定义的,底盘用的是kobuki,托盘是hexagons,3D传感器则是kinect。电池我们先不关心它。
            <arg name="base"      value="$(optenv TURTLEBOT_BASE kobuki)"/> <!-- create, roomba -->
            <arg name="battery"   value="$(optenv TURTLEBOT_BATTERY /proc/acpi/battery/BAT0)"/>  <!-- /proc/acpi/battery/BAT0 -->
            <arg name="gui" default="true"/>
            <arg name="stacks"    value="$(optenv TURTLEBOT_STACKS hexagons)"/>  <!-- circles, hexagons -->
            <arg name="3d_sensor" value="$(optenv TURTLEBOT_3D_SENSOR kinect)"/>  <!-- kinect, asus_xtion_pro --> 
上面还有一个参数gui,它用于指引Gazebo是否打开一个图形界面,与Turtlebot无关。 接着使用通过ROS开启Gazebo的世界一文中描述的套路打开Gazebo世界,这个世界中只有一些障碍物,Turtlebot还没有被召唤出来。
            <include file="$(find gazebo_ros)/launch/empty_world.launch">
                <arg name="use_sim_time" value="true"/>
                <arg name="debug" value="false"/>
                <arg name="gui" value="$(arg gui)" />
                <arg name="world_name" value="$(arg world_file)"/>
            </include>
接下来turtlebot_world.launch引入了一个文件"$(arg base).launch.xml",并赋予了三个配置参数:底盘、托盘、3D传感器。 这里"$(arg base).launch.xml"实际是指kobuki.launch.xml,它的作用就是召唤Turtlebot。当然这个xml文件不仅仅是通过gazebo_ros的spawn_model加载Turtlebot的模型, 它还做了一些额外的工作,在本文后续内容中详述。
            <include file="$(find turtlebot_gazebo)/launch/includes/$(arg base).launch.xml">
                <arg name="base" value="$(arg base)"/>
                <arg name="stacks" value="$(arg stacks)"/>
                <arg name="3d_sensor" value="$(arg 3d_sensor)"/>
            </include>
在加载Turtlebot的时候开启了一个Gazebo的插件用于控制底盘,它会不断地发布一个主题为"/joint_states"的消息, 告知ROS系统机器人的运动状态。为了能够在rviz中同步看到机器人的运动, lauch文件还运行了一个tf广播器"robot_state_publisher",它订阅了"/joint_states"主题,计算各个坐标系之间的变换关系, 并将之广播出去,rviz将作为一个tf监听者接收坐标关系,并同步显示。
            <node pkg="robot_state_publisher" type="robot_state_publisher" name="robot_state_publisher">
                <param name="publish_frequency" type="double" value="30.0" />
            </node>
最后,加载了一个虚拟的激光传感器,它使用了ROS的nodelet技术,将深度图转换为激光扫描数据。nodelet技术是一种在同一个进程中使用共享指针的形式传递消息的方法。 这种方法的设计初衷是为了解决视频、深度图等数据量大实时性要求较高的数据传送问题。ROS原生的xml-rpc通信技术虽然能够方便的通过订阅者模式或者服务器模式传递数据, 但是不可避免地会对数据进行各种拷贝。数据量较小时不会对系统带来什么负担,但随着视觉等相关的传感器的引入,势必产生海量的数据,频繁地拷贝数据将严重降低系统效率。 所以nodelet期望在一个进程中为各种不同的算法之间提供一种零拷贝的通信机制,以提高系统效率。
            <!-- Fake laser -->
            <node pkg="nodelet" type="nodelet" name="laserscan_nodelet_manager" args="manager"/>
            <node pkg="nodelet" type="nodelet" name="depthimage_to_laserscan"
                args="load depthimage_to_laserscan/DepthImageToLaserScanNodelet laserscan_nodelet_manager">
                <param name="scan_height" value="10"/>
                <param name="output_frame_id" value="/camera_depth_frame"/>
                <param name="range_min" value="0.45"/>
                <remap from="image" to="/camera/depth/image_raw"/>
                <remap from="scan" to="/scan"/>
            </node>
        </launch>

kobuki.launch.xml

kobuki.launch.xml是turtlebot_gazebo/launch/includes子目录下的一个launch文件,在turtlebot_world.launch中通过include的形式加载。它的主要作用是在已经打开的Gazebo世界中召唤神龟。 首先定义了三个参数:

        <launch>
            <arg name="base"/>
            <arg name="stacks"/>
            <arg name="3d_sensor"/>
这三个参数分别描述了机器人的底盘、托盘和3D传感器。根据它们找到对应的urdf描述文件,首先通过Xacro加载为参数urdf_file, 然后将之赋予参数服务器的robot_description。这样在ROS系统中,就可以通过参数robot_description来获取机器人的模型了。
            <arg name="urdf_file" default="$(find xacro)/xacro.py '$(find turtlebot_description)/robots/$(arg base)_$(arg stacks)_$(arg 3d_sensor).urdf.xacro'" />
            <param name="robot_description" command="$(arg urdf_file)" />
通过gazebo_ros的spawn_model可以将机器人加载到Gazebo的世界中:
            <!-- Gazebo model spawner -->
            <node name="spawn_turtlebot_model" pkg="gazebo_ros" type="spawn_model"
                args="$(optenv ROBOT_INITIAL_POSE) -unpause -urdf -param robot_description -model mobile_base"/>
这里通过nodelet建立一个速度混合器(Velocity muxer),它是向Gazebo世界中的Turtlebot发送运动指令的媒介,将其输出cmd_vel_mux/output重映射为mobile_base/commands/velocity。
            <!-- Velocity muxer -->
            <node pkg="nodelet" type="nodelet" name="mobile_base_nodelet_manager" args="manager"/>
            <node pkg="nodelet" type="nodelet" name="cmd_vel_mux" args="load yocs_cmd_vel_mux/CmdVelMuxNodelet mobile_base_nodelet_manager">
                <param name="yaml_cfg_file" value="$(find turtlebot_bringup)/param/mux.yaml" />
                <remap from="cmd_vel_mux/output" to="mobile_base/commands/velocity"/>
            </node>
最后,以类似kobuki.launch.xml的套路导入了bumper2pc.launch.xml,看注释说是因为缺少传感器的数据,所以不能够工作。
            <!-- Bumper/cliff to pointcloud (not working, as it needs sensors/core messages) -->
            <include file="$(find turtlebot_bringup)/launch/includes/kobuki/bumper2pc.launch.xml"/>
        </launch>

bumper2pc.launch.xml

按照include的指引,我们找到了bumper2pc.launch.xml。其内容如下,只是装载了一个nodelet而已,没有其它内容。

        <launch>
            <node pkg="nodelet" type="nodelet" name="bumper2pointcloud" args="load kobuki_bumper2pc/Bumper2PcNodelet mobile_base_nodelet_manager">
                <param name="pointcloud_radius" value="0.24"/>
                <remap from="bumper2pointcloud/pointcloud"   to="mobile_base/sensors/bumper_pointcloud"/>
                <remap from="bumper2pointcloud/core_sensors" to="mobile_base/sensors/core"/>
            </node>
        </launch>

2. 系统图分析

运行上述turtlebot_world.launch文件之后,我们可以通过rosnode查看当前正在运行的节点。从打印出来的结果可以看出当前一共运行了9个节点,其中rosout是运行roscore的时候一起加载的节点。 而gazebo和gazebo_gui是Gazebo运行时的客户端和GUI界面,我们可以把它们两个当做是一个节点。

        $ rosnode list 
        /bumper2pointcloud
        /cmd_vel_mux
        /depthimage_to_laserscan
        /gazebo
        /gazebo_gui
        /laserscan_nodelet_manager
        /mobile_base_nodelet_manager
        /robot_state_publisher
        /rosout

运行如下的指令,就可以用ROS的可视化工具查看系统的各个节点之间的消息订阅关系,如下图所示。

        $ rqt_graph
从图中我们可以看到在turtlebot_world.launch文件加载的7个节点。

其中gazebo就是我们的Gazebo世界,它订阅了"/mobile_base/commands/velocity"的主题,主要用于指导Turtlebot在仿真环境中运动。同时它还发布了一个"/joint_states"的主题, 这个主题的消息描述了各个joint的运动状态,它由robot_state_publisher订阅。通过工具rostopic可以查看这两个主题的消息类型:

        $ rostopic list /mobile_base/commands/velocity -v

        Published topics:
         * /mobile_base/commands/velocity [geometry_msgs/Twist] 1 publisher
        
        Subscribed topics:
         * /mobile_base/commands/velocity [geometry_msgs/Twist] 1 subscriber
从rostopic的输出结果中,我们可以看到"/mobile_base/commands/velocity"的消息类型为[geometry_msgs/Twist],其消息格式可以通过rosmsg获得:
        $ rosmsg show geometry_msgs/Twist
        geometry_msgs/Vector3 linear
          float64 x
          float64 y
          float64 z
        geometry_msgs/Vector3 angular
          float64 x
          float64 y
          float64 z
类似的还可以查看/joints_states的消息类型和格式,不再赘述。robot_state_publisher是一个tf的发布器,它订阅"/joints_states"主题,并据此计算坐标变换。

其余5个节点都与nodelet有关,其中节点"laserscan_nodelet_manager"和"mobile_base_nodelet_manager"是两个nodelet管理器。 节点"depthimage_to_laserscan"绑定于"laserscan_nodelet_manager","cmd_vel_mux"和"bumper2pointcloud"绑定于"mobile_base_nodelet_manager"。

3. 完




Copyright @ 高乙超. All Rights Reserved. 京ICP备16033081号-1