ros入門 工程目錄結構、節點通信方式、基本操作命令

ROS(Robot Operating System)

ros的基本框架是斯坦福大學人工智能實驗室在STAIR項目與機器人技術公司Willow Garage的個人機器人項目(Personal Robots Program)之間的合作中爲了提高機器人研發中的軟件複用率,由吳恩達教授指導的Morgan Quigley博士於2007年主導設計與實現的。一年後交由著名的機器人實驗室Willow Garage繼續開發維護。後於2010年正式發佈。2013年,Willow Garage的創辦者也是注資人爲了全身心的投入到自己創辦的公司,關閉了實驗室。此後ros的維護工作交給OSRF(Open Source Robotics Foundation)接管。

ros的特點

1、分佈式的結構

一個進程在ros中稱作一個節點(node),每個功能節點可以單獨編譯,節點之間形成點對點的通信,適合多機協同工作。

2、語言支持廣泛

功能節點的接口與編程語言無關。支持C++、Python、Java等。

3、集成度高功能完備

集成了衆多的開源項目,如OpenCV、pcl(point cloud library)等。

4、豐富的組件化工具

物理仿真環境gazebo,3d的數據可視化工具rviz,數據記錄工具rosbag,Qt工具箱rqt_* 。

5、免費且開源

遵循BSD許可協議,可以隨意修改並商用,功能包的數量迅速增加。

 

總結以上特點,ros以其分佈式的通信機制爲核心,輔以實用的開發工具、豐富的開源功能包,逐漸形成了從社區中汲取知識提升開發效率然後開源分享自己的開發成果的良性生態系統。

 


 

ros的系統實現

文件系統級

所謂文件系統,就是說我們的項目放在硬盤裏,到底是什麼目錄結構,長什麼樣子。一個典型的ros工程如下圖所示:

首先我們看到最頂層的catkin工作空間,它是整個ros工程中層次最高的概念。工作空間也就是我們管理和組織ros工程項目文件的地方。而catkin是ros定製的編譯構建系統,是對CMake的擴展,對ros這樣大體量的工程有更好的支持,同時也簡化了操作。

簡言之,catkin工作空間就是一個文件夾,用來組織和管理ros功能包。我們可以使用catkin對其進行編譯。

那麼工作空間如何創建呢?

首先利用mkdir命令建立一個工作空間文件夾(名字任意取),並在這個文件夾下建立一個名爲src的文件夾(必須取名爲src)

mkdir -p ~/catkin_ws/src

然後進入src文件夾下,調用catkin_init_workspace命令初始化工作空間。

cd ~/catkin_ws/src
catkin_init_workspace

這時我們的src文件夾下會多出一個CMakeLists.txt文件,我們的工作空間也就創建好了。

好的,現在我們回過頭來解釋一下剛剛建立的src目錄以及初始化工作空間後自動生成的CMakeLists.txt文件是幹什麼的。ros工程默認src目錄作爲存放功能包(package)的文件夾,即源文件空間(the source space)。而這裏的(即主目錄下的)CMakeLists.txt文件規定了工程的編譯規則,包括指定工程名稱,指定工程採用的語言,設置編譯類型(debug或release),設置編譯器的類型(eg:C、C++),添加要編譯的子目錄(即功能包,順序任意)。

我們剛剛初始化的工作空間其實就是一個空的工作空間,裏面什麼都沒有,我們還沒有往裏面寫入任何的程序文件,這也是結構最簡單的工作空間。這時我們可以使用catkin_make命令對工作空間進行編譯(事實上,我們可以不必特意對工作空間進行初始化,而直接使用catkin_make命令進行編譯,catkin_make會自動爲我們初始化工作空間)

cd ~/catkin_ws
catkin_make

注意:使用catkin_make編譯之前一定要回到工作空間,在其他目錄下進行catkin_make是會失敗的。

編譯完成之後,我們工作空間下會多出build和devel兩個文件夾,build文件夾下存放的是CMake和catkin的緩存信息、配置信息和其他中間文件,稱爲編譯空間(the build space)。devel文件夾中存放的是編譯後生成的目標文件,包括頭文件、動態&靜態鏈接庫、可執行文件等,稱爲開發空間(the development space)。

一般在catkin_make編譯之後要記得source一下devel目錄下的setup.bash文件,將編譯生成的文件刷新到系統環境中,否則我們調用生成的可執行文件時系統會找不到。

source ~/catkin_ws/devel/setup.bash  #編譯之後要用source刷新環境

對我們來說,在編寫ros工程以及調試程序過程中,只有src目錄是我們直接寫代碼的地方。所以這裏我們主要介紹src目錄。

src目錄中可以平行放置多個功能包

也可以創建多級目錄,把多個功能包放在一個文件夾下

