ROS建模与仿真(四)--Ros_Control与Gazebo

2021/8/11 ROSPython机器人开发

# 一、ros_control

# 1.1概念

ROS提供了相当多的功能包,如:SLAM、导航、MoveIt等等,但是这些功能包的应用缺少了控制的中间件

ros_control就是应用和实际机器人或者机器人仿真器之间的中间件

ros_control就是ROS为用户提供的应用与机器人之间的中间件,包含一系列控制器接口、传动装置接口、硬件接口、控制器工具箱等等,可以帮助机器人应用快速落地,提高开发效率

2021-08-11 09-57-29 的屏幕截图.png

# 1.1ros_control框架

ros_control框架针对不同类型的机器人(移动机器人、机械臂等等)提供了多种类型的控制器(controller),但是这些控制器的接口各不相同

ros_control框架还提供了一个硬件抽象层,负责机器人硬件资源的管理,而controller从抽象层请求资源即可,无需直接接触硬件

2021-08-11 09-57-38 的屏幕截图.png
  • 控制器管理器(Controller Manager)每一个机器人系统可能会有多个控制器,控制器管理器的作用就是提供一种通用的接口来管理不同的控制器,其输入就是ROS上层应用功能包的输出
  • 控制器(Controller)控制器可以完成每个joint的控制,读取硬件资源接口中的状态,然后再发布控制命令并提供PID控制器
  • 硬件资源(Hardware Resource)为上下两层提供硬件资源的接口
  • 机器人硬件抽象(RobotHW)机器人硬件抽象和硬件资源直接交互,通过read和write方法完成对硬件的操作,这一层也包含了关节约束、例句转换、状态转换等功能
  • 真实机器人(Real Robot)真实机器人上需要有自己的嵌入式控制器,将接收到的命令反映到执行器上

下面反映的就是一个完整的ros_control数据流图

2021-08-11 09-58-08 的屏幕截图.png

# 1.2控制器

目前ROS中的ros_controllers功能包提供了以下控制器:

2021-08-11 10-30-11 的屏幕截图.png

2021-08-11 10-30-35 的屏幕截图.png

也可以根据自己的需求,创建需要的controller,然后通过控制器管理器(controller manager)来管理自己创建的controller

可参考https://github.com/ros-controls/ros_control/wiki/controller_interface

# 1.3硬件接口

硬件接口是控制器与机器人硬件抽象(RobotHW)沟通的接口,基本与控制器种类相互对应

2021-08-11 10-36-00 的屏幕截图.png

2021-08-11 10-36-10 的屏幕截图.png

同样可以自己创建需要的接口,可参考https://github.com/ros-controls/ros_control/wiki/hardware_interface

# 1.4传动系统

传动系统(Transmission)可以将机器人的关节指令转化为执行器的控制信号

注意:机器人的每一个需要运动的关节都需要配置相应的传动系统

其配置可以在机器人的URDF文件中按照以下方式进行:

<transmission name="simple_trans">
     <type>transmission_interface/SimpleTransmission</type>
     <joint name="foo_joint">
          <hardwareInterface>EffortJointInterface</hardwareInterface>
     </joint>
     <actuator name="foo_motor">
          <mechanicalReduction>50</mechanicalReduction>
          <hardwareInterface>EffortJointInterface</hardwareInterface>
     </actuator>
</transmission>

# 1.5关节约束

关节约束(Joint Limits)是硬件抽象层中的一部分,维护一个关节约束的数据结构

这些约束数据可以从机器人的URDF文件中加载出来,也可以在ROS的参数服务器中加载(需要先用YAML文件导入ROS参数服务器)

其包含的约束包括:关节速度、位置、加速度、加加速度、力矩、位置软限位、速度边界、位置边界等等

可以使用如下方法在URDF中进行设置:

<joint name="$foo_joint" type="revolute">
  <!-- other joint description elements -->

  <!-- Joint limits -->
  <limit lower="0.0" upper="1.0" effort="10.0" velocity="5.0" />
 
  <!-- Soft limits -->
  <safety_controller k_position="100" k_velocity="10" soft_lower_limit="0.1" soft_upper_limit="0.9" /> 
</joint>

还有一些参数需要通过YAML配置文件事先加载到参数服务器中,示例如下所示:

joint_limits:
  foo_joint:
    has_position_limits: true
    min_position: 0.0
    max_position: 1.0
    has_velocity_limits: true
    max_velocity: 2.0
    has_acceleration_limits: true
    max_acceleration: 5.0
    has_jerk_limits: true
    max_jerk: 100.0
    has_effort_limits: true
    max_effort: 5.0
  bar_joint:
    has_position_limits: false # Continuous joint
    has_velocity_limits: true
    max_velocity: 4.0

# 1.6控制器管理器

控制器管理器提供了一种多控制的机制,可以实现控制器的加载、开始运行、停止运行、卸载等多种操作

此外,控制器管理器还提供了多种工具来辅助操作

# 1.6.1命令行工具

标准使用格式:

$ rosrun controller_manager controller_manager <command> <controller_name>

