在節點少,程序小的情況下可以一個一個節點來啟動,測試運行效果,但是當工程規模大,需要的節點多時就顯得比較費勁,用.launch文件來啟動可以將需要的節點同時啟動,不用再一個一個進行。為工程搭建提高了效率,里面還有很多參數靈活使用會帶來非常高效的調試。
- 在上個例子ROS 消息通訊——發布者/訂閱者的來龍去脈的基礎上創建launch文件去啟動節點看效果會如何。

1 <launch> 2 <node pkg="ros_tutorials_topic" type="topic_publisher" name="topic_publisher1"/> 3 <node pkg="ros_tutorials_topic" type="topic_subscriber" name="topic_subscriber1"/> 4 <node pkg="ros_tutorials_topic" type="topic_publisher" name="topic_publisher2"/> 5 <node pkg="ros_tutorials_topic" type="topic_subscriber" name="topic_subscriber2"/> 6 </launch>
pkg="ros_tutorials_topic"為對應的功能包的名稱;
type="topic_publisher" 節點對應的可執行文件名,一般為.cop的文件名字ros::init(argc,argv,"topic_publisher"); //初始化發布者節點名稱對應,
name="topic_publisher1"運行時顯示的節點名稱,也就是用命令rosnode list 所看到的節點列表里的名稱。這兒定義的名字優先會覆蓋可執行程序(如.cpp里面init()賦予的節點名)當兩者不一樣是以name為准。
查看運行效果:
1 roslaunch ros_tutorials_topic topic.launch --screen//--screen是將通信消息發送到屏幕端

1 roslaunch ros_tutorials_topic topic.launch //沒有--screen屏幕上不顯示通信的消息,但是會正常收發,只是不在終端顯示

查看節點列表:

查看節點關系圖:rqt_graph,從圖中看到兩個發布者同時向兩個訂閱者發布消息,原因是我們只改變了節點的名稱,而沒有改變要使用的消息名字,下面在launch文件里添加一個命名空間標記就可以解決。

-
respawn="true"當roslaunch啟動完所有該啟動的節點之后,會監測每一個節點,保證它們正常的運行狀態。對於任意節點,當它終止時,roslaunch 會將該節點自動重啟 required="true"必要節點,這個出問題了,整個launch結束;注意此屬性不可以與respawn="true"一起描述同一個節點
修改后的launch文件如下;output="screen“,用於將話題信息打印到屏幕。roslaunch ros_tutorials_topic topic.launch后面不用加 --screen也可以實現屏幕顯示信息。
1 <launch> 2 <group ns="ns1"> 3 <node pkg="ros_tutorials_topic" type="topic_publisher" name="topic_publisher" output="screen"/> 4 <node pkg="ros_tutorials_topic" type="topic_subscriber" name="topic_subscriber" output="screen"/> 5 </group> 6 <group ns="ns2"> 7 <node pkg="ros_tutorials_topic" type="topic_publisher" name="topic_publisher" output="screen"/> 8 <node pkg="ros_tutorials_topic" type="topic_subscriber" name="topic_subscriber" output="screen"/> 9 </group> 10 </launch>
運行查看:roslaunch ros_tutorials_topic topic.launch

查看節點關系圖: 
- group標簽對node的批量管理,可以同時終止在同一個group中的節點
<group if="1-or-0"> …… …… …… </group> <group unless="1-or-0"> …… …… …… </group>
第一種情況,當if屬性的值為0的時候將會忽略掉<group> </group>之間的標簽。第二種恰好相反,當if屬性的值為1的時候將會忽略掉<group> </group>之間的標簽。但是我們通常不會直接用1或0來定義if標簽。因為這樣不夠靈活。通常會搭配$(arg arg_name)來使用。
-
<remap>重映射
在上圖中我們看到topic_publisher發布的話題是ros_tutorial_msg,topic_subscriber接收的話題同樣是ros_tutorial_msg,它們才能形成通訊
ros::Publisher ros_tutorial_pub = nh.advertise<ros_tutorials_topic::MsgTutorial>("ros_tutorial_msg", 100);(在topic_publisher.cpp中)
ros::Subscriber ros_tutorial_sub =nh.subscribe<ros_tutorials_topic::MsgTutorial>("ros_tutorial_msg", 100, msgCallback);
(在topic_subscriber.cpp中),只有這兩者對應起來才能,接收和發布的話題相同才能成功通訊。
然而在使用別人給的功能包時候,自己發送的話題和它接收的可能名稱不同,但是內容,格式相同,這時候我們就可以在launch進行重映射。
ros::Publisher ros_tutorial_pub = nh.advertise<ros_tutorials_topic::MsgTutorial>("ros_tutorial_msg", 100);(在topic_publisher.cpp中)
ros::Subscriber ros_tutorial_sub =nh.subscribe<ros_tutorials_topic::MsgTutorial>("remap/ros_tutorial_msg", 100, msgCallback);
(在topic_subscriber.cpp中),這時候訂閱者訂閱的話題為"remap/ros_tutorial_msg",發布者發布的話題為“ros_tutorial_msg",這時就不能進行通訊,需要發生重映射。
在沒有加重映射之前啟動節點查看話題會看到如下結果