功能包是ros文件系統中組織程序文件的基本單元,也就是catkin編譯的基本單元。功能包中不同類型的文件分別放置在不同的文件夾中,同時,一個package可以包含多個可執行文件。

那麼如何判斷一個文件夾是不是package呢?如下圖:

一個文件夾下包含CMakeLists.txt和package.xml兩個文件,即定義了一個軟件包,也就可以被編譯系統編譯了。這也是package最精簡的結構。每個package中都必須包含這兩個文件,這裏的(即子目錄中的)CMakeLists.txt文件中規定了功能包的編譯規則,包括指定功能包名稱,指定編譯依賴項,指定要編譯的源文件,指定要添加的消息格式文件/服務格式文件/動作格式文件,指定生成的消息/服務/動作,指定頭文件搜索目錄,指定鏈接庫搜索目錄,指定生成的靜態鏈接庫文件,指定需要鏈接的庫文件,指定編譯生成的可執行文件以及路徑等等。而package.xml文件定義了功能包的屬性信息,包括包名,版本號,作者,編譯依賴和運行依賴等。

除了這兩個文件,package中還有代碼文件,代碼文件又分爲腳本文件(比如Python文件 *.py,Linux下的shell文件 *.sh)、頭文件(*.h)和源程序文件(*.cpp以及其他編程語言格式),分別存放在scripts目錄、include目錄和src目錄中。

package中還會存放我們自定義的通信格式文件,包括消息(*.msg)、服務(*.srv)以及動作(*.action),分別存放在msg目錄、srv目錄和action目錄中。

最後,package中還有launch文件(*.launch)以及配置文件(*.yaml以及其他的標籤類格式),launch文件的作用是批量運行多個可執行文件,存放於launch目錄。配置文件當中主要包含的是相關的參數配置,比如機器人的尺寸參數,關節座標系的tf變換參數等,存放在config目錄中。

接下來大家可能會問,如何創建一個package呢?我們可以使用catkin_create_pkg命令創建功能包,同時指定依賴:

catkin_create_pkg package_name dep1 dep2 dep3...

創建完成之後,功能包中會產生CMakeLists.txt和package.xml兩個文件。

既然功能包都有各自的依賴項,那麼當我們克隆別人的功能包來使用的時候,我們的系統環境中可能並沒有那個功能包的依賴項,這時該怎麼辦呢?我們可以利用rosdep命令爲功能包安裝所需要的依賴(根據package.xml中指定的依賴):

rosdep install package_name

最後介紹幾個有關功能包的命令行工具:rospack、rosls、roscd、roscp、rosed

  • rospack list 列出所有功能包
  • rospack find package_name 查找功能包路徑
  • rospack depends package_name 查看功能包依賴的包
  • rosls package_name 列出功能包中的內容
  • roscd package_name 進入功能包所在路徑
  • rosed package_name file_name 編輯功能包中的文件
  • roscp  package_name  file_name  target_path 從功能包中複製文件到其他目錄

到此爲止,關於package的內容就介紹完了。下面我們介紹一下有關package的另一個概念。

與功能包相區別,還有一個元功能包(metapackage)的概念。它的作用是將多個功能類似的用於同一目的的功能包組織到一個文件夾當中。元功能包其實是一個虛包,裏面沒有實質性的內容,但是它依賴了很多其他的軟件包,通過這種方法把其他的軟件包組合起來。比如導航功能包navigation,它其實是一個元功能包,元功能包當中有一個與其同名的功能包,其中除了一些log信息,readme文件,就只有CMakeLists.txt和package.xml文件。並且CMakeLists.txt文件中沒有指明要生成什麼可執行文件,或生成什麼庫,其中只多出了一句catkin_metapackage()宏命令,這句命令聲明瞭這個包是一個元功能包。而package.xml中註明了元功能包依賴的功能包。

metapackage的最主要的功能是讓我們安裝的時候更方便,可以一次性下載所有相關的功能包。

OK,文件系統級系統實現的內容就是這些。下面介紹計算圖級的系統實現。

 


 

計算圖級

所謂計算圖,就是指我們的程序運行起來,會形成什麼樣的網絡通信結構,進程之間數據以何種路徑流通。一個常見的計算圖如下圖所示:

這是一個比較簡單的計算圖,其中只包含了幾個進程,以及它們之間的數據流向。

事實上,計算圖級中包含了很多概念。包括節點(node)、節點管理器(master)、話題(topic)、服務(service)、動作(action)、參數服務器(parameter server)等等。

1、節點

ros中將進程稱爲節點,需要注意節點名不一定與對應的可執行文件名相同(一般設置爲相同的名稱)。節點之間各自獨立,它們通過話題、服務和參數服務器進行通信。ros通過使用節點將代碼以及功能解耦(ros節點之間並不直接連接,ros 節點只管發佈它們有用的信息,而不需要擔心是否有其他節點來訂閱這些消息)。

