機器人作業系統(ROS)淺析(肖軍浩 博士 譯) 學習筆記

第一章:
第 1 章 緒論
1.1 選擇 ROS 的理由 
分散式計算 現代機器人系統往往需要多個計算機同時執行多個 程序,例如: 
 (1)一些機器人搭載多臺計算機,每臺計算機用於控制機器人的 部分驅動器或感測器; 
 (2)即使只有一臺計算機,通常仍將程式劃分為獨立執行且相互 協作的小的模組來完成複雜的控制任務,這 也是常見的做法;
 (3)當多個機器人需要協同完成一個任務時,往往需要互相通訊 來支撐任務的完成; 
 (4)使用者通常通過桌上型電腦、筆記本或者移動裝置傳送指令控制機 器人,這種人機互動介面可以認為是機器人軟體的一部分。 
軟體複用:
    (1) ROS 標準包(Standard Packages)提供穩定、可調式的各類重要機器人演算法實現。 
    (2)ROS通訊介面
快速測試 :
    (1)精心設計的 ROS 系統框架將底層硬體控制模組和頂層資料處 理與決策模組分離;
    (2)ROS 另外提供了一種簡單的方法可以在除錯過程中記錄感測 器資料及其他型別的訊息資料,並在試驗後按時間戳回放,這類記錄的資料叫作包(bag), 一個被稱為 rosbag 的工具可以用於記錄和回放包資料。
總結:ROS的主要程式碼由C++語言 3 編寫,本書後續章節也會介紹如何在ROS中使用C++進行程式設計。 ROS 不僅是一個函式庫,除包含客戶端庫(Client Libraries) 外,還包含一箇中心伺服器(Central Server)、 一系列命令列 工具、圖形化介面工具以及編譯環境。
 
1.2 內容概述 
在groovy及之前的版本中,ROS採用rosbuild系 統來完成軟體的編譯,而在新的版本中,則改用catkin編譯系統。 


第 2 章 入門概述
2.1 安裝 ROS
(1)建立檔案/etc/apt/sources.list.d/ros-latest.list
(2)ros-latest.li檔案中新增資訊:deb http://packages.ros.org/ros/ubuntu trusty main 
(3)wget https://raw.githubusercontent.com/ros/rosdistro/master/ros.key 
(4)sudo apt-key add ros.key 
(5)sudo apt-get update 
(6)sudo apt-get install ros-indigo-desktop-full 
(7)安裝 turtlesim 功能包:sudo apt-get install ros-indigo-turtlesim 
(8)為系統設定 rosdep:sudo rosdep init 
2.2 配置賬戶 
(1)rosdep update 
(2)source /opt/ros/indigo/setup.bash 
   (3)編輯賬戶根目 錄中的檔案.bashrc,在最後一行輸入:source /opt/ros/indigo/setup.bash

2.3 使用 TURTLESIM的小例子
    
2.4 功能包/軟體包(PACKAGES)
    (1)在 ROS 中,所有軟體都被組織為軟體包的形式,稱為 ROS軟體包或功能包,有時也簡稱為包。ROS 軟體包是一組用於實現特 定功能的相關檔案的集合,包括可執行檔案和其他支援檔案。
    (2)使用下行命令,可以獲取所有已安裝的ROS軟體包列表清單:rospack list
        每個程式包由一個清單檔案(檔名為 package.xml)定義。 該檔案定義關於包的一些細節,包括其名稱、版本、維護者和依賴關係。
        使用新的 catkin 編譯構建系統的功能包 ——編譯產生的可執行檔案並未存放在功能包目錄下,而 是存放在一個單獨的標準化目錄層次結構中。
        對於使用 apt-get 安裝的功能包,其所在根目錄為/opt/ros/indigo。可 執行檔案儲存在這個根目錄下的 lib 子目錄裡。
        要找到一個軟體包的目錄,使用 rospack find 命令: rospack find package-name  

        (turtlesim是一個軟體包)一些命令: $ rosls turtlesim , $ rosls turtlesim /images ,
                                         $ roscd turtlesim /images/ , $ roscd turtlesim /images/ 
        影象檢視命令: eog box−turtle . png

