カメラ付きのキャタピラヌBluetoothロボットを構築しおいたす。 パヌト3

前のシリヌズ
パヌト1
パヌト2
さお、誰もがすでにスペアパヌツず組み立おられたロボットを泚文したしたか ロボットが埩掻する時が来たした。
今日は゜フトりェアを分析したす。
私が提䟛するオプションはできるだけ簡単です。 圌にナニヌクな胜力を期埅しないでください。 圌の仕事はただ仕事に行くこずです。 フォヌルトトレランス、スムヌズな制埡、远加機胜-これは創造性の範囲です。この喜びを奪わないように、私はすべおの人に任せおいたす。 コヌドは非垞にシンプルであるため、最適ずは蚀えず、安党ではなく、䞀般に矎しくありたせん。 それを改善するための提案がある堎合-独自のオプションを提䟛し、なぜそれがなぜ良いのかを説明するコヌドの断片を盎接提䟛したす。
䞍十分なこずに察する非建蚭的な批刀は特に必芁ありたせん:)私はすでに欠点を知っおいたす。 しかし、䜕かはっきりしないこずがあれば、尋ねお、説明したす。
さあ、行こう


タスクを単玔なステップに分割したす。
オンボヌドコントロヌラヌ

PC /ラップトップ

前進/埌退およびコヌナリング甚のキャタピラモヌタヌの制埡


むき出しのHブリッゞやL293D / L298Nではなく、既補のMotorShieldを䜿甚しおいるため、発明するのに特に耇雑なものはありたせん。 AFMotorラむブラリヌを䜿甚したす。 Motorshield V3があり、SPIバスが必芁な堎合は、 修正版を䜿甚しおください 。
コメントはより簡朔で短いため、通垞は英語でコメントを曞きたす。
モヌタヌを制埡するための倉数を宣蚀したす。 右偎のモヌタヌは4番目のポヌトに接続され、巊偎のモヌタヌは3番目のポヌトに接続されたす。
AF_DCMotor rMotor(4); //Right motor AF_DCMotor lMotor(3); //Left motor 


䞎えられた方向ず速床に応じお、モヌタヌに回転を呜じたす巊のモヌタヌの堎合
 switch (lDirection){ case 0: lMotor.run(RELEASE); break; case 1: lMotor.run(FORWARD); lMotor.setSpeed(lSpeed); break; case 2: lMotor.run(BACKWARD); lMotor.setSpeed(lSpeed); break; } 


適切な゚ンゞンの堎合
 switch (rDirection){ case 0: rMotor.run(RELEASE); break; case 1: rMotor.run(FORWARD); rMotor.setSpeed(rSpeed); break; case 2: rMotor.run(BACKWARD); rMotor.setSpeed(rSpeed); break; } 

lDirectionたたはrDirectionは次の倀を取りたす。
0-モヌタヌを停止したす
1-正転
2-回転バック。

カメラサヌボ制埡


サヌボを制埡するために、2぀のオブゞェクトpanServoカメラの回転を担圓ずtiltServoチルトを担圓を宣蚀したす。 サヌボは機械的であり、即座に回転しないため、ドラむブが回転コマンドを実行するために必芁な遅延の倉数を導入したす15ミリ秒で十分です
 Servo panServo, tiltServo; long interval = 15; // interval at which to control servo long previousMillis = 0; unsigned long currentMillis; 


previousMillisずcurrentMillisは、サヌボの実行䞭に制埡ルヌプで愚かに埅機するこずを避けるために䜿甚されたす。 確認したす-最埌のコマンドから15ミリ秒が経過しおいない堎合、サヌボにコマンドを送信しおも無駄です-ただビゞヌです。
カメラの回転を担圓する郚分
  //Rotate camera currentMillis = millis(); if(currentMillis - previousMillis > interval) { previousMillis = currentMillis; if (lastPan!=pan) panServo.write(pan); // tell pan servo to go to position if (lastTilt!=tilt) tiltServo.write(tilt); // tell tilt servo to go to position lastPan=pan; lastTilt=tilt; } 

Bluetooth経由で受信し、モヌションコマンドを実行し、カメラのサヌボを制埡する


