RUSTでROSパッケージを作る (1/2)

# RUSTでROSパッケージを作る 
## はじめに
[株式会社Rist](https://www.rist.co.jp)のロボットチームのプラダンです。ネパール出身ですが、2年前に来日しました。
タイのアジア工科大学大学院でメカトロニクスの修士号を取得。
Ristに入社する前は研究者、機械エンジニアとして働いていました。

## 概要
これは、[Rist](https://www.rist.co.jp)の同僚による[A Gentle Introduction to rosrust, and its actionlib Support](https://qiita.com/slabua/items/68f889512025ff8169f9)と[Turtlebot3(Burger)をROS BridgeしてRustで動かす準備](https://qiita.com/kazuharayama/items/625787a6c43c47464464)のフォローアップ記事で、ROSで'.rs'のrustファイルを読み込むROSパッケージを作成し、RUSTを使ってパラメータを作成する方法について書かれています。

## Catkin_wsでCargoを使ってパッケージを作成する
パッケージを作るのは簡単な作業ですが、'.rs' ファイルを読み込むパッケージを作るのは、ROS のデフォルトのプログラミング言語が c++ と python であるため、少し面倒です。空のパッケージを作成するには:

```bash
$ roscd
$ cd ../ src
$ catkin_create_pkg rust_test
```
パッケージが作成されたら、catkin_wsのsrcフォルダの中に入って、パッケージを見てください。ROSパッケージには以下のものが含まれているはずです。

```bash
catkin_ws
 ├── build
 ├── devel
 └── src
  	└── rust_test
      	├── CMakeLists.txt
      	└── package.xml
```
**※** **パッケージを作成する前に、catkin_wsのsrcフォルダにあることを確認してください。**
次はcargoを使ってcatkin_wsのsrcフォルダ内にRUSTパッケージを作成します。

```bash
$ cargo new rust_ws
```
**rust_ws**パッケージの中には、以下のように表示されているはずです。

```bash

rust_ws
 ├── Cargo.toml
 └── src
 	└── main.rs
```
catkin_wsの中には、ROSパッケージ**rust_test**とカーゴパッケージ**rust_ws**があります。

##ROSでRUSTをコンパイルす

ROSはまだRUSTをサポートしていないので、 **rust_ws** カーゴパッケージにいくつかの変更を加えなければなりません。
ラストテスト**フォルダから **CMakeLists.txt** と **package.xml** をコピーして、**rust_ws**フォルダに貼り付けてください。新しいパッケージは以下のようになります。

```bash
rust_ws
 ├── Cargo.toml
 ├── CMakeLists.txt
 ├── package.xml
 └── src
 	└── main.rs
```
CMakeLists.txt**と**package.xml**は、現在のパッケージにコピーした別のパッケージのものなので、ROSとRUSTが機能するようにコードを変更する必要があります。

####CMakeLists.txtファイルを変更
このファイルには元のパッケージ名、すなわち **rust_test** が含まれています。
**Original CMakeLists.txt**

```text
cmake_minimum_required(VERSION 2.8.3)
project(rust_test)

find_package(catkin REQUIRED)
include_directories(
# include
# ${catkin_INCLUDE_DIRS}
)
```
**Modified CMakeLists.txt**
**rust_test**を現在のパッケージ名**rust_ws**に変更し、以下のコードを追加してください。

```text
cmake_minimum_required(VERSION 2.8.3)
project(rust_ws)

find_package(catkin REQUIRED)
include_directories(
# include
# ${catkin_INCLUDE_DIRS}
)
add_custom_target(rust_ws
	ALL
	COMMAND cargo build --release -p rust_ws
	COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/cargo/release/rust_ws ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/rust_ws
	COMMENT "Building my Rust library"
)
```

####package.xmlファイルを変更
また、**package.xml**も修正する必要があります。**CMakeLists.txt**ファイルの修正と同様に、元の名前を現在のパッケージ名に変更します。
Original **package.xml** file:

```xml
<?xml version="1.0"?>
<package format="2">
  <name>rust_test</name>
  <version>0.0.0</version>
  <description>The rust_test package</description>
```

Modified **package.xml** file:

```xml
<?xml version="1.0"?>
<package format="2">
  <name>rust_ws</name>
  <version>0.0.0</version>
  <description>The rust_ws package</description>
```

##catkin_wsをcargoで作成した新しいパッケージでコンプする
**CMakeList.txt**ファイルと**package.xml**ファイルを修正したら、catkin_wsをコンパイルしてソースを作成する必要があります。しかし、catkin_wsをコンパイルする前に、ROSがRUSTの'.rs'ファイルを読み込めるようにするために、catkin_wsにもう一つ重要な追加をする必要があります。

- このステップまでは、catkin_wsはこのようになっているはずです。

```bash
catkin_ws
 ├── build
 ├── devel
 └── src
  	├── rust_test
  	└── rust_ws
       	├── Cargo.toml
       	├── CMakeLists.txt
       	├── package.xml
       	└── src
            	└── main.rs   	 
```
- 次のステップでは、設定ファイルを格納する **.cargo** フォルダを隠しフォルダとして作成します。
	- 最も簡単な方法は、cilckを右マウスでクリックして、**New Folder**を選択することです。
	- ※ **.cargo**というフォルダ名を付ける前に、フォルダ内に**config**ファイルを作成しておくことをお勧めします！[config.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683212/a5451b59-e772-90ed-371d-5e57d4493098.png)	
- 無題のフォルダ名 **.cargo** を非表示にします。
	- 設定ファイルに以下を追加します。

```text
[build]
target-dir = "build/cargo"
```
- カーゴ環境用の**Cargo.toml**ファイルを作成します。
 - Cargo.tomlファイルに、以下を追加します。



```toml
[workspace]
members = [ "src/rust_ws" ]
```

上記の手順を完了すると、catkin_wsは以下のようになるはずです。

```bash
catkin_ws
 ├── build
 ├── devel
 ├── src
  	├── rust_test
  	└── rust_ws
       	├── Cargo.toml
       	├── CMakeLists.txt
       	├── package.xml
       	└── src
            	└── main.rs
 └── Cargo.toml	 
```
![cargo_lock.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683212/0e162e50-8ec1-a410-a67f-f868dc5ab68d.png)

最後にcatkin_wsをコンパイルします。
**※:**　**srcではなくcatkin_wsフォルダにあることを確認してください。**

```bash
$ catkin_make
```
エラーが出ないようにコンパイルした後、ソースを出しています。

```bash
$ source devel/setup.bash
```
ソースが作成されると、**Cargo.lock**ファイルがcatkin_wsフォルダに表示されます。これでROSはRUSTの'.rs'スクリプトを読むことができるようになります。

```bash
catkin_ws
 ├── build
 ├── devel
 ├── src
  	├── rust_test
  	└── rust_ws
       	├── Cargo.toml
       	├── CMakeLists.txt
       	├── package.xml
       	└── src
            	└── main.rs
 ├── Cargo.lock
 └── Cargo.toml	 
```
##シンプルなパブリッシャーノードのテスト
Source後、ROSはRUSTと通信できるようになります。RUSTがROSで動作しているかどうかをテストするには、'main.rs'に書かれた簡単なパブリッシャーコードを使用します。

```rust
use env_logger;
use rosrust;

fn main() {
    
	env_logger::init();

	rosrust::init("topic_publisher");

	//create publisher
	let pubs = rosrust::publish("cmd_vel", 10).unwrap();

	let rate = rosrust::rate(10.0);    

	while rosrust::is_ok(){

    	let mut cmd_vel_data = rosrust_msg::geometry_msgs::Twist::default();

    	cmd_vel_data.linear.x = 0.5;
    	cmd_vel_data.linear.y = 0.0;
    	cmd_vel_data.linear.z = 0.0;

    	cmd_vel_data.angular.x = 0.0;
    	cmd_vel_data.angular.y = 0.0;
    	cmd_vel_data.angular.z = 0.5;

    	//publish cmd
    	pubs.send(cmd_vel_data).unwrap();
   	 
	}
	rate.sleep();
	rosrust::spin();

}
```
次に、簡単なlaunchフォルダとlaunchファイル**test.launch**を作成します。
**※**　**rust_ws**フォルダ内で作成するようご注意ください。**
![launch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683212/89f42286-f5d9-c737-875c-436ea130ca31.png)

```xml
<launch>

	<node name ="test" pkg="rust_ws" type="rust_ws"/>
   
</launch>
```
新しいターミナルで **rosrcore** を起動します。

```bash
$ roscore
```
別の新しいターミナルで起動ファイルを実行してください。

```bash
$ roslaunch rust_ws test.launch
```
##rosrust Dependencies
ソーシング後、ROSはRUSTと通信できるようになります。しかし、RUSTはまだROSと通信できず、常にエラーを出します。
これは、RUSTがROSのデフォルト言語ではないため、'.rs'ファイルを実行できるようにするためには、いくつかの小さな、しかし重要な依存関係が必要だからです。
rust_ws**フォルダに戻って、以下の依存関係をrust_wsの**Cargo.toml**ファイルに追加する必要があります。

依存関係を追加する前に：

```toml
[package]
name = "rust_ws"
version = "0.1.0"
authors = ["Praveshkp <pravesh.k.pradhan@gmail.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
```
依存関係を追加した後：

```toml
[package]
name = "rust_ws"
version = "0.1.0"
authors = ["Praveshkp <pravesh.k.pradhan@gmail.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
env_logger = "0.7.1"
rosrust = "0.9"
rosrust_msg = "0.1"
```
要件に応じて、依存関係を追加することができます。
依存関係を追加したら、カーゴビルドで依存関係をダウンロードします。

```bash
$ cargo build
```
**※**　cargo buildを **rust_ws** パッケージの src フォルダで行っていることを確認してください。

cargoが必要な[dependencies]のダウンロードを終えた後、新しいターミナルで rostopic echo を実行してください。

```bash
$ rostopic echo /cmd_vel
```
そして、launchファイルを実行します。

```bash
$ roslaunch rust_ws test.launch
```
![roslaunch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683212/a5c6cdbd-5951-6bc1-b5ee-58b7cd60d492.png)

これは、/cmd_velというトピックと値をpublishするだけのプログラムなので、rostopic echoが値をpublishしてくれます。
![pub_test.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683212/a14574bc-6f07-ae27-a2f6-8eebf5c02052.gif)



##Conclusion
上記の手順を踏むことで、RUST付きのROSパッケージを作成することができます。欠点は、新しい ROS with RUST パッケージを作成しようとすると、毎回同じ処理をしなければならないことです。私と [株式会社Rist](https://www.rist.co.jp) の同僚は現在、このプロセスを自律的にするための作業を行っています。