M5Stackでも動くOpenClaw「ESP Claw」を動かしてみた

先日、名古屋で初めてM5Stack Japan Tourが開催されました。

Station Aiでの開催ということでスタートアップのM5Stackとは相性が抜群なのでは無いでしょうか。

で、せっかく名古屋で開催するのであればということでも僕もLTで喋りました。

M5Stackチームもいるということでスライドは英語で用意しました。(今見るとスライドのタイトル盛大にTypoしとるw)

www.docswell.com

LTなので触りの部分しか喋れてなかったのとシンプルにスライドが英語だったので、今回はM5Stack Core S3でESP Clawを動かす手順を紹介します。

ESP Clawとは?

ESP32で今話題のOpenClawのようなAIエージェントを動かすことができるフレームワークで主にIoT向けに特化しています。実装はMiniClawに影響を受けているようです。

以下の画像のようにそれまで自前で開発環境を構築してそこからプログラムを書いていたものがESP Clawのサーバーで自律的にありとあらゆるタスクを作成したり、遂行することができるようになります。

タスクの実行には組み込みで使用されているスクリプト言語であるLuaを使用しているようです。

https://github.com/espressif/esp-claw

セットアップできると以下のことができるようです

  • 定期実行
  • 天気予報
  • GPIO制御
  • Web検索(検索エンジンのAPIキーが必須)

必要要件

必要な要件としては、少なくとも8MBのフラッシュメモリと8MBのPSRAMです。

執筆時点(2026年5月)だと以下のボードがサポートされています。

  • ESP32-S3
  • ESP32-P4
  • ESP32-C5

上記が使われていればM5Stackでも動かせるというわけですが、先日公式でもESP32-S3が使われているStickS3やCoreS3向けにM5Burnerから書き込めるように対応させたとのことでした。

試してみる

ファームウェアの書き込み

早速試してみます。今回は家に転がってたCore S3にセットアップしてみます。

まずは、以下のURLからM5Burnerをインストールします。M5Stackシリーズにファームウェアの書き込みを簡単することができるツールです。

docs.m5stack.com

インストールできたら、起動しDevice Typeから Core S3 を選択して、検索窓からESP Clawのファームウェアを探して表示されたファームの右下のBurn ボタンをクリックします。

書き込みが終わって以下の通りザリガニの絵が表示されたらファームウェアの書き込みは完了です。

セットアップ

ファームウェアが書き込まれたら、ESP Clawが動かせるようにセットアップをしていきます。

CoreS3に表示されている文字列がWifiのアクセスポイントになっています。アクセスポイントにスマホかPCで接続したら、ブラウザで http://esp-claw.local/ 、またはデバイスのIPアドレスを指定することで設定画面を開きます。(M5Stack側のアクセスポイントに接続したらおそらく設定画面が開くはずです)

開いたら、まずは接続したWifiのSSIDとパスワードを設定します。

接続先のLLMのAPIの接続情報を設定します。

APIは以下のサービスがサポートされています。その他にカスタムのAPIにも対応しています。

  • OpenAI
  • Anthropic
  • Qwen

今回はAnthropicのClaude Haiku 4.5を使います。

Telegram Botをセットアップ

次にESP Clawで連携するチャットサービスを用意します。

以下のサービスがサポートされていますが、今回は日本でも動かせそうなTelegramを使用します。

TelegramでBotをセットアップするにはBotを管理するBotFatherを自分のアカウントに追加します。

追加は以下のURLからできます。

Telegram: Launch @BotFather

追加したら以下の通り /newbot スラッグを送信すると、チャットベースのやりとりで直感的にBotをセットアップできます。

作成できると、APIトークンが作成されるのでコピーして設定画面のTelegramの項目にAPIトークンをコピペします。また、作成したBotはあらかじめアカウントに追加してもらいます。

Web検索

追加で必要があればWeb検索が使えるようにAPIトークンを入れると検索機能が使えるようになります。

今回はTavilyのAPIに接続します。

動かしてみる

それでは実際に動かしてみます。

以下のポストの動画の通りメッセージを送ると、Botから返答されるようになります。

あとは、天気予報や今の時間などを問い合わせたり、定期実行タスクを作成することができます。実際にツールが呼び出されているところもメッセージで返されます。

M5Stack Core S3はハードウェア接続はGroveのみで対応しているモジュールを持ってなかったのでハードウェア連携はまだ試して無いです。

ただClaudeだと複雑なタスクをやろうとすると以下の通り1分あたりの入力トークンの制限に引っかかることがあるので、この対策を色々試行錯誤したほうが良さそうです。

最初は最新のSonnet4.6で試しましたが、ちょっとツール呼び出しただけでコケたので少し古めのHaiku 4.5に変えたらある程度簡単なツール呼び出すタスクはこなせるようになりました。

まとめ

今回はM5Stack Core S3でESP Clawを動かしてみました。

ESP32だと簡易的はWebサーバーを動かせたりしますが、まさかOpenClawインスパイアを動かせるのは面白いと思いました。

ただ、すぐAPI上限が引っかかったりたまにツールの動作が不安定なところもあるので今後の改善に期待したいところです。

M5StackのAIアクセラレーターカード「LLM8850」でLLMを試してみた

気がついたら2026年から2ヶ月経過しましたが新年一発名の投稿ですね(あけましておめでとうございますw)

突然ですが昨年、6年ぶりに深センに行ってきました。社会人になってから初めての海外で結構楽しめました。

この時、深センではMaker Faireが開催するということで海外のMaker Faireに行けるチャンスだと感じてタイミング合わせて行ってきました。

日本とは違った雰囲気で野外で伸び伸びやっているのが印象的でした。

そんな中、M5Stackのブースで気になるやつを見つけました。

デカデカとAIアクセラレータのレプリカが置いてあるそばで実際のボードのデモが行われていました。

画像を認識して骨格や物体を認識したり認識した顔を遠隔操作でスタックチャンがトラッキングするというデモでした。滑らかに画像認識してるしLLMも動かせるなら買うしか無いと思い、その場で衝動買いしましたw。(AIアクセラレータボードを買ったら写真に写ってる新製品はじめいろいろおまけを頂きました!)

で、帰国してから追加パーツも用意せず、ずっと放置してたので今回はこれを開封して実際に触ってみたいと思います

用意するもの

  • LLM8850
  • Raspberry Pi 5(今回は4GBで検証)
  • Raspberry Pi M.2 HAT+
  • 5V5Aの電源(AIアクセラレーターを動かすので、十分な電力が供給されるものが必要)