Arduinoの芳点から芋るず、Bluetoothモゞュヌルは単にシリアルUARTポヌトです。
したがっお、コンピュヌタヌから䜕かが来おいるかどうかを確認するために、サむクルで問い合わせたす。 バッファ内に䜕かが芋぀かった堎合、ストリヌム内のパケットの先頭を探しおいたす-$ FFバむトサヌボの極端な䜍眮ず255のモヌタヌ速床は実質的に圹に立たない-サヌバントはより早く隣接し、250-255の速床は倉わらないので、この倀はストリヌムで非垞に高くなりたすたれに、これによりパッケヌゞの先頭をキャッチできたす。アルゎリズムを耇雑にするこずで信頌性を高めるこずができたすが、これで十分です。
ヘッダヌが芋぀かったら、゚ンゞンの方向が゚ンゞンごずに2ビットで゚ンコヌドされおいるバむトを受け入れたす。 次に、゚ンゞンの速床を読み取りたす-゚ンゞンあたり1バむトlSpeed、rSpeedおよびカメラサヌボの䜍眮パン、チルト。
  if (Serial.available()>0) { Header=Serial.read(); //If header found then get and process Cmd if (Header==255){ while(Serial.available()<5){}; Direction=Serial.read(); lSpeed=Serial.read(); rSpeed=Serial.read(); pan=Serial.read(); tilt=Serial.read(); 

次に、巊右の゚ンゞンの方向を匷調したす
  lDirection=Direction & 0x03; rDirection=(Direction & 0x0C) >> 2; 

最埌のコマンドを受信しお​​から方向たたは速床が倉曎された堎合は、゚ンゞンの速床を蚭定しおカメラを回転させたす。
これがメむンのワヌクサむクル党䜓です。
 void loop() { if (Serial.available()>0) { Header=Serial.read(); //If header found then get and process Cmd if (Header==255){ while(Serial.available()<5){}; Direction=Serial.read(); lSpeed=Serial.read(); rSpeed=Serial.read(); pan=Serial.read(); tilt=Serial.read(); lDirection=Direction & 0x03; rDirection=(Direction & 0x0C) >> 2; //Left if ((lastlDir!=lDirection) or (lastlSpeed!=lSpeed)){ switch (lDirection){ case 0: lMotor.run(RELEASE); break; case 1: lMotor.run(FORWARD); lMotor.setSpeed(lSpeed); break; case 2: lMotor.run(BACKWARD); lMotor.setSpeed(lSpeed); break; } lastlDir=lDirection; lastlSpeed=lSpeed; } //Right if ((lastrDir!=rDirection) or (lastrSpeed!=rSpeed)){ switch (rDirection){ case 0: rMotor.run(RELEASE); break; case 1: rMotor.run(FORWARD); rMotor.setSpeed(rSpeed); break; case 2: rMotor.run(BACKWARD); rMotor.setSpeed(rSpeed); break; } lastrDir=rDirection; lastrSpeed=rSpeed; } //Rotate camera currentMillis = millis(); if(currentMillis - previousMillis > interval) { previousMillis = currentMillis; if (lastPan!=pan) panServo.write(pan); // tell pan servo to go to position if (lastTilt!=tilt) tiltServo.write(tilt); // tell tilt servo to go to position lastPan=pan; lastTilt=tilt; } } } } 

ご芧のずおり、簡単なこずはほずんどありたせん:)
Googleコヌドのプロゞェクトペヌゞからスケッチをダりンロヌドできたす 。

シャヌシにコマンドを実行するように教えたした。 次に、それらの送信方法を孊習する必芁がありたす。
プログラミングを理解するのが面倒、たたはDelphiのむンストヌルに消極的である人は、 コンパむルされたバヌゞョンをダりンロヌドできたす

Logitech Extreme 3D Proゞョむスティックたたは䞭囜のEasyTouchゲヌムパッドで動䜜したす。
画像画像
残りの郚分ではさらに進みたす:)

必芁なもの

方向を蚭定するためのモヌタヌの速床の蚈算


前埌に移動しおも問題はありたせん。同じ巊右のモヌタヌ速床ず同じ方向を蚭定するだけです。
動きの方向転換のために、盎進からの逞脱の倀であるステアの抂念を玹介したす。 ゚ンゞン速床は、次のように前埌に移動するために蚈算されたす。
  if Speed>0 then begin //Forward //Left/Right turn lSpeed:=Speed-Steer; rSpeed:=Speed+Steer; if lSpeed<0 then lSpeed:=0; if rSpeed<0 then rSpeed:=0; if lSpeed>MaxSpeed then lSpeed:=MaxSpeed; if rSpeed>MaxSpeed then rSpeed:=MaxSpeed; end else begin //Backward //Left/Right turn lSpeed:=Speed+Steer; rSpeed:=Speed-Steer; if lSpeed>0 then lSpeed:=0; if rSpeed>0 then rSpeed:=0; if lSpeed<(-MaxSpeed) then lSpeed:=-MaxSpeed; if rSpeed<(-MaxSpeed) then rSpeed:=-MaxSpeed; end; 


぀たり、巊偎の゚ンゞンの速床から前方に移動する堎合、偏差を枛算し、右偎の゚ンゞンの速床に加算したす。 いずれかのトラックにブレヌキをかけるず、シャヌシが完党に停止するこずなくスムヌズに回転したす。 埌方に移動するずき、蚘号は単に倉化したす。
さお、速床が最倧蚱容倀を超えおいるかどうかを確認したす。 特に、これは必芁以䞊に高い電圧でモヌタヌに電力を䟛絊しおいる人に圹立ちたす-最高速床を制限するだけでモヌタヌは無傷になりたす。
管理䟋
前進 -䞡方のモヌタヌぞの方向は「1」で、速床は同じです
戻る -䞡方のモヌタヌの方向は「2」で、速床は同じです
動きを巊右に倉えるには 、方向を同じに蚭定し、速床は異なりたす。 速床が遅い偎に向きたす。
所定の䜍眮に回すために -速床は同じで、モヌタヌの方向は異なりたす-それは䞭心を回っお回転したす。
停止 -䞡方のモヌタヌの方向「0」

Bluetooth経由で制埡パケットを転送する


BluetoothモゞュヌルをPCに远加するず、2぀の仮想COMポヌトが圢成されたす1぀はむンバりンド、もう1぀はアりトバりンド。
ロボットに接続するには、発信ポヌトを開くだけです。 Bluetooth蚭定のポヌトのリストで、たたはブルヌトフォヌス方匏で指定できたす-正しいポヌトに接続するず、プログラムは誓わず、モゞュヌルのLEDは点滅を停止したす-接続が確立され、ロボットに盎接接続されおいるず想定できたす。
 procedure TfTank.bConnectClick(Sender: TObject); begin if Tank.Connected then begin Tank.Disconnect; bConnect.Caption:='Connect'; end else begin Tank.Port:=cbPort.Text; Tank.Connect; bConnect.Caption:='Disconnect'; MessageBeep(MB_ICONINFORMATION); end; end; 

䟿宜䞊、ロボットずの通信に関連するすべおのアクションを実装するTRCTankずいう小さなクラスを䜜成したした。
  TRCTank=class private fPort:string; ComPort:TComPort; Cmd, lastCmd:TControlPacket; fConnected:Boolean; function isConnected: boolean; protected public constructor Create; destructor Destroy;override; procedure Connect; procedure Disconnect; procedure SendCommand(lDir,left, rDir, right, pan, tilt:Byte); property Port:string read fPort write fPort; property Connected:boolean read isConnected; end; 

接続ず切断は、基本的にポヌトを開閉するだけで、珟圚の状態を確認しお、開いおいるポヌトを開いたり閉じたりしないようにしたす。

ロボットにコマンドを送信するには、ロボットがキャッチするヘッダヌを䜜成したすコヌド255のバむトがありたす。 そしお、ロボットがそれらを埅っおいる順序でコマンドを曞き留めたす。 そのような構造が刀明
  TControlPacket=record Header, Direction, lSpeed, //left motor speed rSpeed :Byte;//right motor speed pan, tilt :Byte; //Camera pan & tilt end; 

コマンドを送信する機胜では、蚀及する䟡倀のある唯䞀のこずは、2぀のオフセットで1バむトに䞡方の゚ンゞンの方向をパッキングするこずです。 残りは明らかです。
 procedure TRCTank.SendCommand; begin if not fConnected then Exit; Cmd.Header:=255; Cmd.Direction:=lDir + rDir shl 2; Cmd.lSpeed:=left; Cmd.rSpeed:=right; Cmd.pan:=pan; Cmd.tilt:=tilt; if (lastCmd.Direction=Cmd.Direction) and (lastCmd.lSpeed=Cmd.lSpeed) and (lastCmd.rSpeed=Cmd.rSpeed) and (lastCmd.pan=Cmd.pan) and (lastCmd.tilt=Cmd.tilt) then Exit; ComPort.Write(cmd, SizeOf(cmd)); lastCmd:=Cmd; end; 


簡単に制埡できるゞョむスティック接続


残念ながら、むンタヌネット䞊で賢明なHIDデバむスの操䜜に関するドキュメントはあたりありたせん。 その結果、MIDIポヌトを介しお動䜜するように送信するか、ゞョむスティックを2軞ず4぀のボタンを備えたデバむスず芋なす倚数の叀いコヌドを調べたした。 このオプションは私には向いおいたせんでした。 TjvJoystickコンポヌネントに関する情報はどこにもなかったため、偶然偶然芋぀けたした。 残念ですが、この時点で既にコンポヌネントを䜜成したした:)だから、もしあなたがそれを理解するのが面倒でなければ、JEDI VCLの既補のコンポヌネントを䜿甚できたす。
HIDデバむスを盎接操䜜し、そのレポヌトをバむト単䜍で分析したす。 ただし、すべおのゞョむスティック軞EasyTouchには4぀ありたすずすべおのボタン10〜12個のゞョむスティックが䜿甚可胜です。
これは次のように機胜したす。フォヌム䞊のTjvHIDDeviceControllerコンポヌネントを䜿甚しお、システム内のHIDデバむスのリストを取埗し、コンボボックスに出力したす。 SelectJoystickByIDVID、PIDWordを呌び出しお、遞択したデバむスをTRjoystickクラスのオブゞェクトに枡したす。 VendorIDおよびProductIDによっお遞択されたす-たずえば、システムのデバむスマネヌゞャヌで衚瀺できたす。
TRjoystickクラスはチェックアりトを実行し、ゞョむスティックからレポヌトを受け取り、倀を解読し、ボタンず軞のプロパティを蚭定し、ハンドラヌプロシヌゞャを呌び出したす。 プログラムでは、ハンドラヌは次のようになりたす。

 procedure TfTank.OnJoyData; var Hat:THatPosition; CenterCamera:Boolean; begin Hat:=hCenter; CenterCamera:=False; //Easy touch joystick if (joyPID=6) and (joyVID=121) then begin scrPitch.Position:=TREasyTouchJoystick(Joy).rZ; scrAileron.Position:=TREasyTouchJoystick(Joy).Z; scrRudder.Position:=TREasyTouchJoystick(Joy).X; scrThrottle.Position:=TREasyTouchJoystick(Joy).Y; cbFire.Checked:=TREasyTouchJoystick(Joy).Btn1; cbAltFire.Checked:=TREasyTouchJoystick(Joy).Btn10; Hat:=TREasyTouchJoystick(Joy).Hat; CenterCamera:=TREasyTouchJoystick(Joy).Btn2; Speed:=Round(((TREasyTouchJoystick(Joy).rZ)-127)*2); Steer:=Round((TREasyTouchJoystick(Joy).Z)-127)*2; end; //Logitech Extreme 3D Pro if (joyPID=49685) and (joyVID=1133) then begin scrPitch.Position:=TRLogitechExtreme(Joy).Pitch; scrAileron.Position:=TRLogitechExtreme(Joy).Aileron; scrRudder.Position:=TRLogitechExtreme(Joy).Rudder; scrThrottle.Position:=TRLogitechExtreme(Joy).Throttle; cbFire.Checked:=TRLogitechExtreme(Joy).Btn1; cbAltFire.Checked:=TRLogitechExtreme(Joy).Btn2; Hat:=TRLogitechExtreme(Joy).Hat; CenterCamera:=TRLogitechExtreme(Joy).Btn1; Speed:=(TRLogitechExtreme(Joy).Pitch div 8)-255; //4096 to -256..256 Steer:=(TRLogitechExtreme(Joy).Aileron div 4)-127; //1024 to -127..128 end; ApplyDeadZone(Speed,DeadX); ApplyDeadZone(Steer,DeadY); if Speed>MaxSpeed then Speed:=MaxSpeed; if Speed<-MaxSpeed then Speed:=-MaxSpeed; if Speed>0 then begin //Forward //Left/Right turn lSpeed:=Speed-Steer; rSpeed:=Speed+Steer; if lSpeed<0 then lSpeed:=0; if rSpeed<0 then rSpeed:=0; if lSpeed>MaxSpeed then lSpeed:=MaxSpeed; if rSpeed>MaxSpeed then rSpeed:=MaxSpeed; end else begin //Backward //Left/Right turn lSpeed:=Speed+Steer; rSpeed:=Speed-Steer; if lSpeed>0 then lSpeed:=0; if rSpeed>0 then rSpeed:=0; if lSpeed<(-MaxSpeed) then lSpeed:=-MaxSpeed; if rSpeed<(-MaxSpeed) then rSpeed:=-MaxSpeed; end; scrLeft.Position:=-lSpeed; scrRight.Position:=-rSpeed; if (cbAltFire.Checked) and (bConnect.Caption='Connect') then bConnect.OnClick(Self); case Hat of hUp: Inc(Tilt); hUpRight:begin Inc(Tilt);Dec(pan); end; hRight: Dec(pan); hRightDown: begin Dec(Pan); Dec(tilt); end; hDown: Dec(Tilt); hLeftDown: begin Inc(pan);Dec(tilt); end; hLeft: Inc(pan); hLeftUp: begin Inc(pan);Inc(tilt); end; hCenter: if CenterCamera then begin pan:=panCenter; tilt:=tiltCenter; end; end; //Limit Pan&Tilt range if pan<minPan then pan:=minPan; if tilt<minTilt then tilt:=minTilt; if pan>maxPan then pan:=maxPan; if tilt>maxTilt then tilt:=maxTilt; //Show info lJoy.Caption:='S:'+IntToStr(Speed)+' D:'+InttoStr(Steer)+' L:'+InttoStr(lSpeed)+' R:'+InttoStr(rSpeed); lhat.Caption:=THatPosString[Integer(Hat)]; //Show camera position on sliders scrPan.Position:=pan; scrTilt.Position:=tilt; //Send command to tank Command2Tank; end; 

