1. 程式人生 > >【ROS學習】(七)ROS引數服務(1)

【ROS學習】(七)ROS引數服務(1)

在任何機器人系統中,引數傳遞都是一個十分重要的功能,不論是感測器的設定,還是控制引數的調整,都需要留出方便的引數除錯介面,有的引數只在啟動程式啟動時更改,有的引數卻希望在執行時能夠被動態修改,在ROS中,可以通過引數服務來實現上面的想法。

首先熟悉一下rosparam指令,用rosparam -h可以獲得如下幫助

Commands:
    rosparam set    set parameter
    rosparam get    get parameter
    rosparam load   load parameters from file
    rosparam dump   dump parameters to
file rosparam delete delete parameter rosparam list list parameter names

用上面幾個指令可以方便地用命令列獲得或更改引數。

下面通過一段簡單的程式碼片段來熟悉一下如何向ROS程式傳遞引數

#include <ros/ros.h>

using namespace  std;

int main(int argc, char** argv)
{
    ros::init(argc, argv, "param_demo");
    ros::NodeHandle n;
    ros::NodeHandle pn("~my_namespace"
); string s; int num; n.param<string>("string_param", s, "default_string"); pn.param<int>("int_param", num, 2333); // n.setParam("string_param","default_string"); // pn.setParam("int_param",2333); printf("\nstring_param: %s\n", s.c_str());//輸出初始化值 printf("int_param: %d\n\n"
, num);//輸出初始化值 ros::Rate loop_rate(0.5); while (ros::ok()) { n.getParam("string_param",s); printf("string_param: %s\n", s.c_str()); pn.getParam("int_param",num); printf("int_param: %d\n\n", num); ros::spinOnce(); loop_rate.sleep(); } }

我們來分析一下上面的程式碼片段,首先是引數的初始化

n.param<string>("string_param", s, "default_string");
pn.param<int>("int_param", num, 666);

上面的程式碼表示名稱為”string_param”,值為”default_string”的引數初始化化std::string 型別變數s,用名稱為”int_param”,值為666的引數初始化std::int型別變數num。

//    n.setParam("string_param","default_string"); 
//    pn.setParam("int_param",2333);

這兩句程式碼是設定引數的值,如果程式中註釋掉這兩句程式碼,會產生這樣的後果:

每次新開啟機器時,第一次執行這個節點,s和num的值為我們設定的預設值,但是如果我們用rosparam set指令修改對應引數的值時,引數的值會儲存在引數伺服器中,預設的初始化值就不在有效,也就是說重新啟動這個節點,引數值一直都是修改後的值。

另外註釋掉這兩句程式碼,執行節點時用rosparam list是檢視不到”string_param”和”int_param”兩個引數的,並且對這兩個引數用rosparam get是無效的,但rosparam set仍然有效,可能會帶來一些不方便。

如果不註釋這兩句程式碼,則執行節點時用rosparam list可以在引數列表中看到”string_param”和”int_param”兩個引數,同樣可以使用get和set指令。但會帶來一個問題,就是不管用rosparam set指令將引數設為何值,重新執行節點時引數都被初始化位以上兩句程式碼中給定的值。

所以兩種方法各有優缺點,分場合使用,或者可以使用更方便的launch檔案來配置引數,下文將會提到。

    while (ros::ok())
    {
        n.getParam("string_param",s);   
        printf("string_param:  %s\n", s.c_str());
        pn.getParam("int_param",num);
        printf("int_param:  %d\n\n", num);

        ros::spinOnce();
        loop_rate.sleep();
    }

從引數伺服器中讀取引數值並顯示,可以用rosparam set指令更改引數值。

現在留意一下這個定義

ros::NodeHandle pn("~my_namespace");

私有節點在引數傳遞中十分有用,通常我們定義的普通節點是全域性的,這樣在多個節點具有相同名稱的引數時將會產生問題,設想這樣一種情況,兩個不同的感測器但是都具有一個名為”frequerncy”的引數,當我們用rosparam set將它設定為10hz時,可能會影響到我們並不想配置的那個感測器。所以,我們用私有節點來解決這個問題。私有節點實際上就是給節點指定了它所在的名稱空間。例如,我們可以用rosparam list指令檢視這個節點中的引數,可以看到

/param_demo/my_namespace/int_param
/string_param

如果私有節點定義為

ros::NodeHandle pn("~");

則會看到

/param_demo/int_param
/string_param

在上文提到的引數初始化方式中,如果在程式中給定預設值,則想要改變預設值將需要重新編譯程式,如果要頻繁更改引數的話這種方式就十分不方便,我們還可以在launch檔案中對引數進行設定,避免這個麻煩,參考以下示例

<launch>
  <node name="param_demo" pkg="param_demo" type="param_demo" output="screen"> 
     <param name="string_param" value="from launch file" />
     <param name="int_param" value="999" />
  </node>
</launch>

不過在使用中要注意的是,似乎不能對全域性節點和私有節點的引數同時初始化,所有儘量都使用私有節點來傳遞引數。
用launch檔案初始化引數的優勢是明顯的,可以不檢視源程式就可以知道節點用到的引數以及給定的初始值,並且找到一個合適的引數後,可以將它儲存到launch檔案而不必修改和重新編譯源程式。