2.5 節點管理器(THE MASTER) ——》談如何實際執行 ROS 軟體?
    ROS 的一個基本目標是使機器人專家設計的很多稱為節點 (node)的幾乎相對獨立的小程式能夠同時執行。為此,這些節點必須能夠彼此通訊。ROS中實現通訊的關鍵部分就是 ROS 節點管理器。要啟動節點管理器,使用如下命令: roscore
    大多數 ROS 節點在啟動時連線到節點管理器上,如果執行 中連線中斷,則不會嘗試重新連線。因此,如果 roscore 被終止,當前執行的其他節點將無法建立新的連線,即使稍後重啟 roscore 也無濟於事。 
     roslaunch 的工具,其目的是一次性啟動多個節點。這是一個自適應工具,如果啟動多節點時沒有節 點管理器執行,它會自動啟動節點管理器;如果已經有一個節點 管理器在執行,則會使用已有的。

2.6 節點(NODES)
要啟動節點管理器,使用如下命令: roscore     。當結束時,可以通過在 roscore 終端鍵入 Ctrl-C 停止節點管理器。 
啟動節點(也稱執行ROS程式)的基本命令是rosrun:rosrun package-name executable-name 
如果 roscore 被終止,當前執行的其他節點將無法建立新的連線,即使 稍後重啟 roscore 也無濟於事。 

roscore 
rosrun turtlesim turtlesim_node ——》這個節點是 可執行檔案turtlesim_node的例項化。
rosrun turtlesim turtle_teleop_key ——》是可執行檔案 turtle_teleop_key 的例項化。
 啟動節點:rosrun package-name executable-name 
檢視節點列表:rosnode list ,輸出如下:
                
這裡有三個要點:
    (1)rosout 節點是一個特殊的節點,通過 roscore 自動啟動。/rosout前面的反斜槓“/”表明該節點名稱屬於全域性名稱空間。
    (2)節點名並不一定與對應可執行檔名稱相同。
    (3)檢視節點 要獲得特定節點的資訊,使用如下命令: rosnode info node-name 
                        要終止節點,使用如下命令: rosnode kill node-name 
        終止和重啟節點通常不會 對其他節點有較大影響;即使節點間正在相互交換訊息(message),這些連線也會在節點終止時斷開,在節點重啟時重新連線。 
        還可以用 Ctrl-C 命令終止節點。但使用這種方法時可能不會在節點管理器中登出該節點,因此會導致已終止的節點 仍然在 rosnode 列表中。這雖然沒有什麼壞處,但可能會 讓使用者對當前系統的行為感到困擾。此時可以使用下面的 命令將節點從列表中刪除: rosnode cleanup 

2.7 話題和訊息 
turtlesim 例子中,遙控節點和模擬節點必須以某種方式進行對話。    
遙控節點:上下左右按鍵
模擬節點:移動的海歸

ROS節點之間進行通訊所利用的最重要的機制就是訊息傳遞。在ROS中,訊息有組織地存放在話題裡。且訊息是直接地從釋出節點傳遞到訂閱節點,中間並不經過節點管理器轉交。
ROS節點管理器負責確 保發布節點和訂閱節點能找到對方。


檢視節點構成的計算圖 :在ROS系統中檢視節點之間的釋出-訂閱關係的最簡單方式就是在終端輸入如下命令: rqt_graph ,r 代表 ROS,qt 指的是用來實現這個視覺化程式的 Qt 圖形介面(GUI)工具包。

/teleop_turtle節點向話題/turtle1/cmd_vel釋出訊息(上下左右移動的按鍵), 而/turtlesim節點訂閱了這些訊息(“cmd_vel”是“command velocity”的縮寫)

當按下一個鍵時,/teleop_turtle 節點會以訊息的形式將這些運動控制命令釋出到話題/turtle1/cmd_vel;與此同時,因為 turtlesim_node 訂閱了該話題,因此它會接收到這個些訊息,控制 海龜按照該預定的速度移動.
    節點:/teleop_turtle
    訊息:運動控制命令
    話題:/turtle1/cmd_vel
    
 請注意: rqt_graph 本身就是一個節點。所有的節點發布都向話題/rosout 釋出訊息,該話題由同名的 /rosout 節點訂閱。/rosout這個話題的作用是用來生成各個節點的文字日誌訊息。此處的名稱/rosout 既指節點又指話題。但 ROS 並不會因這 種重複的名字而混淆,因為 ROS 會根據上下文來推測我們 討論的是/rosout 節點還是/rosout 話題。 

