參考資料:
https://www.icourse163.org/course/ISCAS-1002580008?tid=1003713012 //中國大學MOOC
https://www.bilibili.com/video/av23401751 //B站
《ROS操作系統入門講義》PDF下載
鏈接:https://pan.baidu.com/s/1OCja2WLDRnjYXMrpnZ3-sQ
提取碼:mziy
第六章 roscpp
一、ROS支持的客戶端庫和整體的包結構分布
二、roscpp:位於/opt/ros/kinetic // #include<ros/ros.h>
1. 主要部分
(1)ros::init():解析傳入的ROS參數,創建node第一步需要用到的函數 //可以為node命名
(2)ros::NodeHandle:和topic、service、param等交互的公共接口 //類,需要創建句柄對象
(3)ros::master:包含從master查詢信息的函數 //命名空間,無需創建對象
(4)ros::this_node:包含查詢這個進程(node)的函數
(5)ros::service:包含查詢服務的函數
(6)ros::param:包含查詢參數服務器的函數,而不需要用到NodeHandle
(7)ros::names:包含處理ROS圖資源名稱的函數
2. 按功能分類
- Initialization and Shutdown 初始與關閉
- Topics 話題
- Services 服務
- Parameter Server 參數服務器
- Timers 定時器
- NodeHandles 節點句柄
- Callbacks and Spinning 回調和自旋(輪詢)
- Logging 日志
- Names and Node Information 名稱管理
- Time 時鍾
- Exception 異常
三、節點初始化、關閉以及句柄NodeHandle
1. 初始化
(1)ros::init():初始化節點的名稱和其他信息
(2)ros::NodeHandle對象:節點句柄,用來創建Publisher、Subscriber等
注:句柄NodeHandle是對節點資源的描述,通過它對節點進行操作,如為程序提供服務、監聽某個topic上的消息、訪問和修改param等
2. 關閉節點
(1)終端輸入"Ctrl + C" //自動觸發SIGINT句柄關閉進程
(2)調用ros::shutdown() //手動關閉節點
注:常用執行流程如下 // 啟動節點+獲取句柄
3. NodeHandle類的常用成員函數 // 類
四、基於roscpp的topic通信
(1)功能:自定義一個類型為gps的消息(包括位置x,y和工作狀態state信息),一個node以一定頻率發布模擬的gps消息,另一個node接收並處理,算出到原點的距離
(2)自定義gps.msg消息 //類似於C語言中的結構體
string state #工作狀態
float32 x #x坐標
float32 y #y坐標
(3)修改CMakeLists.txt和package.xml //編譯自定義消息
CMakeLists.txt:
package.xml:
<build_depend>message_generation</build_depend>
<run_depend>message_runtime</run_depend>
注:
- 回到工作空間編譯完成后,會在devel路徑下自動生成gps.msg對應的頭文件,其中定義了topic_demo::gps類
- 通過 #include<topic_demo/gps.h>,使用自定義消息類型
-
topic_demo::gps mygpsmsg;
mygpsmsg.x = 1.6;
mygpsmsg.y = 5.5;
mygpsmsg.state = "working";
(4)消息發布節點 talker.cpp //發布gps_info話題
(5)消息訂閱節點 listener.cpp
注:
- 通過定義回調函數,為gps_info話題預先准備一個回調函數,接收到消息時被觸發執行
- 回調函數作為參數被傳入到了另一個函數中(在本例中傳遞的是函數指針),在未來某個時刻(當有新的message到達),就會立即執行
(6)修改CMakeList.txt
(7)spin調用方式 //多線程用於分別處理不同數據
五、基於roscpp的service通信
(1)自定義服務文件Greeting.srv
string name #短橫線上邊部分是服務請求的數據
int32 age
--- #短橫線下面是服務回傳的內容。
string feedback
注:相當於嵌套了請求和響應兩個結構體
(2)修改CMakeList.txt:add_service_files(FILES Greeting.srv)
通過#include <service_demo/Greeting.h>進行調用
service_demo::Greeting grt; //grt分為grt.request和grt.response兩部分
grt.request.name = "HAN"; //不能用grt.name或者grt.age來訪問
grt.request.age = "20";
(3)服務提供節點 server.cpp
注:
- 服務的處理操作由handle_function()函數確定,輸入參數為Greeting的Request和Response兩部分,對對Requst數據進行需要的操作,將結果寫入到Response中
- 返回值為bool值,用於判斷服務是否調用成功 //不輸出Response
(4)服務請求節點 client.cpp
注:CMakeList.txt和package.xml的修改和topic_demo類似
六、基於roscpp的參數服務器設置
(1)兩種方式
- ros::param命名空間
- ros::NodeHandle節點句柄
(2)實際項目中對參數的設置通常都不在程序中,而是利用launch文件 //launch文件可以方便地修改參數,而寫成代碼之后,修改參數必須重新編譯
(3)命名空間對param的影響 // ros::NodeHandle n; 和 ros::NodeHandle nh("~") 的區別
假設參數定義如下:
那么 name_demo.cpp文件如下:
輸出:
可見:
- n為全局命名空間句柄,當訪問節點私有命名空間內的參數時,需要添加節點名
- nh為局部命名空間句柄,當訪問全局命名空間的參數時,需要添加全局命名空間 /
七、時鍾
(1)兩種時間表示方法 //均由秒和納秒組成:int32 sec;int32 nsec
- ros::Time //某個時刻,#include<ros/time.h>
- ros::Duration //某個時段,#include<ros/duration.h>
用法示例:
- ros::Time begin = ros::Time::now(); //獲取當前時間
- ros::Time at_some_time1(5,20000000); //5.2s
- ros::Time at_some_time2(5.2) //同上,重載了float類型和兩個uint類型的構造函數
- ros::Duration one_hour(60*60,0); //1h
- double secs1 = at_some_time1.toSec(); //將Time轉為double型時間
- double secs2 = one_hour.toSec(); //將Duration轉為double型時間
時刻Time和Duration時長之間存在加減運算:
- ros::Time t1 = ros::Time::now() - ros::Duration(5.5); //t1是5.5s前的時刻,Time加減Duration返回都是Time
- ros::Time t2 = ros::Time::now() + ros::Duration(3.3); //t2是當前時刻往后推3.3s的時刻
- ros::Duration d1 = t2 - t1; //從t1到t2的時長,兩個Time相減返回Duration類型
- ros::Duration d2 = d1 -ros::Duration(0,300); //兩個Duration相減,還是Duration
注:不存在Time+Time
(2)休眠功能sleep
注:Rate的功能是指定一個頻率,讓某些動作按照這個頻率來循環執行
(3)定時器Timer:與Rate類似,通過設定回調函數和觸發時間來實現某些動作的反復執行,創建方法類似topic中的subscriber
八、日志和異常
(1)日志log
- 每個節點都會把日志信息發送到統一的話題 /rosout 上
- rosout本身也是一個節點,負責日志的記錄
(2)日志的輸出:#include<ros/console.h>
五個級別:
- DEBUG:ROS_DEBUG("The velocity is %f", vel);
- INFO:ROS_INFO
- WARN:ROS_WARN("Warn: the use is deprecated.");
- ERROR:ROS_ERROR
- FATAL:ROS_FATAL("Cannot start this node.");
(3)異常Exception //針對兩類錯誤
- ros::InvalidNodeNameException //當無效的基礎名稱傳給ros::init(),通常是名稱中有/,就會觸發
- ros::InvalidNameExcaption //當無效名稱傳給了roscpp