支持的<command>命令如下所示:

  • load:加载控制器
  • unload:卸载控制器
  • start:启动控制器
  • stop:停止控制器
  • spawn:加载并启动控制器
  • kill:停止并卸载控制器

如果希望查看某一个控制器的状态,使用命令:

$ rosrun controller_manager controller_manager <command>

支持的<command>命令如下所示:

  • list:根据执行顺序列出所有控制器
  • list-types:显示所有控制器的类型
  • reload-libraries:以插件形式重新加载所有控制器的库,不需要重新启动,方便对控制器的开发和测试
  • reload-libraries--restore:以插件形式重新加载所有控制器的库,并恢复到初始状态

使用spawner命令一次控制多个控制器:

$ rosrun controller_manager spawner [--stopped] name1 name2 name3

加上--stop命令表示仅加载不执行

如果想要停止一系列的控制器,但不需要卸载,使用如下命令:

$ rosrun controller_manager unspawner name1 name2 name3

# 1.6.2launch工具

在launch文件中,同样可以通过运行controller_manager包的命令,来加载和启动一系列controller

<launch>
   <node pkg="controller_manager" type="spawner" args="controller_name1 controller_name2" /> 
</launch>

上边的launch文件会加载并启动controllers,如果只需要加载:

<launch>
  <node pkg="controller_manager" type="spawner" args="--stopped controller_name1 controller_name2" />
</launch>

# 1.6.3可视化工具

controller_manager还提供了可视化工具rqt_controller_manager

安装:rosrun rqt_controller_manager rqt_controller_manager,直接使用下边的命令打开:

$ rosrun rqt_controller_manager rqt_controller_manager

# 二、Gazebo仿真

# 2.1引入Gazebo属性

首先应该确保每个link的<inertia>元素已经进行了合理的设置

然后为每个必要的<link><joint><robot>设置<gazebo>标签

# 2.1.1为link添加<gazebo>标签

每一个link都应该添加<gazebo>标签,其包含的属性只有material,该属性与<visual>中的material作用相同

由于Gazebo无法通过visual来配置外观属性,所以需要单独设置,否则会采用默认的灰白色

<gazebo reference="wheel_${lr}_link">
    <material>Gazebo/Black</material>
</gazebo>

# 2.1.2添加传动

目的:为驱动机器人提供动力源

需要在模型中加入<transmission>元素,将传动与joint绑定

<!-- Transmission is important to link the joints and the controller -->
<transmission name="wheel_${lr}_joint_trans">
    <type>transmission_interface/SimpleTransmission</type>
    <joint name="base_to_wheel_${lr}_joint" />
    <actuator name="wheel_${lr}_joint_motor">
        <hardwareInterface>VelocityJointInterface</hardwareInterface>
        <mechanicalReduction>1</mechanicalReduction>
    </actuator>
</transmission>

代码解析:

  • <joint name="base_to_wheel_${lr}_joint" />用于绑定对应的驱动器joint
  • <type>transmission_interface/SimpleTransmission</type>用于设定使用的传动装置类型
  • <hardwareInterface>VelocityJointInterface</hardwareInterface>配置使用的硬件接口类型,这里使用的是速度接口类型

# 2.1.3添加Gazebo控制器插件

Gazebo插件赋予了URDF模型更加强大的功能,可以帮助模型绑定ROS消息,从而完成传感器的方针输出以及对电机的控制

Gazebo插件可以根据插件的作用范围应用到URDF模型的<robot><link><joint>上,需要使用<gazebo>标签进行包裹

# (1)为robot元素添加插件

<gazebo>
    <plugin name="unique_name" filename="plugin_name.so">
        ... plugin parameters ...
    </plugin>
</gazebo>

如果<gazebo>元素中没有设置reference="x"属性,则默认应用于<robot>标签

# (2)为link、joint添加插件

<gazebo reference="your_link_name">
    <plugin name="unique_name" filename="plugin_name.so">
        ... plugin parameters ...
    </plugin>
</gazebo>

Gazebo插件支持的种类在ROS默认安装路径下的/opt/ros/melodic/lib文件夹中可以找到

所有插件都是以libgazeboXXX.so方式进行命名的

# (3)范例

Gazebo提供的差速控制插件的应用

<!-- controller -->
<gazebo>
    <plugin name="differential_drive_controller" filename="libgazebo_ros_diff_drive.so">
        <rosDebugLevel>Debug</rosDebugLevel>
        <publishWheelTF>true</publishWheelTF>
        <!-- 机器人命名空间,插件所有数据的发布、订阅都在该命名空间中 -->
        <robotNamespace>/</robotNamespace>
        <publishTf>1</publishTf>
        <publishWheelJointState>true</publishWheelJointState>
        <alwaysOn>true</alwaysOn>
        <updateRate>100.0</updateRate>
        <legacyMode>true</legacyMode>
        <!-- 左右轮转动的关节joint,插件需要控制这两个joint转动 -->
        <leftJoint>base_to_wheel_left_joint</leftJoint>
        <rightJoint>base_to_wheel_right_joint</rightJoint>
        <!-- 模型相关尺寸,在计算差速参数时会用到 -->
        <wheelSeparation>${base_link_radius*2}</wheelSeparation>
        <wheelDiameter>${2*wheel_radius}</wheelDiameter>     
        <wheelTorque>30</wheelTorque>
        <!-- 车轮转动的加速度 -->
        <wheelAcceleration>1.8</wheelAcceleration>
        <!-- 控制器订阅的速度控制指令,在ROS中通常都命名为cmd_vel -->
        <commandTopic>cmd_vel</commandTopic>
        <!-- 里程计数据的参考坐标系,在ROS中通常为odom -->
        <odometryFrame>odom</odometryFrame> 
        <odometryTopic>odom</odometryTopic> 
        <robotBaseFrame>base_footprint</robotBaseFrame>
    </plugin>
