人工知能を使用した迷路ソルバーロボット
コンポーネントと消耗品
> |
| × | 1 | |||
| × | 1 | ||||
| × | 2 | ||||
| × | 1 | ||||
| × | 2 | ||||
| × | 2 |
アプリとオンラインサービス
> |
| |||
|
このプロジェクトについて
はじめに
このチュートリアルは、私の最後のプロジェクトであるLine Follower Robot-PID Control-AndroidSetupに基づいて開発されました。ラインフォロー機能を備えたロボットができたら、次の自然なステップは彼にある程度の知性を与えることです。それで、私たちの愛する「レックス、ロボット」は、「迷宮」から最短かつ最速の方法で脱出する方法を見つけようとします(ちなみに、彼はミノタウロスを嫌っています。
まず、迷路の違いは何ですか およびラビリンス ? http://www.labyrinthos.netによると、英語圏の世界では、迷路としての資格を得るには、デザインに経路の選択肢が必要であると考えられることがよくあります。明らかに、これには、ここの2D迷路を含む、エンターテインメントパークや観光名所の近代的なインスタレーションの多くが含まれます。人気のあるコンセンサスは、迷路には、最も複雑で曲がりくねったルートが多いにもかかわらず、入口からゴールまで容赦なくつながる1つの経路があることも示しています。
迷路の大部分は、そのデザインが複雑に見えるかもしれませんが、本質的に、多くの接合部と枝を持つ1つの連続した壁から形成されていました。迷路のゴールを囲む壁が入り口の迷路の周囲に接続されている場合、迷路は常に片手を壁に接触させることで解決できますが、多くの迂回路が関係する可能性があります。これらの「単純な」迷路は、「単連結
」として正しく知られています。 "または" 完全な迷路
"つまり、ループが含まれていない 。
プロジェクトに戻ると、2つの部分(または「 passs
」)に分割されます。 "):
- (ファーストパス) :ロボットは「
未知の完璧な迷路
」から抜け出します "。迷路のどこに置いてもかまいません。"solution
"。
- (セカンドパス) :ロボットが迷路の解決策を見つけたら、その解決策を最適化して、「
開始から終了までの最短経路
」を見つける必要があります。 "。
以下のビデオは、レックスがその方法を見つけた例を示しています。ロボットが迷路を探索するのは初めてですが、もちろん「思考
」には多くの時間が無駄になります。 「交差点で何をすべきかについて。多くの可能性をテストすると、いくつかの間違ったパスと行き止まりが必要になり、長いパスを実行して不要な「 Uターン
」を実行する必要があります。 "。この" 1st Pass "
、ロボットは経験を蓄積し、「メモを取る
"さまざまな交差点について、悪い分岐を排除します。その" 2nd Pass
"、ロボットは間違いや疑いなくまっすぐにそして素早く最後まで進みます。このチュートリアルに沿って、それを行う方法を詳細に探求します:
ステップ1:部品表
材料のリストは、基本的にラインフォロワーロボットで使用されているものと同じですが、左と右の交差点をより正確に検出するために2つのセンサーを追加した点が異なります。
最終的なロボットはまだ非常に安いです(約$ 85.00):
本体(ニーズや入手可能な材料に合わせて調整できます):
- 2 Xウッドスクエア(80X80mm)
- 3Xバインダークリップ
- 2 Xウッドホイール(直径:50mm)
- 1Xボールキャスター
- 9Xゴムバンド
- 3Mコマンドフレームストリップ
- センサー修正用のプラスチックジョイント
- ブレッドボードと配線
- 2 Xセットの4Xニッケル水素電池(各セット5V)
- 2 XSM-S4303R連続回転360度プラスチックサーボ
- Arduino Nano
- HC-06Bluetoothモジュール
- 5つのXラインセンサー(TCRT5000 4CH赤外線ライントラックフォロワーセンサーモジュール+1つの独立したトラックセンサー)
- 2 X ZX03(TCRT5000に基づく)反射型赤外線センサー(アナログ出力)
- 1つのLED
- 1ボタン
注 :上記の項目7は、項目6のようにデジタル出力のセンサーが手元になかったため、アナログ出力で使用しました。可能であれば、すべてのセンサーを等しくすることが理想です。また、元の5つのセンサーのみを保持してプロジェクトをテストしました。動作しますが、交差点の検出にはより敏感な調整が必要です。
ステップ2:体の変化 <図> <図> <図> <図> <図>
5つのラインフォローセンサーの元のセットを削除し、新しい「 FarLEFT」
を修正します および「 FarRIGHT
「サポートプラスチックバーの両端にある反射センサー。7つのセンサーをできるだけ並べることをお勧めします。
ステップ3:新しいセンサーのインストールとテスト <図> <図>
現在 7つのセンサーの新しいアレイ は、元の5つがPID制御(および後で説明する「フルライン」の検出)専用に使用され、新しい2つが左と右の交差検出専用に使用されるように取り付けられています。
簡単なレビューとして、5つの元の「デジタル」センサーがどのように機能するかを思い出してみましょう。
1つのセンサーが黒い線に対して中央に配置されている場合、その特定のセンサーのみがHIGHを生成します。反対に、センサー間のスペースは、2つのセンサーが同時に黒い線の全幅をカバーし、両方のセンサーでHIGH信号を生成できるように計算する必要があります。
2つの新しい「アナログ」センサーの仕組み:
センサーの1つが黒い線に対して中央に配置されている場合、出力はアナログ値になり、通常は「100」以下のArduino ADCで出力を生成します(ADCは0〜1023の出力を生成することに注意してください)。表面が明るいほど、出力値は高くなります(たとえば、白い紙で500〜600をテストしました)。この値は、光と表面の材料のさまざまな状況でテストして、ケースで使用する正しいTHRESHOLD定数を定義する必要があります(ここの写真を参照)。
Arduinoコードを見ると、各センサーは特定の名前で定義されます(左側にある元のラインフォローセンサーには、ラベル「 0
」を割り当てる必要があることを考慮してください。 "):
const int lineFollowSensor0 =12; //デジタル入力を使用constint lineFollowSensor1 =18; //アナログピンA4をデジタル入力として使用constint lineFollowSensor2 =17; //アナログピンA3をデジタル入力として使用constint lineFollowSensor3 =16; //アナログピンA2をデジタル入力として使用constint lineFollowSensor4 =19; //アナログピンA5をデジタル入力として使用constint farRightSensorPin =0; //アナログピンA0constint farLeftSensorPin =1; //アナログピンA1
覚えておくと、線をたどるときに出力される可能性のある5つの元のセンサーアレイは次のとおりです。
1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 0
2つの新しいものを追加すると、可能な出力は次のようになります。
- 極左センサー:しきい値よりも大きいまたは小さいアナログ出力
- 極右センサー:しきい値よりも大きいまたは小さいアナログ出力
各センサーの値を保存するために、元の5つのデジタルセンサーの配列変数が作成されます。
int LFSensor [5] ={0、0、0、0、0};
そして、2つの新しいアナログセンサーの2つの整数変数:
int farRightSensor =0; int farLeftSensor =0;
配列と変数の各位置は、各センサーの出力で常に更新されます。
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);
Follower Line Robotプロジェクトで見られたように、5つのセンサーを持つことで、ライン上のロボットの位置を制御するのに役立つ「エラー変数」の生成が可能になります。また、ロボットが線をたどっている場合は、「モード」と呼ばれる変数が定義に使用されます。 、連続線 、交差点 または行なし まったく。
この変数「 モード 「」は「 極左/右」でも使用されます "センサー。表現のために、3つの可能な状態を持つ左端と右端のセンサーを考えてみましょう:
- H(THRESHOLDより高い)、
- L(THRESHOLDよりも小さい)および
- X(無関係)。
デジタル出力の場合、通常の0、1になり、Xも紹介します:
- H 0 X X X X L ==>モード=RIGHT_TURN;エラー=0; (上の画像の例を参照してください)
- L X X X X 0 H ==>モード=LEFT_TURN;エラー=0;
- X 0 0 0 0 0 X ==>モード=NO_LINE;エラー=0;
- H 0 0 0 0 1 H ==>モード=FOLLOWING_LINE;エラー=4;
- H 0 0 0 1 1 H ==>モード=FOLLOWING_LINE;エラー=3;
- H 0 0 0 1 0 H ==>モード=FOLLOWING_LINE;エラー=2;
- H 0 0 1 1 0 H ==>モード=FOLLOWING_LINE;エラー=1;
- H 0 0 1 0 0 H ==>モード=FOLLOWING_LINE;エラー=0;
- H 0 1 1 0 0 H ==>モード=FOLLOWING_LINE;エラー=-1;
- H 0 1 0 0 0 H ==>モード=FOLLOWING_LINE;エラー=-2
- H 1 1 0 0 0 H ==>モード=FOLLOWING_LINE;エラー=-3;
- H 1 0 0 0 0 H ==>モード=FOLLOWING_LINE;エラー=-4;
- X 1 1 1 1 1 X ==>モード=CONT_LINE;エラー=0;
したがって、上記のロジックを関数に実装します。
void readLFSsensors()
変数「 モード」を返します "と" エラー 「これはプログラムロジックで使用されます。プロジェクトを実行する前にセンサーのロジックをテストすることが重要です。次の関数はコードに含まれており、テスト目的で使用できます。
void testSensorLogic(void){Serial.print(farLeftSensor); Serial.print( "<==LEFT RIGH ==>"); Serial.print(farRightSensor); Serial.print( "モード:"); Serial.print(モード); Serial.print( "エラー:"); Serial.println(エラー);}
ステップ4:迷路を解く-左手の法則 <図> <図> <図>
冒頭で説明したように、迷路の大部分は、そのデザインが複雑に見えるかもしれませんが、基本的に、多くの接合部と分岐を持つ1つの連続した壁から形成されていました。迷路のゴールを囲む壁が入り口の迷路の周囲に接続されている場合、迷路は常に片手を壁に接触させることで解決できますが、多くの迂回路が関係する可能性があります。これらの「単純な」迷路は、「単連結
」として正しく知られています。 。 "
ウィキペディアで検索すると、次のことがわかります。
つまり、 左手の法則 次のように説明できます:
すべての交差点で、迷路全体で、左手を左側の壁に触れたままにします。
- 左手を壁に置きます。
- 前に歩き始めます
- 最終的に、迷路の終わりに到達します。おそらく最短で最も直接的な方法ではありませんが、そこにたどり着きます。
したがって、ここで重要なのは、交差点を特定することです。 、上記のルールに基づいてどのコースを取るかを定義します。特に私たちの種類の2D迷路では、8つの異なるタイプの交差点を見つけることができます(上の最初の写真を参照):
写真を見ると、交差点で可能なアクションは次のとおりです。
1。「クロス」で ":
- 左に移動、または
- 右に移動、または
- まっすぐ進む
2。「 T 」で ":
- 左に移動、または
- 右に移動
3。「右のみ ":
- 右に移動
4。「左のみ」で ":
- 左に移動
5。「まっすぐまたは左」で ":
- 左に移動、または
- まっすぐ進む
6。「ストレートまたはライト」で ":
- 右に移動、または
- まっすぐ進む
7。「行き止まり」で ":
- 戻る(「Uターン」)
8。「迷路の終わり」で ":
- 停止
ただし、「左手の法則」を適用すると、アクションはそれぞれ1つのオプションに削減されます。
- 「クロス」で:左に移動
- 「T」で:左に移動
- 「右のみ」の場合:右に移動
- 「左のみ」の場合:左に移動
- 「まっすぐまたは左」の場合:左に移動
- 「まっすぐまたは正しい」場合:まっすぐ進む
- 「行き止まり」の場合:戻る(「Uターン」)
- 「迷路の終わり」で:停止
私たちは、ほぼ、そこにいる! 「落ち着いて!」
ロボットが「行き止まり」または「迷路の終わり」に到達すると、あいまいな状況が存在しないため、それらを簡単に識別できます(これらのアクションはすでにラインフォロワーロボットに実装されています、覚えていますか?)。問題は、たとえば、ロボットが「LINE」を見つけた場合です。これは、ラインが「Cross」(1)または「T」(2)である可能性があるためです。また、「左または右に曲がる」に達すると、単純な曲がり(オプション3または4)または直進するオプション(5または6)になります。ロボットがどのタイプの交差点であるかを正確に検出するには、追加の手順を実行する必要があります。ロボットは「余分なインチ」を実行し、次の交差点を確認する必要があります(例として上の2番目の図を参照)。
したがって、フローの観点から、可能なアクションは次のように記述できます。
1.「デッドエンド」で:
- 戻る(「Uターン」)
2.「LINE」で: 1インチ余分に実行
- 行がある場合:「クロス」です==>左に移動
- 行がない場合:「T」です==>左に移動
- 別の行がある場合:それは「迷路の終わり」==>停止
3.「右折」時: 1インチ余分に走る
- 線がある場合:それはまっすぐまたは右です==>まっすぐに行きます
- 行がない場合:右のみ==>右に移動
4.「左折」時: 1インチ余分に走る
- 線がある場合:直線または左==>左に移動
- 行がない場合:左のみ==>左に移動
実際、「左折」の場合は、とにかく左折するので、テストをスキップすることができます。説明をわかりやすくするために、より一般的な説明を残しました。実際のコードでは、このテストをスキップします。上の3番目の写真は、テスト目的で使用された、私のラボフロアの非常に単純な迷路を示しています。
ステップ5:Arduinoコードで「壁に左手」アルゴリズムを実装する <図>
readLFSsensors()
を取得したら 追加の2つのセンサーを含むように関数を変更した場合、最後のステップで説明したように、ループ関数を書き直してアルゴリズムを実行できます。
void loop(){readLFSsensors();スイッチ(モード){ケースNO_LINE:motorStop(); goAndTurn(左、180);壊す;ケースCONT_LINE:runExtraInch(); readLFSsensors(); if(mode ==CONT_LINE)mazeEnd();それ以外の場合はgoAndTurn(LEFT、90); //または、「T」または「Cross」です)。どちらの場合も、LEFTブレークに進みます。ケースRIGHT_TURN:runExtraInch(); readLFSsensors(); if(mode ==NO_LINE)goAndTurn(RIGHT、90);壊す;ケースLEFT_TURN:goAndTurn(LEFT、90);壊す;ケースFOLLOWING_LINE:followingLine();壊す; }}
いくつかの新しい関数がここに表示されます:
- followLine() は、Following Line Robotで使用されるものと同じです。ここで、行のみを追跡する場合は、
calculatePID()
を実行する必要があります。 <コード>; コード> PID値に応じてモーターを制御します:motorPIDcontrol();
- runExtraInch(): ロボットを少し前に押し出します。ロボットがどれだけ動くかは、モーターに停止を命じる前に、遅延機能で使用する時間によって異なります。
void runExtraInch(void){motorPIDcontrol(); delay(extraInch); motorStop();}
- goAndTurn(方向、角度): 交差点のタイプに気づいたらすぐにロボットを回転させることはできないので、この特別な機能は重要です。ディファレンシャルロボットを投影したことを思い出してください。回転すると、「軸を中心に回転」するため、90度移動して線を継続的に追跡するには、車輪の中心を交差点の中心に合わせる必要があります。センサーのラインがその軸の前に来たら、ロボットを前方に動かしてセンサーを位置合わせする必要があります。時定数
adjGoAndTurn
斧とセンサーライン間の距離に応じて調整する必要があります( "d
")、ホイールの速度とサイズ(図については上の写真を参照してください)。
void goAndTurn(int direction、intdegrees){motorPIDcontrol(); delay(adjGoAndTurn); motorTurn(方向、度);}
この時点で、ロボットは実際には「迷路を解いている」のです! 「ファーストパス」を終えるだけです。迷路の中のどこから始めても、常に終わりに到達します。
ベロー、プロジェクトのこのフェーズのテスト:
ステップ6:パスを保存する <図>
上の写真の例を考えてみましょう。選択した開始点で、ロボットは迷路の終わりに到達する前に15の交差点を見つけます:
- 左(L)
- 戻る(B)
- 左(L)
- 左(L)
- 左(L)
- 戻る(B)
- ストレート(S)
- 戻る(B)
- 左(L)
- 左(L)
- 戻る(B)
- ストレート(S)
- 左(L)
- 左(L)
- 終了
これらの交差点のいずれかで実行する必要があるのは、実行される各アクションを、それが発生するのとまったく同じシーケンスで保存することです。そのために、ロボットがたどったパスを格納する新しい変数(配列)を作成しましょう:
char path [100] ="";
また、配列と一緒に使用する2つのインデックス変数を作成する必要があります。
unsigned char pathLength =0; // pathintの長さpathIndex =0; //特定の配列要素に到達するために使用されます。
したがって、図に示す例を実行すると、次のようになります。
path =[LBLLLBSBLLBSLL]およびpathLengh =14
ステップ7:パスを単純化(最適化)する <図>
例に戻りましょう。交差点の最初のグループを見ると、最初の左の分岐は実際には「行き止まり」であることがわかりました。したがって、「左-後-左」ではなくロボットがその最初の交差点をまっすぐ通過しただけの場合、多くのエネルギーが発生します。そして時間を節約できます!言い換えれば、シーケンス「LBL」は実際には「S」と同じになります。これがまさにフルパスを最適化する方法です。 「Uターン」が使用されるすべての可能性を分析すると、この「Uターン」(「B」)が表示される3つの交差点のセット(「xBx」)は1つに減らすことができます。
上記はほんの一例です。以下に、可能性の完全なリストを見つけることができます(試してみてください):
- LBR =B
- LBS =R
- RBL =B
- SBL =R
- SBS =B
- LBL =S
フルパスまたは例をとると、それを減らすことができます:
path =[LBLLLBSBLLBSLL] ==> LBL =Spath =[SLLBSBLLBSLL] ==> LBS =Rpath =[SLRBLLBSLL] ==> RBL =Bpath =[SLBLBSLL] ==> LBL =Spath =[SSBSLL ] ==> SBS =Bpath =[SBLL] ==> SBL =Rpath =[RL]
すばらしい!例を見ると、ロボットが最初の交差点で右に進み、その後、左に進むと、最短経路で迷路の終わりに到達することが非常に明確です!
Maze Solverの最初のパスの合計コードは、関数 mazeSolve()に統合されます。 。この関数は、実際には以前に使用されたloop()関数ですが、保存とパスの最適化のすべてのステップが組み込まれています。最初のパスが終了すると、path []配列には最適化されたパスが含まれます。新しい変数が導入されました:
unsigned int status =0; //解決=0; Maze End =1に到達
ファーストパス機能の下:
void mazeSolve(void){while(!status){readLFSsensors();スイッチ(モード){ケースNO_LINE:motorStop(); goAndTurn(左、180); recIntersection( 'B');壊す;ケースCONT_LINE:runExtraInch(); readLFSsensors(); if(mode!=CONT_LINE){goAndTurn(LEFT、90); recIntersection( 'L');} //または、「T」または「Cross」)。どちらの場合も、LEFT else mazeEnd();に移動します。壊す;ケースRIGHT_TURN:runExtraInch(); readLFSsensors(); if(mode ==NO_LINE){goAndTurn(RIGHT、90); recIntersection( 'R');} else recIntersection( 'S');壊す;ケースLEFT_TURN:goAndTurn(LEFT、90); recIntersection( 'L');壊す;ケースFOLLOWING_LINE:followingLine();壊す; }}}
ここで、新しい関数が導入されました: recIntersection(方向)。 この関数は、交差点を保存したり、別の関数を呼び出すために使用されます simpleifyPath() 、 これにより、前に見たように「Uターン」を含む3つの交差点のグループが減ります。
void recIntersection(char direction){path [pathLength] =direction; //交差点をパス変数に格納します。 pathLength ++; simplePath(); //学習したパスを簡略化します。}
simpleifyPath()のクレジット )関数は、パス解決コードのPatrick McCabeに対するものです(詳細については、https://patrickmccabemakes.comにアクセスしてください)。パス単純化の戦略は、シーケンスxBxに遭遇したときはいつでも、行き止まりを切り取ることによってそれを単純化できるということです。たとえば、 LBL ==> S
例で見たように。
void simplePath(){//最後から2番目のターンが 'B'の場合にのみパスを単純化if(pathLength <3 || path [pathLength-2]!='B')return; int totalAngle =0; int i; for(i =1; i <=3; i ++){switch(path [pathLength-i]){case'R ':totalAngle + =90;壊す;ケース 'L':totalAngle + =270;壊す;ケース 'B':totalAngle + =180;壊す; }} //角度を0〜360度の数値として取得します。 totalAngle =totalAngle%360; //これらのターンをすべて1つに置き換えます。 switch(totalAngle){ケース0:path [pathLength-3] ='S';壊す;ケース90:path [pathLength-3] ='R';壊す;ケース180:path [pathLength-3] ='B';壊す;ケース270:path [pathLength-3] ='L';壊す; } //パスが2ステップ短くなりました。 pathLength- =2; }
ステップ8:2番目のパス:迷路をできるだけ早く解決します!
メインプログラム: loop()
そのように単純です:
void loop(){ledBlink(1); readLFSsensors(); mazeSolve(); //迷路を解くための最初のパスledBlink(2); while(digitalRead(buttonPin){} pathIndex =0; status =0; mazeOptimization(); // 2番目のパス:迷路をできるだけ速く実行するledBlink(3); while(digitalRead(buttonPin){} mode =STOPPED; status =0; //最初のパスpathIndex =0; pathLength =0;}
したがって、ファーストパスが終了したときに実行する必要があるのは、最適化されたパス配列をロボットに供給することだけです。実行を開始し、交差点が見つかると、 path []
に保存されている内容に基づいて何をするかを定義します。 。
void mazeOptimization(void){while(!status){readLFSsensors();スイッチ(モード){ケースFOLLOWING_LINE:followingLine();壊す; case CONT_LINE:if(pathIndex> =pathLength)mazeEnd(); else {mazeTurn(path [pathIndex]); pathIndex ++;} break; case LEFT_TURN:if(pathIndex> =pathLength)mazeEnd(); else {mazeTurn(path [pathIndex]); pathIndex ++;} break; case RIGHT_TURN:if(pathIndex> =pathLength)mazeEnd(); else {mazeTurn(path [pathIndex]); pathIndex ++;} break; }}}
何をすべきかを命令するために、新しい関数 mazeTurn(path []) 作成されました。関数 mazeTurn(path []) なります:
void mazeTurn(char dir){switch(dir){case'L '://左に曲がるgoAndTurn(LEFT、90);壊す; case'R '://右に曲がるgoAndTurn(RIGHT、90);壊す; case'B '://元に戻すgoAndTurn(RIGHT、800);壊す; case'S '://まっすぐ進むrunExtraInch();壊す; }}
2回目のパスが完了しました!以下のビデオは、ここで機能する完全な例、1回目と2回目のパスを示しています。このチュートリアルで使用されているArduinoコードの下:
FV6XNJWINJ45XWM.ino F2FXS8MINJ45XX6.h FX5MHFMINJ45XX7.ino FT2S1WXINJ45XXA.ino F9IC3HQINJ45XXB.ino FU2HRXJINJ45XXV.ino
ステップ9:Androidを使用したチューニング <図> <図>
次のラインプロジェクト用に開発されたAndroidアプリもここで使用できます(必要に応じて、Androidアプリとそのコードは次のURLで入手できます:ラインフォロワーロボット-PIDコントロール-Androidセットアップ。最後のステップで提示されたArduinoコードには、 Androidデバイス。Androidアプリを使用したくない場合は、コードが「 transparent
」であるため、問題ありません。 "。
プロジェクト中にAndroidを頻繁に使用して、「 Message Received
」を使用して、ロボットからデバイスにテストデータを送信しました。 "フィールド。ロボットが正しい角度で回転することを保証するには、いくつかの変数を明確に定義する必要があります。最も重要な変数は次のとおりです(太字でマークされているものは数回変更しました):
const int adj =0; float adjTurn =8; int adjGoAndTurn =800; THRESHOLD =150const int power =250; const int iniMotorPower =250; int extraInch =200;
ステップ10:結論 <図>
これは複雑なプロジェクトの2番目で最後の部分であり、人工知能が存在するラインフォロワーロボットの可能性を探ります。 (AI)迷路を解くために単純な概念が使用されました。
私は AI ではありません 専門家であり、私がWebから得たいくつかの情報に基づいて、迷路を解く私たちの小さなレックス、ロボットが行ったことはAIのアプリケーションと見なすことができることを理解しました。以下の2つのソースを見てみましょう:
ウィキペディアから:
または、この大学の論文から:「FreeduinoとLSRBアルゴリズムを使用した迷路解決ロボットInternational Journal of Modern Engineering Research(IJMER)」
このプロジェクトの更新されたファイルはGITHUBにあります。私が他の人に電子機器、ロボット、Arduinoなどについてもっと学ぶために貢献できることを願っています。他のチュートリアルについては、私のブログにアクセスしてください:MJRoBot.org
世界の南からのサルド!
ありがとう
マルセロ
コード
- コードスニペット#1
- コードスニペット#4
- コードスニペット#5
- コードスニペット#6
- コードスニペット#7
- コードスニペット#8
- コードスニペット#12
- コードスニペット#13
- コードスニペット#14
- コードスニペット#15
- コードスニペット#16
- コードスニペット#17
コードスニペット#1 プレーンテキスト
const int lineFollowSensor0 =12; //デジタル入力を使用constint lineFollowSensor1 =18; //アナログピンA4をデジタル入力として使用constint lineFollowSensor2 =17; //アナログピンA3をデジタル入力として使用constint lineFollowSensor3 =16; //アナログピンA2をデジタル入力として使用constint lineFollowSensor4 =19; //アナログピンA5をデジタル入力として使用constint farRightSensorPin =0; //アナログピンA0constint farLeftSensorPin =1; //アナログピンA1
コードスニペット#4 プレーンテキスト
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);
コードスニペット#5 プレーンテキスト
void testSensorLogic(void){Serial.print(farLeftSensor); Serial.print( "<==LEFT RIGH ==>"); Serial.print(farRightSensor); Serial.print( "モード:"); Serial.print(モード); Serial.print( "エラー:"); Serial.println(エラー);}
コードスニペット#6 プレーンテキスト
void loop(){readLFSsensors();スイッチ(モード){ケースNO_LINE:motorStop(); goAndTurn(左、180);壊す;ケースCONT_LINE:runExtraInch(); readLFSsensors(); if(mode ==CONT_LINE)mazeEnd();それ以外の場合はgoAndTurn(LEFT、90); //または、「T」または「Cross」です)。どちらの場合も、LEFTブレークに進みます。ケースRIGHT_TURN:runExtraInch(); readLFSsensors(); if(mode ==NO_LINE)goAndTurn(RIGHT、90);壊す;ケースLEFT_TURN:goAndTurn(LEFT、90);壊す;ケースFOLLOWING_LINE:followingLine();壊す; }}
コードスニペット#7 プレーンテキスト
void runExtraInch(void){motorPIDcontrol(); delay(extraInch); motorStop();}
コードスニペット#8 プレーンテキスト
void goAndTurn(int direction、intdegrees){motorPIDcontrol(); delay(adjGoAndTurn); motorTurn(方向、度);}
コードスニペット#12 プレーンテキスト
void mazeSolve(void){while(!status){readLFSsensors();スイッチ(モード){ケースNO_LINE:motorStop(); goAndTurn(左、180); recIntersection( 'B');壊す;ケースCONT_LINE:runExtraInch(); readLFSsensors(); if(mode!=CONT_LINE){goAndTurn(LEFT、90); recIntersection( 'L');} //または、「T」または「Cross」)。どちらの場合も、LEFT else mazeEnd();に移動します。壊す;ケースRIGHT_TURN:runExtraInch(); readLFSsensors(); if(mode ==NO_LINE){goAndTurn(RIGHT、90); recIntersection( 'R');} else recIntersection( 'S');壊す;ケースLEFT_TURN:goAndTurn(LEFT、90); recIntersection( 'L');壊す;ケースFOLLOWING_LINE:followingLine();壊す; }}}
コードスニペット#13 プレーンテキスト
void recIntersection(char direction){path [pathLength] =direction; //交差点をパス変数に格納します。 pathLength ++; simplePath(); //学習したパスを簡略化します。}
コードスニペット#14 プレーンテキスト
void simplePath(){//最後から2番目のターンが 'B'の場合にのみパスを単純化しますif(pathLength <3 || path [pathLength-2]!='B')return; int totalAngle =0; int i; for(i =1; i <=3; i ++){switch(path [pathLength-i]){case'R ':totalAngle + =90;壊す;ケース 'L':totalAngle + =270;壊す;ケース 'B':totalAngle + =180;壊す; }} //角度を0〜360度の数値として取得します。 totalAngle =totalAngle%360; //これらのターンをすべて1つに置き換えます。 switch(totalAngle){ケース0:path [pathLength-3] ='S';壊す;ケース90:path [pathLength-3] ='R';壊す;ケース180:path [pathLength-3] ='B';壊す;ケース270:path [pathLength-3] ='L';壊す; } //パスが2ステップ短くなりました。 pathLength- =2; }
コードスニペット#15 プレーンテキスト
void loop(){ledBlink(1); readLFSsensors(); mazeSolve(); //迷路を解くための最初のパスledBlink(2); while(digitalRead(buttonPin){} pathIndex =0; status =0; mazeOptimization(); // 2番目のパス:迷路をできるだけ速く実行するledBlink(3); while(digitalRead(buttonPin){} mode =STOPPED; status =0; //最初のパスpathIndex =0; pathLength =0;}
コードスニペット#16 プレーンテキスト
void mazeOptimization(void){while(!status){readLFSsensors();スイッチ(モード){ケースFOLLOWING_LINE:followingLine();壊す; case CONT_LINE:if(pathIndex> =pathLength)mazeEnd(); else {mazeTurn(path [pathIndex]); pathIndex ++;} break; case LEFT_TURN:if(pathIndex> =pathLength)mazeEnd(); else {mazeTurn(path [pathIndex]); pathIndex ++;} break; case RIGHT_TURN:if(pathIndex> =pathLength)mazeEnd(); else {mazeTurn(path [pathIndex]); pathIndex ++;} break; }}}
コードスニペット#17 プレーンテキスト
void mazeTurn(char dir){switch(dir){case'L '://左に曲がるgoAndTurn(LEFT、90);壊す; case'R '://右に曲がるgoAndTurn(RIGHT、90);壊す;ケース 'B'://元に戻すgoAndTurn(RIGHT、800);壊す; case'S '://まっすぐ進むrunExtraInch();壊す; }}
Github
https://github.com/Mjrovai/MJRoBot-Maze-Solverhttps://github.com/Mjrovai/MJRoBot-Maze-Solver 回路図
z7IdLkxL1J66qOtphxqC.fzz 製造プロセス