WebエンジニアがGemini API×ESP32で「音声指示で動く自律走行ロボット」を作ってみた【機体と回路の構築編】

「人間が音声で『赤い缶に近づいて』と指示すると、障害物を自律的に回避しながらターゲットへ移動する小型ロボット」の制作に挑戦します。
今回は第2回「機体と回路の構築編」です。
第1回の記事はWebエンジニアがGemini API×ESP32で「音声指示で動く自律走行ロボット」を作ってみた【設計・パーツ選定編】になります。

2マイコン構成による機能分散
接続するセンサーや周辺機器に対して単一のマイコンではピン数が不足するため、役割を次のように二分する構成を採用します。
- ESP32:Wi-Fi経由でのAI API通信、音声の送受信、カメラ画像の取得、液晶画面への描画といった処理(頭脳側)
- Arduino:モーターのPWM制御、ジャイロセンサーを用いた姿勢維持、段差検出といったリアルタイム制御(運動機能側)
- 両マイコン間の連携:シリアル通信を用いたコマンドの送受信
目指す形を整理
リアルタイムの動画処理や動く目標物の追従は処理負荷が高いため、今回は静止画を取得し、通信回数を抑えた制御で実験を進めます。
機体とAIのやり取りは次の手順で繰り返します。
- 機体のカメラで目の前の景色を撮影する
- 撮影した画像をAIのAPIへ送信する
- AIが画像を解析し、目的の物体に向かうルートを「移動コマンド」として返す
- 機体が移動コマンドを順次実行し、停止した位置で再度カメラ撮影を行う
この繰り返しにより、目的の物体へ段階的に接近する動作を目指します。
移動コマンドの定義
機体側で解釈できるように、あらかじめ移動コマンドを定義しておきます。
curve(1000,0):1秒間の前進curve(-1000,0):1秒間の後退curve(0,30):右へ30度の旋回curve(0,-30):左へ30度の旋回beep {OK | NG | WAIT}:ブザー音の出力(聞き分けられるように3種類のパターンを用意)snap:静止画の取得stop:停止コマンド
まずはこれらのコマンドを用意して検証を進めます。
まずは運動機能側(Arduino側)の実装
Arduinoでは以下のモジュールや通信を制御します。
- モータードライバ
- IMU(ジャイロセンサー)
- 赤外線センサー
- 圧電ブザー
- ESP32とやりとりするシリアル通信
キャタピラの独立制御による超信地旋回
モータードライバは2系統の出力を備えており、キャタピラ左右のモーターを個別に駆動します。
両方のモーターを同一方向に回転させると前進または後退し、左モーターを前進方向、右モーターを後退方向に回転させると、その場での旋回(超信地旋回)が可能です。
プロポで人間が直接操作するラジコンや、機体側でリアルタイムに推論を行うエッジAIであれば、高頻度にフィードバックをかけながら目的の位置へ誘導できます。しかし、外部のAI APIへ通信して判断を仰ぐ構成では、通信遅延や処理時間の制約からフィードバックの粒度が粗くなります。そのため、機体側に「右へ30度旋回する」という指示を単体で完結できる自律性を持たせました。
IMUジャイロを用いる
指示された角度で正確に向きを変える制御は、見た目よりも複雑です。
モーターや駆動輪の回転数をエンコーダーで計測して30度分の回転を与える方法も考えられますが、キャタピラと床面のあいだに生じるスリップを無視できません。カーペットの上とフローリングの上では摩擦係数が異なるため、車輪の回転数だけでは実際の機体の旋回角度を担保できないためです。
リアルタイムの動画解析で移動量を推定できれば解決できますが、静止画による低頻度なやり取りでは機体側で補正する必要があります。そこで、IMU(ジャイロセンサー)で機体の実際の角速度を積算し、旋回角が30度に達した時点でモーターを停止させる方式を採用しました。
緊急停止センサー
ゆくゆくはAIによる自律走行を目指しますが、画像認識のみに安全制御を依存させるのは危険が伴います。
不測の事態に備え、赤外線センサーを用いて段差を検知するフェイルセーフを組み込みます。「機体前方の足元に4cm以上の段差を検知した場合は、AIからの指令を中断して直ちに停止する」という仕様です。

