第1章 緒論

機器人作業系統淺析書本結構

第2章 入門概述

1.功能包/軟體包(Packages):在ROS中,所有軟體都被組織為軟體包的形式,稱為ROS軟體包或功能包,有時也簡稱為包。ROS軟體包是一組用於實現特定功能的相關檔案的集合,包括可執行檔案和其他支援檔案。

2.節點管理器(The Master):ROS的一個基本目標是使節點(node)能夠同時執行,ROS中實現通訊的關鍵部分就是ROS節點管理器。

大多數ROS節點在啟動時連線到節點管理器上,如果執行中連線中斷,則不會嘗試重新連線。因此,如果roscore被終止,當前執行的其他節點將無法建立新的連線,即使稍後重啟roscore也無濟於事。

3.節點(Nodes)

啟動節點(也稱執行ROS程式)的基本命令: rosrun

利用圖形檢視節點之間的連線關係:rqt_graph [qt_graph本身就是一個節點,]

4.話題和訊息:ROS節點之間進行通訊所利用的最重要的機制就是訊息傳遞。訊息傳遞的理念是:當一個節點想要分享資訊時,它就會發布(publish)訊息到對應的一個或者多個話題;當一個節點想要接收資訊時,它就會訂閱(subscribe)它所需要的一個或者多個話題。

5.訊息和訊息型別:

獲取當前活躍的話題:rostopic list
列印訊息內容: rostopic echo topic-name
檢視訊息型別: rosmsg show message-type-name
用命令列釋出訊息: rostopic pub –r rate-in-hz topic-name message-type message-content
.

6.理解訊息型別的命名

和ROS裡其他的程式一樣,每條訊息型別都屬於一個特定的包。訊息型別名總會包含一個斜槓,斜槓前面的名字是包含它的包:

package-name/type-name

7.話題通訊的多對多機制:不管哪個節點發布了該話題/turtle1/cmd_vel的訊息,這些訊息將會傳送給每個訂閱了該話題的節點。

節點之間存在鬆耦合關係,ROS為更加直接的一對一通訊提供了一種稱為服務(services)的機制。

第3章 編寫ROS程式

1.建立工作區和功能包:建立一個新ROS功能包的命令應該在你工作區中的src目錄下執行

catkin_create_pkg package-name 【建立了一個存放這個功能包的目錄,並在那個目錄下生成了兩個配置檔案】

第一個配置檔案,package.xml清單檔案(這些資訊只有在你公開發布程式碼時才變得重要。本著保持文件與實際功能同步的精神,至少填寫description和maintainer兩部分可能是比較合理的); 第二個檔案,CMakeLists.txt,是一個Cmake的指令碼檔案(。這個檔案包含了一系列的編譯指令,包括應該生成哪種可執行檔案,需要哪些原始檔,以及在哪裡可以找到所需的標頭檔案和連結庫)

2.編寫簡單程式:

ros::init函式初始化ROS客戶端庫 ros::NodeHandle(節點控制代碼)物件是你的程式用於和ROS系統互動的主要機制 ROS_INFO_STREAM巨集將生成一條訊息,且這一訊息被髮送到不同的位置,包括控制檯視窗

3.編譯程式

4.宣告依賴庫--為了給出依賴庫,編輯包目錄下的CMakeLists.txt檔案,所依賴的其他catkin包可以新增到這一行的COMPONENTS關鍵字後面,如下所示:

find_package(catkin REQUIRED COMPONENTS package-names)

5.清單檔案中列出依賴庫--通過使用build_depend (編譯依賴)和run_depend(執行依賴)兩個關鍵字實現:

<build_depend>package-name</build_depend>
<run_depend>package-name</run_depend>

清單檔案中宣告的依賴庫並沒有在編譯過程中用到;如果你在此處忽略它們,你可能不會看到任何錯誤訊息,直到釋出你的包給其他人,他們可能在沒有安裝所需包的情況下編譯你釋出的包而導致報錯。

