【Ruby】RubyでROSを動かしてみた

九州工業大学　田中和明研究室の大学院生です。Rubyと産業用ロボットに関する研究に携わっています。


### 背景
<!--
読んだら何がわかるようになるのか、何ができるようになるのかを書きましょう。
-->

ROSでロボットを動かす場合、一般的にはPythonやC++を使用したプログラムが基本です。また、ROSは主にUbuntu上で動作し、ターミナル操作やUNIX系コマンドの習得が初心者にとって大きなハードルとなっています。これらの理由から、ROSの活用には開発環境の習得に時間を要することが多いです。

そこで、今回の提案は、Windows環境でRubyを使用し、Ubuntu上のROSを簡単に操作できるライブラリの開発です。これにより、ROSの操作がより直感的かつ簡単になり、プログラミング初心者やターミナル操作に不慣れなユーザーでもROSを扱いやすくなります。

- RubyでROSを扱うことができる

### 動作環境
- ROS noetic
- Ubuntu 20.04LTS 
- Ruby 3.3.3
- Windows11
- 実機　6軸多関節ロボット:安川電機製 motomini

|OS|version|
|:--|:--|
|Ubuntu|20.04LTS|
|windows|11|


|software|version|
|:--|:--|
|ROS|noetic|
|Ruby|3.3.3|

## 開発環境のセットアップ
システム構成図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770290/d3e3e1de-8b79-f207-686d-353ef0d44360.png)

### ROSの環境構築手順
1. ubuntu20.04LTSにROS(noetic)をインストール(python3のインストールとcatkin buildが使えるようにしてください。)
https://wiki.ros.org/noetic/Installation/Ubuntu
以下必要なパッケージ
https://wiki.ros.org/Industrial/Install
``` bash
sudo apt-get install ros-noetic-industrial-core
sudo apt-get install ros-noetic-abb
sudo apt-get install ros-noetic-ros-canopen
sudo apt install ros-noetic-moveit
sudo apt install ros-noetic-moveit-ros-visualization
```
1. ROS-Bridgeをインストール
```bash
sudo apt update
sudo apt install ros-noetic-rosbridge-suite
```
- ROS-Bridgeの起動を確認
```bash
roslaunch rosbridge_server rosbridge_tcp.launch
```
1. 外部のPCからアクセスするためにポート開放を行う（デフォルトではポートは9090）
```bash
sudo ufw allow 9090/tcp
```
### Rubyの環境構築
- Windows11にRubyをインストール
https://qiita.com/aomrikti/items/e0a8a900dfc687c40319#:~:text=Windows11

<!--
### 実機を使う場合(Motominiの環境構築)
今回は、安川電機製のMotominiというロボットを使用して検証しました。以下はmotominiの環境構築手順です。motomini以外のロボットを使う場合、適宜各メーカーのROSに関する設定を行ってください。

- 実機の設定：https://wiki.ros.org/motoman_driver/Tutorials

### シミュレーションの場合
以下は安川電機のロボットを仕様する場合を例に環境構築をします。
1. githubからmotomanをクローンしてくる
https://github.com/ros-industrial/motoman
```bash
cd $HOME/catkin_ws

git clone https://github.com/ros-industrial/motoman.git src/motoman

rosdep update

rosdep install --from-paths src/ --ignore-src --rosdistro noetic

catkin build
```

2. motominiを使う場合　
https://wwwms.meijo-u.ac.jp/kohara/technicalreport/ros_motoman_gp8_setup#:~:text=ROS%E3%81%A7Motoma
-->
## RubyとROS間の通信
今回はRubyとROS間の通信はTCP通信を用いました。そして、データはJSON形式で送受信します。

### ROS-Bridgeを使った通信
Ubuntu側でROS-Bridgeを起動しておきます。
```bash
roslaunch rosbridge_server rosbridge_tcp.launch
```

github以下URLからクローンして以下のコードと同一フォルダ内にros_service_tcp.rbを置いておきます。
[URL]
Windows側でRubyコードを実行して通信ができているか確認します。
```ruby
require './ros_service_tcp.rb'

robot1 = Robot.new('xxx.xxx.xxx', 9090) #ROSのIPアドレス
robot1.connect
robot1.disconnect
```
### ロボットの設定
まずは安川電機のロボットをgit cloneしてくる。
※他の産業用ロボットでも可(FANUC,Kawasaki,ABB等)
https://github.com/ros-industrial/motoman
``` bash
# change to the root of the Catkin workspace
cd $HOME/catkin_ws/src

# retrieve the latest development version of motoman.
git clone https://github.com/ros-industrial/motoman.git

# check build dependencies. Note: this may install additional packages,
# depending on the software installed on the machine
rosdep update

# be sure to change 'noetic' to whichever ROS release you are using
cd $HOME/catkin_ws
rosdep install --from-paths src/ --ignore-src --rosdistro noetic

# build the workspace (using catkin_tools)
catkin build
```
#### シミュレーションで行う場合
git cloneしてきたワークスペースで以下を実行
``` bash
cd catkin_ws
source devel/setup.bash

roslaunch motoman_ma2010_moveit_config demo.launch
```
画像のようなロボットが表示されれば成功！
![ma2010_sim.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770290/9c6df256-2d81-095b-6de4-727bf3c7313c.png)
※motominiを使う場合は以下を参考にmoveit_configを作成し実行する
https://wwwms.meijo-u.ac.jp/kohara/technicalreport/ros_motoman_gp8_setup
```bash
roslaunch motoman_motmomini_moveit_config demo.launch
```
画像のようなロボットが表示されれば成功！
![motomini_sim.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3770290/f9059a34-2a35-27cb-6069-6b7a514483f8.png)