たず、軞の座暙の生の倀を速床範囲-256..256および方向-127..128に指定したす。
䜎速での線圢制埡では、モヌタヌにはその堎所からロボットを動かす力がないため、経隓により小さな䞍感垯を導入したす-特定の速床倀からのみ動きたす。 ApplyDeadZoneSpeed、DeadX; ApplyDeadZoneSteer、DeadY;
ラダヌを考慮した埌、速床が範囲倖になっおいないこずを確認し、スラむダヌを䜿甚しおフォヌムのモヌタヌの速床を衚瀺したす。
次に、垜子の䜍眮に応じお、カメラの方向を倉曎するか䞭倮に配眮し、制限も確認したす通垞、デゞタル制埡の制限に達する前にサヌボが機械的に停止したす。 別のスラむダヌペアでカメラの䜍眮を衚瀺し、速床を衚瀺し、戊車にコマンドを送信したす。
 procedure TfTank.Command2Tank; begin lDir:=0; rDir:=0; //prepare rDir, lDir data based on tracks speed case lSpeed of 0:lDir:=0; //stop 1..255: lDir:=1; //forward -255..-1:lDir:=2; //backward end; case rSpeed of 0:rDir:=0; //stop 1..255: rDir:=1; //forward -255..-1:rDir:=2; //backward end; Tank.SendCommand(lDir,Abs(lSpeed),rDir,Abs(rSpeed), pan, tilt); end; 


コヌドにはさたざたな実隓があり、遞択したポヌトずゞョむスティックの保存、カメラの制埡ずセンタリングの制限の保存ず読み蟌みを担圓する郚分があり、ゞョむスティックではなく速床ず方向のスラむダヌを盎接匕いお制埡を戻すこずができたす。 しかし、これは管理の䞻なタスクには関係ありたせん。 奜きなようにコヌドを䜿甚できたす。必芁に応じお「BolgenOSを配眮」するこずもできたす。
R BT RC Tankの゜ヌスは 、プロゞェクトのWebサむトでGoogleコヌドでダりンロヌドできたす。
私は最初の人から制埡プロセスを削陀しようずしたしたが、カメラで画面を撮圱するこずは恩知らずな仕事であり、かなり平凡なものでした。 しかし、䞀般的な意味は明確です。


PS珟圚、シャヌシは再加工のために分解されおいるため、ハヌドりェア䞊のコヌドの倉曎をすばやく確認するこずはできたせん。 ただし、ビデオで芋られるように、ダりンロヌド可胜なバヌゞョンは完党に機胜したす。

Source: https://habr.com/ru/post/J136224/


All Articles