6.宣告可執行檔案--CMakeLists.txt中新增兩行,來宣告我們需要建立的可執行檔案。其一般形式是:

add_executable(executable-name
source-files)
target_link_libraries(executable-name
${catkin_LIBRARIES})

7.編譯工作區--catkin_make 因為被設計成編譯你的工作區中的所有包,這個命令必須從你的工作區目錄執行。

    Sourcing setup.bash--最後的步驟是執行名為setup.bash的指令碼檔案,它是catkin_make在你工作區的devel子目錄下生成的。

source devel/setup.bash

這個自動生成的指令碼檔案設定了若干環境變數,從而使ROS能夠找到你建立的功能包和新生成的可執行檔案。它類似於全域性setup.bash,但是是專門為你的工作區量身定做的。除非目錄結構發生變化,否則你只需要在每個終端執行此命令一次,即使你修改了程式碼並且用catkin_make執行了重編譯。

8.執行程式: rosrun

9.釋出者程式:

    9.1建立釋出者物件 釋出訊息的實際工作是由類名為ros::Publisher的一個物件來完成的:

    ros::Publisher
    pub = node_handle.advertise<message_type>(topic_name,
    queue_size);(注意話題名和訊息型別的區別)

    9.2建立並填充訊息物件

    9.3釋出訊息 使用ros::Publisher物件的publish方法可以很簡單地釋出訊息:

    pub.publish(msg);

    9.4定義輸出格式

   9.5 訊息釋出迴圈

    節點是否停止工作的檢查 ros::ok()

    控制訊息釋出頻率 ros::Rate
    rate(2);

   9.6 編譯執行

10.訂閱者程式:

    編寫回調函式
    釋出和訂閱訊息的一個重要的區別是訂閱者節點無法知道訊息什麼時候到達。為了應對這一事實,我們必須把響應收到訊息事件的程式碼放到回撥函式裡,ROS每接收到一個新的訊息將呼叫一次這個函式。訂閱者的回撥函式類似於:

void
function_name(const package_name::type_name &msg)

{

. . .

}

其中引數package_name
和type_name和釋出訊息時的相同,它們指明瞭我們想訂閱的話題的訊息類。回撥函式的主體有許可權訪問接收到訊息的所有域,並以它認為合適的方式儲存、使用或丟棄接收到的資料。

建立訂閱者物件

ros::Subscriber
sub = node_handle.subscribe (topic_name,queue_size,
pointer_to_callback_function);

最後一個引數是指向回撥函式的指標,當有訊息到達時要通過這個指標找到回撥函式。在C++中,你可以通過對函式名使用符號運算子(&,“取址”)來獲得函式的指標

給ROS控制權 只有當我們明確給ROS許可時,它才會執行我們的回撥函式

ros::spinOnce();

這個程式碼要求ROS去執行所有掛起的回撥函式,然後將控制權限返回給我們。另一個方法如下所示:

ros::spin();

這個方法要求ROS等待並且執行回撥函式,直到這個節點關機。

換句話說,ros::spin()大體等於這樣一個迴圈:

while(ros::ok(
))
{
ros::spinOnce();
}

使用ros::spinOnce()還是使用ros::spin()的建議如下:你的程式除了響應回撥函式,如果沒有其他重複性工作要做,那麼使用ros::spin();否則,合理的選擇是寫一個迴圈做其他需要做的事情,並且週期性地呼叫ros::spinOnce()來處理回撥。

忽略ros::spinOnce使程式表現的好像沒有接收到任何訊息

編譯執行

第4章 日誌訊息

ROS日誌系統的核心思想,就是使程式生成一些簡短的文字字元流,即日誌訊息。ROS中,日誌訊息分為五個不同的嚴重級別:

DEBUG
INFO
WARN
ERROR
FATAL

預設的日誌級別是INFO,ROS的C++程式預設只生成INFO或者更高級別的訊息,嘗試生成DEBUG級別的訊息將會被忽略。

第5章 計算圖源命名

1.全域性名稱:

