Try   HackMD

DAY 6 Param & Launch

01. ROS Param

簡介

Rosparam 的全名叫做 Ros parameter,顧名思義是和參數有關的功能,透過 rosparam 的指令,我們可以儲存並操縱 Ros master 底下的 parameter server 裡面的參數,所以在使用 rosparam 時,一定要開啟 master。而這些參數和我們在程式碼中設定的參數有甚麼不同呢?
第一個差異是這些變數為全域變數,在啟動程式碼時,有些參數是一開始就固定好的,例如初始位置、初始速度等,但有些參數我們希望可以在測試中隨時調整的,例如機器人的目標點等,如果把參數寫在 C++ 程式中,每改一次參數就要重新編譯一次,在測試時其實是很花時間的,而 rosparam 就可以解決這樣的問題。

rosparam指令

指令 功能
rosparam list 列出所有參數
rosparam set [param name] [vale] 設置參數
rosparam get [param name] 獲得參數值
rosparam load [file path/file name] 從文件中加載參數到 parameter server
rosparam dump [file path/file name] 將 parameter server 中的參數寫入文件
rosparam delete [param name] 刪除參數

我們可以將需要設置的參數保存在 yaml 文件中,然後使用 rosparam load [文件路徑\文件名] 命令一次將多個參數加載到 parameter server 中。

例如 : 現在有一個叫practice.yaml的檔案

/obstacle_range: 3.0 /raytrace_range: 3.5 /print_freq: 1 /footprint: [1, 2, 3]

我們想要把裡面的參數一次傳到 parameter server 中方便更改,就可以使用下列程式碼,注意要先移動到 yaml 檔的 package 底下 :

cd tutorial_ws/src/basiclearn rosparam load params/practice.yaml

這樣 yaml 檔裡的參數就全部傳進 parameter server中了!

指令範例

Step 1. 建立一個可以操作頻率的 node

code param_practice.cpp
#include <ros/ros.h> int main(int argc, char **argv){ ros::init(argc,argv,"param_practice"); ros::NodeHandle nh; double freq; while(ros::ok()){ nh.getParam("/print_freq",freq); ros::Rate rate(freq); ROS_INFO("%f", freq); rate.sleep(); } return 0; }

記得修改 CMakeList.txt 和編譯。

Step 2. 設置 param(方法一:使用指令)

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
要記得先 roscore

使用 rosparam set 設置一個叫做 /print_freq 的參數值為 1。

rosparam set /print_freq 1

設置完後可以試試看用 rosparam listrosparam get 查看。

Step 2. 設置 param(方法二:創建yaml檔)

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
要記得先 roscore

建立param.yaml

code param.yaml

param.yaml中寫入需要的param

/print_freq: 1

進入package,在Terminal中執行rosparam load載入param.yaml

rosparam load param.yaml

完成後執行rosparam list查看是否有成功

Step 3. 執行結果

rosrun [pkg_name] param_practice

node 以 1Hz 的頻率印出當前的頻率

如果此時重新設置 /print_freq 為 5 。

rosparam set /print_freq 5

後三筆 hello_world 就是以 5Hz 印出來的。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

02. Roslaunch

簡介

Launch 檔是使用 xml 語言編寫的腳本檔,可以把需要執行的 node 貼給 Ros 一次執行,以東京威力比賽時的程式來說,機器上場時需要執行主程式、encoder odometry、IMU、雷射測距、STM 通訊等不同的 node,而如果是用遠端操控的話更是可能會面臨 node 開啟到一半失去連線的風險,launch 檔可以一次呼叫所有需要的 node ,並且編輯需要的參數,相較起來方便許多。另外,上面所提到的rosparam在關掉 master 的時候就會全部消失,因此 launch 檔另一個重要功能就是儲存與控制重要的參數。

建立 Launch File

Step 1. 建立 launch 資料夾

在 pkg 中新增一個 launch 資料夾,所有的 launch 檔都放在這個資料夾下,方便管理。

$ cd tutorial_ws/src/basiclearn #cd [pkg name] $ mkdir launch