LLM8850はM.2で接続する必要がありますが、PCI接続が前提かつUSBでM.2に接続できるアダプタには対応してないので、M.2 HATを使ってRaspberry Pi5を使う構成で試していきます。

ハードウェアのセットアップ

EEPROMの更新

ハードウェアはM.2 HATにLLM8850を取り付けてRaspberry Piに装着するだけです。電源は必ず外した状態でセットアップを行います。

また、ピンヘッダは先にRaspberry Pi側に差し込んでから接続すると確実に接続できます(HAT側に接続したら接続が不安定でした…)

ドライバのセットアップ

ハードウェアをセットアップしたらLLM8850が使えるようにドライバのセットアップをしていきます。

Raspberry Piを起動したら、以下のコマンドでEEPROMのバージョンを確認します。パソコンのBIOSと同様でEEPROMはマザーボード側に書き込まれる設定なので、Raspberry PiのOSを最新にしてもEEPROMが最新になるとは限りません。

sudo rpi-eeprom-update 

結果は以下の通りになります。

BOOTLOADER: update available
   CURRENT: Fri 13 Jun 09:39:26 UTC 2025 (1749807566)
    LATEST: Mon  8 Dec 19:29:54 UTC 2025 (1765222194)
   RELEASE: default (/usr/lib/firmware/raspberrypi/bootloader-2712/default)
            Use raspi-config to change the release

この時 LATESTRELEASE が異なる場合は最新版にアップデートしておきます。

アップデートでは以下のコマンドを実行します。

sudo rpi-eeprom-update -a 

アップデートが終わったら一度再起動します。再起動後、 sudo rpi-eeprom-update で確認して最新版になっていればEEPROMの更新は正常に完了しています。

sudo reboot

続けて接続したアクセラレーターカードが認識しているか確認します。以下のコマンドでPCIで接続されたハードウェアを確認します。

lspci 

以下の実行結果になるかと思います。

0001:00:00.0 PCI bridge: Broadcom Inc. and subsidiaries BCM2712 PCIe Bridge (rev 30)
0001:01:00.0 Multimedia video controller: Axera Semiconductor Co., Ltd Device 0650 (rev 01) # <- これが表示されてたらOK
0002:00:00.0 PCI bridge: Broadcom Inc. and subsidiaries BCM2712 PCIe Bridge (rev 30)
0002:01:00.0 Ethernet controller: Raspberry Pi Ltd RP1 PCIe 2.0 South Bridge

実行結果にある Multimedia video controller: Axera Semiconductor Co., Ltd Device 0650 (rev 01) がLLM8850にあたります。これが表示されていればハードウェアは正常に認識されています。

もし表示されない場合は、ケーブルの接触不良の可能性があるのでケーブルの接続を確認してください。

ドライバーのインストール

今度はLLM8850のドライバーをインストールしていきます。

まずはドライバーのパッケージを以下のコマンドで取得します

sudo wget -qO /etc/apt/keyrings/StackFlow.gpg https://repo.llm.m5stack.com/m5stack-apt-repo/key/StackFlow.gpg
echo 'deb [signed-by=/etc/apt/keyrings/StackFlow.gpg] https://repo.llm.m5stack.com/m5stack-apt-repo axclhost main' | sudo tee /etc/apt/sources.list.d/axclhost.list 

以下のコマンドでドライバーをインストールします

sudo apt update 
sudo apt install dkms 
sudo apt install axclhost 

インストールが終わると、環境変数を反映すればドライバーが使える状態になります。

今作業を行っているターミナルで作業を続ける場合は以下のコマンドを実行すると環境変数が再度読み込まれます。

source /etc/profile 

ドライバーがインストールされたか確認するため、以下のコマンドでデバイスのステータスを確認します。

axcl-smi 

実行すると以下の通りデバイスの仕様ステータスが詳細に表示されます。ステータスが表示されたらLLM8850を動かすセットアップは一通り完了です。

+------------------------------------------------------------------------------------------------+
| AXCL-SMI  V3.6.4_20250822020158                                  Driver  V3.6.4_20250822020158 |
+-----------------------------------------+--------------+---------------------------------------+
| Card  Name                     Firmware | Bus-Id       |                          Memory-Usage |
| Fan   Temp                Pwr:Usage/Cap | CPU      NPU |                             CMM-Usage |
|=========================================+==============+=======================================|
|    0  AX650N                     V3.6.4 | 0001:01:00.0 |                145 MiB /      945 MiB |
|   --   42C                      -- / -- | 1%        0% |                 18 MiB /     7040 MiB |
+-----------------------------------------+--------------+---------------------------------------+

+------------------------------------------------------------------------------------------------+
| Processes:                                                                                     |
| Card      PID  Process Name                                                   NPU Memory Usage |
|================================================================================================|

LLMを走らせてみる

ここまでアクセラレーターカードのセットアップができたので、今度は実際にLLMを試してみます。

今回は公式で紹介されていたDeepSeek R1をセットアップしてみます。

docs.m5stack.com

ファイル一式はHugging FaceからGitでクローンしていきます。この時通常のGit Cloneだと取得できない大きいサイズのモデルがあるため、それを取得するためにGit LFSをインストールします。

sudo apt install git-lfs

以下のコマンドでインストールされたか確認します。

git lfs install

Git LFSがインストールされたら、以下のコマンドでファイル一式をクローンします。

git clone https://huggingface.co/AXERA-TECH/DeepSeek-R1-Distill-Qwen-1.5B-GPTQ-Int4

ls -lh 以下のファイル構成・ファイルサイズになっていれば問題ありません。

-rw-rw-r-- 1 pi pi    0 Feb 23 01:06 config.json
drwxrwxr-x 2 pi pi 4.0K Feb 23 01:08 deepseek-r1-1.5b-gptq-int4-ax650
drwxrwxr-x 2 pi pi 4.0K Feb 23 01:06 deepseek-r1_tokenizer
-rw-rw-r-- 1 pi pi 4.2K Feb 23 01:06 deepseek-r1_tokenizer.py
-rw-rw-r-- 1 pi pi 976K Feb 23 01:07 main_axcl_aarch64
-rw-rw-r-- 1 pi pi 999K Feb 23 01:07 main_axcl_x86
-rw-rw-r-- 1 pi pi 932K Feb 23 01:07 main_prefill
-rw-rw-r-- 1 pi pi  277 Feb 23 01:06 post_config.json
-rw-rw-r-- 1 pi pi 7.4K Feb 23 01:06 README.md
-rw-rw-r-- 1 pi pi  533 Feb 23 01:06 run_deepseek-r1_1.5b_gptq_int4_ax650.sh
-rw-rw-r-- 1 pi pi  538 Feb 23 01:06 run_deepseek-r1_1.5b_gptq_int4_axcl_aarch64.sh
-rw-rw-r-- 1 pi pi  534 Feb 23 01:06 run_deepseek-r1_1.5b_gptq_int4_axcl_x86.sh

クローンされたディレクトリ上でPythonの仮想環境をセットアップします。

python -m venv deepseek 

仮想環境を有効にします。

source deepseek/bin/activate

以下のコマンドで必要なPythonライブラリをインストールします。

pip install transformers jinja2 

tokenizer parserを起動します。

 python deepseek-r1_tokenizer.py --port 12345

別のターミナルを立ち上げてクローンしたレポジトリのディレクトリ上で以下のコマンドを実行し、スクリプトの実行権限を付与します。

chmod +x main_axcl_aarch64 run_deepseek-r1_1.5b_gptq_int4_axcl_aarch64.sh 

以下のコマンドでスクリプトを実行します。

./run_deepseek-r1_1.5b_gptq_int4_axcl_aarch64.sh 

以下の通り対話環境が起動されればセットアップは正常に完了しています。

pi@raspberrypi:~/Codes/DeepSeek-R1-Distill-Qwen-1.5B-GPTQ-Int4 $ ./run_deepseek-r1_1.5b_gptq_int4_axcl_aarch64.sh
build time: Feb 13 2025 15:44:57
[I][                            Init][ 111]: LLM init start
bos_id: 151646, eos_id: 151643
100% | ████████████████████████████████ |  31 /  31 [25.35s<25.35s, 1.22 count/s] init post axmodel okremain_cmm(-1 MB)
[I][                            Init][ 226]: max_token_len : 1023
[I][                            Init][ 231]: kv_cache_size : 256, kv_cache_num: 1023
[I][                     load_config][ 282]: load config:
{
    "enable_repetition_penalty": false,
    "enable_temperature": true,
    "enable_top_k_sampling": true,
    "enable_top_p_sampling": false,
    "penalty_window": 20,
    "repetition_penalty": 1.2,
    "temperature": 0.9,
    "top_k": 10,
    "top_p": 0.8
}

[I][                            Init][ 288]: LLM init ok
Type "q" to exit, Ctrl+c to stop current running
>>

どうやら英語か中国語にしか対応してなさそうなので英語で適当なプロンプトを投げてみました。スペースやタブが文字化けするのが気になりますが、それを解消すれば普通に使えそうです。

Raspberry Pi単体だけでLLMを動かしたことがないのでなんともいえませんが、簡単な質問は秒で返答しますしちょっと複雑な質問でも詰まることなくスムーズに返答を出力できてたので、モデルをファインチューニングして日本語に対応できれば、日本語でも全然使えそうな印象があります。(やるかどうかは別だけど…

>> Hello
<think>ĊOkay,ĠIĠneedĠtoĠhelpĠtheĠuserĠwithĠtheirĠquery.ĠTheyĠstartedĠwithĠ"YouareDeepSeek-R1,Youareahelpfulassistant."ĠItĠseemsĠlikeĠthey'reĠprovidingĠsomeĠinformationĠorĠaĠprompt.ĠTheĠresponseĠwasĠ"Hello!"ĠwhichĠisĠfriendlyĠandĠwelcoming.ĊĊIĠshouldĠconsiderĠwhatĠtheyĠmightĠneed.ĠTheyĠcouldĠbeĠseekingĠassistance,Ġinformation,Ġfeedback,ĠorĠjustĠhappyĠtoĠbeĠpartĠofĠaĠconversation.ĠMaybeĠthey'reĠlookingĠforĠhelpĠwithĠaĠspecificĠtask,ĠorĠtheyĠcouldĠbeĠseekingĠaĠfriendlyĠgreeting.ĊĊIĠshouldĠrespondĠinĠaĠwayĠthat'sĠpositiveĠandĠhelpful.ĠIĠwantĠtoĠacknowledgeĠtheirĠmessageĠpositivelyĠandĠsetĠaĠgoodĠexample.ĠSo,ĠI'llĠsayĠsomethingĠlike,Ġ"I'mĠhereĠtoĠhelp!ĠLetĠmeĠknowĠhowĠIĠcanĠassistĠyouĠtoday."ĠThat'sĠopen-ended,ĠinvitingĠthemĠtoĠaskĠforĠanythingĠtheyĠneed.Ċ</think>ĊĊI'mĠhereĠtoĠhelp!ĠLetĠmeĠknowĠhowĠIĠcanĠassistĠyouĠtoday.

[N][                             Run][ 610]: hit eos,avg 13.90 token/s

>>

まとめ

今回は、M5StackのAIアクセラレーターカードをセットアップしてRaspberry PiでLLMを動かしてみました。

消しゴムぐらいのサイズのアクセラレーターカードですが、動かしてみるとかなりパワフルでLLMもスムーズに返答できるのはかなり面白いです。

他にもMaker FaireでやってたYOLOを使った画像認識などのサンプルもあったので、色々試してみたいところです!

参考

note.com

Jetson Xavier NXをセットアップしてみた

もうすぐ2025年も終わります。

今年はコミュニティ活動も積極的に取り組んだり、仕事でも出張の頻度が多く各地を飛び回ってました。

そんな中、大阪に出張に行ったときに久々に日本橋の電気街を散策していました。

シリコンハウスのガジェット自販機も健在でした!

シリコンハウスには用件済ませて閉店間際に駆け込んだのですが、そこでちょっとおもしろそうなものを買ってきました。

それは、簡易梱包されたJetson Xavier NXが搭載されたミニPCです

写真を撮り忘れましたが、レジの前にこの袋が山積みになっている横にJetson Nanoが搭載されたカメラも置いてあり、どっちを買うか迷いましたがすでにJetson Nanoはすでに持っていたのと、モジュールだけでもいい値段するXavierを手に届く価格で試してみたくて、衝動買いしてしまいましたw

オンラインでも販売されていたようで、以下のURLにある商品です。今は現行のJetsonが高騰しているなかで、3万切るのは安いですよね。(新品ではないけど)

eleshop.jp

とりあえず起動してみたらすでに中にOSがインストールされていそうでした。

ここで終わっても良かったのですが、Jetson Xavier NXに内蔵されたたった16GBのeMMCでしかブートしてなかったようで追加で何かセットアップするには心許なかったです。

で、インストール済のJetPackUbuntu 18.04に対応していますが、調べてみたら20.04に対応したバージョンまで上げることができそうです。

というわけで今回はこのミニPCでUbuntu 20.04が使えるように一からセットアップしていきます。

今回セットアップするPCのスペック

簡単にPCのスペックをまとめると以下の表になります。

項目 スペック
型番 NX211B-OL
インターフェース EthernetHDMI、SDカードスロット、USB 3.0×2、micro USB(OTG用)
ストレージ eMMC 16GB
電源 DC12V(ACアダプタは付属)

用意するもの

  • NX211B-OL
  • 外付けディスク(SSDUSBメモリなど)
  • OTGに対応したマイクロUSB
  • OSを書き込むためのホストPC(UbuntuCentOSまたはWindows。今回はUbuntuを用意しました)

セットアップ

sdk managerをインストール

JetsonのeMMCにOSを書き込むためにホストPCにsdk managerをインストールします。

インストーラーは以下のURLからお使いのPCのOSに合わせてダウンロードします。インストールにはNvidia Developerのアカウントに登録する必要があります。

developer.nvidia.com

僕の場合は、Ubuntuでセットアップしていたので以下のコマンドでインストーラーを使用してインストールします。

sudo apt install ./sdkmanager_2.4.0-13234_amd64.deb

ホストPCをJetsonにつなげる

続いてホストPCとJetsonを接続してOSを書き込むための準備をします。

OTGケーブルでPC同士を接続したら RECOVERY と書かれた穴にあるボタンを押しながらJetsonの電源を入れるとリカバリモードでPCが起動します。

ボタンは深いところにあるので、細めのプラスドライバーで押し込んでいます。

lsusb コマンドを叩いて、 NVIDIA Corp. APX が一覧に表示されていればJetsonはリカバリモードで接続できています。

koki@koki-desktop:~$ lsusb 
Bus 001 Device 002: ID 8087:8000 Intel Corp. 
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 003 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 002 Device 004: ID 0955:7e19 NVIDIA Corp. APX
Bus 002 Device 003: ID 04d9:a088 Holtek Semiconductor, Inc. APX
Bus 002 Device 002: ID 1a40:0101 Terminus Technology Inc. Hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

OSのインストール

接続できたら、SDK Managerを起動します。

初回起動時にSDK Managerをインストールしたときに登録したNvidia Developerのアカウントでログインします。

ログイン後、Jetsonと接続できていれば、以下の通りデバイスの一覧が表示されます。今回はサードパーティーのキャリアボードと接続して使うので、 Jetson Xavier NX を選択し OK ボタンをクリックします。

まずは、セットアップする内容を設定しておきます。

JetPackはXavierで使える最後のバージョンである5.1.5を設定し、DeepStreamのインストールは任意です。

インストールの内容を確認して一番下のライセンス同意のチェックボックスにチェックを入れたら CONTINUE をクリックします。

ダウンロードしたSDKの保存先が自動作成されるので、確認したら Create をクリックします。

管理者権限の処理があるので、ホストPCのユーザーのパスワードを入力して OK をクリックします。

インストールが実行されるのでしばらく待ちます。

インストール中にJetsonのモジュールに書き込む情報が聞かれるので設定していきます。

SSH接続で必要なIPアドレスは特になにもいじらずにユーザー名とパスワードは任意のものをいれておきます。

Storage Deviceには eMMC を設定しておきます。

Jetsonのストレージの拡張

しばらくすると、Jetson側のファンが動いてOSが立ち上がります。

そして、以下のダイアログが表示されたらJetsonにログインできる状態になったので、ダイアログにあるログイン情報でSSH接続ができます。

OSの立ち上がりには時間がかかるので、ログインできないときにはしばらく待ちます。

SSH以外にも画面を接続すればデスクトップでも操作できる状態になるのでデスクトップで操作したいときにはモニター、キーボード、マウスを用意して操作します。

ここでもう一度おさらいするとこの時点ではeMMCにOSが書き込まれている状態で、Jetson用のSDKパッケージであるJetPackをインストールするにはストレージが不足します。

その証拠にすでにeMMCの使用率はほぼ100%に達しています。

nvidia@ubuntu:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/mmcblk0p1   14G   13G     0 100% /
none            3.3G     0  3.3G   0% /dev
tmpfs           3.4G     0  3.4G   0% /dev/shm
tmpfs           684M   53M  631M   8% /run
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           3.4G     0  3.4G   0% /sys/fs/cgroup
tmpfs           684M  4.0K  684M   1% /run/user/1000
nvidia@ubuntu:~$ 

そこでこのタイミングで外付けのディスクを使ってUSBブートをできるようにするための設定を行います。

その手順については以前Jetson互換のreComputerをセットアップするときに一度試したことあり、この手順を参考にセットアップをしていきます。ここではざっと手順をまとめますが、詳細な手順は以下のブログの通りにやれば問題なかったです。

supernove.hatenadiary.jp

まずは必要なスクリプトのレポジトリをクローンします。

git clone https://github.com/limengdu/rootOnUSB.git

続けて以下のコマンドでスクリプトを実行してOSをext4形式にフォーマットした外付けディスクにクローンします。

コピー先のディスクのパスはディスクアプリかlsblkなどで確認しておきます。

./copyRootToUSB.sh -p /dev/sda1

/boot/extlinux/extlinux.conf を開き、rootのパスをコピー先のディスクのパスに書き換えます。

-      APPEND ${cbootargs} root=/dev/mmcblk0p1 rw rootwait rootfstype=ext4 console=ttyTCU0,115200n8 console=tty0 fbcon=map:0 net.ifnames=0 video=efifb:off nospectre_bhb nv-auto-config 
+      APPEND ${cbootargs} root=/dev/sda1 rw rootwait rootfstype=ext4 console=ttyTCU0,115200n8 console=tty0 fbcon=map:0 net.ifnames=0 video=efifb:off nospectre_bhb nv-auto-config 

書き換えたらJetsonを再起動して、再度dfコマンドでストレージの空き容量を確認したときに外付けディスクで追加した容量が認識されていたらストレージの拡張は成功です。

nvidia@ubuntu:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda        234G  9.9G  212G   5% /
none            3.3G     0  3.3G   0% /dev
tmpfs           3.4G   36K  3.4G   1% /dev/shm
tmpfs           684M   27M  657M   4% /run
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           3.4G     0  3.4G   0% /sys/fs/cgroup
tmpfs           684M   20K  684M   1% /run/user/124
tmpfs           684M   28K  684M   1% /run/user/1000

再びホストPCのSDK Managerに戻り、残りのインストールを再開させたらインストールは成功です。

まとめ

今回大阪出張のときに買ったJetson Xavier NX搭載のミニPCをクリーンインストールしてみました。

同じようにシリコンハウスでこのミニPCを買った人たちのブログも見かけますが、なかなかうまくセットアップされなかったりしたのでメモがてら自分がうまくいった手順をまとめました。

なかなか忙しくてあまりさわれてなかったので、年末年始の積みガジェットの消化が一つできて良かったです。

これでUbuntu 20.04が使えるようになったのでLLMとかのセットアップをして色々遊べたらと思います!

ultraArmと吸引ポンプを繋いでみた

先日、ソラコムさんが開催されている年次カンファレンス「SORACOM Discovery」に参加してきました。

discovery.soracom.jp

僕はコロナ禍が収まってからオフライン参加がメインになってきて、何かしら登壇や出展という形で爪痕を残してきました。

今年もIoTプロトタイピングで出展してきました!

今年で10回目の記念すべき回でまた自分の作品を出展することができるのはとてもありがたい話です。

前回はスマートコーヒーメーカーを展示しまして、そのときに使ってたセンサーの使い方をブログで紹介しました。

supernove.hatenadiary.jp

で、今年は30歳になったこともあり、せっかくなので僕が前から作りたかったものを作ろうと思い2年前にモニター提供していただいたultraArmを使ってマルバツゲームの対戦相手を展示しました。

完成品はこんな感じです。

youtu.be

正直当日まで間に合うか心配でしたが、無事前日にギリギリ完成し当日も無事安定稼働で終えることができました!(余談ですが、2年前に登壇したときにもデモとしてロボットアームを持ち込んだのに電源忘れて持ってきたデモが動かせなかったという失敗をしたので今回はそのリベンジを達成できたまでありますw)

で、このプロトタイピングを作るにあたってミソになったところの一つがハンドエフェクターとして吸引ポンプを採用したことです。

もともとmyCobotに対応している機種で最初はmyCobotを使って作品を想定してしましたが、関節に使われているロボットがすぐオーバーヒートを起こして思い通りの姿勢に動かなくなってしまったので急遽ultraArmを使う作戦にピボットすることになりましたw(去年もそんな調子でしたね…)

ピボットしたとて、調べてみると吸引ポンプをultraArmで使う事例がそんなに出て来ず苦戦しました。(そもそも日本語のultraArmの記事このブログしか出てこないw)

というわけで前置きが長くなりましたが、今回は久々のultraArmネタとしてこの吸引ポンプをultraArmと連携する方法を紹介しようと思います。

用意するもの

ハードウェアのセットアップ

ポンプの先端はultraArmに付属のLEGOコネクタに対応したフランジで取り付けておきます。(実際にはUltraArm用ピッキングキット付属の吸引ポンプを使うのがよさそうです)

ポンプは付属のGroveケーブルを使いultraArmの本体裏側の「Switch」端子に接続します。(ピンボケで分かりづらいですが、同じGrove端子になっているのでお分かりいただけるかと思います)

ハードウェアのセットアップはこれで以上です。

PCの環境構築

今回はサクッと動かせるようにPythonを使います。

Pythonのライブラリである pymycobot にultraArmを操作するためのモジュールがあり、それをつかえば ultraArm内臓のインターフェースも操作することができます。

まずはターミナルで以下のコマンドでインストールします。

pip install pymycobot

インストールしたら以下のコードを pump_sample.py で保存します。PORT はお使いのPCの接続先のポート合わせて書き換える必要があります。

from pymycobot import ultraArm

# Connecting the mechanical arm
PORT = '/dev/ttyUSB0'
ua = ultraArm(PORT, 115200)
ua.sleep(0.5)

# Open suction pump
ua.set_gpio_state(0)
ua.sleep(2)

# Close the suction pump
ua.set_gpio_state(1)
ua.sleep(2)

ultraArmで吸引ポンプを動かすサンプルが見当たらないと書きましたが、以下の公式ドキュメントに接続方法から使い方までシンプルにまとめられてましたw

docs.elephantrobotics.com

これで python pump_sample.py を実行するとポンプの吸引、停止の一連の動作を行うことができるので吸引しやすいカードや平たい小物を使って動作確認を行うことが出来ます。

以下の動画はmyCobotだし、適当なモーションを入れてますがポンプの挙動はイメージいただけるかと思います。

まとめ

今回はSORACOM DiscoveryのIoTプロトタイピングで使われていた吸引ポンプをultraArmで動かす方法を紹介しました。

ロボットアームを持っていても何かしらハンドエフェクターが無いと面白みが無いよなーとふわっと思ってましたが、Discoveryを期に試すことが出来たのが良かったです。

何より昔あこがれてた某T社系列の会社が開発してた将棋ロボットのようなロボットアプリケーションとAlpha GoのようなゲームAIをあわせた作品を自分の手で実現出来たのが個人的に大きな成果です。

これでまた、一度やってみたかったことをまた一つ達成できました!

Amazon Q CLIでPyGameを使ったゲームを作ってみた

最近Amazon Qを使ってゲームを作成したらTシャツをゲットするキャンペーンをやっているようです。

aws.amazon.com

結構TLでもAmazon Qを使っている人たちがたくさんいますが、僕は他のAIエージェントを使っていたこともあってAmazon Qをわざわざ触るメリットが無いかなーと思って触っていなかったです。

ですが、TシャツにつられてAmazon Q CLIだけでゲームを作成してみることにしましたw

今回作ったゲーム

今回はAWSアーキテクチャアイコンを使ったツムツム風のゲームを作ってみました。

その昔新卒時代に働いてた会社ではツムツムのようなパズルRPGの開発に携わっていました。(今はそのゲームはサービス終了しています…)

なので、その頃のことを思い出して懐かしくなったのとこれまでPythonを結構触っていましたが、一度もPyGameで自分でゲームを作成したことなかったのでAIのちからを使って自分だけのゲームを作ってみようと思ったのがモチベーションだったりしています。

事前準備

まずはAWSアーキテクチャアイコンを用意します。

本当はそこからAIで用意すべきな気もしますが、さすがに膨大なAWSのアイコンを取得させるのは負荷がおもすぎる気がするのであくまでアセットとして用意することにしました。

AWSのアイコンは以下のリンクにアクセスします。

aws.amazon.com

リンク先ページ「開始方法」のプルダウンからアイコンパッケージをダウンロードします。

ダウンロードしたzipファイルを展開したらサービスアイコンのディレクトリ名を arch_icons に変更してプロジェクトディレクトリに配置します。

そして、今回の主役であるAmazon Q CLIをパソコンにインストールします。

以下のリンクからお使いのOSに合わせてCLIをインストールします(正直今回大事なところはここw)

docs.aws.amazon.com

あとは今回はPyGameを使うので以下のコマンドでインストールします。

pip install pygame

これで準備は完了です。

実装

ここからは実装です。

試しに「AWSアーキテクチャアイコンを使ってディズニーツムツムのようなゲームをつくってください。制限時間内に3個以上のアイコンが揃ったら削除し、制限時間内で削除したアイコンのサービス名を一覧で表示するようにしてください」とプロンプトを叩いた結果が以下のポストの動画です。

雑に投げたのにそれっぽい完成度になりました!ただ、見ての通り縦に2つ消したときに一つしか隙間が埋まってなかったり、ちょこちょこバグがありました。

ただ、それも気になったバグを逐一報告していくと都度修正してくれるし、もちろんUIの修正も細かくやってくれました。

そして、動画をご覧いただくとお分かりいただけるかと思いますが、隣合わせのブロックをいちいちクリックしないといけないのはツムツムらしさがまるでないのでツムツムのように連鎖的に消去する改修にも対応してくれて、本当にAmazon Qだけで全部実装を簡潔することができてしまってびっくりしました!

こんな感じでどんな修正をしたかを毎回リッチなUIこんな感じで丁寧に説明してくれるのも良かったです

### 1. 連鎖選択機能の追加

chain_select という新しいメソッドを追加し、選択したアイコンと同じ種類のアイコンを周囲から自動的に選択するようにしました
:

python

   def chain_select(self, row, col, service_name):
        # 上下左右斜めの8方向をチェック
        directions = [
            (-1, -1), (-1, 0), (-1, 1),
            (0, -1),           (0, 1),
            (1, -1),  (1, 0),  (1, 1)
        ]
        
        for dr, dc in directions:
            new_row, new_col = row + dr, col + dc
            
            # グリッド内かチェック
            if 0 <= new_row < self.grid_size and 0 <= new_col < self.grid_size:
                neighbor = self.grid[new_row][new_col]
                
                # 隣接アイコンが同じタイプで、まだ選択されておらず、マッチしていない場合
                if (neighbor and 
                    neighbor.service_name == service_name and 
                    not neighbor.selected and 
                    not neighbor.matched):
                    
                    # 選択状態にする
                    neighbor.selected = True
                    self.selected_icons.append(neighbor)
                    
                    # 再帰的に連鎖選択を続ける
                    self.chain_select(new_row, new_col, service_name)
    

### 2. select_icon メソッドの改良

select_icon メソッドを改良して、アイコンを選択した後に連鎖選択を行うようにしました:

python

def select_icon(self, row, col):
    icon = self.grid[row][col]
    # If icon is already selected, deselect it
    if icon.selected:
        icon.selected = False
        if icon in self.selected_icons:
            self.selected_icons.remove(icon)
        return
    
    # Check if the icon is the same type as already selected icons
    if self.selected_icons and icon.service_name != self.selected_icons[0].service_name:
        return
    
    # 連鎖選択のために、すでに選択されているアイコンに隣接しているか確認
    can_select = not self.selected_icons  # 最初の選択は常に可能
    
    if self.selected_icons:
        # 隣接チェック
        for selected_icon in self.selected_icons:
            # 選択済みアイコンの位置を探す
            for r in range(self.grid_size):
                for c in range(self.grid_size):
                    if self.grid[r][c] == selected_icon:
                        # 隣接しているか確認(上下左右斜め)
                        if abs(row - r) <= 1 and abs(col - c) <= 1:
                            can_select = True
                            break
                if can_select:
                    break
    
    if can_select:
        icon.selected = True
        self.selected_icons.append(icon)
    
        # 連鎖的に周囲の同じタイプのアイコンを自動選択
        self.chain_select(row, col, icon.service_name)
    
        # If we have enough selected icons, match them
        if len(self.selected_icons) >= self.match_min:
            self.match_selected_icons()


### 主な変更点

1. 連鎖選択の実装: 選択したアイコンの周囲8方向(上下左右斜め)に同じ種類のアイコンがあれば、自動的に選択します。
2. 再帰的な連鎖: 連鎖選択は再帰的に行われるため、連続して同じ種類のアイコンが並んでいる場合、すべて選択されます。
3. 選択条件の調整: 最初のアイコン選択は常に可能で、2つ目以降は隣接しているかつ同じ種類のアイコンのみ選択可能です。

### ゲームプレイへの影響

この変更により、ゲームプレイが以下のように変わります:

1. より簡単な操作: プレイヤーは1つのアイコンをクリックするだけで、周囲の同じ種類のアイコンがすべて選択されます。
2. より大きなコンボ: 同じ種類のアイコンが多く隣接している場合、一度に多くのアイコンを消すことができます。
3. 戦略性の向上: プレイヤーは同じ種類のアイコンを隣接させるように移動させる戦略を考えるようになります。

完成品

何度もCLIと会話をしたことでだいぶ完成度高めのゲームが出来上がりました!

それなりに難易度高かったので簡単な難易度調整も入れました(最小で消せるブロックの数や画面上のブロックの数)

まとめ

今回はAmazon Qを使ってPyGameを使ったゲームを作成してみました。

学生時代にPyGameでゲームのロジックを組むのが難しくて断念していたのですが、Amazon Qを通じて思ってたよりも細かいUI操作ができることに気づけてそれは挫折しても仕方ないと思えてしまいましたw。

何よりAmazon QはAWSのサービスに特化したツールだと思っていましたが、それとは関係ないコーディングにも対応しているうえに1円も課金せずにここまで作ってしまうのは恐ろしいと思いましたw。(そのうち制限が掛かりそう)

後日談

あれから今回作成したゲームでキャンペーンに応募したところ、無事にTシャツをゲットしました!

応募してから大体1週間ぐらいで審査が通り、更にもう1週間ぐらいで配送手配してもらい思ってたよりもスピーディーに対応してもらえた印象です。(配送業者の提示してた配送予定日より1日遅れたけど、AWSさんは関係ないw)

投げ売りされていたHHKB Lite 2 for Macを買ってみた

ここ最近東京に行く機会はちょこちょこありましたが、秋葉原は長らく行ってなかったので久々にまとまった時間を作って散策しました。

今まで秋葉原は何度か散策したのですが、雨の日の散策が初めてでいつも以上にくたびれましたw(ただ、体動かしてなかっただけとも言える)

そんな中、店先でHHKBの投げ売りがされているのを見かけました。

こちらは電気街の端っこにあるアールガーデンという店舗で販売されていました。

www.r-cube.co.jp

価格は動作未確認のジャンク扱いで5000円でした。いくらHHKBは高級とはいえ、強気すぎるとは思ったのですが以前はその半額で販売してたようでそれはそれで安いですよねw。

定価を調べて見たら6000円ぐらいするので、ジャンクで出すならその半額ぐらいで十分にも思えます。

www.pfu.ricoh.com

で、こちらはHHKB Lite 2 for Mac日本語配列で刻印ありの白という普段の僕なら絶対買わないやつですw。

ただ、この機種はもう販売・サポート終了しているのでコレクション的な意味合いと展示会でキーボードを操作するときに日本語配列でコンパクトなやつがほしかったので、条件にピッタリあうこのキーボードを買いました。

というわけで、今回は小ネタですが久々に開封(梱包されてないけど)の儀をしていきます。

外観

店先に置かれたHHKBのポストがやたらバズったことで引用リポストでは好き放題 いろんな書き込みがありましたが、その中で一番多かったコメントが「ジャンクにしてはいい値段するけど状態どんなもん何だ」といった内容です。

結論から使用感はほぼなく、新品に近い状態でした。画像の通り刻印が削られているところも無いですしキーボードの隙間にも目立つ汚れが無くてとてもきれいです。

こちらはMac向けのキーボードなので、Cmdキーや日本語配列にあるかな・英数の入力切り替えキーもありますし、EnterもReturnキーとして表記されています。(久々にMac日本語配列見た)

さらにHHKB初めて触る人でも分かるようにFnキーを押したときの入力もキートップの側面に記載されている親切設計です。

前面にはこんな感じでUSBポートが2つついているので、キーボードをパソコンにつなげるだけでUSBハブとしても使うことができて地味に便利です。ポート数が限られているRaspberry Piに接続するときに活躍しそうな気がしますが、どこかでHHKBを接続すると電力をかなり食うのでRaspberry Piで使うのには電力不足という話を聞いた気もします。

ドライバのセットアップ

早速キーボードをPCに接続していきます。

Macでよくある初回接続時のキーボードアシスタントを済ませて実際に文字入力を試しますが、文字は入力されますが一部のキーは刻印されているものと別の入力がされたり、Cmdに関しては全く機能しないのでまともに使おうと思うと難しいです。

これがジャンクの理由かな…と思ってましたが、どうやら元からそういう挙動みたいです。というのもこのモデルをMacで使うには公式サイトから対応のドライバーをセットアップすることで使えるようになるそうです。

ということで以下のURLでドライバのダウンロードページを開きます。執筆時点(2025年5月)ではサポート終了から1年経っているのでダウンロードページをそのまま残しているのは親切ですね!

happyhackingkb.com

ダウンロードページからお使いのOSバージョンに合わせたドライバーのインストーラーのdmgファイルをダウンロードします。dmgを開いたらドライバーをインストールします。

インストールしたドライバーを起動したら以下の画面が表示されるので内容を確認して利用規約に同意します。

起動すると以下の機能拡張を有効にするかのダイアログが表示されます。ここでOKを押しても設定が有効にならないのでいつまで経ってもドライバーが適用されません。

そこでOKボタンの下にある「システム設定を開く」画面をクリックし、Macの設定画面を開きます。

すると、設定画面が開き、システムソフトウェアの読み込みをブロックされていることが表示されている箇所がありますが、ここで許可ボタンをおします。

許可ボタンが押されるとこれでドライバーが適用されます。以下のエラーダイアログが表示されますが、これは特に気にせずOKを押してもらって大丈夫です。

ドライバは即時に適用されるので、正常にMacでキーボード操作ができる状態になります。

改めて一通りキー入力を試したら全て問題なく反応して一安心です。

まとめ

今回は秋葉原散策で見つけたHHKBを買ってみたので型落ちだけど超簡単な商品レビューをしました。

見つけたときのポストはなぜかやたらバズってしまったので、これはブログに書き起こして擦らなくてはいけないという謎の使命感からこのエントリーをしたためましたw。

あくまで推測ですが、ほぼ新品の状態だけどメーカーのサポートがすでに終了しているから店舗としてなにか問い合わせ合ってもサポートできないからほぼ未使用状態でもジャンクとして販売せざるを得ないのではないかなと思います。

MCPで試すRoombaハック

気づいたら今日は僕の30歳の誕生日です!

このブログも20代、特に社会人になってから本腰入れて更新を始めるようになって本当に多くの方に読んでいただき、少なからずこのブログを続けたおかげで自分のことを知ってもらったり、影響を与えることもできてほそぼそと続けてきて良かったと思います。

皆さんいつもありがとうございます!30代になっても引き続きこのブログを投稿し続けていきたいと思います。

さて、前置きはこのぐらいにして、前回はとあるハドフで見つけたシリアルケーブルを使ってルンバをハックしてみました。

supernove.hatenadiary.jp

記事の内容としてはこれと言って目新しさはなかったのですが、思いの外バズってしまいこれまで書いたブログの最大スター数を上回って、総スター数の殆どをこのブログが占めることになってしまいましたw

このブログを公開してからMCPが流行るようになり、その中で僕が得意なFastAPIを使ってMCPサーバーを公開するネタをZennで公開しました。

zenn.dev

流行りの技術についてネタにしたこともあって、これまたバズってしまいましたw

そんな中、ふとこんなポストがありました

ちょうど次のルンバハックはゲームパッドとかでハックする方法を試そうかとしていたところでこれはいいネタだと思いましたし、ルンバ、MCPとそれぞれのネタを書いてバズった身としてはこの2つをかけ合わせてネタにすることで更にバズるのではと思ってしまったわけです(超絶安直w)

というわけで今回はMCPRoombaをハックする方法を試していきます。

先に今回のネタのソースコードは以下のレポジトリで公開しています。

  • MCPサーバー

github.com

  • Roombaのコントローラー

github.com

前提条件

前回の記事で紹介したルンバハックで必要になるハードウェアの設定を事前に済ませていることが前提条件になります(ソフトウェアについては前回とはまたちょっと違った方法で構築します)

supernove.hatenadiary.jp

セットアップ

Raspberry Pi(ルンバの操作)

Rasbeprry Piでルンバを操作できるように環境構築をしていきます。

mosquittoのセットアップ

まずはMCPサーバーからMQTTで操作するためのブローカーとしてRaspberry Piでmosquittoを使います。

以下のコマンドでmosquittoをインストールします。

sudo apt-get install mosquitto mosquitto-clients

今回はMCPサーバーを動かしている別のPCからRaspberry Piにアクセスする必要があるため、mosquittoを外部アクセスできるように設定を行います。

/etc/mosquitto/mosquitto.conf を開き以下の2行を追加します。

listener 1883
allow_anonymous true

追加したら以下のコマンドでmosquittoを再起動します。

sudo systemctl restart mosquitto

ソースコードのクローン

今回の環境構築で使用するuvをRaspberry Piでセットアップしていきます。

前回も述べたように今はRaspberry Pi OSのシステムに直接pipでライブラリをインストールするのは非推奨になっていますが、uvを使うことでNode.jsのようにプロジェクト単位でパッケージ化することができてしかも仮想環境も簡単に用意できるので、uvを採用していきます。

uvは以下のコマンドでインストールします。

curl -LsSf https://astral.sh/uv/install.sh | sh
source ~/.bashrc

インストールしたらルンバを操作するためのコントローラをクローンしてきます。

git clone https://github.com/Miura55/roomba-mqtt-controller

クローンしたら以下のコマンドで依存パッケージをインストールします。

cd roomba-mqtt-controller
uv sync

Subscriberは以下のコマンドを実行して起動します。

uv run main.py

ソースコードはレポジトリの中にあるものがすべてですが、今回のポイントはMQTTのSubscriberのコードとRoombaの操作を行うコードを別で分けています。

理由はSubscriberはMQTTの通信を受け付けるだけにして、ハードウェアのアクセスやビジネスロジックを分離して可読性を上げたかったのと移動以外にHomeコマンドを実行したときにSubscriberの中で実行すると何故か動かなかったので、Callback関数の処理を以下の通りシェルでルンバの操作を行うだけのシンプルなものにしました。

こうすることで、移動もHomeコマンドで自動でホームベースに戻す挙動も実現できるようになりました。(地味に一番詰まったポイント)

def on_message(client, userdata, msg):
    print(f"Received `{msg.payload.decode()}` from `{msg.topic}` topic")
    
    # コマンドラインでスクリプトを実行(Homeコマンドがきかないので)
    subprocess.run(f"uv run roomba_controller.py \'{msg.payload.decode()}\'", shell=True)

本当はメッセージに対してバリデーションを入れたほうがいいですが、そこはMCPサーバー側でスキーマ定義を行っているのでここはシンプルに来たものを受け入れるだけにしています。

MCPサーバー側

uvをセットアップ

MCPサーバー側も同様にuvをセットアップしていきます。

WindowsでもMacでも動作するので、お使いのOSに合わせてセットアップを行います。

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" 
curl -LsSf https://astral.sh/uv/install.sh | sh

ソースコードをクローン

以下のコマンドでMCPサーバーのソースコードをクローンしていきます。

git clone https://github.com/Miura55/roomba_mcp_server.git

クローンしたら以下のコマンドで依存パッケージをインストールします。

cd roomba_mcp_server
uv sync

セットアップが終わったらMCPサーバーを立ち上げる必要がありますが、その前にRaspberry Pi側のMQTTブローカーに接続するためにあらかじめRaspberry Piのポートを確認、MCPサーバーの環境変数で設定しておく必要があります。

Raspberry Pi側では以下のコマンドでポート番号を確認しておきます。

ifconfig

設定したポートをMCPサーバーを起動するターミナルに対して以下の設定を行います。

export MQTT_ENDPOINT=【Raspberry Piのポート番号】

以下のコマンドでプログラムを実行します。

uv run main.py

実装のポイントとしては、移動のリクエストのときは以下のようにデフォルト値を定義しておくことです。

こうしておくことで、雑なプロンプトを投げて漏れている情報はデフォルトの値でリクエストを受けてくれるのでエラーが起きないようになるはずです。

それ以外はソースコードを見ていただければわかるはずです。

class MoveCommand(BaseModel):
    """Roombaの移動コマンドを表すモデル
    速度と回転速度を指定して移動するためのコマンドを定義します。
    速度はm/s、回転速度はrad/sで指定します。
    durationは移動時間を秒単位で指定します。

    Args:
        BaseModel (_type_): PydanticのBaseModelを継承したクラス
    """
    velocity: Optional[float] = 0.2  # 速度 (m/s)
    yaw_rate: Optional[float] = 0  # 回転速度 (rad/s)
    duration: Optional[int] = 0  # 移動時間 (秒) 

これで必要なプログラムが立ち上がりました。

動かしてみる

mcp-proxy をインストール

これ準備が整ったので、エージェントと接続してみます。

エージェントで接続するために以下のコマンドでmcp-proxyをインストールします。

uv tool install mcp-proxy

エージェントの設定

あとはMCPの接続設定をお使いのエージェントに設定します。

MCPに対応したエージェントに対して以下の設定を追加します。追加したらエージェントを再起動させます。

...
        "roomba-controller":{
            "command":"mcp-proxy",
            "args":[
                "http://localhost:8000/mcp"
            ]
        }
...

動作確認

手順が多かったですが、これでRoombaMCPで動かせるようになったはずです。

以下の動画のようにエージェントに「roombaを3秒前進させて」や「roombaを元に戻して」と話しかけるとMCPサーバーからMQTTでRaspberry Piに対して操作コマンドが送られれば今回のMCPは正常に動作しています。

youtu.be

まとめ

今回はMCPを使ってルンバを操作してみる実験をしました。

MCPでそれなりにスキーマを定義すれば雑にエージェントでプロンプト投げてもエラーなく動いてくれることがわかり、MCPの実装ポイントがまた一つ分かったような気がします。

やっぱりAIが理解してロボットがそれに合わせて動いている様子は学生時代を思い出させるものがあり、個人的には30代の最初にこれを試せてよかったと思いました。

これからもこういう遊び心を大切にしていきたいところ!