Rosparam 的全名叫做 Ros parameter,顧名思義是和參數有關的功能,透過 rosparam
的指令,我們可以儲存並操縱 Ros master 底下的 parameter server 裡面的參數,所以在使用 rosparam 時,一定要開啟 master。而這些參數和我們在程式碼中設定的參數有甚麼不同呢?
第一個差異是這些變數為全域變數,在啟動程式碼時,有些參數是一開始就固定好的,例如初始位置、初始速度等,但有些參數我們希望可以在測試中隨時調整的,例如機器人的目標點等,如果把參數寫在 C++ 程式中,每改一次參數就要重新編譯一次,在測試時其實是很花時間的,而 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中了!
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 和編譯。
roscore
使用 rosparam set
設置一個叫做 /print_freq
的參數值為 1。
rosparam set /print_freq 1
設置完後可以試試看用 rosparam list
和 rosparam get
查看。
roscore
建立param.yaml
code param.yaml
在param.yaml
中寫入需要的param
/print_freq: 1
進入package,在Terminal中執行rosparam load
載入param.yaml
rosparam load param.yaml
完成後執行rosparam list
查看是否有成功
rosrun [pkg_name] param_practice
node 以 1Hz
的頻率印出當前的頻率 。
如果此時重新設置 /print_freq
為 5 。
rosparam set /print_freq 5
後三筆 hello_world 就是以 5Hz
印出來的。
Launch 檔是使用 xml 語言編寫的腳本檔,可以把需要執行的 node 貼給 Ros 一次執行,以東京威力比賽時的程式來說,機器上場時需要執行主程式、encoder odometry、IMU、雷射測距、STM 通訊等不同的 node,而如果是用遠端操控的話更是可能會面臨 node 開啟到一半失去連線的風險,launch 檔可以一次呼叫所有需要的 node ,並且編輯需要的參數,相較起來方便許多。另外,上面所提到的rosparam
在關掉 master 的時候就會全部消失,因此 launch 檔另一個重要功能就是儲存與控制重要的參數。
在 pkg 中新增一個 launch 資料夾,所有的 launch 檔都放在這個資料夾下,方便管理。
$ cd tutorial_ws/src/basiclearn #cd [pkg name]
$ mkdir launch
$ code [launch_name].launch
launch file 不需要編譯,可以直接執行,但新增 launch 檔需要執行 $ source/devel/setup.bash
。
$ roslaunch basiclearn my_launch.launch
雖然 launch 會幫忙開啟 roscore,但還是記得先執行 roscore 喔!
launch 檔的宣告需要以<launch>
開頭,</launch>
結尾。
<launch>
[your code]
</launch>
在 launch 檔中會包含許多不同的 tag ,而這些 tag 宣告方式也都和 launch 檔的宣告一樣是以 <tag>
開頭,</tag>
結尾,而如果 tag 內的指令只有單行的話也可寫成<tag... />
,下圖是大概的架構。
<tag .... /> <!--單行-->
<tag> <!--多行-->
.
.
.
</tag>
用來啟動 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 的名字分別被覆蓋成 publisher1
、subscriber1
、subscriber2
,用 $rosnode list
可以發現這些 node 的名字確實被更改過了
所以如果要啟動同一個 node 多次,launch 檔裡的 name 就必須取不同的名字喔 !
output
: 輸出log的目標。
respawn / required
: 是當該節點由於不明原因停止執行的時候,會自動重新啟動。而 required 比較霸道一點,當該節點停止執行的時候,會讓整個 launch 檔都停止執行並且關閉。
當程式裡面寫的 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>
允許在 launch 檔中包含其它的 roslaunch 檔案或 XML 檔案等。
<include file ="$(find pkg_name) / launch_files / launch_name.launch" />
範例:
<arg>的功能是宣告變數,讓我們可以從 command-line 賦值,或是傳遞<include>檔案的變數,但要注意 arg 標籤是非全域性的,一個宣告只針對一個 launch 檔案,如同區域性變數,如果要在其它檔案中使用,則必須要明確的進行值的傳遞。
<arg>有兩種寫法,一個是使用default
,default
是給一個預設值,可以在執行 roslaunch
時修改,另一個是 value
,value
是吃指定值,無法修改,兩者不能寫在同一行。
<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>
<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>
launch 檔架構
如果加在 <node>
標籤底下會成為區域變數,但不論是全域變數還是區域變數,都可以用 rosparam set / get
接收,只是當它是區域變數時需加上 namespace
。
例如 :
如果把 included.launch 的 <param> 移到 <node> 標籤底下,再次執行,使用 $ rosparam list
查看會發現 param 的名字從 /param 變成 /turtle/param。
<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 檔
可以把數個 node 組織成一組 node,並且一次對整組 node 進行操作, 通常用在大型的 launch 檔中,例如一次幫很多個 node 上 namespace;或是加入判斷式,在某些情況運行某個 group,某些情況則運行另一個 group等,。
ns :指定 node 到特定的名稱空間中,名稱空間可以是全域性或是區域性的
可以用來啟動同名 node,避免因為名稱衝突關閉上一個 node
<!-- text -->
在 VScode 中,直接按Ctrl
+/
就會有了
nh.getParam()
取得。