3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Arduino Uno QでROS2を使う【基本編】

3
Last updated at Posted at 2026-02-01

背景

昨年10月、WRS2025のシミュレーション部門に、勉強会で募った有志と共に参加しました
結果は無事惨敗ではありましたが、仕事や生活の合間の時間で頑張って開発を回し、大会中は学生の合宿の様な気分を味わうこともできて、とても良い経験になりました

惨敗の理由を挙げだすとキリがないのですが、無駄にPCの数が多くて競技者入れ替え時間内でセッティングが終わらなかったり、接続不良でシステムが全部立ち上がらなかったりといった事は特に勿体なかったなーと反省しております
競技後のチームメンバーとの会話でも「どうするのが良かったのかね〜」と話題になりました

会場でてんやわんやしながら機材セッティングしている我々

挙がった改善案の中に、「”自作した操作用ハードウェア”は、マイコンとか使って、電源繋いだら半自動でROSに繋がるぐらいにしとけば良かったね」みたいなのがありました
まあ、その場は単なる雑談程度だったのですが、機会があれば実装したいと思っておりました
しかし、冷静に考えると「マイコン使うといっても何を?」となりました
真っ先に思いつくのは、micro-ROS対応のマイコンを使うとかでしょうか?
しかし、それだと今回メンバーがpythonで作ってくれたパッケージの資産が十分に活用できません(あと正直、自分の知識があまりない^^;)
一方で、最近のラズパイとか使っちゃうと、「もはやPCとほぼ変わらんやん」と思い、ちょっと渋っておりました

そんな中、先日ついに日本でも流通が始まったコイツが、にわかに脳内の皆さんの注目を集めます

  • Qualcommが買収し、魔改造された進化した新しいArduino Unoです
    • MPUとMCUのデュアルプロセッサで、なんとLinuxを載せれるとか
    • 物理I/FはUnoを踏襲していて互換性があるとか

『これなら、一応Arduinoだしマイコンだろう』『Linuxが走るということは、フルのROS2がそのまま動くんじゃねぇの?』『大人の趣味において1万円以下は実質無料!(?)』『いや、これもはやラズパイとかわらんのじゃ…』

というわけで、Uno Qを全力でポチりROS2を動かしてみたので、ここに共有します!

本題

ひとまず上に書いたWRSのことは頭の片隅に置いておいて、本記事においては、基本的なROS2の機能が動いたとこまでを報告します

Linuxの初期設定

  • Arduino app labをインストールしたPCにつないで、場当たり的に適当にポチポチしてたら終わったので、記録できておらず、ちゃんと思い出せません。。。
    • 不正確な情報を流しても迷惑だと思うので、この辺の情報は他の有志の方の報告におまかせします^^;
    • ただ、最初、普段開発に使っているUbuntu24に入れた app labだと上手く行かず、Windows版を試したら上手くいきました。マシンの相性もあるのかな?
  • user名とデバイス名、パスワードが発行されるとsshでアクセスできるようになるので、以降ではsshでログインして操作を行います。クライアントにはVScodeを使っています

micromambaの導入

conda-forgeリポジトリを活用したお手軽環境構築がしたいので、conda互換パッケージマネージャ最軽量のmicromambaを使っていきます

USBストレージのマウント

はじめは、Uno Q標準のストレージにインストールを試みたのですが、eMMC 16Gbのうち殆どはシステムに専有されており、容量不足でインストールが止まってしまいました…
そこで、USBタイプのSSDを使って、領域を無理やり増やします
Uno QのUSB Type-Cポートは、電源供給とデータ通信の兼用ですので、電源を同時供給できるタイプのハブを使って、SSDを接続しました

  • 接続したら、lsblkdf -hコマンドでUSBドライブの名前を確認します
    • ここでは/dev/sda1とします
  • /mnt/usbにマウントします
sudo mkdir -p /mnt/usb
sudo mount /dev/sda1 /mnt/usb

micromambaのprefix置き場の作成

sudo mkdir -p /mnt/usb/micromamba
sudo chown -R $USER:$USER /mnt/usb/micromamba #せんでいいかも

インストール

基本的には公式に載っているとおりですが、prefixの置き場所を訊かれたら先程作ったディレクトリのパスを渡します

"${SHELL}" <(curl -L micro.mamba.pm/install.sh)
# 以下のように訊かれる
# Micromamba binary folder? [~/.local/bin] 
# Init shell (bash)? [Y/n] Y
# Configure conda-forge? [Y/n] Y
# Prefix location? [~/micromamba] 
  • Enter, Y Enter, Y Enter
  • 最後に/mnt/usb/micromambaで Enter

アプデもしときます

micromamba self-update

ROS2の導入

RoboStackを使ってROS2を導入します。RoboStackは、Conda(Mamba)エコシステム上でROS/ROS2を利用可能にするプロジェクト
クロスプラットフォームで開発されており、Armアーキテクチャでも利用できるのがありがたいです
多くのパッケージのバイナリがconda-forgeリポジトリ経由で配布されており、コンパイルなしで利用できます
(対応表はこちら)
ROSの複数バージョンが配布されていて、OSに依存せず好きなバージョンが利用できる点もメリットです
(今回は、WRSのときのバージョンに合わせてROS2 Humbleを使います)

インストール

  • 基本的に公式の通りです
micromamba create -n ros_env -c conda-forge -c robostack-humble ros-humble-desktop
micromamba activate ros_env
micromamba config append channels robostack-humble --env
  • より軽量なros-humble-ros-baseも配布されていますが、後からパッケージを追加するときにバージョンまわりでトラブルが起きる事があるので、GUIツールを使わない場合でもros-humble-desktopを選択しておくことをオススメします
micromamba activate ros_env
micromamba install -c conda-forge ros-dev-tools
  • これで、colconやrosdepも使えるようになります
    • C++系での開発のためには、compilers, cmake, pkg-config, make, ninjaといったパッケージも必要だったような気がしますが、今回はpythonしか使いません

MCUとの連携

MCUを搭載し、従来のArduino資産を有効活用できるのも本機の強みです
といわけで、LinuxとMCUが通信できるようにします

Linux側準備

Arduino app labを使ってもできるのかもしれませんが、イマイチ使い方がわからなかったので、色々調べていると、以下のリポジトリに辿り着きました
ここにあるソースコードを使うとMCUと通信ができそうです

  • しかし、cloneしてpip install -e .でローカルインストールしようとしたら、以下のエラーが出ました
    image.png
  • python3.13以上じゃないと入れられないようです
    • しかし、RoboStackの仮想環境はpythonバージョン指定で、そちらを弄りだすと依存関係の沼にハマりそうでやりたくありません…
  • というわけで、pipでのインストールは諦め「micromambaの仮想環境に入る際に環境変数にソースコードのパスを通す」という力技で使うことにしました
cd ~
git clone https://github.com/arduino/app-bricks-py.git
sudo mkdir -p $(micromamba info --json | grep -o 'micromamba_prefix": *"[^"]*' | cut -d'"' -f4)/etc/conda/activate.d/
# テキストエディタで開きます
code /mnt/usb/micromamba/envs/ros_env/etc/conda/activate.d/env_vars.sh
# 以下を書き込んで保存
# export PYTHONPATH="/home/arduino/app-bricks-py/src:${PYTHONPATH}"

MCU側準備

やはりArduino app labの使い方がイマイチわからなかったので、従来のArduino IDEで作業しました

  • Arduino_RouterBridgeというライブラリを導入します
    image.png
  • ボードとIDEの接続はツールのタブから色々ポチポチ弄っていたらできるようになりました…

動作テスト

今回はUno QのMCU経由で温度センサの値を取得して1秒毎にパブリッシュする簡単なROS2ノードを作ってテストしました

ハードウェア準備

  • 昔、無印Unoで遊ぶ用に買ったGroveセンサとそれを取り付けるためのHATを引っ張り出してきました
  • ピッタリです!
    • 前評判どおり、Unoの周辺機器が使い回せるのはありがたい

プログラム準備

Linux側

import rclpy
from rclpy.node import Node
from std_msgs.msg import Float32

from arduino.app_utils.bridge import call

@call("get_temperature") #inoスケッチと名前を揃える
def get_sensor_value() -> float: ...

class TemperaturePublisher(Node):
    """ROS2 Node that publishes temperature from Arduino UNO Q MCU."""

    def __init__(self):
        super().__init__('temperature_publisher')
        
        self.publisher_ = self.create_publisher(
            Float32,
            'uno_q/temperature',
            10
        )
        
        # タイマー割り込み
        timer_period = 1.0  #sec
        self.timer = self.create_timer(timer_period, self.timer_callback)
        
        self.get_logger().info('Temperature Publisher Node has been started')
        self.get_logger().info('Publishing temperature to topic: uno_q/temperature')

    def timer_callback(self):
        """Timer callback that reads and publishes temperature."""
        try:
            # ここでMCU側の関数を呼んでいる
            temperature = get_sensor_value()
            
            msg = Float32()
            msg.data = float(temperature)
            self.publisher_.publish(msg)
            
            self.get_logger().info(f'Published temperature: {temperature:.2f}')
            
        except Exception as e:
            self.get_logger().error(f'Failed to read temperature: {str(e)}')


def main(args=None):
    """Main function to run the temperature publisher node."""    
    rclpy.init(args=args)
    
    node = TemperaturePublisher()
    
    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        node.get_logger().info('Shutting down temperature publisher node...')
    finally:
        node.destroy_node()
        rclpy.shutdown()


if __name__ == '__main__':
    main()

MCU側

#include "Arduino_RouterBridge.h"
#include <math.h>

const int B = 4275000;            // サーミスタのB値
const int R0 = 100000;            // R0 = 100k
const int pinTempSensor = A0;     // Grove - Temperature SensorをA0に接続
float temperature = 0;

void setup() {
    Bridge.begin();
    Bridge.provide("get_temperature", get_temperature); //ここで、関数の公開設定
}

void loop() {
    int a = analogRead(pinTempSensor);
    float R = 1023.0/a-1.0;
    R = R0*R;

    temperature = 1.0/(log(R/R0)/B+1/298.15)-273.15; // データシートを基に温度に変換
    delay(100);
}

float get_temperature() {
    return temperature;
}
  • MCU側のプログラムはセンサのwikiを参考に作成
  • MCU側で定義した関数を任意の名前で公開し、Linux側ではapp_utilsのcallデコレータを使ってそれをpythonの関数にマップするみたいな事をしている
    • これが正しいやり方なのかは自信ない…
  • それ以外は基本的なROSの定周期パブリッシャーと変わらない
    • パッケージのその他のファイルはgithubに上げました

実行結果

  • ひとまず、同一マシン内でpub-subを
    Screencast from 2026-02-01 15-59-32.gif
  • なんか値が安定しすぎて逆に怪しいですが、、、とりまエラーは出ていないようです

まとめ

ここまでやると、面白いところはほぼ終わっている気もしますが、余力があれば次回はWRSのときのリーダーアームをUno Qと接続しようかと思います

3
5
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
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?