如果把launch文件的話題進行一個重映射如下(最好將映射放到node標簽的前面,這樣后面所有使用這個話題的節點都可以更新)
<launch>
<remap from="ros_tutorial_msg" to="remap/ros_tutorial_msg"/> <group ns="ns1"> <node pkg="ros_tutorials_topic" type="topic_publisher" name="topic_publisher" output="screen"/> <node pkg="ros_tutorials_topic" type="topic_subscriber" name="topic_subscriber" output="screen"/> </group> <group ns="ns2"> <node pkg="ros_tutorials_topic" type="topic_publisher" name="topic_publisher" output="screen"/> <node pkg="ros_tutorials_topic" type="topic_subscriber" name="topic_subscriber" output="screen"/> </group> </launch>
<remap from="ros_tutorial_msg" to="remap/ros_tutorial_msg"/>這樣寫意味着將ros_tutorial_msg變為remap/ros_tutorial_msg,其實是將發布者發布的話題修改

<remap from="remap/ros_tutorial_msg" to="ros_tutorial_msg"/>這樣寫意味着將remap/ros_tutorial_msg變為ros_tutorial_msg,其實是將訂閱者訂閱的話題修改,一般用這種比較多,也容易理解,意思是,我訂閱者想訂閱一個話題但是你的跟我想要的不同,那么我就將自己的話題映射成與你發布的一樣就可以啦。


- 當然也可以將<remap>作為節點的子類包含在節點中(只更新該節點的訂閱消息)
<launch> <group ns="ns1"> <node pkg="ros_tutorials_topic" type="topic_publisher" name="topic_publisher" output="screen"/> <node pkg="ros_tutorials_topic" type="topic_subscriber" name="topic_subscriber" output="screen"/> <remap from="remap/ros_tutorial_msg" to="ros_tutorial_msg"/>
</node> </group> <group ns="ns2"> <node pkg="ros_tutorials_topic" type="topic_publisher" name="topic_publisher" output="screen"/> <node pkg="ros_tutorials_topic" type="topic_subscriber" name="topic_subscriber" output="screen"/> </group> </launch>
-
<param>參數的用法:
在參數的用法——利用參數創建節點中我們利用命令(rosparam set /calculation_method 2)改變參數“calculation_method“來實現加減乘除運算,那么現在我們launch參數命令來改變程序中的參數,看看會是什么效果。
sevice.launch文件如下:順便啟動服務器節點去等待客戶端請求
<launch> <param name="calculation_method" type="int" value="3" /> <node pkg="ros_tutorials_service" type="service_server" name="service_server" output="screen"/> </launch>
同時將這句屏蔽//nh.setParam("calculation_method", PLUS);(在service_server.cpp中),我們從launch文件中給定參數。然后編譯運行:

<arg>參數的用法:
arg雖然也是參數命令,但不儲存在參數服務器中,不能提供給節點使用,只能在launch文件中使用,用來在運行中或直接在文件中修改launch文件中被arg定義的變量中。param則是儲存在參數服務器中,可以被節點使用,這是最本質的區別。
還以現在這個例子來說,<param name="calculation_method" type="int" value="3" />將節點中參數的值賦值為3是進行乘法運算,但是當要進行除法運算時要求將參數變為4,但是在運行時,在launch中變為4,還得編譯重新運行,那么用arg來定義一個變量,用這個變量去改變calculation_method的值就實現的運行過程中的改變:launch文件內容變為如下:
<launch> <arg name="updata_value" default="1"/> <param name="calculation_method" type="int" value="$(arg updata_value)" /> <node pkg="ros_tutorials_service" type="service_server" name="service_server" output="screen"/> </launch>

開始默認為1,運行加法運算,那么在運行是我們將其值變為4,執行除法運算:roslaunch ros_tutorials_service service.launch updata_value:=4

<arg name="arg-name" default="arg-value" /> 注意default定義的值在roslaunch時才可以改變
<arg name="arg-name" value="arg-value" /> 注意value定義的值在roslaunch時不可以改變
- <rosparam>加載一個.yaml的配置文件
在上面我們實現了在launch文件中修改程序的參數問題。但是想象一下如果有成千上萬個參數的時候我們該怎么改,也這樣一句句定義是不現實的。那就用rosparam加載一個配置文件來實現。在功能包下面建一個config文件夾,放入參數配置文件service.yaml;內容為calculation_method: 3;(執行乘法運算)


launch文件內容變為:
<launch> <node pkg="ros_tutorials_service" type="service_server" name="service_server" output="screen"/> <rosparam command="load" file="$(find ros_tutorials_service)/config/service.yaml"/> </launch>
編譯執行效果為:

-
<include>的用法
<include>用於包含其它launch文件,被包含的launch文件將會被一同啟動。
<include file="$(find pkg_name)/launch/demo.launch"/>
- 如果要給
<include > 包含進來的launch元素賦值,由於arg是局部的只能改變本launch文件的參數值,因此需要建立一個包含在<include >標簽之間的arg.格式如下:
<include file="path-to-launch-file">
<arg name="arg-name" value="arg-value"/>
. . .
</include>
- 如果改變
<include > 包含進來的launch元素用的arg名稱與本launch文件的一樣的話需寫為如下格式:位置還放在上面<include >標簽之間
<arg name="arg-name" value="$(arg arg-name)" />

