--- tags : DIT 2023寒假 -- ROS教學 --- # DAY 4 - 使用自定義的 message {%hackmd BJrTq20hE %} ## <font color="orange">01. 建立自定義的 message</font> ### msg folder and files <font color="yellow">Step 1. 在 package 底下建立 msg 資料夾</font> ```bash=1 # Move to workspaces/package folder roscd [PACKAGE_NAME] roscd ros_tutorials # Create the folder mkdir msg ``` <font color="yellow">Step 2. 建立 message 檔案,取為 [message name].msg</font> - :warning: 副檔名須為 .msg ```bash=1 # Move to msg folder cd msg # Create message file use your favorite editor vim example_message.msg code example_message.msg ``` --- ## <font color="orange">02. 自定義需要的 message</font> 在剛剛建立的 msg file 中加入需要用到的 messages - 如同定義變數一樣,將需要的 message 以 [變數型態] [變數名稱] 定義起來 ```txt=1 int64 id_number # int64 = long long int in c++ ``` - 同樣的,可以使用複數行將需要的 message 放進去 ```txt=1 int64 id_number string name uint16 age ``` - 除了平常使用的 ```std_msgs``` 之外,也可以將其他 message 放進去 ```txt=1 geometry_msgs/Point point bool check_point ``` - Example ```=1 float32 time geometry_msgs/Twist position geometry_msgs/Twist velocity ``` --- ## <font color="orange">03. 編譯/生成 message file</font> > 編譯/建置 message file - 修改 ```CMakeList.txt``` 以及 ```pacjage.xml``` 使 message file 能被轉成 source code 編譯及引用 ### In ```package.xml``` - 增加編譯以及執行時會需要用到的 dependency - 原本 package.xml 中應該都有,不過需要將其解開註解 1. <font color="FF9999">確保 package.xml 中有:</font> ```xml=1 <build_depend>message_generation</build_depend> ``` 2. <font color="FF9999">確保 package.xml 中有:</font> ```xml=1 <exec_depend>message_runtime</exec_depend> ``` ### In ```CMakeList.txt``` 1. <font color="FF9999">增加 message_generation</font> ```cmake=10 find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs # Here we add message generation message_generation ) ``` 2. <font color="FF9999">增加 message_runtime</font> ```cmake=106 catkin_package( # Here we add message_runtime CATKIN_DEPENDS roscpp rospy std_msgs message_runtime ) ``` 3. <font color="FF9999">添加 message file,將剛剛建立好的 message 加入</font> ```cmake=49 ## Generate messages in the 'msg' folder add_message_files( FILES example_message.msg ) ``` 4. <font color="FF9999">加入 message 相關的 dependencies ,將以下部份解開註解,同時加上 ```std_msgs```</font> ```cmake=68 ## Generate added messages and services with any dependencies listed here generate_messages( DEPENDENCIES std_msgs ) ``` 5. <font color="FF9999">如果有用到其他 message 需要將其加入喔</font> ```cmake=68 ## Generate added messages and services with any dependencies listed here generate_messages( DEPENDENCIES std_msgs geometry_msgs ) ``` ### Compile and check 1. <font color="FF9999">以上步驟完成之後,記得使用 catkin_make 生成剛剛 message file 需要的 source code</font> ```bash=1 catkin_make ``` 2. <font color="FF9999">確保 message 有被成功編譯,可以移動至 devel 中看看 message 看看是否有生成 header file</font> ```bash=1 # Move to ws/devel roscd # Check include file cd include/[package_name] # List all header ls ``` 3. <font color="FF9999">如果有成功生成的話,會產生 [message file name].h</font> ``` example_message.h ``` --- ## <font color="orange">04. 使用自定義的 message</font> > 用法和內建的 message 都是一樣的,[複習昨天的筆記](/s/5vRzav6JSOeKq6AFyFfqcw#-03.-Messages-的使用方式) ### C++ 1. <font color="cyan">用法和內建的 message 一樣</font> ``` # in example_message.msg int64 id geometry_msgs/Point point ``` 2. <font color="cyan">Include file</font> ```cpp=1 #include <ros/ros.h> #include "geometry_msgs/Point.h" // Here is our customized message #include "msg_test/example_message.h" ``` 3. <font color="cyan">Usage</font> ```cpp=1 // Built-in message geometry_msgs::Point point_0; point_0.x = 0.5; // Customized message msg_test::example_message example_msg; example_msg.id = 0; example_msg.point = point_0; ``` ### Python - Example ```python=1 #!/usr/bin/env python3 #coding:utf-8 import rospy from msg_test.msg import example_message from geometry_msgs.msg import Point def main(): rospy.init_node("customized_message") point_right = Point() point_right.x = 0.5 ex_msg = example_message() ex_msg.id = 0 ex_msg.point = point_right if __name__ == '__main__' : main() ``` :::info :::spoiler EXAMPLE ### Message ```=1 float32 time geometry_msgs/Twist position geometry_msgs/Twist velocity ``` ### Publisher ```cpp=1 #include "ros/ros.h" #include "server/location.h" #include "geometry_msgs/Twist.h" int main(int argc, char** argv){ ros::init(argc, argv, "loc_pub"); ros::NodeHandle nh; ros::Publisher pub = nh.advertise<server::location>("/loc_data", 10); server::location loc_msg; geometry_msgs::Twist loc, vel; loc.linear.x = 0; loc.angular.x = 0; vel.linear.x = 0.3; vel.angular.x = 0.9; loc_msg.time = 0; loc_msg.position = loc; loc_msg.velocity = vel; ros::Rate rate(1); while(ros::ok()){ rate.sleep(); loc_msg.time++; pub.publish(loc_msg); } } ``` ### Subscriber ```cpp=1 #include "ros/ros.h" #include "server/location.h" void loc_data_cb(const server::locationConstPtr &msg){ ROS_INFO_STREAM("-----------------------------"); ROS_INFO_STREAM("Current time : " << msg->time); ROS_INFO_STREAM("Current position : (" << msg->position.linear.x << ", " << msg->position.linear.y << ")"); ROS_INFO_STREAM("Current omega : (" << msg->position.angular.x << ", " << msg->position.angular.y << ", " << msg->position.angular.z << ")"); ROS_INFO_STREAM("Current velocity : (" << msg->velocity.linear.x << ", " << msg->velocity.linear.y << ")"); ROS_INFO_STREAM("-----------------------------"); } int main(int argc, char** argv){ ros::init(argc, argv, "loc_sub"); ros::NodeHandle nh; ros::Subscriber sub = nh.subscribe("/loc_data", 10, loc_data_cb); ros::spin(); } ``` ::: --- ## <font color="orange">05. rosmsg</font> ### 查看 message 型態 - ```show``` ```bash=1 rosmsg show [Message type] rosmsg show Int64 rosmsg show std_msgs/Int64 ``` ### 列出 message - ```list``` <font color="yellow">列出所有 message</font> ```bash=1 rosmsg list ``` - ```package``` <font color="yellow">列出指定的 package 中所有的 message</font> ```bash=1 rosmsg package [Package name] ``` - ```packages``` <font color="yellow">列出所有有自訂 message 的 packages</font> ```bash=1 rosmsg packages ``` ### Example <font color="yellow">查詢 turtlesim 用到的 message</font> ```bash=1 rosmsg package turtlesim ``` - <font color="cyan">輸出如下</font> ```bash=1 turtlesim/Color turtlesim/Pose ``` <font color="yellow">查訊 Pose 用到的資料型態</font> ```bash=1 rosmsg show turtlesim/Pose ``` - <font color="cyan">輸出如下</font> ```bash=1 float32 x float32 y float32 theta float32 linear_velocity float32 angular_velocity ``` <font color="yellow">在執行某一 topic 時,查看它的資料型態</font> ```bash=1 rostopic type /turtle1/color_sensor | rosmsg show ``` - <font color="cyan">輸出如下</font> ```bash uint8 r uint8 g uint8 b ``` --- ## <font color="orange">06. HW-1</font> ### 自定義 message 1. 建立一個能夠互傳資料的 server-client 架構( server 與 client 為不同的 node ) 2. 使 server 傳送 50 筆資料,且 client 須接收並將接收到的資料印出來 3. 印出來的資料需要按照 1 ~ 50 的順序,且盡可能沒有漏掉 4. Topic 所用到的 message 需自行定義 5. 簡易的架構如下 ![](https://i.imgur.com/laappkk.png) ### 進階練習 1. 如上述的題目描述,增加以下條件 2. 將任意完整的訊息切成 50 等分,並由 server 每次傳送一小段給 client 3. Client 需收到所有資料並印出一開始完整的訊息 4. Topic 間的傳輸不夠穩定,因此需要多一條 feedback 使 server 知道要目前要傳哪一筆資料 5. 需更加恰當的設計自訂的 message ![](https://i.imgur.com/L1QIaKt.png) :::info :::spoiler 提示 - 由於 topic 間的傳送並不穩定,有時候會漏掉一些資料,要如何確保每筆資料都有傳送到 subscriber ? - 一次傳送很多筆資料 - 然而一次傳送太多不僅浪費時間,浪費資源,又無法完全確保 subscriber 能接收到所有 message - 利用自定義的 message 使傳送者得知接收者當前狀態並傳送相對應的資料 ![](https://i.imgur.com/5sMWSAI.png) ::: :::info :::spoiler Message 提示 - In message file ``` int32 data_id int32 last_rec_data_id string sender_id string data ``` - ```data_id``` 為當前傳送資料的序號 - ```last_rec_data_id``` 為 client 目前接收到的資料序號 - ```sender_id``` 確認目前這筆 message 是誰傳送的 - ```data``` 任意資料 ::: --- ## <font color="orange">07. HW-2</font> ### Turtlesim - 讓小烏龜自動走直直的正方形 - 注意:若只是使其在 4 個點之間移動的話,必定會有誤差 - 思考:在已知小烏龜位置情況下,如何以更好的控制方式使其移動成正方形 ![](https://i.imgur.com/4ut8ARv.png) :::info :::spoiler 提示 \ turtle 無法更精確的走到指定位置通常為 1. 速度過快,即便到點後馬上將速度調為 0 ,turtle 仍舊向前 **滑行** 一小段距離 2. 由於 **旋轉** 時的誤差,使 turtle 無法走完整的 90 度 \ 要讓 turtle 精確的走到指定的地方,需要 **動態** 的調整他的速度 - 在距離目標遠時,速度快 - 距離目標近時,減速 - **使速度和目前剩餘距離成正比** :::