ros提供了一個處理節點的工具:rosnode,支持的命令如下:

  • rosnode info node_name 輸出此節點信息
  • rosnode list 列出所有當前運行的節點
  • rosnode kill node_name 關閉此節點
  • rosnode machine hostname 列出此主機上運行的節點
  • rosnode cleanup 清除無法訪問節點的註冊信息
  • rosnode ping node_name 測試節點的連通性

ros還提供了一個啓動節點和修改節點名稱、話題名稱以及參數名稱的工具:rosrun,我們無需重新編譯代碼即可重新配置節點。用法如下:

  • rosrun package_name executable_name 啓動某功能包中的某節點
  • rosrun package_name executable_name  _name:=node_name 修改節點名稱,使用 node_name 參數給出的名稱覆蓋節點的默認名
  • rosrun package_name executable_name topic_name:=new_topic_name 修改節點發布或訂閱的話題名稱
  • rosrun package_name executable_name _paramname:=9 修改節點中的參數,注意要在參數名稱前添加一個下劃線

還記得在文件系統級中講過批量啓動ros節點的*.launch文件嗎,我們可以使用roslaunch命令啓動launch文件,它還會幫我們自動啓動節點管理器。

  • roslaunch  package_name  *.launch

2、節點管理器

master用於節點的名稱註冊和查找,也用於設置節點間的通信。因此在啓動節點之前必須要先啓動master:

roscore

這句命令不僅僅啓動了master,還同時啓動了日誌輸出(rosout)和參數服務器(parameter server)。rosout是一個節點,這個節點的作用是用來生成各個節點的文本日誌消息。它訂閱了同名話題rosout,所有的節點都向這個話題發佈消息。參數服務器是通過網絡訪問的,共享的多變量字典,相當於全局變量庫。它使用XMLRPC實現並運行在master中。

參數服務器通信的過程爲:

  • 1、節點1設置參數值;
  • 2、節點2查詢參數值;
  • 3、參數服務器向節點2發送參數值。

三步全部使用RPC(Remote Procedure Call)協議。

ros中關於參數服務器的工具是rosparam,支持的參數如下:

  • rosparam list 列出所有參數
  • rosparam get param_name 獲取參數值
  • rosparam set param_name value 設置參數值
  • rosparam delete param_name 刪除參數
  • rosparam dump filename 將參數字典保存到文件中
  • rosparam load filename 從文件中加載參數字典

啓動了master,接下來我們就可以啓動節點了,每啓動一個節點,就會向master註冊該節點,然後各個節點之間才能進行通信。master的作用是使ros節點之間能夠相互查找,並建立點對點通信,消息直接從發佈節點傳遞到訂閱節點,中間不經過master轉交。注意,ros是一個分佈式網絡系統,我們可以在某一臺電腦上運行master,在其他電腦上運行節點。

3、話題

話題是ros中使用最多的,單向的通信方式,相當於節點間傳輸數據的總線異步(發佈消息的節點只管發佈,不管是否有其他節點接收;訂閱的節點收到消息就處理,收不到就等待,不管是否有其他節點發布消息),多對多(一個話題可以由多個節點發布,也可以被多個節點同時訂閱)。適用於連續、高頻的數據傳輸工作(如激光雷達、里程計發佈數據)。ros話題的消息可以通過TCP/IP和UDP傳輸,基於TCP傳輸稱爲TCPROS,是默認的傳輸方式;基於UDP傳輸稱爲UDPROS,是一種低延遲高效率但不穩定的傳輸方式,可能丟失數據,適合遠程操作任務。

那麼,兩個節點間建立話題通信的過程是什麼樣的呢?請看下圖:

如圖,話題的通信過程(假設節點1爲發佈節點,節點2爲訂閱節點,節點1先啓動):

  • 1、節點1向master註冊,包括髮布話題名稱、節點所在地址等;
  • 2、節點2向master註冊,包括訂閱話題名稱、節點所在地址等;
  • 3、master將註冊列表中的信息進行匹配,匹配完成後將節點1的地址發送給節點2;
  • 4、節點2根據master發來的地址向節點1發送連接請求,包括訂閱的話題名稱、以及通信協議類型(TCP);
  • 5、節點1收到連接請求後反饋一個確認信息,包括自身的TCP地址;
  • 6、節點2收到TCP地址後與節點1建立TCP網絡連接;
  • 7、節點1將數據發送給節點2。

其中,前五個步驟採用XMLRPC的通信協議;後面兩個步驟採用TCP通信協議。