為了獲取當前活躍的話題,使用如下命令: rostopic list 
列印訊息內容:rostopic echo topic-name 
有兩個命令可以用來測量訊息釋出的頻率以及這 些訊息所佔用的頻寬: rostopic hz topic-name ; rostopic bw topic-name 
檢視話題:rostopic info topic-name 
檢視訊息型別: rosmsg show message-type-name 
和 ROS 裡其他的程式一樣,每條訊息型別都屬於一個特定的包。訊息型別名總會包含一個斜槓,斜槓前面的名字是包含它的包: package-name/type-name 
2.7.2 訊息和訊息型別 
訊息裡到底包含了什麼資訊?
——》話題的訊息型別能告訴你該話題中每個訊息攜帶了哪些資訊,以及這些資訊是如何組織的。
命令:rostopic list 
檢視話題命令:rostopic info /turtle1/cmd_vel 
檢視訊息型別:rosmsg show geometry_msgs/Twist
理解訊息型別的命名:訊息型別名總會包含一個斜槓,斜槓前面的名字是包含它的包:package-name/type-name 
                            


輸入命令:rostopic echo /turtle1/cmd_vel ,並按下按鍵時,有一下輸出:

用命令列釋出訊息:rostopic pub –r rate-in-hz topic-name message-type message-content ,這條命令重複地按照指定的頻率給指定的話題釋出指定的訊息。 
            例子:rostopic pub –r 3 /turtle1/cmd_vel geometry_msgs/Twist ’[0,0,0]’ ’[0,0,1]’ 
                              頻率    話題              型別                 訊息

2.8 一個更復雜的例子 
    於絕大多數設計精巧的 ROS 節點是鬆耦合的。每個節點都不需要顯式知道其他節點的存在與否;它們的唯一互動方式是間接地發生在基於話題和訊息的通訊層。
    ROS 為更加直接的一對一通訊提供了一種稱為服務 (services)的機制。


    當ROS沒有按你的預期執行時,一種可能有幫助的工具,也是本章要學習的最後一個命令列工具:roswtf

第 3 章 編寫 ROS 程式 
3.1 建立工作區和功能包  
    (1)現在,終於是時候開始建立你自己的 ROS 程式了。本章將介紹如何建立一個開發工作區,並且 在此工作區編寫三個短程式。
    (2)在我們寫任何程式之前,第一步是建立一個容納我們的功能包的工作區,然後再建立功能包本身。
    (3)建立工作區 :mkdir /home/jokane-/ros
        建立功能包 :建立一個新ROS功能包的命令應該在你工作區中的 src目錄下執行:catkin_create_pkg  package-name ,執行完後會在包中自動生成兩個檔案:CMakeLists.txt, package.xml。
        第一個配置檔案,叫做 package.xml,是我們在 2.4 節討論過 的清單檔案。
        第二個檔案,叫做CMakeLists.txt,是一個Cmake的指令碼檔案。Cmake 是一個符合工業標準的跨平臺編譯系統。這個檔案包 含了一系列的編譯指令,包括應該生成哪種可執行檔案,需要哪些原始檔,以及在哪裡可以找到所需的標頭檔案和連結庫。 當然,這個檔案表明 catkin 在內部使用了 Cmake。
    注意:(1)ROS 包的命名遵循一個命名規範,只允許使用小寫字母、 數字和下劃線。
       (2)這個三層的目錄結構(一個工作區目錄,包含了一個src目錄,src中又包含了一個功能包目錄)對於簡單專案和小工作區似乎有些大材小用,但是catkin編譯系統需要它。 

3.2 你好,ROS! 
清單檔案:package.xml,該檔案定義關於包的一些細節,包括其名稱、版本、維護者和依賴關係。
標頭檔案 ros/ros.h 包含了標準 ROS 類的宣告,將會在每一個 你寫的 ROS 程式中包含它。 
建立功能包 :catkin_create_pkg agitr 
在agitr檔案包中會生成兩個配置檔案:package.xml和CMakeLists.txt

編譯 Hello 程式 ,一共有四個步驟 :
(1)宣告依賴庫:在CMakeLists.txt檔案中
       <build_depend>roscpp</build_depend>
        <run_depend>roscpp</run_depend>  ——》 format="2"時已經不需要這條了。

(2)宣告可執行文,在CMakeLists.txt檔案中
        其一般形式是:
                find_package(catkin REQUIRED COMPONENTS roscpp)

                add_executable(hello hello.cpp) 
                target_link_libraries(hello ${catkin_LIBRARIES}) 