節點、話題、服務和引數統稱為計算圖源,而每個計算圖源由一個叫計算圖源名稱(graph
resource name)的短字串標識。

之所以叫做全域性名稱因為它們在任何地方(譯者注:包括程式碼、命令列工具、圖形介面工具等的任何地方)都可以使用。

2.全域性名稱的幾個組成部分:

前斜槓“/”表明這個名稱為全域性名稱;由斜槓分開的一系列名稱空間(namespace),每個斜槓代表一級名稱空間; 描述資源本身的基本名稱(base name)

3.相對名稱

對計算圖源名稱(ralative graph
resource name),或簡稱為相對名稱(relative name)。相對名稱的典型特徵是它缺少全域性名稱帶有的前斜槓“/”。

4.解析相對名稱 將相對名稱轉化為全域性名稱的過程,ROS將當前預設的名稱空間的名稱加在相對名稱的前面,從而將相對名解析為全域性名稱。理解相對名稱的關鍵是知道ROS解析某個計算圖源時所使用的預設名稱空間。

5.設定預設名稱空間
預設的名稱空間是單獨地為每個節點設定的,而不是在系統範圍進行。為節點選擇一個不同的預設名稱空間的最好也是最常用的方法是在啟動檔案中使用名稱空間(ns)屬性:

_ _ns:=default-namespace

6.私有名稱:

私有名稱,以一個波浪字元(~)開始,是第三類也是最後一類計算圖源名稱。和相對名稱一樣,私有名稱並不能完全確定它們自身所在的名稱空間,而是需要ROS客戶端庫將這個名稱解析為一個全域性名稱。與相對名稱的主要差別在於,私有名稱不是用當前預設名稱空間,而是用的它們節點名稱作為名稱空間。

第6章 啟動檔案 Launch file

利用啟動檔案一次性配置和執行多個節點。

ROS提供了一個同時啟動節點管理器(master)和多個節點的途徑,即使用啟動檔案(launch file)。其基本思想是在一個XML格式的檔案內將需要同時啟動的一組節點羅列出來

執行啟動檔案 :

roslaunch package-name
launch-file-name

roslaunch首先會判斷roscore是否正在執行;如果沒有,則自動啟動roscore。不要將rosrun和roslaunch混為一談,rosrun一次只能啟動一個節點,而roslaunch可以同時啟動多個節點。檔案內所有的節點幾乎都在同一時刻啟動,無法確定各個節點的啟動順序。

建立啟動檔案:將所有啟動檔案統一存放在一個子目錄中,該子目錄通常取名為launch;最簡單的啟動檔案由一個包含若干節點元素(node
elements)的根元素(root element)組成。

插入根元素 啟動檔案是XML檔案,每個XML檔案都必須要包含一個根元素,由一對launch標籤定義:

<launch>
…
<launch>

每個啟動檔案的其他元素都應該包含在這兩個標籤之內。

啟動節點 任何啟動檔案的核心都是一系列的節點元素,每個節點元素指向一個需要啟動的節點。

<node
pkg=”package-name”
type=”executable-name”
name=”node-name”
/>

請求復位
在啟動了啟動檔案中所有的請求節點之後,roslaunch會監視每一個節點,記錄哪一個節點是活躍的。對於每個節點,我們可以設定respawn屬性為真,這樣當節點停止的時候,
roslaunch會重新啟動該節點。

respawn=”true”

這個功能是有意義的,比如在某個節點因為軟體崩潰或硬體故障以及其他原因導致過早退出系統的時候會起到作用。

必要節點(requiring node) 復位之外的另一種策略是將一個節點宣告為必要節點。

required=”true”

當一個必要節點終止的時候,roslaunch會終止所有其他活躍節點並退出。

為節點維護獨立的視窗
使用roslaunch的一個缺點是所有的節點共享一個終端,而使用rosrun的時候,每個節點都有一個獨立的終端。實現該目的的簡潔方法——對節點元素使用啟動字首(launch-prefix)屬性:

Launch-prefix=“command-prefix”

