3.8 通信機制實操
本節(jié)主要介紹通信機制相關的一些練習,這些練習基于turtlesim功能包,練習類型覆蓋了話題、服務、動作、參數這四種通信機制。
準備
終端下進入工作空間的src目錄,調用如下命令創(chuàng)建C++功能包。
ros2 pkg create cpp07_exercise --build-type ament_cmake --dependencies rclcpp turtlesim base_interfaces_demo geometry_msgs rclcpp_action
功能包下新建launch目錄以備用。
3.8.1 話題通信案例分析
1.案例需求
需求:啟動兩個turtlesim_node節(jié)點,節(jié)點2中的烏龜自動調頭180°,我們可以通過鍵盤控制節(jié)點1中的烏龜運動,但是不能控制節(jié)點2的烏龜,需要自實現(xiàn)功能:可以根據烏龜1的速度生成并發(fā)布控制烏龜2運動的速度指令,最終兩只烏龜做鏡像運動。

2.案例分析
在上述案例中,主要需要關注的問題有三個:
如何創(chuàng)建兩個turtlesim_node節(jié)點,且需要具有不同的節(jié)點名稱、話題名稱。
如何控制烏龜掉頭?
核心實現(xiàn)是如何訂閱烏龜1的速度并生成發(fā)布控制烏龜2運動的速度指令的?
思路:
問題1我們可以通過為turtlesim_node設置namespace解決;
問題2可以通過調用turtlesim_node內置的action功能來實現(xiàn)烏龜航向的設置;
問題3是整個案例的核心,需要編碼實現(xiàn),需要訂閱烏龜1的位姿相關話題來獲取烏龜1的速度,并且按照“鏡像運動”的需求生成烏龜2的速度指令,并且該節(jié)點需要在掉頭完畢后啟動。
最后,整個案例涉及到多個節(jié)點,我們可以通過launch文件集成這些節(jié)點。
3.流程簡介
主要步驟如下:
編寫速度訂閱與發(fā)布實現(xiàn);
編寫launch文件集成多個節(jié)點;
編輯配置文件;
編譯;
執(zhí)行。
3.8.2 話題通信實現(xiàn)
1.速度訂閱與發(fā)布
功能包cpp07_exercise的src目錄下,新建C++文件exe01_pub_sub.cpp,并編輯文件,輸入如下內容:

2.launch文件
功能包cpp07_exercise的launch目錄下,新建launch文件exe01_pub_sub.launch.py,并編輯文件,輸入如下內容:
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import ExecuteProcess,RegisterEventHandler
from launch.event_handlers import OnProcessExit
def generate_launch_description():
? ?
? ?# 1.創(chuàng)建兩個 turtlesim_node 節(jié)點
? ?
? ?t1 = Node(package="turtlesim",executable="turtlesim_node")
? ?
? ?t2 = Node(package="turtlesim",executable="turtlesim_node",namespace="t2")? ??
?# 2.讓第二只烏龜掉頭
? ?
? ? rotate = ExecuteProcess(
? ? ? ?
? ? cmd=["ros2 action send_goal /t2/turtle1/rotate_absolute turtlesim/action/RotateAbsolute \"{'theta': 3.14}\""],
? ? ? ?
? ? output="both",
? ? ? ?
? ? shell=True
? ?
? ) ? ?
? ? ?# 3.自實現(xiàn)的訂閱發(fā)布實現(xiàn)
? ?
? ? ?pub_sub = Node(package="cpp07_exercise",executable="exe01_pub_sub") ? ?
? ? ?# 4.烏龜掉頭完畢后,開始執(zhí)行步驟3
? ?
? ? ?rotate_exit_event = RegisterEventHandler(
? ? ? ?
? ? ? ? event_handler=OnProcessExit(
? ? ? ? ? ?
? ? ? ? ? ?target_action=rotate,
? ? ? ? ? ?
? ? ? ? ? ?on_exit=pub_sub
? ? ? ?
? ? ? ? ?)
? ?
? ?) ? ?
? ?return LaunchDescription([t1,t2,rotate,rotate_exit_event])
3.編輯配置文件
1.package.xml
在創(chuàng)建功能包時,所依賴的功能包已經自動配置了,配置內容如下:

2.CMakeLists.txt
CMakeLists.txt 中發(fā)布和訂閱程序核心配置如下:

4.編譯
終端中進入當前工作空間,編譯功能包:
colcon build --packages-select cpp07_exercise
5.執(zhí)行
當前工作空間下,啟動終端輸入如下指令:
. install/setup.bash?
ros2 launch cpp07_exercise exe01_pub_sub.launch.py
指令執(zhí)行后,將生成兩個turtlesim_node節(jié)點對應的窗口,并且其中一個窗口的烏龜開始調頭。
再啟動一個終端,輸入如下指令:
ros2 run turtlesim turtle_teleop_key
待烏龜調頭完畢,就可以通過鍵盤控制烏龜運動了,最終運行結果與演示案例類似。
3.8.3 服務通信案例分析
1.案例需求
需求:在turtlesim_node節(jié)點的窗體中在指定位置生成一只新烏龜并可以輸出兩只烏龜之間的直線距離。

