---
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. 簡易的架構如下

### 進階練習
1. 如上述的題目描述,增加以下條件
2. 將任意完整的訊息切成 50 等分,並由 server 每次傳送一小段給 client
3. Client 需收到所有資料並印出一開始完整的訊息
4. Topic 間的傳輸不夠穩定,因此需要多一條 feedback 使 server 知道要目前要傳哪一筆資料
5. 需更加恰當的設計自訂的 message

:::info
:::spoiler 提示
- 由於 topic 間的傳送並不穩定,有時候會漏掉一些資料,要如何確保每筆資料都有傳送到 subscriber ?
- 一次傳送很多筆資料
- 然而一次傳送太多不僅浪費時間,浪費資源,又無法完全確保 subscriber 能接收到所有 message
- 利用自定義的 message 使傳送者得知接收者當前狀態並傳送相對應的資料

:::
:::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 個點之間移動的話,必定會有誤差
- 思考:在已知小烏龜位置情況下,如何以更好的控制方式使其移動成正方形

:::info
:::spoiler 提示
\
turtle 無法更精確的走到指定位置通常為
1. 速度過快,即便到點後馬上將速度調為 0 ,turtle 仍舊向前 **滑行** 一小段距離
2. 由於 **旋轉** 時的誤差,使 turtle 無法走完整的 90 度
\
要讓 turtle 精確的走到指定的地方,需要 **動態** 的調整他的速度
- 在距離目標遠時,速度快
- 距離目標近時,減速
- **使速度和目前剩餘距離成正比**
:::