#### 実機で行う場合(motomanの例)
以下を参考にlaunchファイルを生成する
1. 実機のコントローラの設定を以下を参考に行う
https://wiki.ros.org/motoman_driver/Tutorials/indigo/InstallServer
2. 実機のlaunchファイルを生成する
https://wwwms.meijo-u.ac.jp/kohara/technicalreport/ros_motoman_gp8_setup
### Rubyでトピックにデータを送る
今回は、6軸多関節ロボットで検証してみます。トピック /move_group/goalに各関節角を送信し、ロボットを動かしてみました。
1. ros bridgeを起動
``` bash
roslaunch rosbridge_server rosbridge_tcp.launch
```
2. 別のターミナルで以下を実行(例：motomini)
```bash
roslaunch motoman_motmomini_moveit_config demo.launch
```
3. windows側でrubyを実行
以下のプログラムと同じディレクトリにプログラム(ros_tcp.rb)を配置しておく。rubyプログラムを以下からgit cloneしてくる。
https://github.com/mruby-lab/ros-ruby.git
``` ruby
require './ros_tcp.rb'

robot1 = Robot.new('xxx.xxx.x.xx', 9090)
robot1.connect

# メッセージを準備
# グループ名と各関節の名前はロボットによって適宜変更してください
request_message = {
  goal: {
    request: {
      group_name: 'motomini',  # ロボットアームのグループ名
      goal_constraints: [{
        joint_constraints: [
          { joint_name: 'joint_1_s', position: 1.0 },
          { joint_name: 'joint_2_l', position: 0.0 },  # joint_2_lの目標位置
          { joint_name: 'joint_3_u', position: 0.0 },  # joint_3_uの目標位置
          { joint_name: 'joint_4_r', position: 0.0 },  # joint_4_rの目標位置
          { joint_name: 'joint_5_b', position: 0.0 },  # joint_5_bの目標位置
          { joint_name: 'joint_6_t', position: 0.0 }   # joint_6_tの目標位置
        ]
      }]
    }
  }
}

robot1.send_msg('/move_group/goal', request_message, 1)
robot1.disconnect
```




# プログラムros_tcp.rbの説明

## クラス名: `Robot`


### 概要
[ROSとの通信制御、ロボットへのデータ送受信、ROSの機能の呼び出し]




### Robotクラスのクラス図

```plantuml
@startuml
class Robot {
    - host: String
    - port: Integer
    - socket: TCPSocket
    - logger: Logger
    + initialize(host, port)
    + connect()
    + disconnect()
    + receive_msg(topic_name)
    + list_topics()
    + service(service_name)
    + topic_exists?(topics, topic_name)
    + send_msg(topic_name, message, n)
}
@enduml
```

- **クラス変数** :

| 変数名          | 型      | 説明                                             |
|-----------------|---------|--------------------------------------------------|
| `host`       | `string`    | ROSのIPアドレス              |
| `port`       | `int`       | TCP通信のポート番号 |
|`socket`|`no`|TCP通信のソケット|
|`logger`|`no`|ログ出力|
- **属性（Attributes）**:
  - `host`: ロボットへの接続先ホスト名またはIPアドレス（String）。
  - `port`: ロボットへの接続ポート番号（Integer）。
  - `socket`: TCPSocketオブジェクト（接続用ソケット）。
  - `logger`: Loggerオブジェクト（ログ出力用）。

- **メソッド（Methods）**:
  - `initialize(host, port)`: クラスの初期化メソッド。ホストとポートを受け取ってインスタンスを初期化します。
  - `connect()`: ロボットに接続します。
  - `disconnect()`: ロボットから切断します。
  - `receive_msg(topic_name)`: 特定のトピックからメッセージを受信します。
  - `list_topics()`: 利用可能なトピックのリストを取得します。
  - `service(service_name)`: 特定のサービスを呼び出します。
  - `topic_exists?(topics, topic_name)`: トピックが存在するかを確認します。
  - `send_msg(topic_name, message, n)`: 特定のトピックにメッセージを送信します。引数のnは送信する回数基本1でよい