ROS中提供了豐富的機器人應用:SLAM、導航、MoveIt......但是你可能一直有一個疑問,這些功能包到底應該怎么樣用到我們的機器人上,也就是說在應用和實際機器人或者機器人仿真器之間,缺少一個連接兩者的東西。
ros_control就是ROS為用戶提供的應用與機器人之間的中間件,包含一系列控制器接口、傳動裝置接口、硬件接口、控制器工具箱等等,可以幫助機器人應用快速落地,提高開發效率。
一、總體框架
上圖是ros_control的總體框架,可以看到正對不同類型的控制器(底盤、機械臂等),ros_control可以提供多種類型的控制器,但是這些控制器的接口各不相同,為了提高代碼的復用率,ros_control還提供一個硬件的抽象層。
硬件抽象層負責機器人硬件資源的管理,而controller從抽象層請求資源即可,並不直接接觸硬件。
上圖是ros_control的數據流圖,可以更加清晰的看到每個層次包含的功能:
- Controller Manager:每個機器人可能有多個controller,所以這里有一個控制器管理器的概念,提供一種通用的接口來管理不同的controller。controller manager的輸入就是ROS上層應用的輸出。
- Controller:controller可以完成每個joint的控制,請求下層的硬件資源,並且提供了PID控制器,讀取硬件資源接口中的狀態,在發布控制命令。
- Hardware Rescource:為上下兩層提供硬件資源的接口。
- RobotHW:硬件抽象層和硬件直接打交道,通過write和read方法來完成硬件的操作,這一層也包含關節限位、力矩轉換、狀態轉換等功能。
- Real Robot:實際的機器人上也需要有自己的嵌入式控制器,接收到命令后需要反映到執行器上,比如接收到位置1的命令后,那就需要讓執行器快速、穩定的到達位置1。
二、Controllers
ros_controllers這個功能包提供了已有的一些controllers:
當然,我們也可以根據自己的需求,創建需要的controller,然后通過controller來管理自己創建的controller,可以參考https://github.com/ros-controls/ros_control/wiki/controller_interface
三、Hardware Interface
Hardware Interface是controller和RobotHw溝通的接口,基本上和controllers的種類是對應的,同樣可以自己創建需要的接口,可以參考:https://github.com/ros-controls/ros_control/wiki/hardware_interface
四、Transmissions
Transmissions就是機器人的傳動系統,機器人每個需要運動的關節都需要配置相應的Transmission,
可以通過代碼完成https://github.com/ros-controls/ros_control/wiki/transmission_interface,但大部分情況下,都會在URDF文件中直接添加(http://ros.org/wiki/urdf/XML/Transmission):
<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>
五、Joint Limits
Joint Limits是硬件抽象層中的一塊,維護一個關節限位的數據結構,這些限位數據可以從機器人的URDF文件中加載,也可以ROS的參數服務器上加載(先用YAML配置文件導入ROS parameter server),這些限位數據不僅包含關節速度、位置、加速度、加加速度、力矩等方面的限位,還包含安全作用的位置軟限位、速度邊界(k_v)和位置邊界(k_p)等等。
我們來看一個URDF中設置Joint Limits的例子:
<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配置文件先加載到參數服務器中,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
另外,我們還可以在代碼中使用joint_limits_interface來加載和設置關節的限位參數,可以參考:joint_limits_interface
六、controller manager
controller_manager提供了一種多controller控制的機制,可以加載、開始運行、停止運行、卸載不同的controller,並且提供了多種工具來完成這些操作。
1.命令行工具
命令行的格式為:
$ rosrun controller_manager controller_manager <command> <controller_name>
支持的<command> :
- load: load a controller (construct and initialize)
- unload: unload a controller (destruct)
- start: start a controller
- stop: stop a controller
- spawn: load and start a controller
- kill: stop and unload a controller
如果想要查看某個controller的狀態,可以使用下邊的命令:
$ rosrun controller_manager controller_manager <command>
支持的<command> :
- list: list all the controllers in the order they are executed, and give the state of each controller
- list-types: list all the controller types the controller manager knows about. If your controller is not in this list, you won't be able to spawn it.
- reload-libraries: Reloads all the controller libraries that are available as plugins. This is convenient when you are developing a controller and you want to test your new controller code, without restarting the robot every time. This does not restart controllers which were running before.
- reload-libraries --restore: Reloads all the controller libraries that are available as plugins and restores all controllers to their original state.
但是很多時候我們需要控制的controller有很多,比如六軸機器人,至少有六個controller,這時也可以使用“spawner ”這個命令來一次控制多個controller:
rosrun controller_manager spawner [--stopped] name1 name2 name3
上邊的命令可以自動加載、啟動controller,如果加上--stopped參數,那么contrller則只會被加載,但是並不會開始運行。如果想要停止一系列controller,但是不需要卸載,還需要運行的話,可以使用下邊的命令:
$ rosrun controller_manager unspawner name1 name2 name3
2.launch工具
在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>
3.可視化工具rqt_controller_manager
controller_manager還提供了可視化工具rqt_controller_manager,安裝rosrun rqt_controller_manager rqt_controller_manager,直接使用下邊的命令打開:
rosrun rqt_controller_manager rqt_controller_manager
不過目前我用的indigo ros版本里邊,這個工具貌似有問題,找不到executable。
七、案例分析
OK,前邊學習了這么多新的概念,我們還是找一個實際的案例來分析一下。gazebo的tutorials里邊提供了gazebo_ros_control的教程,用到一個兩個關節的機械臂作為案例,我們就來分析一下這個案例中都是怎樣落實上邊這些概念的。源碼可以在這里找到。
首先來看一張gazebo結合ros_control的架構圖,其實和上邊的數據流圖差別不大。
1.Transmissions
rrbot有兩個關節,每個關節都有一個傳動裝置,所以應該有兩個Transmissions,在rrbot的URDF文件rrbot.xacro文件中,我們可以找到這兩個Transmissions:
<transmission name="tran1">
<type>transmission_interface/SimpleTransmission</type>
<joint name="joint1">
<hardwareInterface>EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor1">
<hardwareInterface>EffortJointInterface</hardwareInterface>
<mechanicalReduction>1</mechanicalReduction>
</actuator>
</transmission>
<transmission name="tran2">
<type>transmission_interface/SimpleTransmission</type>
<joint name="joint2">
<hardwareInterface>EffortJointInterface</hardwareInterface>
</joint>
<actuator name="motor2">
<hardwareInterface>EffortJointInterface</hardwareInterface>
<mechanicalReduction>1</mechanicalReduction>
</actuator>
</transmission>
同時,為了讓Gazebo可以識別<transmission>標簽,還需要家在一個gazebo的ros_control插件:
<gazebo>
<plugin name="gazebo_ros_control" filename="libgazebo_ros_control.so">
<robotNamespace>/rrbot</robotNamespace>
</plugin>
</gazebo>
2.controller和controller_manager
首先通過一個YAML文件rrbot_control.yaml來聲明我們所需要的controller,以及對應的參數:
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,來控制發布每個關節的實時狀態。
然后使用launch文件rrbot_control.launch,運行controller_manager中的spawner,加載並運行這些上邊這些controller:
<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="joint1_position_controller joint2_position_controller joint_state_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>
這個例程沒有涉及到Joint Limits。通過下邊命令就可以在gazebo中啟動rrbot並且開始控制:
roslaunch rrbot_gazebo rrbot_world.launch
roslaunch rrbot_control rrbot_control.launch
也可以手動調用完成controller的加載:
rosservice call /rrbot/controller_manager/load_controller "name: 'joint1_position_controller'"
rosservice call /rrbot/controller_manager/load_controller "name: 'joint2_position_controller'"
啟動controller:
rosservice call /rrbot/controller_manager/switch_controller "{start_controllers: ['joint1_position_controller','joint2_position_controller'], stop_controllers: [], strictness: 2}"
停止controller:
rosservice call /rrbot/controller_manager/switch_controller "{start_controllers: [], stop_controllers: ['joint1_position_controller','joint2_position_controller'], strictness: 2}"
使用下邊的命令就可以讓機器人動起來:
rostopic pub -1 /rrbot/joint1_position_controller/command std_msgs/Float64 "data: 1.5"
rostopic pub -1 /rrbot/joint2_position_controller/command std_msgs/Float64 "data: 1.0"