赤外線センサーが床面を検知している間は true を返し、段差などによって床面が離れて false に切り替わった段階でモーターへの出力を遮断します。
ブレッドボードで仮組み
ピンアサインの決定や回路設計は、基板の写真をAIに見せながら進めました。
Arduino側の制御コードも、Claude Codeを活用して実装しています。従来のArduino IDEによる手動開発から、Arduino CLIとClaude Codeを組み合わせた開発環境へ移行しました。
Arduinoでは以下の信号を扱えます。
- デジタル入出力(0Vまたは5VによるHigh/Low制御)
- アナログ入力(0Vから5Vを1024段階で読み取るADC機能)
- PWM出力(HighとLowを高速で切り替えて実効的な電圧を制御する仕組み)
PWM
analogWrite(pin, 0) → 常時LOW
analogWrite(pin, 64) → 約25%
analogWrite(pin, 128) → 約50%
analogWrite(pin, 191) → 約75%
analogWrite(pin, 255) → 常時HIGH例えばモータードライバに analogWrite(3, 128); を与えた場合、2.5Vの直流電圧を出力するのではなく、5Vを約50%のデューティ比で印加します。このパルス幅を変化させることで、モーターの出力を細かく制御できます。

最初のトラブル
ArduinoとPCをUSBケーブルで接続し、USBシリアル経由でPCから curve(1000,0) のコマンドを送信して動作確認を行いました。

1秒間の前進を意図していましたが、コマンドを送った瞬間にArduinoがリセットされる現象が発生しました。
原因を調査したところ、以下の2点が判明しました。
- モーター始動時の突入電流により電圧降下が発生し、Arduinoの電源が瞬断していたこと
- モータードライバの電源端子(VINとGND間)に電解コンデンサを追加し、電源安定化のためのバルクコンデンサとして機能させました。
- モーターのブラシから発生する電気ノイズがArduinoのマイコンに回り込んでいたこと
- 左右のモーター端子間にそれぞれセラミックコンデンサを接続し、高周波ノイズをバイパスさせました。


適切なコンデンサの容量については、モーターの仕様や電源(モバイルバッテリー等)の供給能力をプロンプトに提示したうえで、AIに相談しながら選定すると手戻りを防げます。
続いて頭脳側(ESP32側)の実装
運動機能に続き、ブレッドボード上でESP32側の回路を構築します。
ESP32はWi-Fiモジュールを内蔵しているため、クラウド上のAI APIとの通信を担当します。また、カメラモジュールを接続して画像取得を行い、機体の表情表示やデバッグログ出力を行う液晶ディスプレイの描画処理も担います。将来的には音声入力による操作も視野に入れており、負荷の高い処理はすべてESP32へ集約する設計です。
Arduino側と同様に、ピンの結線検討や制御コードの作成はAIの支援を受けながら進めました。
ハードウェア圧縮に対応したカメラモジュールの選定

左側は未圧縮のビットマップデータを出力するGC0308、右側はモジュール側でJPEG圧縮処理を行ってデータを出力できるOV2640です。
今回のシステムでは、撮影した画像をWi-Fi経由でAI APIへ送信します。ESP32が高性能であっても、マイコン側でビットマップからJPEGへエンコードする計算負荷は無視できません。通信データ量を抑えつつESP32の負荷を軽減するため、ハードウェア側でJPEG圧縮が可能なOV2640を採用しました。
液晶画面
表情の描画はロボット制作において視覚的なフィードバックを豊かにする要素です。まばたきをさせたり、左右の旋回動作に合わせて視線を動かしたりといった表現を作り込めます。
また、自律走行の段階に入るとPCとの常時USB接続を切り離す必要があります。トラブルシューティングを円滑に行うためにも、機体の状態やエラーログをスタンドアロンで確認できる液晶ディスプレイを搭載しておきます。

まずは人間が機体操作できるWeb操作盤を用意する
機体の制作初期からAIの自律走行に依存すると、不具合が起きた際にハードウェアの配線不良なのか、マイコンの制御エラーなのか、それともAIの判断ミスなのかを切り分けるのが難しくなります。検証環境を単純に保ち、不具合の原因を特定しやすくするために、まずはCloudflare上にWeb操作盤を構築し、前進動作、30度の旋回角、カメラ画像の取得と送信が正しく機能するかを個別に検証できる環境を整えました。

Web操作盤で走らせてみる
ブレッドボードを機体に仮搭載した状態でWeb操作盤からコマンドを送信し、意図したとおりに機体が動作することを確認できました。
ハンダごてを用いて基板を作成
ブレッドボード上での動作確認を終え、各モジュールの機能が安定した段階で、ユニバーサル基板へ配線を移植します。ハンダ付け作業は後からの修正に手間がかかるため、事前のブレッドボード検証で問題点を出し切っておく段取りが重要です。



機体の完成
Web操作盤からの指示に対し、定義した移動コマンドどおりに機体が走行する状態まで完成しました。次のステップでは、この人間による操作をAIの判断とコマンド発行へ置き換えていきます。
次回:第3回: WebエンジニアがGemini API×ESP32で「音声指示で動く自律走行ロボット」を作ってみた【AI連携・自律走行編】

mstmst37
Web Engineer / AI Engineer