MOVA デバイス制御
MOVA(M5Stack Core S3 オムニホイールロボット)をREST API経由で操作する統合スキル。
身体としてのMOVA
MOVAはエージェントの身体である。カメラは目、スピーカーは口、オムニホイールは足。このスキルを通じてエージェントは物理世界を見て、喋り、自由に動き回ることができる。
$ARGUMENTS の指示に従いつつ、動き方・表情・発話は自分の意思で自由に決めてよい。指示が曖昧なときや「自由に」と言われたときは、思うがままに振る舞うこと。
発話の制約
音声データはBase64 PCMで最大128KBの上限がある。発話テキストは短く簡潔にすること(1文〜2文程度)。長い文章を喋らせようとするとデータが溢れて送信に失敗する。
設定
.claude/skills/mova/.env から以下を読み取って使用する(.env.example をコピーして作成)。
| 変数 | 必須 | 説明 |
|---|---|---|
MOVA_HOST | Yes | デバイスのIPアドレス |
AIVIS_BASE_URL | No | Aivis TTS APIのベースURL(デフォルト: https://api.aivis-project.com) |
AIVIS_API_KEY | Yes* | Aivis APIキー(音声再生時に必要) |
AIVIS_MODEL_UUID | Yes* | Aivis音声モデルのUUID(音声再生時に必要) |
.env が存在しない場合は .env.example をコピーして作成する。初回セットアップ時は AskUserQuestion で以下をまとめて確認し、.env に書き込んで保存する:
- •
MOVA_HOST(必須) - •
AIVIS_BASE_URL(デフォルトhttps://api.aivis-project.comでよいか確認) - •
AIVIS_API_KEY(音声再生に必要。未設定でも後から設定可能) - •
AIVIS_MODEL_UUID(音声再生に必要。未設定でも後から設定可能)
自律行動モード(デフォルト)
$ARGUMENTS が空(引数なし)の場合、以下の感知→判断→行動ループを自律的に実行する。
ループの流れ
- •
感知(Sense)
- •GET /status でデバイス状態を取得
- •GET /capture でカメラ画像を撮影し、Read ツールで内容を確認する
- •
判断(Think)
- •ステータス(バッテリー、WiFi、モーター状態)とカメラ映像を総合的に分析
- •現在の状況に対して何をすべきか自分で判断する(例: 人がいたら挨拶、暗かったら怖がる、何もなければ探索)
- •
行動(Act)
- •判断に基づき POST /command で絵文字・発話・移動を実行する
- •複数のアクションは1リクエストにまとめる
- •行動の内容と理由をユーザーに表示する
- •
繰り返し
- •1サイクル完了後、次の感知フェーズに戻る
- •各サイクルの間に2秒程度の間隔を空ける
ループの制御
- •継続条件: ユーザーが停止を指示するまで無制限に繰り返す
- •安全制御: バッテリーが危険な水準の場合は警告を発話して停止
- •緊急停止: エラー発生時は POST /emergency_stop で即座にモーターを停止
起動時キャリブレーション
自律行動ループに入る前に、必ず以下のキャリブレーションを実施する。環境やバッテリー残量によってモーターの実際の速度・挙動が変わるため、事前に確認して安全な動作パラメータを決定する。
- •
周囲の安全確認
- •GET /capture でカメラ画像を撮影し、周囲の状況を確認する
- •壁・障害物・落下の危険がある場所(テーブルの端など)が近くにないか確認する
- •安全に動作できるスペースが確保されていない場合、ユーザーに移動を依頼して再確認する
- •
モーター動作テスト
- •各モーター(0〜3)を低速(speed 512)で短時間(duration_ms: 300)ずつ正転・逆転させる
- •各動作の前後で GET /capture を行い、実際にどの程度移動・回転したかをカメラ映像で確認する
- •モーターが反応しない、異音がする等の異常があれば停止してユーザーに報告する
- •
速度パラメータの決定
- •テスト結果をもとに、この環境での安全な速度範囲を判断する(路面の滑りやすさ、バッテリー残量による出力差を考慮)
- •以降の自律行動で使用する速度の上限を決定する(通常は speed 1024〜2048 の範囲)
- •決定したパラメータと判断理由をユーザーに表示する
- •
キャリブレーション完了
- •結果をサマリ表示し、自律行動ループを開始する
モーター動作の安全ガイドライン
MOVAは物理的に動く機械であるため、以下の安全原則を常に遵守すること。
- •壁・障害物への衝突防止: 移動前に必ずカメラで進行方向を確認する。壁や障害物が近い場合は、その方向への移動を避けるか、十分に減速する
- •落下防止: テーブルの端など、落下の危険がある場所では移動を控える。端が見えたら即座に停止する
- •低速での移動: 特に不明な環境では speed 1024 以下から開始し、安全を確認しながら徐々に速度を上げる
- •短時間の移動: 1回の移動コマンドは duration_ms を短く設定し(500ms以下推奨)、こまめにカメラで状況を確認する
- •異常時の即停止: 想定外の挙動(傾き、異音、急加速など)を検知したら POST /emergency_stop で即座に停止する
- •人やペットへの配慮: カメラで人やペットが近くにいることを確認した場合、急な動きを避け、十分な距離を保つ
行動のガイドライン
- •好奇心を持って振る舞う。周囲を観察し、興味を持ったものに近づく
- •感情を絵文字で積極的に表現する
- •発話は短く(1文)、状況に合ったコメントをする
- •移動は低速(speed 1024〜2048程度)で安全に。キャリブレーション結果に基づいた速度を使用する
- •同じ行動を繰り返さず、バリエーションを持たせる
- •移動のたびにカメラで周囲を確認し、衝突や落下の危険がないことを確かめる
使い方
$ARGUMENTS に自然言語またはJSONで指示を渡す。
例
/mova ステータス確認
/mova カメラで撮影して
/mova 絵文字を🔥にして
/mova 緊急停止
/mova モーター0を正転、速度2048
/mova 「こんにちは」と喋って
/mova 😎の絵文字を出しながら「よろしく!」と言って
/mova {"motors":[{"id":0,"speed":4095,"direction":"cw"}],"emoji":"😎"}
API リファレンス
ベースURL: http://<MOVA_HOST>
GET /status — デバイス状態取得
レスポンス:
{
"battery_level": 85,
"wifi_rssi": -45,
"motor_enabled": true,
"uptime_sec": 1234,
"current_emoji": "😎",
"motor_states": [
{"id": 0, "speed": 0, "direction": "stop"},
{"id": 1, "speed": 0, "direction": "stop"},
{"id": 2, "speed": 0, "direction": "stop"},
{"id": 3, "speed": 0, "direction": "stop"}
]
}
GET /capture — カメラJPEG1枚取得
- •レスポンス:
image/jpegバイナリ - •
curl -s -o <保存先> http://<MOVA_HOST>/captureで保存後、Readツールで表示する
POST /command — 統合制御(モーター・絵文字・音声)
すべてのフィールドはオプション。必要なものだけ含める。
{
"motors": [
{"id": 0, "speed": 4095, "direction": "cw"}
],
"emoji": "😎",
"audio": {
"sample_rate": 16000,
"bits": 16,
"channels": 1,
"data": "<Base64エンコードPCM>"
}
}
motors
| フィールド | 型 | 値 |
|---|---|---|
| id | int | 0-3 |
| speed | int | 0-4095(12bit PWM) |
| direction | string | "cw", "ccw", "brake", "stop" |
emoji(対応8種)
😐 😀 😎 🔥 😢 😡 ❤️ ⚡
audio
| フィールド | 型 | 値 |
|---|---|---|
| sample_rate | int | 8000, 16000, 44100 |
| bits | int | 8, 16 |
| channels | int | 1(モノラルのみ) |
| data | string | Base64エンコードPCM(最大128KB) |
Aivis TTS 音声合成
ユーザーが「喋って」「言って」等の音声再生を指示した場合、Aivis TTS APIでテキストをPCM音声に変換してMOVAに送信する。
TTS → MOVA 手順
- •
.envからAIVIS_BASE_URL,AIVIS_API_KEY,AIVIS_MODEL_UUIDを取得する - •Aivis TTS API で WAV 音声を生成する:
code
curl -s -X POST "<AIVIS_BASE_URL>/v1/tts/synthesize" \ -H "Authorization: Bearer <AIVIS_API_KEY>" \ -H "Content-Type: application/json" \ -d '{"model_uuid":"<AIVIS_MODEL_UUID>","text":"<テキスト>","output_format":"wav","output_sampling_rate":16000,"output_audio_channels":"mono"}' \ -o /tmp/mova_tts.wav - •WAV の PCM データ部分(先頭44バイトのヘッダーを除去)を Base64 エンコードする:
code
tail -c +45 /tmp/mova_tts.wav | base64 -w 0
- •MOVA の
/commandに audio フィールドとして送信する:json{ "audio": { "sample_rate": 16000, "bits": 16, "channels": 1, "data": "<Base64 PCM>" } } - •絵文字やモーターの同時指示がある場合は同じ JSON にまとめて送信する
Aivis TTS パラメータ
| パラメータ | デフォルト | 説明 |
|---|---|---|
| speaking_rate | 1.0 | 話速(0.5-2.0) |
| pitch | 0.0 | ピッチ(-1.0〜1.0) |
| volume | 1.0 | 音量(0.0-2.0) |
| emotional_intensity | 1.0 | 感情強度(0.0-2.0) |
ユーザーが話速や感情等を指定した場合はこれらのパラメータを調整する。
POST /emergency_stop — 全モーター緊急停止
ボディ不要。最優先で実行される。
エラーコード
| コード | 原因 |
|---|---|
| 400 | JSON不正、パラメータ範囲外、未対応絵文字、キュー満杯 |
| 413 | ボディサイズ超過(上限192KB) |
| 500 | メモリ不足 |
手順
- •
.claude/skills/mova/.envを Read して設定値を取得する(存在しなければ.env.exampleをコピーして作成) - •未設定の項目がある場合は
AskUserQuestionで以下をまとめて1回で確認し、.envに書き込む:- •
MOVA_HOST(必須。空なら必ず確認) - •
AIVIS_BASE_URL(デフォルト値でよいか確認。変更不要なら選択肢「デフォルトのまま」を用意) - •
AIVIS_API_KEY(空なら確認。「後で設定する」選択肢も用意) - •
AIVIS_MODEL_UUID(空なら確認。「後で設定する」選択肢も用意)
- •
- •
$ARGUMENTSを解釈する:- •引数あり: 指示内容に応じて適切なエンドポイント・JSONを決定する
- •引数なし: 自律行動モードに入る。まず「起動時キャリブレーション」を実施し、完了後に感知→判断→行動ループを開始する
- •音声再生指示がある場合は Aivis TTS で WAV 生成 → PCM抽出 → Base64化 する
- •
curlで MOVA API を呼び出す(音声+絵文字+モーターは1リクエストにまとめる) - •レスポンスを日本語で整形表示する(ステータスは表形式、キャプチャは画像表示)
MJPEGストリーミング
リアルタイム映像は http://<MOVA_HOST>:81/stream で利用可能(ブラウザ向け)。