2.案例分析
在上述案例中,需要關注的問題有兩個:
如何在指定位置生成一只新烏龜?
計算兩只烏龜的距離應該使用何種通信模式又如何實現(xiàn)?
思路:
問題1可以通過調用turtlesim_node內置的名稱為/spawn的服務功能來實現(xiàn)新烏龜的創(chuàng)建;
問題2可以通過服務通信來實現(xiàn),客戶端發(fā)送新生成的烏龜的位姿到服務端,服務端根據該坐標以及原生烏龜的坐標計算距離并響應。當然如果使用服務通信,還需要自定義服務接口。
最后,整個案例涉及到多個節(jié)點,我們可以通過launch文件集成這些節(jié)點。
3.流程簡介
主要步驟如下:
編寫服務接口文件;
編寫服務端實現(xiàn);
編寫客戶端實現(xiàn);
編寫launch文件;
編輯配置文件;
編譯;
執(zhí)行。
3.8.4 服務通信實現(xiàn)
1.服務接口文件
功能包base_interfaces_demo的srv目錄下,新建srv文件Distance.srv,并編輯文件,輸入如下內容:

2.服務端實現(xiàn)
功能包cpp07_exercise的src目錄下,新建C++文件exe02_server.cpp,并編輯文件,輸入如下內容:
/*
??
? 需求:處理請求發(fā)送的目標點,計算烏龜與目標點之間的直線距離。
??
? 步驟:
? ? ?
? ? ? ?1.包含頭文件;
? ? ??
? ? ? ?2.初始化 ROS2 客戶端;
? ? ??
? ? ? ?3.定義節(jié)點類;
? ? ? ? ? ?
? ? ? ? ?3-1.創(chuàng)建烏龜姿態(tài)訂閱方,回調函數中獲取x坐標與y坐標;? ? ? ? ? ? ? ? ? ?3-2.創(chuàng)建服務端;
? ? ? ? ? ?
? ? ? ? ?3-3.解析目標值,計算距離并反饋結果。
? ? ??
? ? ? ?4.調用spin函數,并傳入節(jié)點對象指針;
? ? ?
? ? ? ?5.釋放資源。?
*/
// 1.包含頭文件;
using namespace std::chrono_literals;
// 3.定義節(jié)點類;
class ExeDistanceServer:?
public rclcpp::Node {public:
? ?ExeDistanceServer():Node("exe_distance_server"),turtle1_x(0.0),turtle1_y(0.0){ ? ? ? ?// 3-1.創(chuàng)建烏龜姿態(tài)訂閱方,回調函數中獲取x坐標與y坐標;? ? ? ? ??
? ? ?? pose_sub = this->create_subscription<turtlesim::msg::Pose>("/turtle1/pose",10,std::bind(&ExeDistanceServer::poseCallBack, this, std::placeholders::_1)); ? ? ? ?
? ? ? ?// 3-2.創(chuàng)建服務端;
? ? ? ?
? ? ? ?distance_server = this->create_service<base_interfaces_demo::srv::Distance>("distance",std::bind(&ExeDistanceServer::distanceCallBack, this, std::placeholders::_1, std::placeholders::_2));
? ?
? ?}
private: ? ?
? ?void poseCallBack(const turtlesim::msg::Pose::SharedPtr pose){? ? ? ? ???turtle1_x = pose->x;
? ? ? ?
? ? turtle1_y = pose->y;
? ?
? ?} ? ?
? ?// 3-3.解析目標值,計算距離并反饋結果。
? ?
? ?void distanceCallBack(const base_interfaces_demo::srv::Distance_Request::SharedPtr request,
? ? ? ? ? ? ? ? ? ?base_interfaces_demo::srv::Distance_Response::SharedPtr response
? ?
? ? ){ ? ? ? ?
? ? // 解析目標值
? ? ? ?
? ?float goal_x = request->x; ? ? ? ?
? ?float goal_y = request->y; ? ? ? ?
? ? // 距離計算
? ? ? ?
? ? float x = goal_x - turtle1_x; ? ? ? ?
? ? float y = goal_y - turtle1_y; ? ? ? ?
? ? // 將結果設置到響應
? ? ? ?
? ? response->distance = std::sqrt(x * x + y * y);
? ? ? ?
? ? RCLCPP_INFO(this->get_logger(),"目標坐標:(%.2f,%.2f),距離:%.2f",goal_x,goal_y,response->distance);
? ?
? ?}
? ?
? ? ?rclcpp::Subscription<turtlesim::msg::Pose>::SharedPtr pose_sub;? ? ? ?
? ? ?rclcpp::Service<base_interfaces_demo::srv::Distance>::SharedPtr distance_server; ? ?
? ? ?float turtle1_x; ? ?
? ? ?float turtle1_y;?
};
int main(int argc, char const *argv[])
{ ? ?
? ?// 2.初始化 ROS2 客戶端;
? ?
? ?rclcpp::init(argc,argv); ? ?
? ?// 4.調用spin函數,并傳入節(jié)點對象指針;? ? ? ?
? ?rclcpp::spin(std::make_shared<ExeDistanceServer>()); ? ?
? ?// 5.釋放資源。
? ?
? ?rclcpp::shutdown(); ? ?
? ?return 0;?
}
3.客戶端實現(xiàn)
功能包cpp07_exercise的src目錄下,新建C++文件exe03_client.cpp,并編輯文件,輸入如下內容:
……

B站有完整的ros系列教程視頻,可以觀看完整內容ros課程ROS2理論與實踐
更多內容將在猛獅知識星球社區(qū)更新最新課程,后續(xù)將推出更多優(yōu)質內容——詳情可關注猛獅集訓營公眾號和猛獅集訓營官方網站。