---
# System prepended metadata

title: DAY 4 - 使用自定義的 message
tags: [DIT 11th 教學 -- ROS]

---

---
tags : DIT 11th 教學 -- ROS
---

# DAY 4 - 使用自定義的 message
{%hackmd @HungPin/BkVDWAea3 %}

## <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 精確的走到指定的地方，需要 **動態** 的調整他的速度
- 在距離目標遠時，速度快
- 距離目標近時，減速
- **使速度和目前剩餘距離成正比**

:::