(3)編譯工作區 
        在工作區目錄執行:catkin_make

(4)Sourcing setup.bash 
執行 hello 程式 :
        A:在終端執行:roscore
        B:執行:source devel/setup.bash 
        C:執行:rosrun agitr hello
 
程式程式碼解析:
(1)ros::init函式初始化ROS客戶端庫。請在你程式的起始處呼叫一次該函式。函式最後的引數是一個包含節點預設名的字串:ros::init( argc , argv , " hello_ros" ) ; 
(2)ros::NodeHandle(節點控制代碼)物件是你的程式用於和ROS系統互動的主要機制。建立此物件會將你的程式註冊成一個節點。僅僅在第一個 NodeHandle 物件建立時才會在節點管理器註冊新的節點。 同樣,只有當所有的 NodeHandle 物件都銷燬後。

3.3 釋出者程式 
    原始檔稱為pubvel.cpp:傳送隨機生成的速度指令到一個turtlesim海龜,使它漫無目的地巡遊。
一、釋出訊息 :
(1)包含訊息型別宣告 
    每一個 ROS 話題都與一個訊息型別相關聯。每一個訊息型別都有一個相對應 C++標頭檔案。你需要在你的程式中為每一個用到 的訊息型別包含這個標頭檔案,程式碼如下所示: 
    #include <package_name/type_name.h> 
當引用 C++程式碼中的訊息類時,你將會使 用雙分號(::)來區分開包名和型別名,雙分號也稱為範圍解析運算子。
#include <geometry_msgs/Twist.h> ,標頭檔案定義了一個名為 geometry_msgs::Twist 的類。

(2)建立釋出者物件 
    釋出訊息的實際工作是由類名為ros::Publisher 的一個物件來完成的。
        ros::Publisher pub = node_handle.advertise<message_type>( topic_name, queue_size); 
    如果想從同一個節點發布關於多個話題的訊息,你需要為每個話題建立一個獨立的 ros::Publisher 物件。建議為每一個話題建立一個釋出者,並且在你程式執行的全 過程中一直使用那個釋出者。因為建立一個釋出者是一個很耗時的操作。

(4)釋出訊息 :使用 ros::Publisher 物件的 publish 方法可以很簡單地釋出訊息:
                    pub.publish(msg); 
    這個方法將所給的訊息新增到釋出者的輸出訊息佇列中,從這裡, 它會盡快被髮送到相同話題的訂閱者那裡。

(5)定義輸出格式  
二、訊息釋出迴圈 
 (1)節點是否停止工作的檢查:pubvel 的 while 迴圈的條件是: ros::ok()  
        這個函式檢查我們的程式作為 ROS 節點是否仍處於執行良好的狀態。

 (2)控制訊息釋出頻率     
        ros::Rate rate(2); 
        鄰近每次迴圈迭代的結尾,我們調 用此物件的 sleep 方法:
                         rate.sleep();
        每次呼叫此方法時就會在程式中產生延遲。延遲的持續時間被用來阻止迴圈的迭代速率超過指定的速率。沒有這種控制,程式會以計算機允許的最快速度釋出訊息,這樣會佔滿釋出和訂閱的序列,並且浪費計算和網路資源。
 (3)編譯 pubvel 
宣告訊息型別依賴庫:  因為pubvel使用了來自geometry_msgs包 的訊息型別,
            find_package(catkin REQUIRED COMPONENTS roscpp geometry_msgs)
    在 package.xml 檔案中,我們新增新的依賴項: 
        <build_depend>geometry_msgs</build_depend> 
 (4)執行 pubvel :
        rosrun agitr pubvel