ros中用於主題操作的工具是rostopic,支持參數如下:

  • rostopic list 列出當前存在的話題
  • rostopic info topic_name 輸出話題的相關信息,包括髮布者、訂閱者、以及相關服務的信息
  • rostopic bw topic_name 顯示話題所佔帶寬
  • rostopic echo topic_name 輸出本話題的消息
  • rostopic find msg_type 按照消息類型查找話題
  • rostopic hz topic_name 顯示話題發佈的頻率
  • rostopic pub topic_name msg_data 向話題發佈消息
  • rostopic type topic_name 輸出本話題的消息類型(即消息名稱)

4、服務

服務是ros中的雙向通信方式。同步(一個節點向另一個節點發送服務請求,並等待請求被處理,等待過程中阻塞停止運行。服務節點收到請求後立即處理,處理完成後向請求節點反饋處理結果。請求節點收到回覆後繼續運行。),一對多(一個服務只能由一個服務節點提供,可以向多個請求節點提供服務)。適用於偶爾調用的邏輯處理功能或具體的任務(如開關傳感器、運動學求解等)。

服務通信過程如下圖:

服務的通信過程(假設節點1爲服務節點,節點2爲請求節點,節點1先啓動):

  • 1、節點1向master註冊,包括髮布話題名稱、節點所在地址等;
  • 2、節點2向master註冊,包括訂閱話題名稱、節點所在地址等;
  • 3、master將註冊列表中的信息進行匹配,匹配完成後將節點1的地址發送給節點2;
  • 4、請求節點與服務節點建立TCP連接併發送請求數據;
  • 5、服務節點處理請求數據並反饋應答數據。

其中,前三個步驟採用XMLRPC的通信協議;後面兩個步驟採用TCP通信協議。

ros中用於服務操作的工具是rosservice,支持參數如下:

  • rosservice list 列出當前存在的服務
  • rosservice info service_name 輸出服務信息,包括服務節點名稱、服務的ROSRPC uri、服務類型(即服務格式的名稱)、服務的參數
  • rosservice call service_name request_param 調用服務
  • rosservice find srv_type 根據服務類型查找服務
  • rosservice args service_name 打印服務的參數
  • rosservice type service_name 輸出服務的類型
  • rosservice uri service_name 輸出服務的ROSRPC uri

5、消息類型和服務類型

ros使用了一種簡化的類型描述語言來描述ros節點發布的數據。通過這種描述語言,ros能夠使用多種編程語言編寫不同節點,並實現節點間通信。一個典型的msg文件如下:

int32 id

float32 vel

string name

ros提供了很多預定義的消息類型,我們也可以自定義消息以及服務類型。ros所使用的標準數據類型如下:

基本類型 序列化 C++ Python
bool(1) unsigned 8-bit int uint8_t(2) bool
int8 signed 8-bit int int8_t int
uint8 unsigned 8-bit int uint8_t int(3)
int16 signed 16-bit int int16_t int
uint16 unsigned 16-bit int uint16_t int
int32 signed 32-bit int int32_t int
uint32 unsigned 32-bit int uint32_t int
int64 signed 64-bit int int64_t long
uint64 unsigned 64-bit int uint64_t long
float32 32-bit IEEE float float float
float64 64-bit IEEE float double double
string ascii string(4) std::string string
time secs/nsecs signed 32-bit ints ros::Time rospy.Time
duration secs/nsecs signed 32-bit ints ros::Duration rospy.Duration

在ros的消息類型中,有一種特殊的消息類型是std_msgs/Header,叫做報文頭。報文頭其實是由三個字段組成,結構如下:

uint32 seq

time stamp

string frame_id

報文頭主要用於添加消息序號、時間戳、座標位置(frame_id可以是0/1;0:no frame,1:global frame)。消息是可以嵌套的,報文頭就常常嵌套在自定義消息中使用。

ros使用命令行工具rosmsg來獲取有關消息的信息:

  • rosmsg list 列出所有消息名
  • rosmsg md5 msg_name 輸出一條消息數據的MD5求和結果
  • rosmsg package package_name 列出功能包中所有的消息
  • rosmsg packages  列出所有具有消息文件的功能包
  • rosmsg show msg_name 顯示一條該消息的數據
  • rosmsg users  msg_name 搜索使用該消息類型的代碼文件
  • rosmsg info msg_name 輸出消息數據格式

ros使用命令行工具rossrv來獲取有關服務的信息,支持的參數和rosmsg相同。

 


 

開源社區級

主要指ros的相關資源:

1、系統發行版(Distribution)

2、軟件源(Repository)

3、ROS wiki(維基手冊)

4、ROS Answers(諮詢ros相關問題的網站)

5、博客

6、郵件列表(Mailing list):交流ros更新的主要渠道

 


 

OK,到這裏我們應該對ros有一個總體的認識了,相關的命令需要一些練習去熟練掌握,下次我們將更加深入的介紹ros的基礎編程方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章