Step 2. 建立 launch 檔

$ code [launch_name].launch

執行 Launch File

launch file 不需要編譯,可以直接執行,但新增 launch 檔需要執行 $ source/devel/setup.bash

$ roslaunch basiclearn my_launch.launch

雖然 launch 會幫忙開啟 roscore,但還是記得先執行 roscore 喔!

撰寫 Launch File

1. 宣告 launch 檔

launch 檔的宣告需要以<launch>開頭,</launch> 結尾。

<launch> [your code] </launch>

2. launch 檔的架構

在 launch 檔中會包含許多不同的 tag ,而這些 tag 宣告方式也都和 launch 檔的宣告一樣是以 <tag> 開頭,</tag> 結尾,而如果 tag 內的指令只有單行的話也可寫成<tag... />,下圖是大概的架構。

<tag .... /> <!--單行--> <tag> <!--多行--> . . . </tag>

3. launch 檔常用指令

(1) <node>

用來啟動 node,但不保證 node 的啟動順序。

<node type="talker" pkg="basiclearn" name="publisher1" output="screen"/>

pkg:欲啟動的 node 所在的 package。
type:欲啟動的 node 的執行檔名稱,也就是在 CMakeLists.txt 中取的名字 (橘字的部分)

name:也是 node 的名稱,但可以重新命名覆蓋掉原本的名稱,以新的名稱表示。

舉例來說

<launch> <node type="talker" pkg="basiclearn" name="publisher1" output="screen" /> <node type = "listener" pkg = "basiclearn" name = "subscriber1" output = "screen" respawn = "true" required = "false"/> <node type="listener" pkg="basiclearn" name="subscriber2" output="screen" /> </launch>

這個 launch 檔總共執行 talker 這個 node 1次,listener 這個 node 兩次,並且在這個 launch 檔中,這些 node 的名字分別被覆蓋成 publisher1subscriber1subscriber2,用 $rosnode list 可以發現這些 node 的名字確實被更改過了

所以如果要啟動同一個 node 多次,launch 檔裡的 name 就必須取不同的名字喔 !

output: 輸出log的目標。
respawn / required: 是當該節點由於不明原因停止執行的時候,會自動重新啟動。而 required 比較霸道一點,當該節點停止執行的時候,會讓整個 launch 檔都停止執行並且關閉。

(2) <remap>

當程式裡面寫的 topic 名稱不是最終想要的,remap 可以幫助你改變程式所知道的topic名稱。

<remap from= "original topic_name" to= "new topic_name" /> <!-- 通常會寫在 <node> 底下 -->
<node type="talker" pkg="basiclearn" name="publisher1" output="screen" > <remap from="counter" to="counter_1"/> </node> <!-- publish 原本發佈到"counter",更改為"counter_1" --> <node type="listener" pkg="basiclearn" name="subscriber1" output="screen" > <remap from="counter" to="counter_1"/> </node> <!-- subsrciber 原本接收到"counter",更改為"counter_1" --> <node type="listener" pkg="basiclearn" name="subscriber2" output="screen" > <remap from="counter" to="counter_1"/> </node>

(3) <include>

允許在 launch 檔中包含其它的 roslaunch 檔案或 XML 檔案等。

<include file ="$(find pkg_name) / launch_files / launch_name.launch" />

範例:

(4) <arg>

<arg>的功能是宣告變數,讓我們可以從 command-line 賦值,或是傳遞<include>檔案的變數,但要注意 arg 標籤是非全域性的,一個宣告只針對一個 launch 檔案,如同區域性變數,如果要在其它檔案中使用,則必須要明確的進行值的傳遞。

<arg>有兩種寫法,一個是使用defaultdefault是給一個預設值,可以在執行 roslaunch 時修改,另一個是 valuevalue是吃指定值,無法修改,兩者不能寫在同一行

<arg name="arg_name" default="arg_value" /> <arg name="arg_name" value="arg_value" />

my_launch.launch :

<arg name="vel" default="100" /> <include file="$(find basiclearn)/launch/included.launch" > <arg name="velocity" value="$(arg vel)" /> </include>

