每當我們需要運行一個ROS節點或工具時,都需要打開一個新的終端運行一個命令。當系統中的節點數量不斷增加時,每個節點一個終端的模式會變得非常麻煩。那么有沒有一種方式可以一次性啟動所有節點呢?答案當然是肯定的。
啟動文件(Launch File)便是ROS中一種同時啟動多個節點的途徑,還可以自動啟動ROSMaster節點管理器,而且可以實現每個節點的各種配置,為多個節點的操作提供了很大便利。
一、基本元素
首先來看一個簡單的launch文件:
<launch>
<node name="sim1" pkg="turtlesim" type="turtlesim_node"/>
<node name="sim2" pkg="turtlesim" type="turtlesim_node"/>
</launch>
這是一個簡單而完整的launch文件,采用XML的形式進行描述,包含一個根元素<launch>
和兩個節點元素<node>
。
1.1 launch
XML文件必須要包含一個根元素,launch文件中的根元素采用<launch>
標簽定義,文件中的其他內容都必須包含在這個標簽之中:
<launch>
……
……
……
</launch>
1.2 node
啟動文件的核心是啟動ROS節點,采用<node>
標簽定義,語法如下:
<node name="node-name" pkg="package-name" type="executable-name"/>
從上邊的定義規則可以看出,在啟動文件中啟動一個節點需要三個屬性:name、pkg和type。其中name
屬性用來定義節點運行的名稱,將覆蓋節點中ros::init()
定義的節點名稱;pkg
屬性定義節點所在的功能包名稱,type
屬性定義節點的可執行文件名稱,這兩個屬性等同於在終端中使用rosrun命令執行節點時的輸入參數。這是三個最常用的屬性,在某些情況下,我們還有可能用到以下屬性:
屬性 | 屬性作用 |
---|---|
output="screen" |
終端輸出轉儲在當前的控制台上,而不是在日志文件中 |
respawn="true" |
當roslaunch啟動完所有該啟動的節點之后,會監測每一個節點,保證它們正常的運行狀態。對於任意節點,當它終止時,roslaunch 會將該節點重啟 |
required="true" |
當被此屬性標記的節點終止時,roslaunch會將其他的節點一並終止。注意此屬性不可以與respawn="true" 一起描述同一個節點 |
ns = "NAME_SPACE" |
這個屬性可以讓你在自定義的命名空間里運行節點 |
args = "arguments" |
節點需要的輸入參數 |
實際應用中的launch文件往往會更加復雜,使用的標簽也會更多,例如一個啟動機器人的launch文件如下:
<launch>
<node pkg="mrobot_bringup" type="mrobot_bringup" name="mrobot_bringup" output="screen" />
<arg name="urdf_file" default="$(find xacro)/xacro --inorder '$(find mrobot_description)/urdf/mrobot_with_rplidar.urdf.xacro'" />
<param name="robot_description" command="$(arg urdf_file)" />
<node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" />
<node pkg="robot_state_publisher" type="robot_state_publisher" name="state_publisher">
<param name="publish_frequency" type="double" value="5.0" />
</node>
<node name="base2laser" pkg="tf" type="static_transform_publisher" args="0 0 0 0 0 0 1 /base_link /laser 50"/>
<node pkg="robot_pose_ekf" type="robot_pose_ekf" name="robot_pose_ekf">
<remap from="robot_pose_ekf/odom_combined" to="odom_combined"/>
<param name="freq" value="10.0"/>
<param name="sensor_timeout" value="1.0"/>
<param name="publish_tf" value="true"/>
<param name="odom_used" value="true"/>
<param name="imu_used" value="false"/>
<param name="vo_used" value="false"/>
<param name="output_frame" value="odom"/>
</node>
<include file="$(find mrobot_bringup)/launch/rplidar.launch" />
</launch>
目前,我們只關注其中的標簽元素,除了上邊介紹的<launch>
和<node>
,這里還出現了<arg>
、<param>
、<remap>
,這些都是常用的標簽元素。
二、參數設置
為了方便設置和修改,launch文件支持參數設置的功能,類似於編程語言中的變量聲明。關於參數設置的標簽元素有兩個:<param>
、<arg>
,一個代表parameter,另一個代表argument。這兩個標簽元素翻譯成中文都是“參數”的意思,但是這兩個“參數”的意義是完全不同的。
2.1 param
parameter是ROS系統運行中的參數,存儲在參數服務器中。在launch文件中可以通過<param>
元素加載parameter。launch文件執行后,parameter就加載到ROS的參數服務器上了。
每個活躍的節點都可以通過 ros::param::get()接口來獲取parameter的值,用戶也可以在終端中通過rosparam命令獲得parameter的值。
比如現在在參數服務器中添加一個名為demo_param,值為666的參數
<param name="demo_param" type="int" value="666"/>
運行launch文件后,demo_param這個parameter的值就設置為666,並且加載到ROS參數服務器上了。但是在很多復雜的系統中,參數的數量很多,如果這樣一個一個的設置會非常麻煩,ROS也為我們提供了另外一種類似的參數加載方式——<rosparam>
:
<rosparam file="$(find 2dnav_pr2)/config/costmap_common_params.yaml" command="load" ns="local_costmap" />
<rosparam>
可以幫助我們將一個yaml格式文件中的參數全部加載到ROS參數服務器中,需要設置command屬性為“load”,還可以選擇設置命名空間“ns”。
2.2 arg
arg標簽用來在launch文件中定義參數,arg和param在ROS里有根本性的區別,就像局部變量和全局變量的區別一樣。arg不儲存在參數服務器中,不能提供給節點使用,只能在launch文件中使用。param則是儲存在參數服務器中,可以被節點使用。
<arg name="demo"/>
像上面這樣,就簡單地聲明了一個參數,名叫demo,但是聲明不等於定義,我們需要給他賦值,在賦值之后參數才能夠發揮作用。
<arg name="demo1" value="666"/>
<arg name="demo2" default="666"/>
以上是兩種簡單的賦值方法,兩者的區別是使用后者賦值的參數可以在命令行中像下面這樣被修改,前者則不行。
roslaunch demo demo.launch demo:2=6666
launch文件中需要使用到argarg-name時,可以使用如下方式調用:
<arg name="arg-name" value="666"/>
<param name="foo" value="$(arg arg-name)" />
<node name="node" pkg="package" type="type "args="$(arg arg-name)" />
當$(arg arg_name)
出現在launch文件任意位置時,將會自動替代為所給參數的值。
三、重映射機制
ROS的設計目標是提高代碼的復用率,所以ROS社區中的很多功能包我們都可以拿來直接使用,而不需要關注功能包的內部實現。那么問題就來了,別人功能包的接口不一定和我們的系統兼容呀?
ROS提供一種重映射的機制,簡單來說就是取別名,類似於C++中的別名機制,我們不需要修改別人功能包的接口,只需要將接口名稱重映射一下,取個別名,我們的系統就認識了(接口的數據類型必須相同)。launch文件中的<remap>
標簽顧名思義重映射,emap標簽里包含一個original-name
和一個new-name
,及原名稱和新名稱。
比如turtlebot的鍵盤控制節點,發布的速度控制指令話題可能是/turtlebot/cmd_vel
,但是我們自己的機器人訂閱的速度控制話題是/cmd_vel
,這個時候使用<remap>
就可以輕松解決問題,將/turtlebot /cmd_vel
重映射為/cmd_vel
,我們的機器人就可以接收到速度控制指令了:
<remap from="/turtlebot/cmd_vel" to="/cmd_vel"/>
重映射機制在ROS中的使用非常廣泛,也非常重要,方法不止這一種,也可以在終端rosrun命令中實現重映射,大家一定要理解好這種機制。
四、嵌套復用
在復雜的系統當中,launch文件往往有很多,這些launch文件之間也會存在依賴關系。如果需要直接復用一個已有launch文件中的內容,可以使用<include>
標簽包含其他launch文件,這和C語言中的include幾乎是一樣的。
<include file="$(find demo)/launch/demo.launch" ns="demo_namespace"/>
屬性 | 屬性作用 |
---|---|
file ="$(find pkg-name)/path/filename.xml" |
指明我們想要包含進來的文件 |
ns="NAME_SPACE" |
相對NAME_SPACE 命名空間導入文件 |
總而言之,launch是ROS框架中非常實用、靈活的功能,它類似於一種高級編程語言,可以幫助我們管理啟動系統時的方方面面。在使用ROS的過程中,很多情況下我們並不需要編寫大量代碼,僅需要使用已有的功能包,編輯一下launch文件,就可以完成很多機器人功能。
五、拓展說明
使用
roslaunch
命令 和 使用rosrun
命令 單獨運行每個節點之間的重要區別
默認情況下,roslaunch 命令 從啟動節點開始,標准輸出信息會重定向到一個日志文件中,而不會像 rosrun 命令那樣,將 log 信息顯示在終端(console)上。日志文件所在路徑: ∼/.ros/log/run_id/node_name-number-stdout.log
Q: 如何將標准輸出信息顯示在終端(console)上?
A: 在 node 元素中使用 output 屬性:output=”screen”。
擴展: node 元素的 output 屬性只能影響這個節點自己。除了 output 屬性,我們可以使用 roslaunch命令行工具的 –screen 命令行選項強制性的在終端的窗口中顯示所有節點的輸出信息。
$ roslaunch --screen package-name launch-file-name
參考: