編集履歴一覧に戻る
ProjectSRのアイコン画像

ProjectSR が 2021年02月28日19時05分18秒 に編集

コメント無し

記事種類の変更

+

製作品

本文の変更

🔳知れる事 --- ◇Aruino同士でSerial通信  →(可変抵抗からの数値を送受信) ◇ワイヤレスで,複数のモータを制御  →パケット設定して,送信側・受信側で通信を行う ◇ジョイスティックを操作しやすくする →不感帯の設定 🔳概要・基本構成 --- ①送信側のArduinoがオペレータのジョイスティックの操作量を読み取ります. ②操作量に応じたモータの回転速度を計算します. ③計算後は,受信側のArduinoへ数値を送ります. ④受信側のArduinoは数値を受け取って,モータへ回転指令を送ります. 🔳解説 --- 基本構成は, ①送信側のArduinoが,オペレータのジョイスティックなどの操作量を読み取って, モータの回転速度に関する数値を計算する. ②計算した後は,スレーブ側のArduinoへ数値を送って,モータの速度を調整しつつロボットが動く. 解説動画 --- ユーチューブ ニコニコ動画 にて 解説もしています. ロボットがどのような動作をされるか見たい方は,ご参照ください. @[youtube](https://youtu.be/7zY-21NXDjU) ### ニコニコ動画 [Arduino XBeeジョイスティックワイヤレスコントローラシステム製作・解説](https://www.nicovideo.jp/watch/sm37291000) 構成 --- 基本構成は, ①送信側のArduinoが,オペレータのジョイスティックなどの操作量を読み取って,  モータの回転速度に関する数値を計算する. ②計算した後は,スレーブ側のArduinoへ数値を送って,モータの速度を調整しつつロボットが動く. ポイント --- ●オペレータに不快感なく,モータを制御するため,送信側のArduinoが操作量を読み取る事 ●複数のモータの回転速度に関係する複数の数値を送受信する事 です. ではArduinoが操作量を読み取るには どうすればよいのか? 疑問に思われるかもしれませんが,よくプレステのコントローラに 搭載されているジョイスティック(アナログスティック)がありますよね? 実は,アナログスティックは電子部品で販売されています. アナログスティックには,可変抵抗という電子部品が搭載されています. ⇓図のように,縦方向・横方向それぞれに操作したときの操作量を読み取るため,可変抵抗と呼ばれる電子部品が2つあります. ![キャプションを入力できます](https://camo.elchika.com/7576f2d1f76a0f15a2557d9b2879813d21b0f4c3/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65636165613738332d303336612d346532652d383632662d3732393461656431353662352f33386132653933372d636363652d343663352d613233622d373534323965623233636237/) 可変抵抗とは,文字通り,抵抗の値を変えることができる電子部品です.こここの製作例では,Arduinoに可変抵抗にかかる電圧を読み取らせます. ![キャプションを入力できます](https://camo.elchika.com/2c83e3a45c5796cb748eef54aef546adad3826dc/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65636165613738332d303336612d346532652d383632662d3732393461656431353662352f30333462383263352d363539332d343438382d623631352d613432383932386563653435/) 1⃣可変抵抗値読み取り→A/D変換→map関数変換 --- 送信側Arduinoが電圧を読み取った後, Arduino内のA/Dコンバータで値を変換し,操作量としてそのまま受信側Arduinoへ送ることも出来ます が 受信側Arduinoは,analogwriteを使い,PWM信号出力を行って,モータの回転速度を調整するほかに, 回転方向の制御も行うため,負担が増え,通信に支障が出る可能性があります. そのため,送信側Arduinoで予め,A/Dコンバータで変換した値から 更に map関数という関数を使って, 数値を変換し,受信側Arduinoへ送信しています. まず可変抵抗の読み取り方とArduinoで値の送受信を抑えて行きます. 練習目的で,可変抵抗からの電圧値取得→A/D変換→map関数変換→Arduino送受信の一連の流れを, 簡単な回路とプログラムを組んでみました. ArduinoのA0(黄色線),GND(黒線),5V(赤線)と可変抵抗を接続して,電圧を読み取ります. 送信側ArduinoのTXと受信側ArduinoのRXを接続(緑線)し,受信側ArduinoとPCを接続すれば回路は完成です. (送信側Arduinoにも給電は必要です.USB,DCジャック付き電池ボックスで給電しましょう.) ![キャプションを入力できます](https://camo.elchika.com/57fe4da8df2c5e70f1e1f983226e744ea8be7c36/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65636165613738332d303336612d346532652d383632662d3732393461656431353662352f64323739656534642d663133362d343439662d623836372d383231373666613034393939/) ```html:□Program送信 const int analogPin = A0; int inputValue = 0; int outputValue = 0; void setup() { Serial.begin(9600); } void loop() { inputValue = analogRead(analogPin); outputValue = map(inputValue,0,1023,0,255); Serial.write(outputValue); delay(100); } ``` ```html:□Program受信 int incomingByte = 0; // 受信データ用 void setup() { Serial.begin(9600); // 9600bpsでシリアルポートを開く } void loop() { if (Serial.available() > 0) { // 受信したデータが存在する incomingByte = Serial.read(); // 受信データを読み込む if(incomingByte >= 0){ Serial.println(incomingByte); delay(100); } } } ``` 2⃣ジョイスティックワイヤレス通信+複数モータ制御 --- この記事のメインテーマですね. 1⃣の方法では,可変抵抗1個の操作量を変換して送受信していましたが, 目的は,複数モータの回転方向と回転速度を制御する事なので,1⃣の処理だけでは足りません. なので,以下の処理を行えるプログラムを組む必要があります. ①配列の準備と送受信 ②パケット設定 ③ジョイスティックの不感帯設定 順に説明します. ### ①配列の設定 配列を用意して,モータの回転方向と回転速度の数値を格納します. ![キャプションを入力できます](https://camo.elchika.com/2d1375faf03d357763474863fe3818c930abf2ea/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65636165613738332d303336612d346532652d383632662d3732393461656431353662352f32633430393935312d326265342d343066662d623562312d363962306533353737326336/) ですが,送信中にノイズが入ると,回転方向tの値が変動して,受信側Arduinoは 回転速度の値なのか判別ができなくなります.このため,モータの指令が適切に行えず, モータは暴走する可能性があります. ![キャプションを入力できます](https://camo.elchika.com/9f84470cb928121d9ecd9e650dd8e311f715f72a/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65636165613738332d303336612d346532652d383632662d3732393461656431353662352f62306364373035612d323533302d343437302d613164302d303662633535633033353333/) そこで,モータの回転方向・速度の数値前後に送受信の開始と終了を知らせる目印となる 数値を用意します.つまり,データ開始+モータ関連数値+データ終了 このデータ1塊のデータを送受信します.開始の数値は255,これを2回 その後モータ関連の数値を送受信して,最後に0を送受信し,次のデータ処理を実施します. ### ②パケット通信 ![キャプションを入力できます](https://camo.elchika.com/8d5e7e3bb01b6ad5caebb1c1b9d2b85934075dab/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65636165613738332d303336612d346532652d383632662d3732393461656431353662352f35373539356361652d343561342d346234352d613361662d316264303838366238323063/) こうすることで,ノイズの影響を受けても.受信側のArduinoは判断を誤る可能性は低くなります. この「データ開始+モータ関連数値+データ終了」1塊のデータの送受信を行い,処理を行う方法を パケット通信といいます. また,今回のモータ回転速度の最大値は254としていて,「データ開始」の数値と重ならないようにしています. ### ③ジョイスティックの不感帯設定 ジョイスティックが中立位置にいると,可変抵抗をArduinoで読み取ると, map関数で0~255の間で変換を行った場合,125の値になります. 送信側Arduinoでこの値を中心に操作領域を設けて,操作量を算出して受信側に送信します. ジョイスティックの垂直位置の値をVDout 垂直位置の値をHDoutとして,モータ回転速度を計算 する式は,各値が125と比較して場合分けを行います VDout>125の場合 (VDout - 125)×2 VDout=<125の場合 (125-VDout)×2 HDout>125の場合 (HDout -125)×2 HDout =< 125の場合 (125-HDout)×2 ![キャプションを入力できます](https://camo.elchika.com/8b7f39780920ee2fb372c4e5317708b3131ad465/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65636165613738332d303336612d346532652d383632662d3732393461656431353662352f64306530343062322d306261322d346462322d613962312d343039343563656538616132/) ですが,例えばオペレータがジョイスティックに指を添えるつもりでスティックに触れ, 中立位置から少しでも動かすと,その分受信側は動作指令が届いたと判断して モータ回転指令が出ます. そこで,左図のように,不感帯という,ある程度スティックが動いても, モータ回転指令が出ないように,ジョイスティックの領域を設けます. 今回は垂直 水平にそれぞれ140,110を不感帯領域としています(左図の赤枠の部分) ![キャプションを入力できます](https://camo.elchika.com/6d8f00d14875b78dee72327205ecbd6fa0367e76/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65636165613738332d303336612d346532652d383632662d3732393461656431353662352f61373234313566362d323833372d346665302d386634342d386430313962356161363361/) スティックが不感帯領域にいる場合は, 送信側Arduinoでは,モータの回転方向の配列に2を格納して受信側へ送信. 受信側は2を受信したら,モータの回転速度は0にするようにプログラムを作成しております. ![キャプションを入力できます](https://camo.elchika.com/f74a976419dc1ae786065eeac19a7ed2d1c2367a/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65636165613738332d303336612d346532652d383632662d3732393461656431353662352f36313034313266312d396161302d346631302d396565372d383066373732333736383862/) 超信地旋回操作領域(緑枠)にスティックがある場合は,モータ回転方向は互い違いになるように, 送信側,受信側Arduinoでは1・0 or 0・1の組み合わせで処理するようにプログラムをしております. ![キャプションを入力できます](https://camo.elchika.com/5d866a5bc73e851e13720af702fdbba3925fa3db/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65636165613738332d303336612d346532652d383632662d3732393461656431353662352f64633263636334392d633939632d346463642d626661612d346539323032393763613330/) ### 制御フローチャート 送信側,受信側の制御フローチャートを記載します. #### 送信側 ![キャプションを入力できます](https://camo.elchika.com/55f3b9e9db42d542178b7bdb39bcb64b6d198355/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65636165613738332d303336612d346532652d383632662d3732393461656431353662352f37343430666661312d636333342d343165652d386464352d336337343939633730616431/) #### 受信側 ![キャプションを入力できます](https://camo.elchika.com/8f6d2de596d81368d6f3b7c92e2c76c0a47ed1ab/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65636165613738332d303336612d346532652d383632662d3732393461656431353662352f30363730306536342d306331342d346361652d613561352d353033663932336337636535/) ### 電子回路 送信側,受信側の電子回路を記載します. Arduinoと電池ボックスの間にはDCジャックを半田付け モータドライバーとDCモータ,電池ボックス間にはArduino用基板を用いたうえで,端子を半田付けして 接続しています ###送信側 ![キャプションを入力できます](https://camo.elchika.com/33f5bf890967b48b17bc4a5497b1aadf30d5640a/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65636165613738332d303336612d346532652d383632662d3732393461656431353662352f63613562616435352d633238622d343639322d386636612d376638366139613832613037/) ### 受信側 ![キャプションを入力できます](https://camo.elchika.com/5cc8091649d7acdb229d82b39ca801f8471fc499/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f65636165613738332d303336612d346532652d383632662d3732393461656431353662352f66613639613031342d336137352d343238352d393565332d316263373261386665323033/) ### プログラム #### 送信側 ```html int SendBuf[4]; int i; //const int analogPin(0) = A0; //const int analogPin(1) = A1; int HDatain,VDatain,HDataout,VDataout; uint8_t DirR; uint8_t DirL; uint8_t Duty,Duty1,Duty2; void setup() { // put your setup code here, to run once: Serial.begin(9600); } void loop() { // put your main code here, to run repeatedly: VDatain = analogRead(A0); VDataout = map(VDatain,0,1023,0,254); HDatain = analogRead(A1); HDataout = map(HDatain,0,1023,0,254); //前進後退 if(VDataout>140){ if(HDataout>140){ DirL = 0; DirR = 0; Duty1 =VDataout; Duty2 =HDataout; } else if(140>=HDataout && 110<HDataout){ DirL = 0; DirR = 0; Duty1 =VDataout; Duty2 =VDataout; } else{ DirL = 0; DirR = 0; Duty1 =(120-HDataout)*2; Duty2 =VDataout; } } else if(VDataout<=140 && VDataout>=125){ if(HDataout>140){ DirL = 1; DirR = 0; Duty1 =HDataout; Duty2 =HDataout; } else if(HDataout<110){ DirL = 0; DirR = 1; Duty1 =(120-HDataout)*2; Duty2 =(120-HDataout)*2; } else{ DirL = 2; DirR = 2; Duty1 =0; Duty2 =0 } } else if(125>VDataout && VDataout>=110) { if(HDataout>140){ DirL = 1; DirR = 0; Duty1 =(HDataout-140)*2; Duty2 =(HDataout-140)*2; } else if(HDataout<110){ DirL = 0; DirR = 1; Duty1 =(110-HDataout)*2; Duty2 =(110-HDataout)*2; } else{ DirL = 2; DirR = 2; Duty1 =0; Duty2 =0; } } else{ if(HDataout>140){ DirL = 1; DirR = 1; Duty1 =(120-HDataout)*2; Duty2 =(120-VDataout)*2; } else if(HDataout<140 && 110<HDataout){ DirL = 1; DirR = 1; Duty1 =(110-VDataout)*2; Duty2 =(110-VDataout)*2; } else{ DirL = 1; DirR = 1; Duty1 =(110-VDataout)*2; Duty2 =(110-VDataout)*2; } } //SendBuf[0] = 0xFF; //SendBuf[1] = 0xFF; SendBuf[0] = DirL; SendBuf[1] = Duty1; SendBuf[2] = DirR; SendBuf[3] = Duty2; //SendBuf[4] = 0; Serial.write(255); Serial.write(255); //Serial.print(255); //Serial.print(255); for(i=0;i<4;i++){ //Serial.print(SendBuf[i]); Serial.write(SendBuf[i]); } //Serial.print(DirL); //Serial.print(DirR); //Serial.print(HDataout); //Serial.println(VDataout); Serial.write(0); delay(100); } ``` #### 受信側 ```html #define PIN_IN1 5 #define PIN_IN2 6 #define PIN_IN3 2 #define PIN_IN4 3 #define PIN_VREF1 9 #define PIN_VREF2 10 int RcvData=0;// int RcvBuf[4]; int MT_Rin=5; int MT_Lin=6; int MT_RoutSerial=0; int MT_LoutSerial=0; int i=0; int RcvState=0; //int RcvCountF=0; //int RcvCountS=0; int RcvC=0; int MT1D=0; int MT2D=0; int MT1S=0; int MT2S=0; int RcvCount=0; void setup() { Serial.begin(9600); // 9600bpsでシリアルポートを開く pinMode(PIN_IN1,OUTPUT); pinMode(PIN_IN2,OUTPUT); pinMode(PIN_IN3,OUTPUT); pinMode(PIN_IN4,OUTPUT); } void loop() { while(Serial.available() > 0){ RcvData=Serial.read(); //Serial.println(RcvData); //Serial.println(RcvState); switch(RcvState){ case 0: if(RcvData==255){ RcvState = 1; //Serial.print("RcvData1"); //Serial.println(RcvState); } break; case 1: if(RcvData==255){ RcvState = 2; //Serial.print(" RcvData2"); //Serial.println(RcvData); } else{ RcvState = 0; } break; case 2: RcvBuf[RcvCount]=RcvData; RcvCount++; if(RcvCount >= 4){ RcvCount=0; RcvState = 3; //Serial.print(" RcvData*"); //Serial.println(RcvData); } break; case 3: MotorCnt(); RcvState=0; break; } } } void MotorCnt(){ //モータ1の制御 //digitalWrite(13, HIGH ); //delay(100); //digitalWrite(13, LOW ); //delay(100); if(RcvBuf[0]==0 || RcvBuf[0]==1){ if(RcvBuf[0] == 0){ MT_LoutSerial = RcvBuf[1]; //Serial.print("左前"); //Serial.print(MT_LoutSerial); analogWrite(PIN_VREF1,MT_LoutSerial); digitalWrite(PIN_IN1,HIGH); digitalWrite(PIN_IN2,LOW); } else{ MT_LoutSerial = RcvBuf[1]; //Serial.print("左後"); //Serial.print(MT_LoutSerial); analogWrite(PIN_VREF1,MT_LoutSerial); digitalWrite(PIN_IN1,LOW); digitalWrite(PIN_IN2,HIGH); } if(RcvBuf[2] == 0){ MT_RoutSerial = RcvBuf[3]; //Serial.print("右前"); //Serial.println(MT_RoutSerial); analogWrite(PIN_VREF2,MT_RoutSerial); digitalWrite(PIN_IN3,LOW); digitalWrite(PIN_IN4,HIGH); } else{ MT_RoutSerial = RcvBuf[3]; //Serial.print("右後"); //Serial.println(MT_RoutSerial); analogWrite(PIN_VREF2,MT_RoutSerial); digitalWrite(PIN_IN3,HIGH); digitalWrite(PIN_IN4,LOW); } } else{ //Serial.println("停止"); digitalWrite(PIN_IN1,LOW); digitalWrite(PIN_IN2,LOW); digitalWrite(PIN_IN3,LOW); digitalWrite(PIN_IN4,LOW); } delay(100); } ```

+

電子部品 --- 最後に本製作例で使用した電子部品を紹介します. |部品名|購入先| |---|---|---| |Arduino uno|秋月電子通商orマルツパーツor千石電商| |TA7291P|amazon(ただし販売終了なので中古)代替品でTB6643KQ| |タミヤダブルギアボックス|秋月電子通商orマルツパーツor千石電商| |電線|秋月電子通商orマルツパーツor千石電商or近くのホームセンター| |Arduino変換基盤|秋月電子通商orマルツパーツor千石電商| |XBeeと変換基盤|秋月電子通商orマルツパーツor千石電商| |ジョイスティックレバー|秋月電子通商orマルツパーツor千石電商| |ピンヘッダとピンソケット|秋月電子通商orマルツパーツor千石電商| |電子基板|秋月電子通商orマルツパーツor千石電商|