</gazebo> 

# 2.2模型显示

首先需要更改launch文件,运行Gazebo并加载机器人模型,并启动必要节点

<launch>

    <!-- 设置launch文件的参数 -->
    <arg name="world_name" value="$(find mrobot_gazebo)/worlds/playground.world"/>
    <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"/>

    <!-- 运行gazebo仿真环境 -->
    <include file="$(find gazebo_ros)/launch/empty_world.launch">
        <arg name="world_name" value="$(arg world_name)" />
        <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>

    <!-- 加载机器人模型描述参数 -->
    <param name="robot_description" command="$(find xacro)/xacro --inorder '$(find mrobot_gazebo)/urdf/mrobot.urdf.xacro'" /> 

    <!-- 运行joint_state_publisher节点,发布机器人的关节状态  -->
    <node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" ></node> 

    <!-- 运行robot_state_publisher节点,发布tf  -->
    <node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher"  output="screen" >
        <param name="publish_frequency" type="double" value="50.0" />
    </node>

    <!-- 在gazebo中加载机器人模型-->
    <node name="urdf_spawner" pkg="gazebo_ros" type="spawn_model" respawn="false" output="screen"
          args="-urdf -model mrobot -param robot_description"/> 

</launch>

该launch文件中主要做了两项工作:

  • 启动机器人状态并发布节点,同事家在带有Gazebo属性的机器人URDF模型
  • 启动Gazebo,并将机器人模型加载到Gazebo仿真环境中

启动命令:roslaunch mrobot_gazebo view_mrobot_gazebo.launch

结果如下所示:

2021-08-11 12-24-58 的屏幕截图.png

# 2.3运动控制

由于模型中已经加入了libgazebo_ros_diff_drive.so插件,可以使用差速控制器控制机器人运动

启动命令:$ roslaunch mrobot_teleop mrobot_teleop.launch,即可运转

2021-08-11 12-42-55 的屏幕截图.png

# 2.4摄像头仿真

为机器人模型添加一个摄像头插件,让机器人能看到Gazebo中的虚拟环境

同样的需要在摄像头camera的URDF模型文件中添加<gazebo>相关配置

两个标签分别配置模型的样式和配置摄像头的各种属性

配置完成后运行仿真环境:roslaunch mrobot_gazebo view_mrobot_with_camera_gazebo.launch

使用rqt工具查看当前机器人眼前的世界

$ rqt_image_view

启动结果如下所示:

2021-08-11 13-57-00 的屏幕截图.png

# 2.5Kinect仿真

同样的,使用标签对URDF模型中的参数进行相关配置

完成后运行仿真环境:roslaunch mrobot_gazebo view_mrobot_with_kinect_gazebo.launch

运行仿真环境后可以使用RViz查看Kinect的点云数据:

$ rosrun rviz rviz

如果要在RViz中查看点云信息需要在软件中将Fixed Frame设置为camera_frame_optical并添加PointCloud2类型插件

结果如下所示:

2021-08-11 14-05-02 的屏幕截图.png

# 2.6激光雷达仿真

同摄像头仿真和Kinect仿真,激光雷达仿真在配置gazebo相关设定后启动

$ roslaunch mrobot_gazebo view_mrobot_with_laser_gazebo.launch

另外使用RViz可以查看激光数据

$ rosrun rviz rviz

如果要在RViz中查看点云信息需要在软件中将Fixed Frame设置为base_footprint并添加LaserScan类型插件

结果如下所示:

2021-08-11 14-18-59 的屏幕截图.png

# 三、问题解决

# 3.1Resource not found: gazebo_ros

在首次运行命令式出现如下错误,问题根本原因在于没有安装Gazebo仿真环境

2021-08-11 12-14-22 的屏幕截图.png
  • 解决办法:
$ sudo apt-get install ros-melodic-gazebo-ros-pkgs ros-melodic-gazebo-ros-control

# 3.2Gazebo [Err] [REST.cc:205] Error in REST request

运行终端命令启动Gazebo报错,Gazebo页面卡住黑屏,在终端出现Gazebo [Err] [REST.cc:205] Error in REST request

2021-08-11 12-21-00 的屏幕截图.png
  • 解决办法:
 $  sudo gedit ~/.ignition/fuel/config.yaml

打开文件并将url : https://api.ignitionfuel.org修改为url: https://api.ignitionrobotics.org