AI搭載迷路解決ロボット – Arduino NanoとBluetoothで自律走行
必要な部品と材料
| 部品 | 数量 | 備考 |
|---|---|---|
| Arduino Nano R3 | 1 | メイン制御ボード |
| HC-06 Bluetoothモジュール | 1 | Androidアプリとの通信用 |
| TCRT5000 4CH赤外線ライントラッカー | 1 | ライン追従用(5センサー内蔵) |
| SparkFun RedBotラインフォロワーセンサー | 1 | ライン追従用 |
| ZX03反射型赤外線センサー(アナログ出力) | 2 | 左右の分岐検出用(TCRT5000ベース) |
| Androidデバイス | 1 | チューニング・監視用 |
| RobotGeek連続回転サーボ | 2 | 駆動用 |
| 単3電池ホルダー(4本用) | 2 | 電源用 |
使用するアプリとオンラインサービス
- Arduino IDE
- MIT App Inventor 2(Androidアプリ作成用)
プロジェクト概要
はじめに
このチュートリアルは、前回のプロジェクト「PID制御ラインフォロワーロボット - Androidセットアップ」を発展させたものです。ライン追従機能を持つロボットができたら、次のステップは「知能」を与えることです。愛称「レックス」と名付けたロボットが、今や「迷宮」から最短・最速で脱出を試みます(ちなみに彼はミノタウロスが大嫌いです)。
迷路と迷宮の違い
Labyrinthos.netによると、英語圏では「選択肢のある経路」を持つものを迷路、単一の経路で入り口から目標まで到達するものを迷宮と区別することが多いようです。今回扱う2D迷路は前者に当たります。
多くの迷路は本質的に「一つの連続した壁」から形成されており、目標を囲む壁が入り口の外周と繋がっている場合、「左手の法則(左手を壁につけて進む)」で必ず解けることが知られています。このような迷路は「単連結」または「完全迷路」と呼ばれ、ループを含まないのが特徴です。
二段階のアプローチ
- 第1パス(探索): 未知の完全迷路内で解を発見。スタート位置に関わらずゴールに到達します。
- 第2パス(最適化): 発見した解から「スタートからゴールまでの最短経路」を導出し、高速走行します。
第1パスでは多くの時間を「思考」に費やし、行き止まりでUターンを繰り返しますが、この過程で経験を蓄積し、悪い分岐を排除します。第2パスでは迷いなく最短ルートを駆け抜けます。
ステップ1: 部品表(BOM)
基本的にはラインフォロワーロボットと同じですが、左右の分岐検出精度向上のため2つのセンサーを追加しています。
総費用は約85ドル(約12,000円)と非常に安価です。
詳細な部品リスト(クリックで展開)
- ボディ材料(80×80mm木材2枚、バインダークリップ3個、直径50mm木製車輪2個、ボールキャスター1個、ゴムバンド9本、3Mコマンドフレームストリップ、センサー固定用プラスチックジョイント、ブレッドボードと配線)
- ニッケル水素電池セット(5V)×2
- SM-S4303R連続回転サーボ ×2
- Arduino Nano
- HC-06 Bluetoothモジュール
- ラインセンサー5個(TCRT5000 4CHモジュール+独立センサー1個)
- ZX03アナログ反射センサー ×2
- LED 1個、ボタン 1個
注: アナログ出力センサーを使用していますが、理想的には全センサーをデジタル出力で統一すべきです。元の5センサーのみでも動作しますが、分岐検出により敏感な調整が必要です。
ステップ2: ボディの改造
元の5センサーモジュールを取り外し、支持バーの両端に「ファー・レフト(Far LEFT)」と「ファー・ライト(Far RIGHT)」反射センサーを取り付けます。7つのセンサーをできるだけ一直線に配置することが推奨されます。
ステップ3: 新センサーの取り付けとテスト
新しい7センサー構成では、元の5センサーをPID制御と「全面ライン」検出専用に、新しい2センサーを左右分岐検出専用に割り当てています。
センサーの動作原理
元の5デジタルセンサー: 黒線の中心上にあるセンサーのみHIGH出力。センサー間隔は2センサーが同時に黒線を覆えるよう設計。
新しい2アナログセンサー: 黒線上ではADC値100以下、白紙上では500-600程度。環境光や材質により閾値(THRESHOLD)を決定する必要があります。
センサー読み取りコード
const int lineFollowSensor0 = 12; // デジタル入力
const int lineFollowSensor1 = 18; // アナログピンA4をデジタル入力として使用
const int lineFollowSensor2 = 17; // アナログピンA3をデジタル入力として使用
const int lineFollowSensor3 = 16; // アナログピンA2をデジタル入力として使用
const int lineFollowSensor4 = 19; // アナログピンA5をデジタル入力として使用
const int farRightSensorPin = 0; // アナログピンA0
const int farLeftSensorPin = 1; // アナログピンA1
int LFSensor[5] = {0, 0, 0, 0, 0};
int farRightSensor = 0;
int farLeftSensor = 0;
void readLFSsensors() {
LFSensor[0] = digitalRead(lineFollowSensor0);
LFSensor[1] = digitalRead(lineFollowSensor1);
LFSensor[2] = digitalRead(lineFollowSensor2);
LFSensor[3] = digitalRead(lineFollowSensor3);
LFSensor[4] = digitalRead(lineFollowSensor4);
farRightSensor = analogRead(farRightSensorPin);
farLeftSensor = analogRead(farLeftSensorPin);
// modeとerrorの決定ロジックは本文参照
}
センサーロジックのテスト用関数も用意されています。
ステップ4: 迷路解法 - 左手の法則
ウィキペディアによると、左手の法則は以下のように要約されます:
- 左手を壁に置く
- 前進し続ける
- 最短ルートではないが、必ずゴールに到達する
2D迷路では8種類の分岐が存在しますが、左手の法則を適用すると各分岐でのアクションが一意に決まります:
- 十字路 → 左折
- T字路 → 左折
- 右のみ → 右折
- 左のみ → 左折
- 直進または左 → 左折
- 直進または右 → 直進
- 行き止まり → Uターン(180度旋回)
- 迷路の終わり → 停止
ただし、「ライン」検出時は十字路かT字路か、「左/右ターン」検出時は単純な曲がりか直進可能かを判別するため、「エクストラインチ(少し前進)」して先を確認する必要があります。
ステップ5: Arduinoコードへの実装
void loop() {
readLFSsensors();
switch (mode) {
case NO_LINE:
motorStop();
goAndTurn(LEFT, 180);
break;
case CONT_LINE:
runExtraInch();
readLFSsensors();
if (mode == CONT_LINE) mazeEnd();
else goAndTurn(LEFT, 90); // T字路も十字路も左折
break;
case RIGHT_TURN:
runExtraInch();
readLFSsensors();
if (mode == NO_LINE) goAndTurn(RIGHT, 90);
break;
case LEFT_TURN:
goAndTurn(LEFT, 90);
break;
case FOLLOWING_LINE:
followingLine();
break;
}
}
新しい関数:
- followingLine(): PID制御でライン追従
- runExtraInch(): 少し前進して分岐タイプを確認
- goAndTurn(direction, angle): センサー位置と車輪中心のオフセットを考慮して旋回
これでロボットは実際に「迷路を解く」ことができます!スタート位置に関わらず必ずゴールに到達します。
ステップ6: 経路の記録
例:15個の分岐を通過
L, B, L, L, L, B, S, B, L, L, B, S, L, L, End
(L=左折, B=Uターン, S=直進)
各分岐でのアクションを順序通り配列に保存します:
char path[100] = "";
unsigned char pathLength = 0;
int pathIndex = 0;
ステップ7: 経路の簡略化(最適化)
「LBL(左→Uターン→左)」は実は「S(直進)」と等価です。Uターン(B)を含む3分岐の組み合わせ「xBx」を単一の動作に置き換えることで経路を最適化します。
全パターン:
- LBR = B
- LBS = R
- RBL = B
- SBL = R
- SBS = B
- LBL = S
例の経路 [LBLLLBSBLLBSLL] を順次簡略化:
[LBLLLBSBLLBSLL] → LBL=S → [SLLBSBLLBSLL]
→ LBS=R → [SLRBLLBSLL]
→ RBL=B → [SLBLBSLL]
→ LBL=S → [SSBSLL]
→ SBS=B → [SBLL]
→ SBL=R → [RL]
なんと、最初に右折し次に左折するだけで最短到達可能です!
簡略化アルゴリズム(Patrick McCabe氏のコードに基づく):
void simplifyPath() {
if (pathLength < 3 || path[pathLength-2] != 'B') return;
int totalAngle = 0;
for (int i=1; i<=3; i++) {
switch(path[pathLength-i]) {
case 'R': totalAngle += 90; break;
case 'L': totalAngle += 270; break;
case 'B': totalAngle += 180; break;
}
}
totalAngle %= 360;
switch(totalAngle) {
case 0: path[pathLength-3] = 'S'; break;
case 90: path[pathLength-3] = 'R'; break;
case 180: path[pathLength-3] = 'B'; break;
case 270: path[pathLength-3] = 'L'; break;
}
pathLength -= 2;
}
ステップ8: 第2パス - 最速走行
void loop() {
ledBlink(1);
readLFSsensors();
mazeSolve(); // 第1パス:迷路を解く
ledBlink(2);
while (digitalRead(buttonPin)) { } // ボタン待ち
pathIndex = 0;
status = 0;
mazeOptimization(); // 第2パス:最速走行
ledBlink(3);
while (digitalRead(buttonPin)) { }
mode = STOPPED;
status = 0;
pathIndex = 0;
pathLength = 0;
}
第2パスでは記録された最適経路 path[] に従って動作します:
void mazeOptimization() {
while (!status) {
readLFSsensors();
switch (mode) {
case FOLLOWING_LINE: followingLine(); break;
case CONT_LINE:
case LEFT_TURN:
case RIGHT_TURN:
if (pathIndex >= pathLength) mazeEnd();
else { mazeTurn(path[pathIndex]); pathIndex++; }
break;
}
}
}
void mazeTurn(char dir) {
switch(dir) {
case 'L': goAndTurn(LEFT, 90); break;
case 'R': goAndTurn(RIGHT, 90); break;
case 'B': goAndTurn(RIGHT, 800); break; // Uターン
case 'S': runExtraInch(); break;
}
}
ステップ9: Androidアプリによるチューニング
前回プロジェクトのAndroidアプリ(MIT App Inventor 2製)を流用可能です。Bluetooth経由でセンサーデータやパラメータをリアルタイム監視・調整できます。
調整が重要なパラメータ(太字は頻繁に調整したもの):
const int adj = 0;
float adjTurn = 8;
int adjGoAndTurn = 800;
THRESHOLD = 150
const int power = 250;
const int iniMotorPower = 250;
int extraInch = 200;
ステップ10: 結論
ラインフォロワーロボットの可能性を探る複雑なプロジェクトの第2部・完結編です。人工知能(AI)の基本概念を用いて迷路を解くことに成功しました。
AIの専門家ではありませんが、Wikipediaや大学論文("Maze Solving Robot Using Freeduino and LSRB Algorithm")を参照すると、このロボットの振る舞いはAIの一応用と見なせます。
プロジェクトファイルはGitHubで公開中。更なるチュートリアルはMJRoBot.orgで。
南半球より、サルードス!
Marcelo
コードスニペット集
主要な関数実装は本文中に含まれています。完全なコードはGitHubリポジトリからダウンロード可能です。
回路図
z7IdLkxL1J66qOtphxqC.fzz(Fritzingファイル)
製造プロセス
- Bridge Shield でスマートロボットを構築:Raspberry Pi と Arduino の統合
- Raspberry Pi でジェスチャー制御ロボットを構築 – 加速度センサーと RF 通信でハンドフリー操作
- Pythonで作るWi-Fi制御Raspberry Piロボット - ステップバイステップガイド
- Microsoft KinectをRaspberry Piと統合しSonbiロボットでリアルタイム人物検出
- ボッシュ、ハノーバーメッセでAI駆動製造ソリューションを発表
- AI駆動ロボティクス:最先端インテリジェントマシン
- ワークスペースに協働ロボット(コボット)を選ぶ理由
- ロボットが触覚で90種類以上の物体を識別、視覚・触覚融合のマルチモーダルAI
- 小売におけるAI:一過性の流行ではなく実証済みのメリット
- KubernetesがAIスタートアップに拡張可能なコンテナ化ソリューションを提供
- AIを活用したアマゾン熱帯雨林の森林減少監視