ラインフォロワーロボット-PID制御-Androidセットアップ
コンポーネントと消耗品
> |
| × | 1 | |||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 2 | ||||
| × | 2 |
アプリとオンラインサービス
> |
| |||
|
このプロジェクトについて
このプロジェクトの目的は、PID制御を備えたラインフォロワーロボットを構築することです。また、Androidデバイスを使用して、メインの制御パラメーターを簡単にセットアップし、より適切で高速なチューニングを実現します。
このプロジェクトは、ラインフォロワーロボットの可能性を探求することを目的とした、2つの部分からなるより複雑なプロジェクトの最初のものです。第2部:Arduinoで人工知能を使用する迷路ソルバーロボットでは、ロボットは単純な人工知能技術を使用して迷路を探索して解決します。
以下は、ライン回路をたどるロボットを示すビデオです:
ステップ1:部品表
必要な材料のリストは非常に単純で、最終的なロボットは非常に安価です(約$ 75.00):
本体(ニーズに合わせて調整可能):
- 2つの木製の正方形(80X80mm)
- 3つのバインダークリップ
- 2つの木製ホイール(直径:50mm)
- 1ボールキャスター
- 9つのゴムバンド
- 3Mコマンドフレームストリップ
- センサー修正用のプラスチックジョイント
- ブレッドボードと配線
- 2セットの4XNi-Metal Hydrideバッテリー(各セット5V)
- 2 XSM-S4303R連続回転360度プラスチックサーボ
- Arduino Nano
- HC-06Bluetoothモジュール
- 5つのXラインセンサー(TCRT5000 4CH赤外線ライントラックフォロワーセンサーモジュール+1つの独立したトラックセンサー)
- 1つのLED
- 1ボタン
ステップ2:モーターを設定する <図> <図> <図> <図>
モーターには、2つの連続サーボ(SM-S4303R)を使用しました。写真でわかるように、それらは「接着」されて単一の固体ブロックになります(3Mコマンドストリップ、接着剤、または両面テープを使用してください)。これらのサーボは、データ入力で受信したパルス幅によって定義される特定の速度で動作します。この特定のサーボの場合、パルス幅は1.0ms(1,000マイクロ秒)から2.0ms(2,000マイクロ秒)になります。他のサーボは、異なるパルス幅で動作する可能性があります。
詳細を見る:
- 1.5msのパルスは、サーボをニュートラル位置、つまり「停止」させます。
- 1.0msのパルスは、サーボを一方向にフルスピード(約70 RPM)に命令します
- 反対方向に全速力2.0msのパルス。
- 1.0〜1.5msまたは1.5ms〜2.0msのパルスで、比例した速度が生成されます。
両方のサーボを物理的に接続したら、上記の描画回路に従ってそれらを供給し(外部5Vまたは6V)、Arduinoの信号を供給します:
- 左サーボ:Arduinoピン5
- 右サーボ:Arduinoピン3
すべて接続したら、最初に行う必要があるのは、1.5msのパルスを送信して、モーターが「停止」(動作していない)かどうかを確認することです。そうでない場合は、サーボを完全に停止するように調整する必要があります(黄色のボルトを探し、サーボの下にあります)。
注 :サーボにこの物理的調整がない場合は、完全に停止するまで、関数内のパラメーター「1500」マイクロ秒を変更してみてください(上または下)。
以下のArduinoコードはその仕事をすることができます:
#include //サーボライブラリServoleftServo; Servo rightServo; Void setup(){leftServo.attach(5); rightServo.attach(3); leftServo.writeMicroseconds(1500); rightServo.writeMicroseconds(1500);} void loop(){}
ステップ3:動作テストのためにボディとモーターを組み立てます <図> <図> <図> <図> <図> <図> <図>
- 3Mコマンドフレームストリップを使用して、2つのサーボを四角い木片の1つに固定します。
- バインダークリップを使用して、2番目の四角い木を上の木に固定します。プラットフォームの長さを必要に応じて調整します。
- バインダークリップを使用してボールキャスターを固定します。
- モーターの電源は、5Vセットのバッテリーの1つから供給されます。このバッテリーセットは、ブレッドボードとボディフレームの間に取り付けられます。
- サーボで使用するバッテリーを接続します。サーボソース専用の横方向の電力グリッドの1つを残します
- ArduinoNanoをブレッドボードに接続します
- 電力グリッドのGNDをArduinoGNDに接続します。
- サーボをArduinoに接続します:LEFT ==>ピン5;右==>ピン3
- LEDをArduinoピン13に接続します
- ボタンをArduinoピン9に接続します
サーボが(反対に)取り付けられているため、速度範囲は次のようになります。
- 右サーボの前進速度は1,500us(停止)から2,000us(フルスピード)になります
- 左サーボの前進速度は1,500us(停止)から1,000(フルスピード)になります
信号化とテストの目的で、外部LEDがピン13に追加されます(必要に応じて外部LEDの代わりに内部Arduino LEDを使用できますが、ケーブルの真ん中で見えにくいことを考慮してください)。
また、ボタンはピン9に接続されています。このボタンは、テスト目的およびロボットの起動に非常に役立ちます。
例:
while(digitalRead(buttonPin)){} motorTurn(LEFT、500); motorTurn(RIGHT、500);
ロボットに左に曲がり、500ミリ秒待ってから右に曲がるように命令する2つの線は、ボタンを押した後にのみ発生することに注意してください(buttonPin =0)。その前に、プログラムは無限ループで停止します。
以下のコードは、完全なモーターテスト(前進、後退、終止符、左折、右折)のベースとして使用できます。必要に応じて、モーターに応じて必要な回転角度の遅延を調整する必要があります(また、モーターのバランスの欠如を補うために、左右のパルス値が少し異なる場合があります。
FDDQRQOIN4TTVY0.ino
ステップ4:Bluetoothモジュール(オプション) <図> <図>
BluetoothモジュールHC-06は、図に示すようにブレッドボードに取り付ける必要があります。 ArduinoライブラリSoftSerialが使用されます。
HC-06ピン接続の下:
- 送信ピン Arduinoへピン10 (処方箋)
- RXピン Arduinoへピン11 (Tx)
- VCC / GND Arduinoへ 5V / GND
ロボットはBluetoothを使用して、または使用せずに動作します。コードは、BTをアクティブにしない場合、デフォルトのパラメーターがロボットによって使用されるパラメーターになるように構築されています。したがって、HC-06モジュールをインストールしない場合でも、コードは正常に機能しますので、ご安心ください。このチュートリアルの最後の部分では、Androidアプリを使用してデータを送信し、ロボットのパラメーターをより適切に調整したり、ロボットを手動モードで移動したりする方法について説明します。たとえば、誰かが競技用のラインフォロワーロボットの使用をさらに検討したい場合に備えて、Bluetoothとアプリの使用はオプションとして残しておきます。
ステップ5:ラインセンサーを追加する <図> <図> <図> <図> <図> <図> <図> <図> <図> <図>
以下のようにケーブルをArduinoピンに接続します:
- センサー0 =12
- センサー1 =18
- センサー2 =17
- センサー3 =16
- センサー4 =19
- 写真に示すように、プラスチックバーに5つのセンサーを固定します
- テスト目的でセンサーにラベルを付けることをお勧めします。センサーの名前は、「0」(左に行くほど)から「4」(右に行くほど)になります。
- ケーブルをフレームの下に通し、ゴムバンドを使用してケーブルを固定します。ホイールやキャスターと混同しないように注意してください。
- 5Vバッテリーの2番目のセットを修正し、ArduinoVinに接続します。
私の場合、4つのセンサーが統合されたモジュールと1つの追加のモジュールを使用します。それらはすべて互換性があります。簡単にするために、図には5つのスタンドアロンセンサーが相互に接続されています。最終的な結果は、両方の構成で同じです。
ステップ6:IRセンサーロジックの実装 <図> <図> <図> <図> <図>
IRセンサーは、個別のIRLEDとIRフォトダイオードで構成されています。 LEDによって放出されたIR光は表面に当たり、反射してIRフォトダイオードに戻ります。次に、フォトダイオードは、表面の反射率レベルに比例した出力電圧を生成します(「明るい表面」の場合は高い値、「黒/暗い表面」の場合は低い値)。
使用するセンサーの場合、モジュールの集積回路は、出力として単純なデジタル信号(HIGH:暗い、LOW:明るい)を生成します。モジュールに取り付けられたポテンショメータ(写真を参照)は、「暗い」または「明るい」と見なされるように正しいレベルの光を調整します。反射光の色が黒/暗い場合、出力でHIGH( "1")デジタルレベルが生成され、別の明るい色ではLOW( "0")が生成されるように機能します。ここでは、4つのセンサーを備えた統合モジュールと、唯一のセンサーを備えた追加のモジュールを使用しました(形状は異なりますが、ロジックは同じです)。この組み合わせは、以下で説明するように、5つのセンサーの配列であり、スムーズな制御に適していることがわかりました。
5つのセンサーのアレイは、1つのセンサーのみが黒い線に対して中央に配置されている場合、その特定のセンサーのみがHIGHを生成するように取り付けられています。反対側では、2つのセンサーが同時に黒い線の全幅をカバーし、両方のセンサーでHIGHを生成できるように、センサー間のスペースを計算する必要があります(上の写真を参照)。
線をたどるときに出力される可能性のあるセンサーアレイは次のとおりです。
- 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
5つのセンサーがあると、以下に示すように、ライン上のロボットの位置を制御するのに役立つ「エラー変数」の生成が可能になります。
ロボットが中央に配置され、「中央のセンサー」(センサー2)のすぐ下に線がある場合が最適な状態であると考えてみましょう。配列の出力は00 1 0 0になり、この状況では「エラー」は「ゼロ」になります。ロボットが左に移動し始めた場合(線が「右に移動したように見える」)、エラーは正の信号で増加する必要があります。ロボットが右に移動し始めた場合(線が「左に移動したように見える」)、同様に、エラーは増加する必要がありますが、負の信号になります。
センサーのステータスに関連するエラー変数は次のようになります。
0 0 1 0 0 ==>エラー=0
- 0 0 0 0 1 ==>エラー=4
- 0 0 0 1 1 ==>エラー=3
- 0 0 0 1 0 ==>エラー=2
- 0 0 1 1 0 ==>エラー=1
- 0 1 1 0 0 ==>エラー=-1
- 0 1 0 0 0 ==>エラー=-2
- 1 1 0 0 0 ==>エラー=-3
- 1 0 0 0 0 ==>エラー=-4
Arduinoコードを見ると、各センサーは特定の名前で定義されます(左側のラインフォローセンサーにはラベル「0」を割り当てる必要があることを考慮してください):
const int lineFollowSensor0 =12; const int lineFollowSensor1 =18; const int lineFollowSensor2 =17; const int lineFollowSensor3 =16; const int lineFollowSensor4 =19;
各センサーの値を保存するために、配列変数が作成されます:
int LFSensor [5] ={0、0、0、0、0};
アレイの各位置は、各センサーの出力で常に更新されます:
LFSensor [0] =digitalRead(lineFollowSensor0); LFSensor [1] =digitalRead(lineFollowSensor1); LFSensor [2] =digitalRead(lineFollowSensor2); LFSensor [3] =digitalRead(lineFollowSensor3); LFSensor [4] =digitalRead(lineFollowSensor4);
各センサーの値を使用して、エラー変数を生成するロジックを実装する必要があります。
if((LFSensor [0] ==0)&&(LFSensor [1] ==0)&&(LFSensor [2] ==0)&&(LFSensor [3] ==0)&&(LFSensor [4] ==1))error =4; else if((LFSensor [0] ==0)&&(LFSensor [1] ==0)&&(LFSensor [2] ==0)&&(LFSensor [3] ==1)&&(LFSensor [4] ==1))エラー=3; else if((LFSensor [0] ==0)&&(LFSensor [1] ==0)&&(LFSensor [2] ==0)&&(LFSensor [3] ==1)&&(LFSensor [4] ==0))error =2; else if((LFSensor [0] ==0)&&(LFSensor [1] ==0)&&(LFSensor [2] ==1)&&(LFSensor [3] ==1)&&(LFSensor [4] ==0))error =1; else if((LFSensor [0] ==0)&&(LFSensor [1] ==0)&&(LFSensor [2] ==1)&&(LFSensor [ 3] ==0)&&(LFSensor [4] ==0))エラー=0; else if((LFSensor [0] ==0)&&(LFSensor [1] ==1)&&(LFSensor [2] ==1)&&(LFSensor [3] ==0)&&(LFSensor [4] ==0))エラー=-1; else if((LFSensor [0] ==0)&&(LFSensor [1] ==1 )&&(LFSensor [2] ==0)&&(LFSensor [3] ==0)&&(LFSensor [4] ==0))error =-2; else if((LFSensor [0] ==1)&&(LFSensor [1] ==1)&&(LFSensor [2] ==0)&&(LFSensor [3] ==0)&&(LFSensor [4] ==0))error =-3; else if((LFSensor [0] ==1)&&(LFSensor [1] ==0)&&(LFSensor [2] ==0)&&(LFSensor [3] ==0)&&(LFSensor [4] ==0))エラー=-4;
ステップ7:方向を制御する(比例制御-P) <図>
完全!この時点で、ロボットは組み立てられ、動作しています。モーターを使用していくつかの基本的なテストを実行し、センサーの出力を読み取り、ライン上でテストする必要があります。欠けているのは、「人工知能」の最初のステップである本当の「頭脳」です。これを取得し、ロボットがラインを追跡し続けることを保証する制御ロジックを実装します。
単純な比例制御:
ロボットがライン上を実行していて、センサーアレイの出力が次のようになっているとします。 "0 0 1 0 0" 。対応するエラーは「0」です。この状況では、両方のモーターが一定の速度で前進している必要があります。
例:
変数の定義: iniMotorSpeed =250 ;これは、LEFTサーボが1,250usのパルスを受信し、RIGHTサーボが1,750usのパルスを受信することを意味します。これらのパラメータを使用すると、ロボットは半分の速度で前進します。右サーボの前進速度は、パルス長が1,500us(停止)から2,000us(全速)、左サーボが1,500us(停止)から1,000us(全速)の範囲であることに注意してください。
rightServo.writeMicroseconds(1500 + iniMotorPower); leftServo.writeMicroseconds(1500-iniMotorPower);
ここで、ロボットが左に駆動され(「LINEが右に移動」のように)、センサー3もカバーしたとします。アレイの出力は次のようになります。 "0 0 1 1 0" および エラー=1 。この状況で必要なのは、ロボットを右に向けることです。そのためには、RIGHTサーボの速度を下げる必要があります。つまり、パルスの長さを短くする必要があります。また、LEFTサーボの速度を上げる必要があります。つまり、LEFTサーボパルスの長さを短くする必要があります。そのためには、モーターコントロール機能を変更する必要があります:
rightServo.writeMicroseconds(1500 + iniMotorPower-エラー); ==>正のエラー:velocityleftServo.writeMicroseconds(1500-iniMotorPower-エラー);を減らします。 ==>正のエラー:速度を上げる
上記のロジックは正しいですが、パルス長で「1」マイクロ秒を加算または減算しても、現実的な時間で必要な補正が生成されないことは容易に理解できます。加算または減算する数値は、たとえば50、100などより大きくする必要があることは直感的です。これを取得するには、「エラー」に定数を掛ける必要があります(これを「K」と呼びます)。この定数の影響が誤差に比例するようになったら、「比例定数:Kp」という名前を付けます。 。
モーター機能は次のようになります:
int Kp =50; rightServo.writeMicroseconds(1500 + iniMotorPower-Kp * error); leftServo.writeMicroseconds(1500-iniMotorPower-Kp * error);
以下に示すように、モーターで何が起こるかを再開できます。
- センサーアレイ: 0 0 1 0 0 ==> エラー=0 ==>右サーボパルス長=1,750us ==>左サーボパルス長=1,250us(両方のモーターが同じ速度)
- センサーアレイ: 0 0 1 1 0 ==> エラー=1 ==>右サーボパルス長=1,700us(遅い)==>左サーボパルス長=1,200us(速い)
状況が逆で、ロボットが右に駆動されている場合、エラーは「負」になり、サーボの速度が変化するはずです。
- センサーアレイ: 0 0 1 0 0 ==> エラー=0 ==>右サーボパルス長=1,750us ==>左サーボパルス長=1,250us(両方のモーターが同じ速度)
- センサーアレイ: 0 1 1 0 0 ==> エラー=-1 ==>右サーボパルス長=1,800us(速い)==>左サーボパルス長=1,300us(遅い)
この時点で、ロボットが片側に駆動されるほど、エラーが大きくなり、中央に戻る速度が速くなることは明らかです。ロボットの速度はエラーに反応し、それに比例します。これは「比例制御」と呼ばれます 、これは、より複雑な制御ネットワークであるPDI(比例、微分、積分)の「P」コンポーネントです。
ステップ8:PID制御(オプション) <図> <図> <図>
この部分でジャンプしたい場合もOKです。最後のステップで説明した比例制御を維持することも、頭脳を燃やしてロボットにさらに複雑な制御システムを実装することもできます。これはあなたの選択です。
決心したら行こう!
PID(比例、微分、積分)は、最も一般的な制御スキームの1つです。ほとんどの産業用制御ループは、ある種のPID制御を使用します。この例で使用されている手動の手法を含め、PIDループを調整する方法はたくさんあります。
PIDを単純なばねと考えてください。ばねには元の長さがあり、膨張または収縮によって乱されると、可能な限り短い時間で元の長さに戻る傾向があります。同様に、システムのPIDアルゴリズムには、「 set point
」と呼ばれる、制御される特定の物理量の設定値があります。 ’は、何らかの理由で変更された場合、システムがその中の他の必要な機能を制御して、可能な限り短い時間で元の設定値に戻るようにします。 PIDコントローラーは、物理量を制御し、指定された値と等しくする必要がある場合に使用されます。例、クルーズコントローラー 車、ロボット、温度調整器、電圧調整器などで。
PIDはどのように機能しますか?
システムは「 error
」を計算します 」、または「偏差
センサーを使用してその物理量の現在の値を測定することにより、設定値からの物理量の ’。設定値に戻るには、この「 error
’は最小化する必要があり、理想的にはゼロに等しくする必要があります。また、このプロセスは可能な限り迅速に行う必要があります。理想的には、設定値の変更に対するシステムの応答にゼロラグがあるはずです。
詳細については、ここを含む多くの書籍やWebサイトを参照してください。
PIDの実装
i)エラー期間(e):
これは、設定値と制御されている量の現在の値との差に等しくなります。エラー= set_point
– current_value
(この場合、エラー変数は、ライン上のロボットの位置から取得されます
ii)比例期間(P):
この項は誤差に比例します。
P =エラー
この値は、設定値を達成するために物理量に必要な変化の大きさの原因となります。比例項は、制御ループの立ち上がり時間または設定値に到達する速度を決定するものです。
iii)積分項(I):
この項は、以前のすべてのエラー値の合計です。
I =I +エラー
この値は、設定値からの変更に対するシステムの応答の速さを担っています。積分項は、比例項に必要な定常状態誤差を排除するために使用されます。通常、小さなロボットは、定常状態のエラーを気にせず、「ループチューニング
」を複雑にする可能性があるため、積分項を使用しません。 "。
iv)微分または微分項(D):
この項は、設定値からの瞬間誤差と前の瞬間からの誤差の差です。
D =エラー-previousError
この値は、物理量が設定値に近づくと、物理量の変化率を遅くする役割を果たします。微分項は、オーバーシュートまたはシステムが「 overcorrect
」する必要がある量を減らすために使用されます "。
方程式:
PIDvalue =(Kp * P)+(Ki * I)+(Kd * D)
場所:
Kp は、 変化の大きさを変化させるために使用される定数です。 設定値を達成するために必要です。 Ki は、 変化率を変化させるために使用される定数です。 設定値を達成するために物理量を持ち込む必要があります。 Kd 安定性を変化させるために使用される定数です システムの。
ループを調整するための1つのアプローチは、 試行エラーの暫定的な方法です。 :
Kd変数を0に設定 最初にKp項だけを調整します。 25のKp ここから始めるのに適した場所です。最後のステップでは、ロボットで非常にうまく機能する50のKpを使用しました。ロボットが適切に応答したら、制御ループの派生部分を調整します( Kd )。まず、Kp値とKd値をそれぞれKp値の1/2に設定します。たとえば、ロボットがKp =50で妥当な応答をする場合は、Kp =25およびKd =25に設定して開始します。 Kd(微分)ゲインを上げてオーバーシュートを減らし、ロボットが不安定になった場合は下げます。
- ロボットの反応が遅すぎる場合は、値を大きくしてください。
- ロボットの反応が速く不安定になっているように見える場合は、値を小さくしてください。
考慮すべきループのもう1つのコンポーネントは、実際の サンプル/ループレートです。 。このパラメータを速くしたり遅くしたりすると、ロボットのパフォーマンスに大きな違いが生じる可能性があります。これは 遅延によって設定されます コードにあるステートメント。最適な結果を得るのは、試行錯誤の暫定的な方法です。
上記のアプローチに基づいて、以下の機能が実装されました:
voidcalculatePID(){P =エラー; I =I +エラー; D =エラー-previousError; PIDvalue =(Kp * P)+(Ki * I)+(Kd * D); previousError =エラー;}
最後のステップで使用された単純なKp定数は、このより完全な PIDvalue
に置き換えられます。 :
void motorPIDcontrol(){int leftMotorSpeed =1500 --iniMotorPower --PIDvalue; int rightMotorSpeed =1500 + iniMotorPower-PIDvalue; leftServo.writeMicroseconds(leftMotorSpeed); rightServo.writeMicroseconds(rightMotorSpeed);}
ただし、Kdと Ki =0
がある場合は注意してください 、 PIDvalue
最後のステップで使用されたのはKp * errorのみです。
ステップ9:最終的なコード <図> <図>
このステップで、ロボットは一定のループをたどることができ、停止せずにそれを実行します。ループプログラムは次のようになります:
void loop(){readLFSsensors(); //センサーを読み取り、センサーアレイで値を保存し、「エラー」を計算しますcalculatePID(); motorPIDcontrol();}
ただし、より完全で実際の操作を行うには、少なくとも2つの基本的な「コマンド
」を追加することが重要です。 "完了" 行で
"。たとえば、新しい変数" mode
を導入しましょう。 "。この変数には3つの状態を定義します:
モード:
-
#define STOPPED 0
-
#define FOLLOWING_LINE 1
-
#define NO_LINE 2
すべてのセンサーが黒い線を検出した場合、センサーアレイの出力は次のようになります。111 1 1.この状態では、モードを「STOPPED」と定義でき、ロボットは「 full stop
"。
if((LFSensor [0] ==1)&&(LFSensor [1] ==1)&&(LFSensor [2] ==1)&&(LFSensor [3] ==1)&&(LFSensor [4] ==1)){mode =STOPPED;}
Follower Line Robotsのその他の一般的な状況は、「 no line
」が見つかる場合です。 "、またはセンサーアレイの出力は次のとおりです。00000。この場合、ラインが検出されて通常のラインフォロー状態が再開されるまで、180°戻るか小さな角度で回転するようにプログラムできます。
else if((LFSensor [0] ==0)&&(LFSensor [1] ==0)&&(LFSensor [2] ==0)&&(LFSensor [3] ==0)&&( LFSensor [4] ==0)){mode =NO_LINE;}
完全な loop()
次のようになります:
void loop(){readLFSsensors();スイッチ(モード){ケースSTOPPED:motorStop();壊す;ケースNO_LINE:motorStop(); motorTurn(LEFT、180);壊す;ケースFOLLOWING_LINE:calculatePID(); motorPIDcontrol();壊す; }}
実際の最終的なコードは、いくつかの追加のロジックと初期化する必要のあるいくつかの変数などを統合します。上記の説明では、簡単にするためにそれらを省略しましたが、最終的なコードを見るとすべてが明確である必要があります。
以下は最終的なArduinoコードです:
FUXCUEAIN699SZC.ino FLD2J3KIN699SZF.h FOFYXFZIN699SZT.ino FMPX9KJIN699SZU.ino FFE5AZ3IN699T06.ino
ステップ10:Androidアプリを使用してPID制御を調整する <図> <図> <図>
前のコードでは、「 robotDefines.h
」にあります。 "PIDコントロールで使用される定数について、次の定義をタブで移動します。
float Kp =50; float Ki =0; float Kd =0;
前のステップで説明したように、PIDコントローラーで使用される正しい定数を定義する最良の方法は、「試行エラー」手法を使用することです。その悪い面は、プログラムを変更するたびにプログラムを再コンパイルする必要があることです。プロセスをスピードアップする1つの方法は、Androidアプリを使用して、「セットアップフェーズ」で定数を送信することです。 。
そのためだけにAndroidアプリを開発しました。要するに、従来の手動コマンドがあります:
- アプリがBTモジュールに送信するFW、BW、Left、Right、Stop: 'f'、 'b'、 'l'、 'r'、 's'。
また、各PID定数に1つずつ、合計3つのスライダーが含まれています。
- Kd: "d / XXX"ここで、 "XXX"は0〜100の数字です。
- Kp: "p / XXX"
- Ki: "i / XXX"
ArduinoPin9に接続されているボタンとまったく同じように機能する追加のボタンが含まれています。どちらでもかまいません。
以下に .aia
があります。 MIT AppInventor で変更できるファイル および .apk
Androidデバイスに直接インストールするファイル。
ステップ11:PIDリモートチューニングのコードを変更する
セットアップ中に、ロボットをライン上に配置する前にPIDパラメーターをロボットに送信できるループを導入します:
while(digitalRead(buttonPin)&&!mode){checkBTcmd(); //コマンドがBTリモコンから受信されているかどうかを確認しますmanualCmd();コマンド=""; } checkPIDvalues(); mode =STOPPED;
手動コマンド機能は次のようになります:
void manualCmd(){switch(command [0]){case'g ':mode =FOLLOWING_LINE;壊す;ケース 's':motorStop(); //両方のモーターをオフにします。ケース 'f':motorForward();壊す;ケース 'r':motorTurn(RIGHT、30); motorStop();壊す;ケース 'l':motorTurn(LEFT、30); motorStop();壊す;ケース 'b':motorBackward();壊す;ケース 'p':Kp =command [2];壊す;ケース 'i':Ki =command [2];壊す;ケース 'd':Kd =command [2];壊す; }}
ビデオでは、Androidアプリを使用したいくつかのテストを見ることができます。 Androidを介したPID設定を含む最終コードの下:
FGAEB9BIN7QQQAW.ino FBMONSNIN7QQQCD.ino F8B3CDHIN7QQQCL.h FNOMRUNIN7QQQCP.ino FA3K57ZIN7QQQCR.ino
ステップ12:結論 <図>
これは、より複雑なプロジェクトの最初の部分であり、ラインフォロワーロボットの可能性を探ります。次のパートでは、このプロジェクトに基づいて、迷路解決ロボットを開発します。うまくいけば、私は他の人が電子機器、ロボット、Arduinoなどについてもっと学ぶために貢献することができます。
このプロジェクトの更新ファイルはGITHUBにあります。その他のチュートリアルについては、私のブログにアクセスしてください:MJRoBot.org
世界の南からのご挨拶!
ありがとう
マルセロ
コード
- コードスニペット#1
- コードスニペット#2
- コードスニペット#3
- コードスニペット#5
- コードスニペット#6
- コードスニペット#10
- コードスニペット#11
- コードスニペット#12
- コードスニペット#15
- コードスニペット#17
- コードスニペット#18
コードスニペット#1 プレーンテキスト
#include//サーボライブラリServoleftServo; Servo rightServo; Void setup(){leftServo.attach(5); rightServo.attach(3); leftServo.writeMicroseconds(1500); rightServo.writeMicroseconds(1500);} void loop(){}
コードスニペット#2 プレーンテキスト
while(digitalRead(buttonPin)){} motorTurn(LEFT、500); motorTurn(RIGHT、500);
コードスニペット#3 プレーンテキスト
const int lineFollowSensor0 =12; const int lineFollowSensor1 =18; const int lineFollowSensor2 =17; const int lineFollowSensor3 =16; const int lineFollowSensor4 =19;
コードスニペット#5 プレーンテキスト
LFSensor [0] =digitalRead(lineFollowSensor0); LFSensor [1] =digitalRead(lineFollowSensor1); LFSensor [2] =digitalRead(lineFollowSensor2); LFSensor [3] =digitalRead(lineFollowSensor3); LFSensor [4] =digitalRead( lineFollowSensor4);
コードスニペット#6 プレーンテキスト
if((LFSensor [0] ==0)&&(LFSensor [1] ==0)&&(LFSensor [2] ==0)&&(LFSensor [3] ==0)&&(LFSensor [4] ==1))エラー=4; else if((LFSensor [0] ==0)&&(LFSensor [1] ==0)&&(LFSensor [2] ==0)&&(LFSensor [3] ==1 )&&(LFSensor [4] ==1))エラー=3; else if((LFSensor [0] ==0)&&(LFSensor [1] ==0)&&(LFSensor [2] ==0)&&(LFSensor [3] ==1)&&(LFSensor [4] ==0))error =2; else if((LFSensor [0] ==0)&&(LFSensor [1] ==0)&&(LFSensor [2] ==1)&&(LFSensor [3] ==1)&&(LFSensor [4] ==0))error =1; else if((LFSensor [0] ==0)&&(LFSensor [1] ==0)&&(LFSensor [2] ==1)&&(LFSensor [ 3] ==0)&&(LFSensor [4] ==0))エラー=0; else if((LFSensor [0] ==0)&&(LFSensor [1] ==1)&&(LFSensor [2] ==1)&&(LFSensor [3] ==0)&&(LFSensor [4] ==0))エラー=-1; else if((LFSensor [0] ==0)&&(LFSensor [1] ==1 )&&(LFSensor [2] ==0)&&(LFSensor [3] ==0)&&(LFSensor [4] ==0))error =-2; else if((LFSensor [0] ==1)&&(LFSensor [1] ==1)&&(LFSensor [2] ==0)&&(LFSensor [3] ==0)&&(LFSensor [4] ==0))error =-3; else if((LFSensor [0] ==1)&&(LFSensor [1] ==0)&&(LFSensor [2] ==0)&&(LFSensor [3] ==0)&&(LFSensor [4] ==0))エラー=-4;
コードスニペット#10 プレーンテキスト
voidcalculatePID(){P =エラー; I =I +エラー; D =エラー-previousError; PIDvalue =(Kp * P)+(Ki * I)+(Kd * D); previousError =エラー;}
コードスニペット#11 プレーンテキスト
void motorPIDcontrol(){int leftMotorSpeed =1500 --iniMotorPower --PIDvalue; int rightMotorSpeed =1500 + iniMotorPower-PIDvalue; leftServo.writeMicroseconds(leftMotorSpeed); rightServo.writeMicroseconds(rightMotorSpeed);}
コードスニペット#12 プレーンテキスト
void loop(){readLFSsensors(); //センサーを読み取り、センサーアレイで値を保存し、「エラー」を計算しますcalculatePID(); motorPIDcontrol();}
コードスニペット#15 プレーンテキスト
void loop(){readLFSsensors();スイッチ(モード){ケースSTOPPED:motorStop();壊す;ケースNO_LINE:motorStop(); motorTurn(LEFT、180);壊す;ケースFOLLOWING_LINE:calculatePID(); motorPIDcontrol();壊す; }}
コードスニペット#17 プレーンテキスト
while(digitalRead(buttonPin)&&!mode){checkBTcmd(); //コマンドがBTリモコンから受信されているかどうかを確認しますmanualCmd();コマンド=""; } checkPIDvalues(); mode =STOPPED;
コードスニペット#18 プレーンテキスト
void manualCmd(){switch(command [0]){case'g ':mode =FOLLOWING_LINE;壊す;ケース 's':motorStop(); //両方のモーターをオフにします。ケース 'f':motorForward();壊す;ケース 'r':motorTurn(RIGHT、30); motorStop();壊す;ケース 'l':motorTurn(LEFT、30); motorStop();壊す;ケース 'b':motorBackward();壊す;ケース 'p':Kp =command [2];壊す;ケース 'i':Ki =command [2];壊す;ケース 'd':Kd =command [2];壊す; }}
Github
https://github.com/Mjrovai/MJRoBot-Line-Followerhttps://github.com/Mjrovai/MJRoBot-Line-Follower 回路図
oIDhLcHQ30lDgVBXZvb8.fzz 製造プロセス