8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ROS2Advent Calendar 2022

Day 11

rclrustを使って1から自分でROS2 ノードを作成してみる

Last updated at Posted at 2022-12-10

これは何?

RustでROS2のノードを書く方法についての解説です。基本的にはこのドキュメントに沿ってノードを作成しその動作確認についてまとめていきます。

今回はDockerを使わず、関連ソフトのインストールも手動でやっていきます。

かなり前の記事でros2-rustのについて書いたのですが、中々フォローできず2年も立ってしまいました。。

対象とする読者

  • RustでROS2を書いてみたいと、常々思っている人
  • 一通りRustの基本的な文法標準ライブラリを理解している人

環境

  • Ubuntu 22.04 (ROS Humble)
  • 言語 Rust 1.65.0(1.63以上のインストールが必須)

作業準備

関連ソフトのインストール

ROS2で使用するの基本的なツール類とcargo及びcolconのプラグインをインストールします。
端末で以下のコマンドを実行してください。

# 基本的なツール類
sudo apt install -y git libclang-dev python3-pip python3-vcstool
# cargoとcolconへそれぞれお互い用のプラグインをインストール
cargo install --debug cargo-ament-build
pip install git+https://github.com/colcon/colcon-cargo.git
pip install git+https://github.com/colcon/colcon-ros-cargo.git

ワークスペースの作成と関連ソースのインポート

ワークスペースを作成し、ros2_rustのソースコードと関連するパッケージ類をインポートします。
端末で以下のコマンドを実行してください。

mkdir -p workspace/src && cd workspace
git clone https://github.com/ros2-rust/ros2_rust.git src/ros2_rust
vcs import src < src/ros2_rust/ros2_rust_humble.repos

作業内容

パッケージ作成

パッケージの作成には端末で以下のコマンドをworkspaceディレクトリで実行してください。
cargoを用いてパッケージ作成をします。残念ながらros2 pkg createのようなツールがrclrsにはまだないそうです。

cargo new republisher_node && cd republisher_node

またROS2のビルドに関連するファイルも手動で作成する必要があります。作成したパッケージ直下にpackage.xmlを作成してください。

<package format="3">
  <name>republisher_node</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="user@todo.todo">user</maintainer>
  <license>TODO: License declaration</license>

  <depend>rclrs</depend>
  <depend>std_msgs</depend>

  <export>
    <build_type>ament_cargo</build_type>
  </export>
</package>

CMakeLists.txtは必要ありません。

コードを記述する

今回は/in_topicというトピックを受信したら、/out_topicというトピックへ1秒毎にpublishし続けるノードを作成します。

ソースコードは以下の通りです。main.rsを以下の内容で更新してください。

main.rs
use std::sync::{Arc,Mutex};
use std_msgs::msg::String as StringMsg;

struct RepublisherNode {
    node: rclrs::Node,
    _subscription: Arc<rclrs::Subscription<StringMsg>>,
    publisher: rclrs::Publisher<StringMsg>,
    data: Arc<Mutex<Option<StringMsg>>>,
}

impl RepublisherNode {
    fn new(context: &rclrs::Context) -> Result<Self, rclrs::RclrsError> {
        let mut node = rclrs::Node::new(context, "republisher")?;
        let data = Arc::new(Mutex::new(None));
        let data_cb = Arc::clone(&data);
        let _subscription = node.create_subscription(
            "in_topic",
            rclrs::QOS_PROFILE_DEFAULT,
            move |msg: StringMsg| { 
                *data_cb.lock().unwrap() = Some(msg);
            },
        )?;

        let publisher = node.create_publisher("out_topic",rclrs::QOS_PROFILE_DEFAULT)?;

        Ok(Self {
            node,
            _subscription,
            publisher,
            data,
        })
    }

    fn republish(&self) -> Result<(),rclrs::RclrsError>{
        if let Some(s) = &*self.data.lock().unwrap(){
            self.publisher.publish(s)?;
        }
        Ok(())
    }
}

fn main() -> Result<(), rclrs::RclrsError> {
    let context = rclrs::Context::new(std::env::args())?;
    let republisher = Arc::new(RepublisherNode::new(&context)?);
    let republisher_other_thread = Arc::clone(&republisher); 
    std::thread::spawn(move || ->Result<(),rclrs::RclrsError> {
        loop {
            use std::time::Duration;
            std::thread::sleep(Duration::from_millis(1000));
            republisher_other_thread.republish()?;
        }
    });
    rclrs::spin(&republisher.node)
}


個人的にはかなり素直なRustで書けている印象です。
Arc<Mutex<XXX>>等の多重ジェネリクスがあるので、若干びっくりしますが、Rustのマルチスレッドの基本的な書き方で書かれています。

dataで受け取ったメッセージをpublisherが一秒毎に、トピックとして送信しています。

実行方法1(cargoを使う方法)

作成したパッケージのディレクトリ下で端末を開きcargo runを実行します。
別の端末(端末A)を開き以下のコマンドを実行します。

ros2 topic echo /out_topic

さらに別の端末(端末B)を開き、以下を実行します。

ros2 topic pub /in_topic std_msgs/msg/String '{data: "Bonjour"}' -1

端末Aで以下が表示されれば成功です。

$ ros2 topic echo /out_topic
data: Bonjour
---
data: Bonjour
---
data: Bonjour
---
data: Bonjour
---
data: Bonjour
---

動作確認2(colconを使う方法)

ワークスペース直下に移動し、以下のコマンドを用いてビルドします

colcon build

実行時は以下のコマンドを使ってください。(端末A)

ros2 run republisher_node republisher_node

別の端末(端末B)を開き以下のコマンドを実行します。

ros2 topic echo /out_topic

さらに別の端末(端末C)を開き、以下を実行します。

ros2 topic pub /in_topic std_msgs/msg/String '{data: "Bonjour"}' -1

端末Bで以下が表示されれば成功です。

$ ros2 topic echo /out_topic
data: Bonjour
---
data: Bonjour
---
data: Bonjour
---
data: Bonjour
---
data: Bonjour
---

成果物

Githubに作ったものをまとめておいておきましたので、参考にしてください

私が詰まったところ

  • std_msgs等のメッセージはソースから持ってこないとビルドできない
  • rustcのバージョンが低いのでrustupを使ってバージョンを上げる必要があった(1.63以上)

参考資料

8
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?