事實上,roslaunch在啟動節點時的內部工作原理是呼叫相應的命令列工具,即rosrun。啟動字首的主要思想是在其命令列前面新增給出的命令列字首。在我們的例子中,遙操作節點使用了這個功能:

launch-prefix=”xetrm-e” (在三個基本屬性之後新增字首屬性)

在名稱空間內啟動節點:通常方法是使用一個啟動檔案,並對其節點元素配置名稱空間(ns)屬性:

ns=”namespace”(在三個基本屬性之後新增空間屬性)

名稱重對映(Remapping names):在ROS應用任何重對映之前,所有的名稱都要先解析為全域性名稱,包括重對映中的原始名稱和新名稱。

啟動檔案的其他元素:

如果想在啟動檔案中包含其他啟動檔案的內容(包括所有的節點和引數),可以使用包含(include)元素:

<include file=”path-to-launch-file”>

此處file屬性的值應該是我們想包含的檔案的完整路徑。由於直接輸入路徑資訊很繁瑣且容易出錯,大多數包含元素都使用查詢(find)命令搜尋功能包的位置來替代直接輸入路徑:

<include file=”$(find package-name)/launch-file-name”>

啟動引數(launch arguments):

引數賦值 :啟動檔案中的每一個引數都要進行賦值:

roslaunch package-name
launch-file-name arg-name:=arg-value

除此之外,你也可以使用以下兩種語法,將引數值作為arg宣告的一部分:

<arg name=”arg-name”
default=”arg-value”/>
<arg name=”arg-name”
value=”arg-value”/>

兩者的唯一區別在於命令列引數可以覆蓋預設值default,但是不能覆蓋引數值value。

建立組(Creating groups):組(group)元素,它提供了一種在大型啟動檔案內管理節點的便捷方式

組可以把若干個節點放入同一個名稱空間內:

<group ns=”namespace”/>
…
</group>

組內的每個節點都從給定的預設名稱空間啟動。

組可以有條件地使能或禁用一個節點:

<group if=”0 or 1”/>
…
</group>

第7章 引數

ROS還提供另一種引數(parameters)機制用於獲取節點的資訊。其主要思想是使用集中引數伺服器(parameter
server)維護一個變數集的值,包括整數、浮點數、字串以及其他資料型別,每一個變數用一個較短的字串標識。

所有的引數都屬於引數伺服器而不是任何特定的節點。這意味著引數——即使是由節點建立的——在節點終止時仍將繼續存在。

查詢引數 :rosparam get parameter_name
設定引數 :rosparam set parameter_name parameter_value

建立和載入引數檔案 為了以YAML檔案的形式儲存名稱空間中的所有引數,可以使用rosparam dump命令:

rosparam dump filename namespace

從一個檔案中讀取引數,並將它們新增到引數伺服器:

rosparam load filename namespace

第8章 服務

服務呼叫是雙向的,一個節點給另一個節點發送資訊並等待響應,因此資訊流是雙向的。服務呼叫實現的是一對一通訊。每一個服務由一個節點發起,對這個服務的響應返回同一個節點。

從命令列檢視和呼叫服務

列出所有服務 :rosservice list
查詢提供服務的節點 即查詢提供給定服務的節點:rosservice node service-name
檢視服務資料型別 :rossrv show service-data-type-name
從命令列呼叫服務 :rosservice call service-name request-content

客戶端程式

  1. 宣告請求和響應的型別
  2. 建立客戶端物件
  3. 建立請求和響應物件
  4. 呼叫服務
  5. 宣告依賴

伺服器程式

建立一個ros::ServiceServer來代替ros::Subscriber,唯一的區別在於伺服器端可以通過一個響應物件和一個表明成功與否的布林變數給客戶端回傳資料。

  1. 編寫服務的回撥函式,服務的回撥函式原型如下:
  2. 建立伺服器物件
  3. 提供ROS控制權
bool function_name(
package_name::service_type::Request
&req),
package_name::service_type::Response
&resp)
) {
…
}

第9章 訊息錄製與回放

通過rosbag,我