WiDC: Wi-Fi FPVロボット – ライブ映像付きリモートコントロール
部品と材料
| 部品 | 数量 |
|---|---|
| Arduino UNO | 1 |
| Espressif ESP8266 ESP-01 | 1 |
| Arduino Proto Shield | 1 |
| 車輪付きDCモーター | 1 |
| Hブリッジ | 1 |
アプリとオンラインサービス
- Autodesk Fusion 360(3D設計用)
- Arduino IDE(プログラミング用)
プロジェクト概要
周囲の探索、到達困難な場所へのアクセス、監視、写真撮影、動画撮影、ライブ配信などの目的で、リモートコントロール可能なロボットを作成してみませんか?このチュートリアルでは、それらすべてを実現する低コストなロボットの作り方を紹介します。
本チュートリアルは、以前公開した「Wi-Fi Controlled FPV Rover Robot with Arduino ESP8」の改良版です。2輪駆動のロボットをWi-Fiネットワーク経由で遠隔操作する方法を、2つのDCモーター、Arduino Uno、ESP8266 Wi-Fiモジュールを使って解説します。
ロボットは通常のWebブラウザから、HTMLで設計されたインターフェースで操作可能です。Androidスマートフォンをロボットに搭載し、カメラ映像と音声をコントロール画面へストリーミング配信できます。
前回のチュートリアルと同様に、3Dプリンターやレーザーカッター、CNCルーターといった特殊な工作機械を使わず、一般的な工具だけで低コストなロボットフレームを設計・製作する方法も紹介します。
なお、ここで使用している部品構成は必ずしも最適ではありません。例えば、ArduinoとESP8266の組み合わせ 대신 NodeMCUを使用したり、Raspberry Piとカメラでスマートフォンの役割を兼ねたり、果てはAndroidスマートフォン自体をロボットの「頭脳」として使うことも可能です。
このチュートリアルは、私が関わっている別プロジェクト「Joy Robot」(Hackster.io版)から派生したもので、シンプルさを優先して同じハードウェア構成を採用しています。Arduino Unoを選んだのは、入手しやすく初心者にも扱いやすいためです。オリジナルプロジェクトではサーボモーターやLEDマトリクスも制御し、ESP8266と連携しています。スマートフォンの代わりにタブレットを使用し、追加アプリも動作させています。関連チュートリアルは以下の通りです:
本ガイドは、形状やコントロールインターフェースを変更して、あなたのニーズに合わせて応用可能です。
別のチュートリアルでは、Blynkアプリを使ってスマートフォンからロボットを制御する方法も紹介しています:
Wi-Fi Controlled Robot using Wemos D1 ESP8266 and Blynk
ステップ1: 必要な工具
このプロトタイプの製作に使用した工具は以下の通りです:
- 手ノコギリ(アクリル板の切断用)
- ドライバー(ボルト・ナット締め付け用)
- 定規(寸法測定用)
- カッターナイフ(構造体の切断と穴あけ用)
- 電動ドリル(アクリル板へのネジ穴あけ用)
- サンドペーパー(切断面のバリ取り用)
- はんだごて(モーター端子のはんだ付け用)
- コンピューター(Arduinoコードのコンパイル・書き込み用)
これらの工具は、ロボットの機械構造の製作、組み立て、電子部品の配線に使用します。市販のロボットキットを購入する場合は、一部の工具が不要になります。また、メーカースペースの設備に応じて、3Dプリンターやレーザーカッターなどを活用することも可能です。
ステップ2: 機械構造と材料
機械構造に使用した材料は以下の通りです:
- 2mm厚アクリル板
- ギアモーター付き車輪(×2)
- 30mmキャスターホイール(×1)
- M2 × 10mmネジ(×5)
- M2 × 1.5mmナット(×5)
- M3 × 10mmネジ(×9)
- M3 × 40mmネジ(×4)
- M3 × 1.5mmナット(×12)
- 5/32インチ × 1インチネジ(×4)
- 5/32インチナット(×12)
- 汎用スマートフォンホルダー
- スチール製L字アングルブラケット(30×30mm)(×4)
ステップ3: 電子部品
このロボットで使用する電子部品は以下の通りです:
- Arduino Uno
- ESP8266
- プロトシールド(コンパクト化用)または通常のブレッドボード
- 1kΩ抵抗(×2)
- 10kΩ抵抗(×1)
- ギアモーター付き車輪(×2)
- Hブリッジモジュール
- ジャンパーワイヤー多数
- 5V USBモバイルバッテリー
すべての部品は主要なECサイトで容易に入手できます。冒頭で述べた通り、部品選定は別プロジェクトからの流用であり、別のマイコンボードを使用するなどで最適化可能です。
ステップ4: 構造設計
まずロボットの機械構造を設計します。市販のロボットキットを購入する場合は、ステップ6へ進んでください。本チュートリアルでは、モーターやその他部品の取り付け用に、低コストなアクリルフレームを設計します。この構造はFusion 360で3Dモデリングし、特殊機械なしで一般的な工具のみで製作可能です。同じ原理で、あなたのニーズに合ったカスタム構造を設計できます。
主要コンポーネントの3Dモデルを作成し、配置を検討しました。一部のモデルはGrabCADコミュニティライブラリから入手しています:
電子部品を平面上に配置してフットプリントを決定し、安定性を考慮してモーターと車輪の位置を決めました。それに基づき、ベースプレート、左右2枚のサイドプレート、ブラケットで接続する3枚プレート構造を設計しました。
ステップ5: 構造の製作と組み立て
ベース構造の製作手順は以下の通りです:
1. 2D図面に従ってアクリルベースを切断
レーザーカッターがあればそれを使用し、なければ一般工具で行います。2D図面を粘着紙に印刷し、アクリル板に貼り付けてマスクとします。カッターナイフと定規で直線に切り込みを入れ(完全に切断せず、折り目をつける程度)、平らな面に置いてクランプで固定し、圧力をかけて割ります。すべての切断が完了したら、サンドペーパーでバリを取ります。手ノコギリでの切断も可能です。
2. 2D図面の指定位置に穴あけ
マスクの指示位置に電動ドリルで穴あけします。アクリルは比較的穴あけしやすい素材です。ドリルがない場合は、カッターナイフなどの鋭利な工具で手作業でも可能です。ネジサイズに合わせて穴を拡張することもできます。マスクを剥がせばベース完成です。
3. 3D図面に従ってネジとナットで部品を取り付け
動画を参考に、ネジとナットで各部品を取り付けます。M3ネジでブラケットとDCモーターを、M2ネジで電子部品を、5/32インチネジで前輪とスマートフォンクリップを固定します。構造が完成したら、次のステップで回路を組み立てます。
ステップ6: 電子回路の組み立て
この回路では、Arduino Unoをメインコントローラーとし、ESP8266でWi-Fi通信を行います。ArduinoはHブリッジ駆動回路を介して2つのDCモーターを独立して正逆制御します。
電源にはモバイルバッテリーを使用し、ArduinoのUSBポートに直接接続します。これにより、充電が容易で交換も簡単、安全な5V電源を確保できます。
回路図に従ってすべての部品を接続します。ESP8266モジュールはブレッドボード非対応のため、ジャンパーワイヤーで接続します。プロトシールド(コンパクト化)、通常のブレッドボード、または自作Arduinoシールドのいずれかを使用できます。ESP8266-01はブレッドボード非対応のため、ジャンパーワイヤーで接続し、構造体への固定は行いません。
ArduinoにUSBケーブルを接続し、次のステップへ進みます。
wiDC_Esquem_C3_A1tico.pdf / wiDC.fzz / wiDC_bb.pdf
ステップ7: Arduinoコード
最新版Arduino IDEをインストールしてください。ESP8266通信やモーター制御に特別なライブラリは不要です。使用しているESP8266のボーレートを確認し、コード内で適切に設定してください。Arduinoコード(WiDC.ino)をダウンロードし、XXXXXをWi-FiルーターのSSID、YYYYYをパスワードに置き換えてください。ArduinoをPCのUSBポートに接続し、コードを書き込みます。
// ライブラリのインクルード
#include <SoftwareSerial.h>
SoftwareSerial esp8266(3, 2); // RX=3, TX=2
// 変数定義
#define DEBUG true
int state = 5; // 初期状態: 5=スタンバイ
// モーターピン定義
const int motor1Pin1 = 5;
const int motor1Pin2 = 6;
const int motor2Pin1 = 9;
const int motor2Pin2 = 10;
// モーター速度(PWM)
int motorSpeed = 150;
// ***** SETUP *****
void setup() {
pinMode(motor1Pin1, OUTPUT);
pinMode(motor1Pin2, OUTPUT);
pinMode(motor2Pin1, OUTPUT);
pinMode(motor2Pin2, OUTPUT);
Serial.begin(9600);
esp8266.begin(9600);
sendData("AT+RST\r\n", 2000, DEBUG); // モジュールリセット
sendData("AT+CWMODE=1\r\n", 1000, DEBUG); // ステーションモード設定
sendData("AT+CWJAP=\"XXXXX\",\"YYYYY\"\r\n", 2000, DEBUG); // Wi-Fi接続
delay(5000); // 接続待機
sendData("AT+CIFSR\r\n", 1000, DEBUG); // IPアドレス表示
sendData("AT+CIPMUX=1\r\n", 1000, DEBUG); // 複数接続許可
sendData("AT+CIPSERVER=1,80\r\n", 1000, DEBUG); // ポート80でWebサーバー開始
}
// ***** MAIN LOOP *****
void loop() {
if (esp8266.available()) {
if (esp8266.find("+IPD,")) {
String msg;
esp8266.find("?");
msg = esp8266.readStringUntil(' ');
String command = msg.substring(0, 3);
Serial.println(command);
if (command == "cm1") state = 1; // 前進
else if (command == "cm2") state = 2; // 後進
else if (command == "cm3") state = 3; // 右旋回
else if (command == "cm4") state = 4; // 左旋回
else if (command == "cm5") state = 5; // 停止
}
}
// 状態に応じたモーター制御
if (state == 1) { // 前進
analogWrite(motor1Pin1, motorSpeed); digitalWrite(motor1Pin2, LOW);
analogWrite(motor2Pin1, motorSpeed); digitalWrite(motor2Pin2, LOW);
} else if (state == 2) { // 後進
digitalWrite(motor1Pin1, LOW); analogWrite(motor1Pin2, motorSpeed);
digitalWrite(motor2Pin1, LOW); analogWrite(motor2Pin2, motorSpeed);
} else if (state == 3) { // 右旋回
analogWrite(motor1Pin1, motorSpeed); digitalWrite(motor1Pin2, LOW);
digitalWrite(motor2Pin1, LOW); analogWrite(motor2Pin2, motorSpeed);
} else if (state == 4) { // 左旋回
digitalWrite(motor1Pin1, LOW); analogWrite(motor1Pin2, motorSpeed);
analogWrite(motor2Pin1, motorSpeed); digitalWrite(motor2Pin2, LOW);
} else if (state == 5) { // 停止
digitalWrite(motor1Pin1, LOW); digitalWrite(motor1Pin2, LOW);
digitalWrite(motor2Pin1, LOW); digitalWrite(motor2Pin2, LOW);
}
}
// ***** 補助関数 *****
String sendData(String command, const int timeout, boolean debug) {
String response = "";
esp8266.print(command);
long int time = millis();
while ((time + timeout) > millis()) {
while (esp8266.available()) {
char c = esp8266.read();
response += c;
}
}
if (debug) Serial.print(response);
return response;
}
コード解説
Arduino Unoにはハードウェアシリアルポートが1つしかないため、SoftwareSerialライブラリを使ってデジタルピン2,3で第2のシリアルポートを作成し、ESP8266と通信しています。もう1つのハードウェアシリアルはPCとの通信(シリアルモニター)に使用します。
セットアップでは両シリアル通信を開始し、ボーレートを設定します(ここでは9600bps。デフォルトの115200bpsではSoftwareSerialが動作しないため変更が必要です)。専用ライブラリを使わず、ESP8266のデフォルトファームウェアに含まれるATコマンドのみで制御しています:
AT+RST: モジュールリセットAT+CWMODE: ステーション/アクセスポイントモード設定AT+CWJAP: SSIDとパスワードでWi-Fi接続AT+CIPMUX: 複数/単一接続モード設定AT+CIPSERVER: 指定ポートでWebサーバー開始
sendData関数でコマンド送信、応答受信、シリアルモニター表示を行っています。これにより、モジュールリセット→ネットワーク参加→接続待機→IP表示→Webサーバー起動の流れを実行し、メインループでコマンド待機状態に入ります。
メインループでは、+IPD,で着信データを検知し、?以降の最初の3文字をコマンドとして解釈します。5つのコマンド(cm1〜cm5)に対応し、それぞれ前進、後進、右旋回、左旋回、停止の状態に遷移します。異なるコマンドを受信するまでその状態を維持します。
各状態ではモーターピンへの信号を定義します。digitalWrite(pin, LOW)で0V、analogWrite(pin, motorSpeed)でPWM出力を行い、モーター速度を可変制御しています。モーターは3-6Vで動作するため、5V電源使用時はPWMで平均電圧を3-5Vの間で調整し、速度制御を実現しています(ただし細かな速度制御には限界があります)。
ステップ8: Webベースのコントロールインターフェース
ロボット操作用のHTMLインターフェースを設計しました。interface.rarをダウンロード・解凍し、Firefoxで開いてください。テキストボックスにESPモジュールのIPアドレスと、ビデオ/オーディオサーバー(Android IP Webcamアプリ)のIPアドレスを入力します。テストボタンでロボットを回転させ、キーボードの矢印キーで前後左右に操作します。
Androidスマートフォンでビデオと音声を配信します。Google Playストアから「IP Webcam」アプリをインストールしてください。
HTMLコード解説
インターフェースは2つの領域で構成されます:音声・映像表示エリア(Android IP Webcamサーバーから)と、コマンド送信エリアです。
音声・映像エリアには、WebcamサーバーのIPアドレス入力フォームとロードボタンがあります。デフォルトは192.168.0.5ですが変更可能です。映像と音声は<object>タグで埋め込みます。
<div id="video_and_audio" style="position: absolute; top: 50px; width:25%">
<form name="myform" action="" method="GET">
IP Webcam (IP): <input type="text" name="inputbox" value="192.168.0.5">
<input type="button" name="button1" value="Load" onClick="readUrlAV(this.form)">
</form>
<object id="video" type="text/html" data="https://192.168.0.5:8080/video" style="position: absolute; width:100%; height:480px;"></object>
<object id="audio" type="text/html" data="https://192.168.0.5:8080/audio.opus" style="position: absolute; width:388px; height:30px; left:8px; top:425px;"></object>
</div>
コマンドエリアには、ESP8266のIPアドレス入力フォームとテストボタン、キーボード操作説明があります。
<div id="commands" style="position: absolute; top: 50px; left:35%; width:65%">
<form name="myform2" action="" method="GET">
Arduino IP Address: <input type="text" name="inputbox" Value="192.168.0.5">
<input type="button" name="button3" Value="Test" onClick="testarArduino(this.form)">
</form>
<img src="images/keyboard.png" style="margin-left:100px; margin-top:50px;">
<h4> Press and hold keyboard arrow keys to move the robot </h4>
</div>
JavaScript(jquery.js, myscript.js)でインターフェース制御とArduinoへのデータ送信を行います。
JavaScript解説
readUrlAV関数でフォームからIPアドレスを読み取り、映像・音声オブジェクトに設定します。
function readUrlAV(form) {
TextVar = form.inputbox.value;
VideoVar = "https://" + TextVar + ":8080/video";
AudioVar = "https://" + TextVar + ":8080/audio.opus";
document.getElementById("video").setAttribute('data', VideoVar);
document.getElementById("audio").setAttribute('data', AudioVar);
}
キーボード入力を定期的に監視し、矢印キー(上=38, 下=40, 左=37, 右=39)が押されたら、指定IPアドレスへコマンド(cm1〜cm4)を送信します。ラッチ機能で同じコマンドの重複送信を防止し、キーが押されている間のみデータを送信します。
var latch = false;
document.onkeydown = checkKeyDown;
function checkKeyDown(e) {
e = e || window.event;
if (e.keyCode == '38') { // 上矢印
if (!latch) { sendCommand("cm1"); latch = true; }
} else if (e.keyCode == '40') { // 下矢印
if (!latch) { sendCommand("cm2"); latch = true; }
} else if (e.keyCode == '37') { // 左矢印
if (!latch) { sendCommand("cm3"); latch = true; }
} else if (e.keyCode == '39') { // 右矢印
if (!latch) { sendCommand("cm4"); latch = true; }
}
}
矢印キーが離されるとdoNothing関数が実行され、停止コマンド(cm5)を送信してラッチを解除します。
document.onkeyup = checkKeyUp;
function checkKeyUp(e) {
e = e || window.event;
if (e.keyCode=='38'||e.keyCode=='40'||e.keyCode=='37'||e.keyCode=='39') {
setTimeout(doNothing, 200);
}
}
function doNothing() {
sendCommand("cm5");
latch = false;
}
ステップ9: 使い方
Arduinoを再起動すると、自動的にWi-Fiネットワークへ接続を試みます。シリアルモニターで接続成功と、ルーターから割り当てられたESP8266のIPアドレスを確認してください。HTMLファイルをブラウザ(Firefox推奨)で開き、このIPアドレスをテキストボックスに入力します。ルーターの管理画面などでIPを確認することも可能です。
ArduinoをPCから外し、モバイルバッテリーに接続します。再度ネットワーク接続を待ちます。ロボットに取り付けたスマートフォンでIP Webcamアプリを起動し、コントロールインターフェースのビデオ/オーディオIP欄に入力してサーバー接続すれば操作可能になります。アプリ側で映像解像度を下げると伝送遅延を減らせます。キーボードの矢印キーを押し続けることで、ロボットを前後左右に動かせます。
このロボットはオープンループ制御のため、モーターの個体差やアライメントのズレが累積し、真っ直ぐ進ませるのは困難です。また、ESP8266がメッセージを取りこぼすと、例えば停止コマンド(cm5)が届かず、キーを離してもロボットが動き続ける問題が発生することがあります。この問題への対処は継続中です。コマンド解釈の仕組みを変更して改善することも可能です。
コード
- Arduinoコード: WiDC.ino
- インターフェース: interface.rar
カスタムパーツとエンクロージャ
回路図
回路図は上記のPDFファイルを参照してください。
製造プロセス
- Bluetooth 制御 Raspberry Pi ロボットに音声フィードバックを追加して構築
- Arduinoデジタルダイス: LCD表示の乱数ジェネレーター自作
- DIY Arduino USBゲームコントローラー - 高性能ゲームパッドを自作
- Arduino UNOで作る完全自律ライントラッカーロボット ─ 入門に最適な自作キット
- Arduino Nanoで音声制御ロボットを自作
- MobBob:Arduino製ロボットをAndroidスマホでシームレスに制御
- Pixel Chaser: Arduino Nanoで作るワンタップLEDゲーム
- Arduinoで作る斥力式磁気浮上装置: シンプル回路とDIYガイド
- PiBot:Arduino駆動の精密ピアノ演奏ロボット
- 自律型ホームアシスタントロボット「O'nine」の構築 – ROS、Alexa、ロボットアーム完全ガイド
- Arduino Nanoで作るNeoPixelマトリックス版Pongゲーム