<arg> 也可以用在 <include> 中直接給定變數值,但是 arguments 是區域變數,因此從 command-line 更改的數值無法改到 <include> 標籤下的 arguments,所以上面的程式在 <include> 外多宣告了一個 argument,讓 <include> 裡的 argument 吃外面的值。

included.launch :

<launch> <!-- declare arg to be passed in --> <arg name="velocity" default:"10"/> <node pkg="turtlesim" type="turtlesim_node" name="turtle" output="screen" /> <!-- read value of arg --> <param name="param" value="$(arg velocity)"/> <!-- $() 是獲取變量的意思 --> </launch>

在 command window 執行 launch 檔時可以直接更改 default 的值

$ roslaunch basiclearn my_launch.launch velocity:=50

但如果將 my_launch.launch 中的 default 改成 value,velocity 的值就沒辦法從 command-line 給。

<include file="$(find basiclearn)/launch/included.launch" > <arg name="velocity" value="100" /> </include>

(5) <param>

<param> 的功能是宣告要在 parameter server 中設置的變數。

<!-- string --> <param name="frame_id" type="string" value="laser_frame" /> <!-- float --> <param name="pose_x" type="double" value="20.5" /> <!-- int --> <param name="baudrate" type="int" value="512000" />

<param><arg> 功能相似,都是負責宣告變數,而主要的差別在於 <param> 無法用在 <include> 中傳遞變量,<arg> 則是可以用在 <include><node> 中。

比較 <arg> & <param>

  • <param> : 寫在 <launch> 下當全域變數、從 launch 檔傳入參數給 node ( 用 `nh.getParam()` ),或是寫在 <node>下,用 args 傳遞變數。
  • <arg> : 從 command-line 直接給參數值,或是寫在<include>下,從另一個 launch file 給值。

launch 檔架構

如果加在 <node> 標籤底下會成為區域變數,但不論是全域變數還是區域變數,都可以用 rosparam set / get 接收,只是當它是區域變數時需加上 namespace

例如 :
如果把 included.launch 的 <param> 移到 <node> 標籤底下,再次執行,使用 $ rosparam list 查看會發現 param 的名字從 /param 變成 /turtle/param。

(6) <rosparam>

<arg>是原生 xml 的標籤,<param><rosparam>算是ros新增出來的標籤,<param>用法跟<arg>很像,但是可以被ros master管理。而<rosparam>標籤就是用來管理<param>的,跟cmd直接下rosparam有一樣的效果。

<rosparam command="delete" param="/param_name" /> <rosparam command="load" file="$(find basiclearn)/params/practice.yaml" /> <rosparam command="dump" file="$(find basiclearn)/params/practice.yaml" />

delete:刪除變數
load:將指定的 yaml 檔變數資料匯入 param server 裡面,和前面提過的直接在 command-line 下指令是一樣的意思
dump:將 param server 裡面的所有變數寫入指定的 yaml 檔

(7) <group>

可以把數個 node 組織成一組 node,並且一次對整組 node 進行操作, 通常用在大型的 launch 檔中,例如一次幫很多個 node 上 namespace;或是加入判斷式,在某些情況運行某個 group,某些情況則運行另一個 group等,。

ns :指定 node 到特定的名稱空間中,名稱空間可以是全域性或是區域性的
可以用來啟動同名 node,避免因為名稱衝突關閉上一個 node

(8) 註解

<!-- text -->

在 VScode 中,直接按Ctrl+/就會有了

03. HW

  1. 讓 turtle 跑到指定地點
  2. 寫一個 yaml 檔儲存小烏龜的速度目標點
  3. 創建一個新的 launch 檔。
  4. 新的 launch 檔要包含
    • 開啟 "發送速度與接收位置的 node" 的指令 (以下用 node_day3 代稱)
    • 讀取yaml 檔的參數值
    • 把 publish 的 topic 名稱換掉,再用 <remap> 改回 cmd_vel
  5. 把 node_day3 的速度值跟目標點用 nh.getParam() 取得。