ROSでの通信遅延時間検証　その②

#背景
ROSでリアルタイムシステムを構築している際に、ROS(melodic)のノード間通信の遅延が気になったので検証してみました。
下記の続きで、今回は送信するメッセージのデータサイズを変えて、データサイズと通信遅延の関係を確認しました。
[ROSでの通信遅延時間検証　その①](https://qiita.com/moominparis/items/79ccd5b96548e958e837)

#検証用コード
検証用のpublisher(送信)のコードです。

```cpp:number_publisher.cpp
#include <ros/ros.h>
#include <unistd.h>
#include "std_msgs/Int32MultiArray.h"

int main(int argc, char** argv)
{
  ros::init(argc, argv, "number_publisher");
  ros::NodeHandle nh;
  ros::Publisher pub = nh.advertise<std_msgs::Int32MultiArray>("number", 10);
  ros::Rate loop_rate(30);


  int try_num = 8;
  int count =1;
  int data_pow =2;

  sleep(5);

  while (ros::ok())
  {
    int num_size = pow(2,data_pow)/4;

    std_msgs::Int32MultiArray number;
    number.data.resize(num_size);
    
    for(int i=0;i<num_size;i++){
    number.data[i] = i;
    };

    ROS_INFO("Published size: %d",int(pow(2,data_pow)));
    pub.publish(number);
    count++;
    //ROS_INFO("Count: %d",count);

    ros::spinOnce();
    loop_rate.sleep();

    if(count>try_num){ 
      data_pow++;
      count = 1;
    }
  
  }
  return 0;
}
```
コードの詳細な説明は省きますが、2のべき乗でデータサイズを大きくしながら、下記で指定した試行回数分テストを行っています。
`int try_num = 8;`

下記がsubscriber(受信)のコードになります。

```cpp:number_subscriber.cpp

#include <ros/ros.h>
#include "std_msgs/Int32MultiArray.h"

void numberCallback(const std_msgs::Int32MultiArray& msg)
{
    int num = msg.data.size();
    ROS_INFO("Subscribed size: %d", int(num*4));
}

int main(int argc, char** argv)
{
  ros::init(argc, argv, "number_subscriber");
  ros::NodeHandle nh;
  ros::Subscriber sub = nh.subscribe("number", 10, numberCallback);

  ros::spin();
  return 0;
}
```

#検証結果
上記を実行すると、`ROS_INFO`の内容がROSのlogとして残るので、これを下記のスクリプトで解析しました。

```python:check_time_stamp_pubsub.py
import csv
import numpy as np
import math
import matplotlib.pyplot as plt

# Specify file name of log file
file = 'rosout.log'
log_file = open(file, "r", encoding="UTF-8")
dataList = log_file.readlines()

# Specify extract name
extract_word1 = 'Published' 
extract_word2 = 'Subscribed' 

published_time = [] 
subscribed_time = [] 

for row in dataList:
    if extract_word1 in row:
        published_time.append([float(row[:20]), float(row[163:])])
        
    if extract_word2 in row:
        subscribed_time.append([float(row[:20]), float(row[166:])])

diff_pub_time = []

for t in range(0,len(published_time)-1):
   diff_pub_time.append(subscribed_time[t][0] - published_time[t][0])

plt.figure()
data_size_meas= ([float(row[1]) for row in subscribed_time])

x_axis = []
for x in data_size_meas:
    x_axis.append(math.log2(x))
print(len(diff_pub_time))
plt.hlines(1/30, 0, pow(2,30),colors='red', linestyle='dashed')
plt.scatter(data_size_meas,diff_pub_time,s=10)
plt.xscale('log')
plt.xlim(0,pow(2,28)+100000000)
plt.ylim(-0.1,1.5)
plt.xlabel('Data size [byte]')
plt.ylabel('Latency [sec.]')
plt.show()
```

上記のスクリプト実行で下記のような結果が得られます。青い点が8回の試行結果をすべてプロットした物になります。今回、送信側は30Hzで送っているので、0.033sec(=1/30)のラインに赤の点線をプロットしています。つまり、この赤の点線以下であれば30Hzでドロップなく送る事ができるという事になります。10MByte程度まではほぼドロップなしで送る事ができそうです。
ただし、1kByteから10kByteで若干遅延が大きくなっているので、この範囲では送信レートを下げる等の対策が必要になりそうです。原因が分かりませんが、送信プロトコルの仕様だと思います・・・
![20211229_latency.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332098/b1abcfa8-cdcf-9f3c-22ba-82e8f3a001a6.png)


#まとめ
ROSでの通信遅延とデータサイズの関係を検証しました。
今回の検証環境では10Mbyte以下のデータを30Hzで送る分にはドロップ無しで送る事ができそうな事がわかりました。
ただし適当なLaptop PCのVM上で検証した結果なので、実際の開発の際にはその都度、通信遅延を確認しておいた方が良さそうです。