ROS launch文档介绍
本文章轉自:https://charlyhuangrostutorial.wordpress.com/2015/08/12/20/
前面已經提過關于launch 檔的角色,很類似bash 檔,基本上就是把所有為了執行某個特定功能所需要的指令都寫在一張紙上,交給ROS 一次執行開來。舉例來說,今天我想執行SLAM (即時建圖及定位)的功能,但是這個演算法并不簡單,我必須喂好幾個輸入資料進去,所以,在不知道launch 檔好處的情況下,我就必須在終端機上個別開分頁這樣操作:
- 把雷射打開
- 把里程計(odometry)打開
- 把tf 程式打開
- 把gmapping 節點打開
- 把馬達驅動器打開
- 把teleoperation 節點打開,因為我想直接操作機器人
- 把圖形介面打開,因為我像直接看結果
這如果是在同一臺電腦上操作還好,但是如果今天如果是遠端怎么辦?尤其是碰到頻寬有限的問題,那么遠端頻頻斷線,工作起來真的會要人命。所以,睿智且聰明的設計者們便設計了launch 檔。只要執行它一次,便會call所有需要的launch 檔和節點,并且可以自訂輸入的參數,可以說是相當方便。但是怎么個方便法?讓我們繼續看下去。
內容一覽
- 撰寫第一份Launch 檔
- 宣告launch 檔<launch> … </launch>
- 引數<arg>
- 注解<!– –>
- 呼叫節點<node />
- 呼叫其他launch 檔<include>
- 邏輯判斷式if & unless
- 開發一個大型專案的Launch 寫法
- 怎么在終端機下指令
- 實際范例
撰寫第一份Launch 檔
如果你有機會下載一個別人寫好的包裹,并且打開資料夾進里面看,可能會找到一個Launch資料夾,這里就是存放所有Launch檔的地方,這種存放方法我把它視為某種約定俗成的習慣,因為方便管理嘛,哈!
我們將以Spencer People Tracking為范例,將這個專案中一部份的Launch file拿出來參考。但是在開始看之前,讓我們先了解launch的語法。
Launch 檔實際上使用YAML 格式,說穿了,Launch 檔就是一種腳本語言(Duh!)所以如果你有碰過XML, XAML, HTML等語言過,便可駕輕就熟。有幾種關鍵語法:
宣告launch 檔<launch> … </launch>
在launch文件一開頭和結尾都必須用這個宣告框出來,像這樣:
<launch>
…
</launch>
引數<arg>
引數通常用來作為執行各節點或launch 檔所需要的輸入參數,換句話說,設定區域變數,通常需要使用者輸入所需的數值,但也可以事先寫好預設的數值。另外一種用法,是用引數作為一個邏輯判斷,決定那些節點要執行,哪些不用。
引數的語法會像這樣:
<arg name="…" value="…">
其中name是參數的名稱。Value 是參數的值。有時候也用default=”…”來設定預設值。以下舉幾個例子:
<arg name=”max_value” value=”0.5”>
<arg name=”height_above_ground” default=”1.6”>
<arg name=”camera_input” value=”/camera”>
<arg name=”sensors_on” value=”true”> <!—下面章節會再提及這個指令的用法–>
注解<!– –>
舉幾個例子:
?<!—Turn on laser–>
<!—Fire up Rviz–>
<!—Just want to comment out this line–>
<!–<node name=”foo” pkg=”foo_pkg” type=”foo”>–>
呼叫節點<node />
呼叫節點會包含以下幾個參數:
<node pkg="…" type="…" name="…" respawn=true ns="…" args=”….”/>?
<!—記得后面要寫成/>要不然執行的時候會出錯!–>
里面的參數及其公用:
| pkg | 表示該節點所在的包裹 |
| type | 表示這個節點實際的名稱,也就是開發的時候取的名字 |
| name | 雖然也是指該節點的名稱,不過你可以再另外幫這個節點取名字,那么該節點便會把原名給覆蓋掉,以這個名稱表示。你可以在執行時,用rqt或者 rosnode list, rosnode info等指令查看到。 |
| respawn/required | 是當該節點由于不明原因停止執行的時候,會自動重新啟動。而required比較霸道一點,當該節點停止執行的時候,會讓整個launch? 檔都停止執行、關閉。 |
| ns | 指明在哪一個工作區間(workspace)的時候執行該節點,當你必須在多個子類別的實體(instance)中執行同一個節點的時候會很用。 |
若要設定該節點的印用參數,可以在節點內下以下指令:
<args name=”” value=””>
基本上跟上述的引數用法差不多,但是當要引用使用者在上面小節給的數值的話,記得這樣打:
<arg name=”camera” value=”/camera/rgb/image_raw”> <!—這是文件一開頭時的引數–>….
<node pkg=”foo_pkg” type=”foo” name=”foo”>
<args name=”camera_namespace” value=”$(arg camera)”>
</node>?
<!—記得要加入這個做結尾呦–>
其中,$(arg ….) 會自動去前面的<arg>找數值讀進去。
除了<args>以外,還有其他選項,如以下:
| <remap> | 用法是<remap from=”…” to=”…”>。將原本節點的輸入管道,接"到"新的topics? 上面。我都這樣想像,把節點想像成大象,把他那條長長的鼻子(輸入的topics,from=”…”)拉到我要他吃進去的topics? 上面去(to=”target topic”) 。 |
| <env> | 讓該節點讀入環境變數 |
| <rosparam> | 讓該節點讀進參數設定檔 |
| <param> | 設定該節點所需的參數 |
這邊只是列舉幾個比較常見參數。當然,還有更多參數選項,可以參考ROS Wiki文件。
呼叫其他launch 檔<include>
它的語法其實就是讓ROS去找目標launch檔的路徑,一個很有用的寫法,是用$(find?
<pkg>)這種語法來直接找包裹下的路徑,所以不管這個包裹的路徑被更改,程式照樣能找得到目標。請看下面范例:
<include file="$(find openni2_launch)/launch/openni2.launch">
<arg name="camera" value="rgbd_front_top"/>
<arg name="d??evice_id" value="#1″/>
<arg name="d??epth_registration" default="true"/>
</include>
以上是一個啟動openni2.launch這個launch?
檔的語法,包含在<include>里面的則是其引數。那又要怎么知道設定那些引數呢?最簡單的方法就是去看看目標launch檔一開頭的<arg>?
標簽,看看有那些設定可以更改。
邏輯判斷式if & unless
講到這邊,可能你會有一個疑問。那這樣的腳本語言有沒有判斷式,在某個情況下執行特定節點,另外一個特定情況不要執行呢?有的,但是并不像是你看過的任何高階語言那樣:
If (foo=true){
Return yes ;
}
Else
{
Return no ;
}
那怎么辦?其實只要轉念一想,我們可以拿作為邏輯判斷的方式,但是必須搭配標簽使用,寫法如下:
<arg name="load_driver" default="true"/>
<group if="$(arg load_driver)">
<include file="$(find openni2_launch)/launch/openni2.launch"/>
</group>
同樣的,也可以把<group>標簽中的if 換成unless,整個設定就變得像是"直到收到值為真或1時,執行該節點或launch檔"。
到時候在終端機執行這個launch 檔的時候,如果要關閉或執行某節點或launch檔,請輸入:
$ roslaunch pkg node load_driver:=false
或者
$ roslaunch pkg node load_driver:=true
這樣就能決定是否執行或跳過某部分不執行。還有,要打”:=”,否則launch檔要不就不理你繼續執行,或者是跳出語法錯誤的訊息。
開發一個大型專案的Launch 寫法
其實我尚未真的開發過一個大型專案,不過我們可以結合前人的智慧結晶以及自己的開發經驗。
最重要的,就是專案由于功能眾多,有許多節點互相連結,所以會被隔成一層層的,所以,一個rule of thumb就是最上層的節點盡量解結的呼叫下一層的
launch?
檔,然后下一層的launch檔在呼叫下一層的launch檔。而參數的設定盡量不要越級,該層級的參數設定就直接寫在該層的launch檔內,而不要上面好幾層的launch檔直接介入。這樣的方法在除錯和閱讀上會清晰不少。
另外一個我之前開發碰到的問題就是,直接將他人的包裹直接加進自己的專案內部。站在版本控制的觀念而言,每個包裹都是一個檔案庫(repository),除了在本地端維護外,更新的版本也會隨時上傳到云端。問題就發上在,一旦你將別人的檔案庫加進自己的專案,然后推上遠端自己的檔案庫后,這些檔案庫變成你專案的一部分,再也不是他人的檔案庫,因此也無法更新成最新的版本。當我要把我的這個擁腫的包裹下載到另外一臺電腦編譯時,又與我之前安裝的他人的同樣的包裹名稱起沖突。在把他人的包裹去掉,安裝自己的包裹后,發現編譯出錯,但是密密麻麻的訊息,已經讓我很難知道錯誤的源頭。因此,后來也就決定把他人的智慧結晶從我的專案中移除,往后有需要使用到他人包裹中的某些功能時,直接用launch檔呼叫即可。
從這個錯誤中我學到的教訓是,不要把別人的檔案庫直接加進自己的檔案庫內,而是各別克隆(git?
clone)和編譯,然后自己的檔案庫只負責自己寫的程式和launch檔。這樣做的好處有四:
怎么在終端機下指令
像上面的錯誤反省有提到的,Launch檔可以在自己的包裹內呼叫其他包裹的launch檔或節點,在實用上更方便。那么在終端機時,只要用roslaunch指令即可,語法是:
$ roslaunch <pkg name> <launch file> <arg1>:=… <arg2>:=… <arg3>:=…
先宣告launch檔所在的包裹名稱,再來是launch檔名稱,后面的引數arg則是前面小節已經提到過的標簽,其值可以被終端機上的指令覆蓋掉。實際的例子:
$ roslaunch rtabmap_ros rgbd_mapping.launch rviz:=true rtabmapviz:=false
讓我們來細看上面這行指令。
| Pkg name | rtabmap_ros |
| Launch file | rgbd_mapping.launch |
| <arg 1> | rviz:=true |
| <arg 2> | rtabmapviz:=false |
為了加快并簡化launch的指令,其實可以直接把自打到一半,按Tab鍵,會自動補齊,按兩下Tab鍵則會跳出更多選項讓使用者輸入正確的launch檔,但是注意,有時候電腦不會幫你寫后面的.launch,需要自己寫完或在按Tab補齊。如果你按Tab老半天,電腦都沒有反應,有兩個選項,一個就是把名字自己打完執行看看,要不然就是直接source,讓ROS連結到正在使用的工作空間上,如下?然后再試試看roslaunch一次。
$ souce ~/your_ws/devel/setup.bash
實際范例
好了,大概講完了,我們來看launch檔實際范例,這是spencer_people_tracking中的tracking_single_rgbd_senosr.launch?
在終端機執行時,請打:?以下是Launch檔:
$ roslaunch spencer_people_tracking tracking_single_rgbd_sensor.launch
<launch>
<!– Launch file arguments –>
<arg name="height_above_ground" default="1.6″/> <!– in meters, assumes a?
horizontally oriented RGB-D sensor; important for accurate detection –>
<arg name="load_driver" default="true"/> <!– set to false if you are already?
running OpenNi from elsewhere –>
<arg name="visualization" default="true"/>
<arg name="d??ummy_transforms" default="true"/>
<!– Run OpenNi2 driver –>
<group ns="spencer/sensors" if="$(arg load_driver)">
<include file="$(find openni2_launch)/launch/openni2.launch">
<arg name="camera" value="rgbd_front_top"/>
<arg name="d??evice_id" value="#1″/>
<arg name="d??epth_registration" default="true"/>
</include>
</group>
<!– Set ground plane distance –>
<rosparam?
param="/spencer/perception_internal/people_detection/ground_plane/distance"?
subst_value="true">$(arg height_above_ground)</rosparam>
<!– Set up dummy transforms into an imaginary robot and odom frame –>
<group if="$(arg dummy_transforms)">
<node name="tf_base_footprint" pkg="tf" type="static_transform_publisher"?
args="0 0 $(arg height_above_ground) 0 0 0 base_footprint rgbd_front_top_link?
10″/>
<node name="tf_odom" pkg="tf" type="static_transform_publisher" args="0 0 0 0 0?
0 odom base_footprint 10″/>
</group>?
<!– Detectors –>
<include file="$(find?
spencer_people_tracking_launch)/launch/detectors/front_rgbd_detectors.launch"/>
<!– People tracking –>
<include file="$(find?
spencer_people_tracking_launch)/launch/tracking/people_tracking.launch">
<arg name="rgbd" default="true"/>
<arg name="laser_low_confidence_detections" default="false"/>
</include>
<!– As there is not yet any high-recall/low-confidence detector for RGB-D, and?
we are not using laser, tracks may get deleted too quickly in case of missed?
detections.
To deal with this, for the moment, we increase the maximum number of occluded?
frames to be a bit more tolerant towards missed detections.
This works fine in uncrowded environments which are not very dynamic. –>
<rosparam?
param="/spencer/perception_internal/people_tracking/srl_nearest_neighbor_tracker/max_occlusions_before_deletion">50</rosparam>
<rosparam?
param="/spencer/perception_internal/people_tracking/srl_nearest_neighbor_tracker/max_occlusions_before_deletion_of_mature_track">200</rosparam>
<!– Group tracking –>
<include file="$(find?
spencer_people_tracking_launch)/launch/tracking/group_tracking.launch"/>
<!– RViz visualization –>
<node name="tracking_visualization_rviz" pkg="rviz" type="rviz" args="-d $(find?
spencer_people_tracking_launch)/rviz/?tracking-single-rgbd-sensor.rviz?" if="$(arg?
visualization)"/ >
總結
以上是生活随笔為你收集整理的ROS launch文档介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 甩开暑期档第二名4.7亿:《侏罗纪世界3
- 下一篇: php dao类设计,DAO数据访问对象