• ROS入门(六)——从RRBot到自建小车控制


    ROS入门(六)——从RRBot到自建小车控制
      iwehdio的博客园:https://www.cnblogs.com/iwehdio/
    参考教程:https://www.generationrobots.com/blog/en/robotic-simulation-scenarios-with-gazebo-and-ros/

    1、RRBot的文件结构

    • RRbot 在工作空间下主要有三个部分:

      /rrbot_description
      	/launch		rrbot_rviz.launch; rrbot.rviz
      	/meshes		hokuyo.dae
      	/urdf		rrbot.gazebo; rrbot.xacro; materials.xacro
      /rrbot_gazebo
      	/worlds		rrbot.world
      	/launchr	rrbot_world.launch
      /rrbot_control
      	/config		rrbot_conrtol.yaml
      	/launch		rrbot.control; rrbot_rqt.launch
      
    • 回顾一下 launch 文件的相关命令:

      • 启动 launch 文件:roslaunch 功能包名 launch文件名
      • 节点:<node>。pkg:节点所在的功能包名称。type:节点的可执行文件名称。name:节点运行时的名称。output 是否输出日志、respawn 意外关闭是否重启、required 是否必须启动、ns 命名空间、args 输入的参数。
      • 存储 ROS 全局参数:<param>。name:参数名。value:参数值。
      • 从文件加载参数:<rosparam file=filename command="load" ns="param">
      • 内部参数:<arg name="arg-name" default="arg-value">
        • 被调用时形式为 "$(arg arg-name)"
      • 重映射:<remap>。from:原命名。to:映射之后的命名。
      • 嵌套:<include>。file:包含其他 launch 文件路径,类似头文件。

    2、rrbot中各个包的分析

    (1)、rrbot_description包

    • rrbot_rviz.launch 文件:

      <launch>
        <param name="robot_description"
          command="$(find xacro)/xacro --inorder '$(find rrbot_description)/urdf/rrbot.xacro'" />
      
        <!-- send fake joint values -->
        <node name="joint_state_publisher_gui" pkg="joint_state_publisher_gui" type="joint_state_publisher_gui">
          <param name="use_gui" value="TRUE"/>
        </node>
      
        <!-- Combine joint values -->
        <node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher"/>
      
        <!-- Show in Rviz   -->
        <node name="rviz" pkg="rviz" type="rviz" args="-d $(find rrbot_description)/launch/rrbot.rviz"/>
      
      </launch>
      
      • 第一个节点启动了关节状态发布者的GUI界面,也就是启动 Rviz 时我们能看到的手动调节关节状态的滑块。

      • 第二个节点启动了机器人的状态发布者,发布关节的实时状态。通过指令rostopic echo /joint_states可以看到发布的数据:

      • 第三个节点启动 Rviz。

    • rrbot.xacro 文件:

      <?xml version="1.0"?>
      <!-- Revolute-Revolute Manipulator -->
      <robot name="rrbot" xmlns:xacro="http://www.ros.org/wiki/xacro">
      
        <!-- Constants for robot dimensions -->
        <xacro:property name="PI" value="3.1415926535897931"/>
        <xacro:property name="mass" value="1" /> <!-- arbitrary value for mass -->
        <xacro:property name="width" value="0.1" /> <!-- Square dimensions (widthxwidth) of beams -->
        <xacro:property name="height1" value="2" /> <!-- Link 1 -->
        <xacro:property name="height2" value="1" /> <!-- Link 2 -->
        <xacro:property name="height3" value="1" /> <!-- Link 3 -->
        <xacro:property name="camera_link" value="0.05" /> <!-- Size of square 'camera' box -->
        <xacro:property name="axel_offset" value="0.05" /> <!-- Space btw top of beam and the each joint -->
      
        <!-- Import all Gazebo-customization elements, including Gazebo colors -->
        <xacro:include filename="$(find rrbot_description)/urdf/rrbot.gazebo" />
        <!-- Import Rviz colors -->
        <xacro:include filename="$(find rrbot_description)/urdf/materials.xacro" />
      
      • 这一部分引入了文件中的变量(类似于宏定义),并且关联了rrbot.gazebo 和 materials.xacro。
      <!-- Used for fixing robot to Gazebo 'base_link' -->
      <link name="world"/>
      
      <joint name="fixed" type="fixed">
          <parent link="world"/>
          <child link="link1"/>
      </joint>
      
      <!-- Base Link -->
      <link name="link1">
          <collision>
              <origin xyz="0 0 ${height1/2}" rpy="0 0 0"/>
              <geometry>
                  <box size="${width} ${width} ${height1}"/>
              </geometry>
          </collision>
      
          <visual>
              <origin xyz="0 0 ${height1/2}" rpy="0 0 0"/>
              <geometry>
                  <box size="${width} ${width} ${height1}"/>
              </geometry>
              <material name="orange"/>
          </visual>
      
          <inertial>
              <origin xyz="0 0 ${height1/2}" rpy="0 0 0"/>
              <mass value="${mass}"/>
              <inertia
                       ixx="${mass / 12.0 * (width*width + height1*height1)}" ixy="0.0" ixz="0.0"
                       iyy="${mass / 12.0 * (height1*height1 + width*width)}" iyz="0.0"
                       izz="${mass / 12.0 * (width*width + width*width)}"/>
          </inertial>
      </link>
      
      <joint name="joint1" type="continuous">
          <parent link="link1"/>
          <child link="link2"/>
          <origin xyz="0 ${width} ${height1 - axel_offset}" rpy="0 0 0"/>
          <axis xyz="0 1 0"/>
          <dynamics damping="0.7"/>
      </joint>
      
      • 这一部分先是声明了世界是一个 link ,而且与 RRBot 最下边的一个连杆是固连的(与世界固连的 模型需要做如此声明)。
      • 然后声明了最下方的连杆 link1。<collision>标签是碰撞属性,<visual>标签是视觉属性,一般而言二者相同。其中的 origin 表示质心,geometry 指定了几何形状和尺寸。<inertila>标签是惯性属性,mass 表示质量,inertia 表示转动惯量。
      • 然后声明了下方的关节 joint1。type 表示关节的连接类型。<parent><child>指定连接关系中的父与子(连接时子运动父不动)。origin 表示关节的连接位置。axis 表示关节的链接方向是 x、y、z。dynamics 表示阻尼。
      • 中间和上方的连杆也完全相同。
      <!-- Hokuyo Laser -->
      <link name="hokuyo_link">
          <collision>
              <origin xyz="0 0 0" rpy="0 0 0"/>
              <geometry>
                  <box size="0.1 0.1 0.1"/>
              </geometry>
          </collision>
      
          <visual>
              <origin xyz="0 0 0" rpy="0 0 0"/>
              <geometry>
                  <mesh filename="package://rrbot_description/meshes/hokuyo.dae"/>
              </geometry>
          </visual>
      
          <inertial>
              <mass value="1e-5" />
              <origin xyz="0 0 0" rpy="0 0 0"/>
              <inertia ixx="1e-6" ixy="0" ixz="0" iyy="1e-6" iyz="0" izz="1e-6" />
          </inertial>
      </link>
      
      <joint name="camera_joint" type="fixed">
          <axis xyz="0 1 0" />
          <origin xyz="${camera_link} 0 ${height3 - axel_offset*2}" rpy="0 0 0"/>
          <parent link="link3"/>
          <child link="camera_link"/>
      </joint>
      
      • 这一部分是对激光雷达的模型定义。其实与普通的 link 并没有区别,只不过激光雷达导的是创建好的 hokuyo.dae 模型。
      • 相机的模型定义类似,在该文件中并没有体现出传感器的特殊之处。
      <transmission name="tran1">
          <type>transmission_interface/SimpleTransmission</type>
          <joint name="joint1">
              <hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
          </joint>
          <actuator name="motor1">
              <hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
              <mechanicalReduction>1</mechanicalReduction>
          </actuator>
      </transmission>
      
      <transmission name="tran2">
          <type>transmission_interface/SimpleTransmission</type>
          <joint name="joint2">
              <hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
          </joint>
          <actuator name="motor2">
              <hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
              <mechanicalReduction>1</mechanicalReduction>
          </actuator>
      </transmission>
      
      • 这部分是关于传动的声明,每个<transmission>中定义了一个主动传动。<type>中指定了传动类型,<joint>中指定了所要传动的关节和硬件层接口。<actuator>定义了传动的执行器,mechanicalReduction 是减速比。
    • rrbot.gazebo 文件:

      <!-- ros_control plugin -->
      <gazebo>
          <plugin name="gazebo_ros_control" filename="libgazebo_ros_control.so">
              <robotNamespace>/rrbot</robotNamespace>
              <robotSimType>gazebo_ros_control/DefaultRobotHWSim</robotSimType>
          </plugin>
      </gazebo>
      
      • 这一部分指定了 ros_control 插件。通过 gazebo 标签内包含<plugin>标签,指定产假名、机器人的名字空间和机器人模型类型。
      <!-- Link3 -->
      <gazebo reference="link3">
          <mu1>0.2</mu1>
          <mu2>0.2</mu2>
          <material>Gazebo/Orange</material>
      </gazebo>
      
      • 这一部分描述了 link 的物理属性。mu 表示摩擦系数,material 表示材料,这里指定的是自己定义的材料。
          <!-- hokuyo -->
          <gazebo reference="hokuyo_link">
              <sensor type="gpu_ray" name="head_hokuyo_sensor">
                  <pose>0 0 0 0 0 0</pose>
                  <visualize>false</visualize>
                  <update_rate>40</update_rate>
                  <ray>
                      <scan>
                          <horizontal>
                              <samples>720</samples>
                              <resolution>1</resolution>
                              <min_angle>-1.570796</min_angle>
                              <max_angle>1.570796</max_angle>
                          </horizontal>
                      </scan>
                      <range>
                          <min>0.10</min>
                          <max>30.0</max>
                          <resolution>0.01</resolution>
                      </range>
                      <noise>
                          <type>gaussian</type>
                          <!-- Noise parameters based on published spec for Hokuyo laser
                         achieving "+-30mm" accuracy at range < 10m.  A mean of 0.0m and
                         stddev of 0.01m will put 99.7% of samples within 0.03m of the true
                         reading. -->
                          <mean>0.0</mean>
                          <stddev>0.01</stddev>
                      </noise>
                  </ray>
                  <plugin name="gazebo_ros_head_hokuyo_controller" filename="libgazebo_ros_gpu_laser.so">
                      <topicName>/rrbot/laser/scan</topicName>
                      <frameName>hokuyo_link</frameName>
                  </plugin>
              </sensor>
          </gazebo>
      </robot>
      
      • sensor 标签指定了这个 link 是一个传感器。update_rate 是刷新率。scan 标签中指定了采样率、解析度和最大最小传感角度。range 标签中设置了最大最小传感距离和解析度。noise 标签中指定了噪声。<plugin>中指定了一个插件,使用话题向外发布激光雷达的数据。
      • 相机的基本流程与之类似,但有许多不同的属性。
    • material.xacro 文件:

      <?xml version="1.0"?>
      <robot>
        <material name="black">
          <color rgba="0.0 0.0 0.0 1.0"/>
        </material>
      </robot>
      
      • 为材料指定 RGBA 颜色属性。

    (2)、rrbot_gazebo包

    • rrbot_world 文件:

      <?xml version="1.0" ?>
      <sdf version="1.4">
        <!-- We use a custom world for the rrbot so that the camera angle is launched correctly -->
      
        <world name="default">
          <include>
            <uri>model://ground_plane</uri>
          </include>
      
          <!-- Global light source -->
          <include>
            <uri>model://sun</uri>
          </include>
      
          <!-- Focus camera on tall pendulum -->
          <gui fullscreen='0'>
            <camera name='user_camera'>
              <pose>4.927360 -4.376610 3.740080 0.000000 0.275643 2.356190</pose>
              <view_controller>orbit</view_controller>
            </camera>
          </gui>
      
        </world>
      </sdf>
      
      • 创建世界,引入空地和太阳模型,并且指定视角。
    • rrobot_world.launch 文件:

      <launch>
        <!-- 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"/>
        <!-- We resume the logic in empty_world.launch, changing only the name of the world to be launched -->
        <include file="$(find gazebo_ros)/launch/empty_world.launch">
          <arg name="world_name" value="$(find rrbot_gazebo)/worlds/rrbot.world"/>
          <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>
      
      • 这一部分指定了launch文件内的宏定义参数,引入了空世界launch文件。
      <!-- Load the URDF into the ROS Parameter Server -->
        <param name="robot_description"
          command="$(find xacro)/xacro --inorder '$(find rrbot_description)/urdf/rrbot.xacro'" />
      
        <!-- 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 rrbot -param robot_description"/>
      
      </launch>
      
      • param 标签将 URDF 模型的路径加载到 ROS 参数服务器中。
      • node 节点运行了 urdf_spawner 脚本启动了一个服务,从 ROS 参数服务器中取出 robot_description 路径指向了 URDF 文件加载到世界中。

    (3)、rrbot_control 包

    • rrbot_control.launch 文件:

      <launch>
        <!-- Load joint controller configurations from YAML file to parameter server -->
        <rosparam file="$(find rrbot_control)/config/rrbot_control.yaml" command="load"/>
      
        <!-- load the controllers -->
        <node name="controller_spawner" pkg="controller_manager" type="spawner" respawn="false"
      	output="screen" ns="/rrbot" args="joint_state_controller
      					  joint1_position_controller
      					  joint2_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="/rrbot/joint_states" />
        </node>
      </launch>
      
      • rosparam 标签载入 yaml 配置文件。
      • 第一个节点启动了 joint_state_controller、joint1_position_controller、 joint2_position_controller 三个控制器。
      • 第二个节点启动了 robot_state_publisher 话题发布者。并且通过重映射重命名。
    • rrbot_rqt.launch 文件:

      <launch>
      
        <!-- Load RQT with a pre-setup GUI for Baxter controls from a perspective file  -->
        <node name="rrbot_rqt" pkg="rqt_gui" type="rqt_gui" respawn="false"
      	output="screen" args="--perspective-file $(find rrbot_control)/launch/rrbot_rqt.perspective"/>
      
      </launch>
      
      • 通过运行 rrbot_rqt.perspective 运行 rqt 的 GUI界面。
    • rrbot_control.yaml 文件:

      rrbot:
        # Publish all joint states -----------------------------------
        joint_state_controller:
          type: joint_state_controller/JointStateController
          publish_rate: 50  
        
        # Position Controllers ---------------------------------------
        joint1_position_controller:
          type: effort_controllers/JointPositionController
          joint: joint1
          pid: {p: 100.0, i: 0.01, d: 10.0}
        joint2_position_controller:
          type: effort_controllers/JointPositionController
          joint: joint2
          pid: {p: 100.0, i: 0.01, d: 10.0}
      
      • 先是指定了 joint_state_controller 关节状态发布者的类型和一秒内的发布速率。
      • 然后指定了 joint1_position_controller 和 joint2_position_controller 关节位置控制者的类型、所控制的关节和 PID 参数。

    3、自己做一个移动小车

    (1)、创建底盘

    • 先创建工作空间,将移动小车命名为 mycar。创建 mycar_description、mycar_gazebo 和 mycar_control。

    • 进入 mycar_gazebo 下的 worlds 目录,创建包含空地和太阳的最基本的世界文件 mycar.world。

      <?xml version="1.0"?>
      <sdf version="1.4">
        <world name="myworld">
      	<include>
      		<uri>model://sun</uri>
      	</include>
      	<include>
      		<uri>model://ground_plane</uri>
      	</include>
        </world>
      </sdf>
      
    • 进入 mycar_gazebo 下的 worlds 目录,创建最基本的启动世界文件 mycar.world 的 launch 文件 mycar_world.launch。

      <launch>	  
      	<include file="$(find gazebo_ros)/launch/empty_world.launch">	    
      		<arg name="world_name" value="$(find mycar_gazebo)/worlds/mybot.world"/>	    
      		<arg name="gui" value="true"/>	  
      	</include>	
      </launch>
      
    • 进入 mycar.descripiton 下的 urdf 目录,创建小车模型文件 mycar.urdf。

      <?xml version="1.0"?>
      <robot name="mycar" xmlns:xacro="http://www.ros.org/wiki/xacro">
              <!-- Put here the robot description -->
          <!-- 宏定义各个部分的尺寸 -->
          <xacro:property name="PI" value="3.1415926535897931"/>
      
          <xacro:property name="chassisHeight" value="0.1"/>
          <xacro:property name="chassisLength" value="0.4"/>
          <xacro:property name="chassisWidth" value="0.2"/>
          <xacro:property name="chassisMass" value="50"/>
          <xacro:property name="casterRadius" value="0.05"/>
          <xacro:property name="casterMass" value="5"/>
          <xacro:property name="wheelWidth" value="0.05"/>
          <xacro:property name="wheelRadius" value="0.1"/>
          <xacro:property name="wheelPos" value="0.2"/>
          <xacro:property name="wheelMass" value="5"/>
          <xacro:property name="cameraSize" value="0.05"/>
          <xacro:property name="cameraMass" value="0.1"/>
          
          <!-- 导入三个依赖文件 -->
           <xacro:include filename="$(find mycar_description)/urdf/mycar.gazebo" />
       <xacro:include filename="$(find mycar_description)/urdf/materials.xacro" />
       <xacro:include filename="$(find mycar_description)/urdf/macros.xacro" />
          
          <link name="footprint" />
      
          <joint name="base_joint" type="fixed">
            <parent link="footprint"/>
            <child link="chassis"/>
          </joint>
          <!-- 添加一个矩形底座 -->
          <link name='chassis'>
              <collision> 
                  <origin xyz="0 0 ${wheelRadius}" rpy="0 0 0"/> 
                  <geometry> 
                      <box size="${chassisLength} ${chassisWidth} ${chassisHeight}"/> 
                  </geometry> 
              </collision>
              <visual> 
                  <origin xyz="0 0 ${wheelRadius}" rpy="0 0 0"/> 
                  <geometry> 
                      <box size="${chassisLength} ${chassisWidth} ${chassisHeight}"/> 
                  </geometry> 
                  <material name="orange"/>
              </visual>
              <inertial> 
                  <origin xyz="0 0 ${wheelRadius}" rpy="0 0 0"/> 
                  <mass value="${chassisMass}"/> 
                  <box_inertia m="${chassisMass}" x="${chassisLength}" y="${chassisWidth}" z="${chassisHeight}"/>
              </inertial>
          </link>
      
      </robot>
      
      • 这一部分先是宏定义了小车各部分的尺寸,引入了依赖文件。
      • 然后创建了小车的矩形底盘部分,小车的其他部分将在稍后添加。
    • 进入 mycar.descripiton 下的 urdf 目录,创建小车在gazebo中的属性文件 mycar.gazebo。目前只指定了矩形底盘的材料。

      <?xml version="1.0"?>
      <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
          <gazebo reference="chassis">
            <material>Gazebo/Orange</material>
          </gazebo>
      </robot>
      
    • 进入 mycar.descripiton 下的 urdf 目录,创建小车的材料属性文件 materials.xacro。此处指定了所有小车需要用到的材料(颜色)。

      <?xml version="1.0"?>
      <robot>
        <material name="black">
          <color rgba="0.0 0.0 0.0 1.0"/>
        </material>
        <material name="blue">
          <color rgba="0.0 0.0 0.8 1.0"/>
        </material>
        <material name="green">
          <color rgba="0.0 0.8 0.0 1.0"/>
        </material>
        <material name="grey">
          <color rgba="0.2 0.2 0.2 1.0"/>
        </material>
        <material name="orange">
          <color rgba="${255/255} ${108/255} ${10/255} 1.0"/>
        </material>
        <material name="brown">
          <color rgba="${222/255} ${207/255} ${195/255} 1.0"/>
        </material>
        <material name="red">
          <color rgba="0.8 0.0 0.0 1.0"/>
        </material>
        <material name="white">
          <color rgba="1.0 1.0 1.0 1.0"/>
        </material>
      </robot>
      
    • 进入 mycar.descripiton 下的 urdf 目录,创建小车的属性宏定义文件 macros.xacro。

      <?xml version="1.0"?>
      <robot>
           <macro name="cylinder_inertia" params="m r h">
              <inertia  ixx="${m*(3*r*r+h*h)/12}" ixy = "0" ixz = "0"
                        iyy="${m*(3*r*r+h*h)/12}" iyz = "0"
                        izz="${m*r*r/2}" /> 
            </macro>
      
            <macro name="box_inertia" params="m x y z">
              <inertia  ixx="${m*(y*y+z*z)/12}" ixy = "0" ixz = "0"
                        iyy="${m*(x*x+z*z)/12}" iyz = "0"
                        izz="${m*(x*x+z*z)/12}" /> 
            </macro>
           <macro name="sphere_inertia" params="m r">
              <inertia  ixx="${2*m*r*r/5}" ixy = "0" ixz = "0"
                        iyy="${2*m*r*r/5}" iyz = "0"
                        izz="${2*m*r*r/5}" /> 
            </macro>
      </robot>
      
      • 对一些属性的宏定义,这里是对圆柱、矩形和球形的转动惯量的宏定义,通过输入的 params 中的参数计算转动惯量,在 mycar.urdf 中有用到。
    • 在 mycar.gazebo 目录下的 mybot_world.launch 文件中添加内容,使得世界启动时加载机器人模型。

      <!-- urdf xml robot description loaded on the Parameter Server, converting the xacro into a proper urdf file-->
      <param name="robot_description" command="$(find xacro)/xacro.py '$(find mycar_description)/urdf/mycar.xacro'" />
      
      <!-- push robot_description to factory and spawn robot in gazebo -->
      <node name="mycar_spawn" pkg="gazebo_ros" type="spawn_model" output="screen"
       args="-urdf -param robot_description -model mycar" />
      
      • 这部分首先将 robot_description 下的 mycar.xacro 路径存储到 ROS 参数服务器中,然后再加载入世界。
      • 一定要注意不要将中文注释粘贴进去。
    • 运行 mycar_world.launch 文件:

      $ roslaunch mycar_gazebo mycar_world.launch
      
    • 运行结果为:

    (2)、添加轮子

    • 在 mycar.xacro 文件中添加以添加球形脚轮:

      <joint name="fixed" type="fixed">
        <parent link="chassis"/>
        <child link="caster_wheel"/>
      </joint>
      
      <link name="caster_wheel">
        <collision>
          <origin xyz="${casterRadius-chassisLength/2} 0 ${casterRadius-chassisHeight+wheelRadius}" rpy="0 0 0"/>
          <geometry>
            <sphere radius="${casterRadius}"/>
          </geometry>
        </collision>
        
        <visual>
          <origin xyz="${casterRadius-chassisLength/2} 0 ${casterRadius-chassisHeight+wheelRadius}" rpy="0 0 0"/>
          <geometry>
            <sphere radius="${casterRadius}"/>
          </geometry>
          <material name="red"/>
        </visual>
      
        <inertial>
          <origin xyz="${casterRadius-chassisLength/2} 0 ${casterRadius-chassisHeight+wheelRadius}" rpy="0 0 0"/>
          <mass value="${casterMass}"/>
          <sphere_inertia m="${casterMass}" r="${casterRadius}"/>
        </inertial>
      </link>
      
    • 在 materilas.xacro 文件中添加脚轮的属性:

      <gazebo reference="caster_wheel">
        <mu1>0.0</mu1>
        <mu2>0.0</mu2>
        <material>Gazebo/Red</material>
      </gazebo>
      
    • 在 macro.xacro 宏定义文件中,为两个轮子使用宏定义简化书写。

      <macro name="wheel" params="lr tY">
      
      <link name="${lr}_wheel">
        <collision>
          <origin xyz="0 0 0" rpy="0 ${PI/2} ${PI/2}" />
          <geometry>
            <cylinder length="${wheelWidth}" radius="${wheelRadius}"/>
          </geometry>
        </collision>
      
        <visual>
          <origin xyz="0 0 0" rpy="0 ${PI/2} ${PI/2}" />
          <geometry>
            <cylinder length="${wheelWidth}" radius="${wheelRadius}"/>
          </geometry>
          <material name="black"/>
        </visual>
      
        <inertial>
          <origin xyz="0 0 0" rpy="0 ${PI/2} ${PI/2}" />
          <mass value="${wheelMass}"/>
          <cylinder_inertia m="${wheelMass}" r="${wheelRadius}" h="${wheelWidth}"/>
        </inertial>
      </link>
      
      <gazebo reference="${lr}_wheel">
        <mu1 value="1.0"/>
        <mu2 value="1.0"/>
        <kp  value="10000000.0" />
        <kd  value="1.0" />
        <fdir1 value="1 0 0"/>
        <material>Gazebo/Black</material>
      </gazebo>
      
      <joint name="${lr}_wheel_hinge" type="continuous">
        <parent link="chassis"/>
        <child link="${lr}_wheel"/>
      <origin xyz="${-wheelPos+chassisLength/2} ${tY*wheelWidth/2+tY*chassisWidth/2} ${wheelRadius}" rpy="0 0 0" />
        <axis xyz="0 1 0" rpy="0 0 0" />
        <limit effort="100" velocity="100"/>
        <joint_properties damping="0.0" friction="0.0"/>
      </joint>
      
      <transmission name="${lr}_trans">
      		  <type>transmission_interface/SimpleTransmission</type>
      		  <joint name="${lr}_wheel_hinge">
      			  <hardwareInterface>hardware_interface/ffortJointInterface</hardwareInterface>
      		  </joint>
      		  <actuator name="${lr}Motor">
      			<hardwareInterface>hardware_interface/EffortJointInterface</hardwareInterface>
      			<mechanicalReduction>10</mechanicalReduction>
      		  </actuator>
      		</transmission>
      </macro>
      
      • 宏定义的标签是<wheel>,指定了 link 链接和 joint 关节,以及其属性。最后定义了传动。输入的参数为左右轮和沿Y轴的正反方向。
    • 将宏定义运用于 mycar.xacro 文件中。

      <wheel lr="left" tY="1"/>
      <wheel lr="right" tY="-1"/>
      
    • 运行 mycar_world.launch 文件:

      $ roslaunch mycar_gazebo mycar_world.launch
      
    • 运行结果为:

    (3)、将小车连接到ROS

    • 在 mybot.gazebo 中添加插件访问车轮的关节。

      <gazebo>
        <plugin name="gazebo_ros_control" filename="libgazebo_ros_control.so">
          <robotNamespace>/mycar</robotNamespace>
        </plugin>
      </gazebo>
      
    • 在 mycar_control 包下的 config 目录创建 mycar_control.yaml 配置文件。该文件将定义三个控制器:每个车轮一个控制权,通过变速箱标签与关节的连接表示;一个用于发布关节状态的控制器。它还定义了用于此控制器的PID增益:

      mycar:
        # Publish all joint states -----------------------------------
        joint_state_controller:
          type: joint_state_controller/JointStateController
          publish_rate: 50  
        
      
        # Effort Controllers ---------------------------------------
        leftWheel_effort_controller:
          type: effort_controllers/JointEffortController
          joint: left_wheel_hinge
          pid: {p: 100.0, i: 0.1, d: 10.0}
        rightWheel_effort_controller:
          type: effort_controllers/JointEffortController
          joint: right_wheel_hinge
          pid: {p: 100.0, i: 0.1, d: 10.0}
      
    • 在 mycar_control 包下的 launch 目录创建 mycar_control.launch 文件用于启动控制器。

      <launch>
      
        <!-- Load joint controller configurations from YAML file to parameter server -->
        <rosparam file="$(find mycar_control)/config/mycar_control.yaml" command="load"/>
      
        <!-- load the controllers -->
        <node name="controller_spawner"
          pkg="controller_manager"
          type="spawner" respawn="false"
          output="screen" ns="/mycar"
          args="joint_state_controller
            rightWheel_effort_controller
            leftWheel_effort_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">
          <param name="robot_description" command="$(find xacro)/xacro.py '$(find mycar_description)/urdf/mycar.xacro'" />
          <remap from="/joint_states" to="/mycar/joint_states" />
        </node>
      
      </launch>
      
      • 加载配置和控制器。
      • 启动一个节点,该节点发布机器人状态信息。
    • 在 mybot_world.launch 中添加一行来启动控制器:

      <!-- ros_control mybot launch file -->
      <include file="$(find mycar_control)/launch/mycar_control.launch" />
      
    • 启动世界:

      $ roslaunch mycar_gazebo mycar_world.launch
      
    • 查看话题列表,可以看到三个相关的话题话题:/mycar/joint_states、
      /mycar/leftWheel_effort_controller/command和
      /mycar/rightWheel_effort_controller/command。查看第一个话题的数据和第二个话题的信息如下图。

    • 发布运动指令与停止指令:

      $ rostopic pub -1 /mycar/leftWheel_effort_controller/command std_msgs/Float64 "data: 1.5"
      $ rostopic pub -1 /mycar/rightWheel_effort_controller/command std_msgs/Float64 "data: 1.0"
      
      $ rostopic pub -1 /mycar/leftWheel_effort_controller/command std_msgs/Float64 "data: 0.0"
      $ rostopic pub -1 /mycar/rightWheel_effort_controller/command std_msgs/Float64 "data: 0.0"
      
    • 小车开始运动:

    (4)、机器人的遥控

    • 之前的配置允许我们可以单独控制关节,但是当想让移动机器人四处移动时,这样做并不方便。使用另一个称为差分驱动器的插件来简化,在 mycar.gazebo 中添加:

      <gazebo>
        <plugin name="differential_drive_controller" filename="libgazebo_ros_diff_drive.so">
          <alwaysOn>true</alwaysOn>
          <updateRate>100</updateRate>
          <leftJoint>left_wheel_hinge</leftJoint>
          <rightJoint>right_wheel_hinge</rightJoint>
          <wheelSeparation>${chassisWidth+wheelWidth}</wheelSeparation>
          <wheelDiameter>${2*wheelRadius}</wheelDiameter>
          <torque>20</torque>
          <commandTopic>mycar/cmd_vel</commandTopic>
          <odometryTopic>mycar/odom_diffdrive</odometryTopic>
          <odometryFrame>odom</odometryFrame>
          <robotBaseFrame>footprint</robotBaseFrame>
        </plugin>
      </gazebo>
      
      • 此插件将订阅由<commandTopic>标记指定的cmd_vel主题。
    • 要使用键盘对机器人进行遥控,可以使用turtlesim或turtlebot软件包中提供的遥控节点。我们只需要重新映射主题名称即可将其连接到我们的机器人:

      $ rosrun turtlesim turtle_teleop_key /turtle1/cmd_vel:=/mycar/cmd_vel
      
    • 用键盘控制机器人:

    (5)、添加相机

    • 在 mycar.xacro 中添加照相机:

      <joint name="camera_fix" type="fixed">
        <parent link="chassis"/>
        <child link="camera"/>
        <origin xyz="${chassisLength/2} 0 ${chassisHeight}" rpy="0 0 0" />
        <axis xyz="1 0 0" rpy="0 0 0" />
      </joint>
          
      <link name="camera">
        <collision>
          <origin xyz="0 0 0" rpy="0 0 0"/>
          <geometry>
            <box size="${cameraSize} ${cameraSize} ${cameraSize}"/>
          </geometry>
        </collision>
      
        <visual>
          <origin xyz="0 0 0" rpy="0 0 0"/>
          <geometry>
            <box size="${cameraSize} ${cameraSize} ${cameraSize}"/>
          </geometry>
          <material name="blue"/>
        </visual>
      
        <inertial>
          <mass value="${cameraMass}" />
          <origin xyz="0 0 0" rpy="0 0 0"/>
          <box_inertia m="${cameraMass}" x="${cameraSize}" y="${cameraSize}" z="${cameraSize}" />
        </inertial>
      </link>
      
      
      
    • 在 mycar.gazebo 文件中添加相机插件:

      <gazebo reference="camera">
          <material>Gazebo/Blue</material>
          <sensor type="camera" name="camera1">
              <update_rate>30.0</update_rate>
              <camera name="head">
                  <horizontal_fov>1.3962634</horizontal_fov>
                  <image>
                      <width>800</width>
                      <height>800</height>
                      <format>R8G8B8</format>
                  </image>
                  <clip>
                      <near>0.02</near>
                      <far>300</far>
                  </clip>
              </camera>
              <plugin name="camera_controller" filename="libgazebo_ros_camera.so">
                  <alwaysOn>true</alwaysOn>
                  <updateRate>0.0</updateRate>
                  <cameraName>mycar/camera1</cameraName>
                  <imageTopicName>image_raw</imageTopicName>
                  <cameraInfoTopicName>camera_info</cameraInfoTopicName>
                  <frameName>camera_link</frameName>
                  <hackBaseline>0.07</hackBaseline>
                  <distortionK1>0.0</distortionK1>
                  <distortionK2>0.0</distortionK2>
                  <distortionK3>0.0</distortionK3>
                  <distortionT1>0.0</distortionT1>
                  <distortionT2>0.0</distortionT2>
              </plugin>
          </sensor>
      </gazebo>
      
    • 使用image_view工具直接对相机图像进行可视化:

      $ rosrun image_view image_view image:=/mycar/camera1/image_raw
      
    • 结果为:


    iwehdio的博客园:https://www.cnblogs.com/iwehdio/

  • 相关阅读:
    回车与换行的区别
    C# 验证数字
    FCKeditor 2.6.6在ASP中的安装及配置方法分享--ZZ转载自网络
    关于Application.Lock…Application.Unlock有什么作用?
    关于Application.Lock和Lock(obj)
    C#解决Linq OrderBy() 失效的小技巧
    文件夹添加 IIS 应用程序池用户权限
    we7调用模板如何区分栏目页与详细页
    第二阶段冲刺(第十天)
    每周总结(第十六周)
  • 原文地址:https://www.cnblogs.com/iwehdio/p/12774226.html
Copyright © 2020-2023  润新知