3.4 訂閱者程式
訂閱 turtlesim_node釋出的/turtle1/pose 話題。這一話題的訊息描述了海龜的位姿(位置和朝向)。
這裡有三個新的知識點:
    (1)編寫回調函式 
        回撥函式類似於: 
            void function_name(const package_name::type_name &msg){ . . . } 
    其中引數 package_name 和 type_name 和釋出訊息時的相同,它們指明瞭我們想訂閱的話題的訊息類。

    (2)建立訂閱者物件 
        建立一個 ros::Subscriber物件: 
            ros::Subscriber sub = node_handle.subscribe(topic_name,queue_size, pointer_to_callback_function); 
        e.g:ros::Subscriber sub = nh.subscribe ( " turtle1/pose " ,1000,&poseMessageReceived );
    可以通過如下兩個方法減少訂閱者佇列溢位的可能性:
        (1)通過呼叫 ros::spin 或者 ros:spinOnce 確 保允許回調發生;
        (2)減少每個回撥函式的計算時間。  
    (3)給ROS控制權 
        最後的複雜之處在於只有當明確給ROS許可時,它才會執行的回撥函式。
        實際上有兩個略微不同的方式來做到這一點:
            (1)ros::spinOnce(); ——》這個程式碼要求 ROS 去執行所有掛起的回撥函式,然後將控制權限返回給我們。
            (2)ros::spin(); ——》這個方法要求 ROS 等待並且執行回撥函式,直到這個節點關機。 
                ros::spin()大體等於這樣一個迴圈: 
            while(ros::ok( ))          
            {
                 ros::spinOnce(); 
            }
    使用 ros::spinOnce()還是使用 ros::spin()的建議如下:你的程 序除了響應回撥函式,還有其他重複性工作要做嗎?如果答案是 “否”,那麼使用 ros::spin();否則,合理的選擇是寫一個迴圈,做 其他需要做的事情,並且週期性地呼叫 ros::spinOnce()來處理回撥。 
注意:忽略了呼叫 ros::spinOnce 或 ros::spin。在這種情況下,ROS 永遠沒有機 會去執行你的回撥函式。
3.4.1 編譯並執行 subpose 
    執行:rosrun turtlesim turtlesim_node    ——》 rosrun agitr pubvel (啟動釋出訊息) ——》 rosrun sub subpose(啟動訂閱訊息)

第 4 章 日誌訊息
4.1 嚴重級別 
    按照嚴重性程度遞增,這些級別有: 
        DEBUG INFO WARN ERROR FATAL
4.2 示例程式
        ROS_DEBUG_STREAM
         ROS_INFO_STREAM
        ROS_WARN_STREAM
        ROS_ERROR_STREAM
         ROS_FATAL_STREAM
    預設的最小級別是INFO.
4.3 生成日誌訊息
        ROS_DEBUG_STREAM(message);
         ROS_INFO_STREAM(message); 
        ROS_WARN_STREAM(message); 
        ROS_ERROR_STREAM(message); 
        ROS_FATAL_STREAM(message); 
        ——》其中各個巨集的引數message可以是C++中標準輸出流(ostream)中的各種表示式。
    (1)生成一次性日誌訊息 :
        ROS 提供了可以僅僅生成一次日誌訊息的簡單的巨集。 
        ROS_DEBUG_STREAM_ONCE(message); 
        ROS_INFO_STREAM_ONCE (message); 
        ROS_WARN_STREAM_ONCE (message); 
        ROS_ERROR_STREAM_ONCE (message); 
        ROS_FATAL_STREAM_ONCE (message); 
    (2)生成頻率受控的日誌訊息 :
        ROS_DEBUG_STREAM_THROTTLE(interval, message); 
        ROS_INFO_STREAM_THROTTLE(interval, message); 
        ROS_WARN_STREAM_THROTTLE(interval, message); 
        ROS_ERROR_STREAM_THROTTLE(interval, messge); 
        ROS_FATAL_STREAM_THROTTLE(interval, message); 
    ——》引數 interval 是 double型別的,它表示以秒為單位的時間量,這是相鄰日誌訊息出現的最小時間間隔。
4.4 檢視日誌訊息
(1)最明顯的是,日誌訊息可以被送至控制檯。
        DEBUG和INFO訊息被列印至標準輸出(standard output),而 WARN、 ERROR和FATAL訊息將被送至標準錯誤(standard error)。
        格式化控制檯訊息 :[${severity}] [${time}]: ${message} 
(2)除了在控制檯上顯示,每一個日誌訊息都被髮布到話題 /rosout 上。
        話題的訊息型別是 rosgraph_msgs/Log。
        /rosout 話題的主要作用是它在一個流中包含了系統中所有節點的日誌訊息。
        由於/rosout 只是一個普通的話題,你當然可以通過 :
            rostopic echo /rosout  這條命令直接檢視訊息內容。
        檢視/rosout訊息最簡單的方式是使用下面這條命令: 
                    rqt_console  ——》rqt_console 訂閱的是/rosout_agg,而不是/rosout。字尾_agg 表示訊息實際上是被 rosout 節點聚合到一起的。 /rosout 話題釋出        的訊息都通過 rosout 節點輸出到 /rosout_agg 話題上。 
(3)日誌訊息的第三個,也是最後一個目的地,是由 rosout 節點生成的日誌檔案。
        由 rosout 節點生成的日誌檔案。作為/rosout 話題回撥函式的一部分,該節點可以將日誌訊息作為一行寫入到一個日誌檔案,檔名類似於: 
            ~/.ros/log/run_id/rosout.log 這裡的 rosout.log 日誌檔案是純文字檔案。
    使用下面這條命令來檢視當前賬戶中被ROS日誌消 耗的硬碟空間: 
         rosclean check 
    如果日誌正在消耗過多的硬碟空間,可以通過下面的命令刪除所有已經存在的日誌: 
         rosclean purge 

4.5 啟用和禁用日誌訊息 
rqt_console 會接收任何輸入的日誌訊息,其過濾選項只是選擇性地顯示其中的一部分。
設定日誌的方法有兩種:
(1)為了通過命令列設定一個節點的日誌級別,可以使用與以下類似的命令: 
rosservice call  /node-name/set_logger_level   ros.package-name  level 
——》這條命令呼叫 set_logger_level 服務,該服務由各個節點自動提供。 
 node-name 是你期望設定日誌級別的節點名稱 
 package-name 正如你猜測的一樣,是擁有這個節點的功能包 的名稱 
 level 引數是 DEBUG、INFO、WARN、ERROR、FATAL 中的一個字串,即為節點設定的日誌級別。
(2)通過圖形介面設定日誌級別 :
        以嘗試以下命令: rqt_logger_level 
  (3)通過 C++程式碼設定日誌級別 :
最直接的方式是呼叫 ROS 來實現日誌功能的 log4cxx 提供的介面:
#include <log4cxx/logger.h> 
log4cxx::Logger::getLogger(ROSCONSOLE_DEFAULT_NAME)->setLevel(
     ros::console::g_level_lookup[ros::console::levels::Debug] );
 ros::console::notifyLoggerLevelsChanged(); 
 呼叫 ros::console::notifyLoggerLevelsChanged()是有必要的,因為每個日誌的啟用或者禁用是快取了的。

第 5 章 計算圖源命名 
5.1 全域性名稱 
(1)節點、話題、服務和引數統稱為計算圖,而每個計算圖源由一個叫計算圖源名稱(graph resource name)的短字串標識。
一個全域性名稱的幾個組成部分: e.g:/turtle1/cmd_vel 
 前斜槓“/”表明這個名稱為全域性名稱。 
 由斜槓分開的一系列名稱空間(namespace),每個斜槓代表 一級名稱空間。e.g:turtle1
     描述資源本身的基本名稱(base name)。e.g:cmd_vel

5.2 相對名稱 
     相對名稱的典型特徵是它缺少全域性名稱帶 有的前斜槓“/”。
     預設名稱空間的名稱 + 相對名稱 = 全域性名稱,e.g:/turtle1  +  cmd_vel =》/turtle1/cmd_vel 
                   預設名稱空間   相對名稱      全域性名稱 
     為節點選擇一個不同的預設命 名空間的最好也是最常用的方法是在啟動檔案中使用名稱空間 (ns)屬性。 ROS 還提供了一些其他機制支援這種操作:
    (1)大部分 ROS 程式接 受叫做__ns 的命令列引數,此引數將為程式指定一個預設命 名空間。 
            __ns:=default-namespace
     (2)還可以利用環境變數為在 shell 內執行的 ROS 程式設定預設名稱空間。 
            Export ROS_NAMESPACE=default-namespace 

5.3 私有名稱
     與相對名稱的主要差別在於,私有名稱不是用當前預設名稱空間,而是用的它們節點名稱作為名稱空間。每個節點內部都有這樣一些資源,這些資源只與本節點有關,而不會與其他節點打交道,這些資源 就可以使用私有名稱。
其私有名稱∼max_vel 轉換至如下全域性名稱:
 /sim1/pubvel  + ~max_vel => /sim1/pubvel/max_vel    
             私有名稱 全域性名稱 
5.4 匿名名稱(ANONYMOUS NAMES)
    為了請求一個匿名名稱,節點需要將 ros::init_options::Anonymous-Name 作為第四個引數傳遞給 ros::init 方法: 
ros::init(argc, argv, base_name, ros::init_options::AnonymousName);