Snake Robot No.2 White Snake その3
その3 ソフトウェア編 2021年4月7日
このSnakeRobotの原型は2017年にjoesinstructablesがinsutructables circuitsに発表された作品を参考にしています。
ここにハード面・ソフト面共に詳しく解説されています。興味のある方は製作の前に塾続して下さい。
先ずは、White Snakeの動画をご覧下さい。
コントローラにBlynkを使います。
先の1号機では専用の自作BLEリモコンを使いましたが、White Snakeでは汎用性を考慮してスマートフォンアプリ
Blinkを使ったコントローラーを使います。
BlynkはBluetoothLEに対応していて更に、OSがAndroidとiPhpneのスマートフォンを共にサポートしています。
Blynk APPをAndroid版はGoogle PlayにてBlynkを、iPhone版はAPP StoreにてBlynk - IoT for Arduino, ESP32を
夫々ダウンロードとインストールを済ませておきます。
Blynkを使ったソースファイル
ArduinoIDEに張り付け用の行番号なしのファイルはこちをクリックすると開きます。⇒ WhiteSnake.txt
使用するサーボモータの特性やServo libraryの関係でハード的に90度に正しく設定していても左右どちらかへ偏る
ことが有り、ソフト面これを補正する必要があります。
また、IRセンサーの感知角度が狭いことや車型ロボットと違って蛇行しながら進むので障害物感知が難しいと思います。
このソースファイルは私のWhite Snake用で、新たに作られる方はそのロボットに合わせた変更が必要です。
以下に要点のみ注釈を付けました。
最後までご覧頂きまして有難うございます。
皆様の参考になれば幸いです。
by Paradise
このSnakeRobotの原型は2017年にjoesinstructablesがinsutructables circuitsに発表された作品を参考にしています。
ここにハード面・ソフト面共に詳しく解説されています。興味のある方は製作の前に塾続して下さい。
先ずは、White Snakeの動画をご覧下さい。
コントローラにBlynkを使います。
先の1号機では専用の自作BLEリモコンを使いましたが、White Snakeでは汎用性を考慮してスマートフォンアプリ
Blinkを使ったコントローラーを使います。
BlynkはBluetoothLEに対応していて更に、OSがAndroidとiPhpneのスマートフォンを共にサポートしています。
Blynk APPをAndroid版はGoogle PlayにてBlynkを、iPhone版はAPP StoreにてBlynk - IoT for Arduino, ESP32を
夫々ダウンロードとインストールを済ませておきます。
Blynkを使ったソースファイル
ArduinoIDEに張り付け用の行番号なしのファイルはこちをクリックすると開きます。⇒ WhiteSnake.txt
使用するサーボモータの特性やServo libraryの関係でハード的に90度に正しく設定していても左右どちらかへ偏る
ことが有り、ソフト面これを補正する必要があります。
また、IRセンサーの感知角度が狭いことや車型ロボットと違って蛇行しながら進むので障害物感知が難しいと思います。
このソースファイルは私のWhite Snake用で、新たに作られる方はそのロボットに合わせた変更が必要です。
以下に要点のみ注釈を付けました。
006:#define BLYNK_PRINT Serial 007:#define BLYNK_USE_DIRECT_CONNECT 008:#include "BlynkSimpleEsp32_BLE.h" 009:#include "ESP32Servo.h" // Ver.0.90 https://www.arduinolibraries.info/libraries/esp32-servo 010:#include "Wire.h" // I2c 測距センサーに使用。 011:#define SENSOR_ADRS 0x40 012:#define DISTANCE_ADRS 0x5E 013://先の1号機より、サーボモーターを2個減らして12個+1個としました。 014:Servo servo1; 015:Servo servo2; 016:Servo servo3; 017:Servo servo4; 018:Servo servo5; 019:Servo servo6; 020:Servo servo7; 021:Servo servo8; 022:Servo servo9; 023:Servo servo10; 024:Servo servo11; 025:Servo servo12; 026:Servo servo_pan; 027://使用するピン番号も変更しています。 028:int servo1Pin = 13; 029:int servo2Pin = 12; 030:int servo3Pin = 14; 031:int servo4Pin = 27; 032:int servo5Pin = 26; 033:int servo6Pin = 25; 034:int servo7Pin = 33; 035:int servo8Pin = 15; 036:int servo9Pin = 2; 037:int servo10Pin = 4; 038:int servo11Pin = 16; 039:int servo12Pin = 17; 040:int servo_panPin = 32; // 測距センサー用サーボモータ 041:// 以下の2行はSG90サーボモータ用設定値。必要に応じて調整が可能です。 042:int minUs = 500; 043:int maxUs = 2400; 044:int counter = 0; // ループカウンタの変数 045:float lag = .5712; // セグメント間の位相遅れ 046:int frequency = 1; // セグメントの発振周波数。 047:int amplitude = 40; // 蛇行運動の振幅 048:int rightOffset = 6; // Right turn Offset 使用したサーボモーターの特性で左へ偏るので右側を5+1としています。 049:int leftOffset = -5; // Left turn Offset 050:int delayTime = 5; // Delay between limb movements 051:int startPause = 5000; // ロボットを配置するための遅延時間 052:int Offset = 1; // 048行と同じ理由で直進せずに少し左寄りに進むため、右寄りに+1補正しています。 053:// 上のOffsetはサーボモータの偏りを補正しています。. 054:int ModeLED = 5; // Mode Indicator LED 055:int Mode = 0; // Mode initial value 056:int button2 = 0; // Forwardボタンの初期値 057:int button3 = 0; // TurnRightボタン初期値 058:int button4 = 0; // Backwardボタンの初期値 059:int button5 = 0; // TurnLeftボタンの初期値 060:int wallDistanceTolerance = 30; // 障害物感知距離の設定、ここでは30㎝に設定しています。 061:byte des[2] ; 062:uint8_t Front_distance = 0; 063:uint8_t Left_distance = 0; 064:uint8_t Right_distance = 0; 065:char auth[] = "********************"; // Blynkの設定時に取得したAuth Tokenを記入します。 066: 067:void setup() { 068: Serial.begin(115200) ; // USB Serial 069: Wire.begin() ; // I2c 070: Serial.println("Waiting for connections..."); 071: Blynk.setDeviceName("WHITE SNAKE"); // 判りやすい任意の名前を付けます。 072: Blynk.begin(auth); 073: pinMode(ModeLED, OUTPUT); digitalWrite(ModeLED, HIGH); // Mode LED default settings. 074: // Attach segments to Pins 075: servo1.setPeriodHertz(50); 076: servo2.setPeriodHertz(50); 077: servo3.setPeriodHertz(50); 078: servo4.setPeriodHertz(50); 079: servo5.setPeriodHertz(50); 080: servo6.setPeriodHertz(50); 081: servo7.setPeriodHertz(50); 082: servo8.setPeriodHertz(50); 083: servo9.setPeriodHertz(50); 084: servo10.setPeriodHertz(50); 085: servo11.setPeriodHertz(50); 086: servo12.setPeriodHertz(50); 087: servo_pan.setPeriodHertz(50); 088: 089: servo1.attach(servo1Pin, minUs, maxUs); 090: servo2.attach(servo2Pin, minUs, maxUs); 091: servo3.attach(servo3Pin, minUs, maxUs); 092: servo4.attach(servo4Pin, minUs, maxUs); 093: servo5.attach(servo5Pin, minUs, maxUs); 094: servo6.attach(servo6Pin, minUs, maxUs); 095: servo7.attach(servo7Pin, minUs, maxUs); 096: servo8.attach(servo8Pin, minUs, maxUs); 097: servo9.attach(servo9Pin, minUs, maxUs); 098: servo10.attach(servo10Pin, minUs, maxUs); 099: servo11.attach(servo11Pin, minUs, maxUs); 100: servo12.attach(servo12Pin, minUs, maxUs); 101: servo_pan.attach(servo_panPin, minUs, maxUs); 102: 103: // 初期の姿勢になります。1モーションが終了すると元の姿勢で止まります。 104: servo1.write(90 + Offset + amplitude * cos(5 * lag)); 105: servo2.write(90 + Offset + amplitude * cos(4 * lag)); 106: servo3.write(90 + Offset + amplitude * cos(3 * lag)); 107: servo4.write(90 + Offset + amplitude * cos(2 * lag)); 108: servo5.write(90 + Offset + amplitude * cos(1 * lag)); 109 servo6.write(90 + Offset + amplitude * cos(0 * lag)); 110: servo7.write(90 + Offset + amplitude * cos(-1 * lag)); 111: servo8.write(90 + Offset + amplitude * cos(-2 * lag)); 112: servo9.write(90 + Offset + amplitude * cos(-3 * lag)); 113: servo10.write(90 + Offset + amplitude * cos(-4 * lag)); 114: servo11.write(90 + Offset + amplitude * cos(-5 * lag)); 115: servo12.write(90 + Offset + amplitude * cos(-6 * lag)); 116: servo_pan.write(90); 117:} 118: 119:BLYNK_WRITE(V0) { // 自動/手動 切り替えボタンの設定 120: int button0 = param.asInt(); 121: if (button0 == 1) 122: { 123: Serial.print("Autonomous Mode\n"); 124: Mode = 1; // Autonomous Mode 125: digitalWrite(ModeLED, LOW); 126: } else { 127: Serial.print("Manual Mode\n"); 128: Mode = 0; // Manual Mode 129: digitalWrite(ModeLED, HIGH); 130: } 131:} 132: 133:BLYNK_WRITE(V1) { // キャリブレーションボタンの設定 134: int button1 = param.asInt(); 135: if (button1 == 1) 136: { 137: Serial.print("Calibrate ON\n"); 138: Calibrate(); 139: } else { 140: Serial.print("Calibrate OFF\n"); 141: } 142:} 143: 144:BLYNK_WRITE(V2) { // Forwardボタンの設定 145: button2 = param.asInt(); 146:} 147: 148:BLYNK_WRITE(V3) { // TurnRightボタンの設定 149: button3 = param.asInt(); 150:} 151: 152:BLYNK_WRITE(V4) { // Backwardボタンの設定 153: button4 = param.asInt(); 154:} 155: 156:BLYNK_WRITE(V5) { // TurnLeftボタンの設定 157: button5 = param.asInt(); 158:} 159: 160:void Forward() { // 前進 161: for (counter = 0; counter < 360; counter += 1) { 162: delay(delayTime); 163: servo1.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 + 5 * lag)); 164: servo2.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 + 4 * lag)); 165: servo3.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 + 3 * lag)); 166: servo4.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 + 2 * lag)); 167: servo5.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 + 1 * lag)); 168: servo6.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 + 0 * lag)); 169: servo7.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 - 1 * lag)); 170: servo8.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 - 2 * lag)); 171: servo9.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 - 3 * lag)); 172: servo10.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 - 4 * lag)); 173: servo11.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 - 5 * lag)); 174: servo12.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 - 6 * lag)); 175: } 176:} 177: 178:void Backward() { // 後退 179: for (counter = 360; counter > 0; counter -= 1) { 180: delay(delayTime); 181: servo1.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 + 5 * lag)); 182: servo2.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 + 4 * lag)); 183: servo3.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 + 3 * lag)); 184: servo4.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 + 2 * lag)); 185: servo5.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 + 1 * lag)); 186: servo6.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 + 0 * lag)); 187: servo7.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 - 1 * lag)); 188: servo8.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 - 2 * lag)); 189: servo9.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 - 3 * lag)); 190: servo10.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 - 4 * lag)); 191: servo11.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 - 5 * lag)); 192: servo12.write(90 + Offset + amplitude * cos(frequency * counter * 3.14159 / 180 - 6 * lag)); 193: } 194:} 195: 196:void Turnleft() { // 左旋回 197: for (counter = 0; counter < 10; counter += 1) { 198: delay(delayTime); 199: servo1.write(90 + Offset + .1 * counter * leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 5 * lag)); 200: servo2.write(90 + Offset + .1 * counter * leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 4 * lag)); 201: servo3.write(90 + Offset + .1 * counter * leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 3 * lag)); 202: servo4.write(90 + Offset + .1 * counter * leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 2 * lag)); 203: servo5.write(90 + Offset + .1 * counter * leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 1 * lag)); 204: servo6.write(90 + Offset + .1 * counter * leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 0 * lag)); 205: servo7.write(90 + Offset + .1 * counter * leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 1 * lag)); 206: servo8.write(90 + Offset + .1 * counter * leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 2 * lag)); 207: servo9.write(90 + Offset + .1 * counter * leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 3 * lag)); 208: servo10.write(90 + Offset + .1 * counter * leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 4 * lag)); 209: servo11.write(90 + Offset + .1 * counter * leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 5 * lag)); 210: servo12.write(90 + Offset + .1 * counter * leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 6 * lag)); 211: } 212: // Continue left turn 213: for (counter = 11; counter < 350; counter += 1) { 214: delay(delayTime); 215: servo1.write(90 + Offset + leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 5 * lag)); 216: servo2.write(90 + Offset + leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 4 * lag)); 217: servo3.write(90 + Offset + leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 3 * lag)); 218: servo4.write(90 + Offset + leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 2 * lag)); 219: servo5.write(90 + Offset + leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 1 * lag)); 220: servo6.write(90 + Offset + leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 0 * lag)); 221: servo7.write(90 + Offset + leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 1 * lag)); 222: servo8.write(90 + Offset + leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 2 * lag)); 223: servo9.write(90 + Offset + leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 3 * lag)); 224: servo10.write(90 + Offset + leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 4 * lag)); 225: servo11.write(90 + Offset + leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 5 * lag)); 226: servo12.write(90 + Offset + leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 6 * lag)); 227: } 228: // Ramp down turn Offset 229: for (counter = 350; counter < 360; counter += 1) { 230: delay(delayTime); 231: servo1.write(90 + Offset + .1 * (360 - counter)*leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 5 * lag)); 232: servo2.write(90 + Offset + .1 * (360 - counter)*leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 4 * lag)); 233: servo3.write(90 + Offset + .1 * (360 - counter)*leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 3 * lag)); 234: servo4.write(90 + Offset + .1 * (360 - counter)*leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 2 * lag)); 235: servo5.write(90 + Offset + .1 * (360 - counter)*leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 1 * lag)); 236: servo6.write(90 + Offset + .1 * (360 - counter)*leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 0 * lag)); 237: servo7.write(90 + Offset + .1 * (360 - counter)*leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 1 * lag)); 238: servo8.write(90 + Offset + .1 * (360 - counter)*leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 2 * lag)); 239: servo9.write(90 + Offset + .1 * (360 - counter)*leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 3 * lag)); 240: servo10.write(90 + Offset + .1 * (360 - counter)*leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 4 * lag)); 241: servo11.write(90 + Offset + .1 * (360 - counter)*leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 5 * lag)); 242: servo12.write(90 + Offset + .1 * (360 - counter)*leftOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 6 * lag)); 243: } 244:} 245: 246:void Turnright() { // 右旋回 247: for (counter = 0; counter < 10; counter += 1) { 248: delay(delayTime); 249: servo1.write(90 + Offset + .1 * counter * rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 5 * lag)); 250: servo2.write(90 + Offset + .1 * counter * rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 4 * lag)); 251: servo3.write(90 + Offset + .1 * counter * rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 3 * lag)); 252: servo4.write(90 + Offset + .1 * counter * rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 2 * lag)); 253: servo5.write(90 + Offset + .1 * counter * rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 1 * lag)); 254: servo6.write(90 + Offset + .1 * counter * rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 0 * lag)); 255: servo7.write(90 + Offset + .1 * counter * rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 1 * lag)); 256: servo8.write(90 + Offset + .1 * counter * rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 2 * lag)); 257: servo9.write(90 + Offset + .1 * counter * rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 3 * lag)); 258: servo10.write(90 + Offset + .1 * counter * rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 4 * lag)); 259: servo11.write(90 + Offset + .1 * counter * rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 5 * lag)); 260: servo12.write(90 + Offset + .1 * counter * rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 6 * lag)); 261: } 262: // Continue right turn 263: for (counter = 11; counter < 350; counter += 1) { 264: delay(delayTime); 265: servo1.write(90 + Offset + rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 5 * lag)); 266: servo2.write(90 + Offset + rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 4 * lag)); 267: servo3.write(90 + Offset + rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 3 * lag)); 268: servo4.write(90 + Offset + rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 2 * lag)); 269: servo5.write(90 + Offset + rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 1 * lag)); 270: servo6.write(90 + Offset + rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 0 * lag)); 271: servo7.write(90 + Offset + rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 1 * lag)); 272: servo8.write(90 + Offset + rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 2 * lag)); 273: servo9.write(90 + Offset + rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 3 * lag)); 274: servo10.write(90 + Offset + rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 4 * lag)); 275: servo11.write(90 + Offset + rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 5 * lag)); 276: servo12.write(90 + Offset + rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 6 * lag)); 277: } 278: // Ramp down turn Offset 279: for (counter = 350; counter < 360; counter += 1) { 280: delay(delayTime); 281: servo1.write(90 + Offset + .1 * (360 - counter)*rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 5 * lag)); 282: servo2.write(90 + Offset + .1 * (360 - counter)*rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 4 * lag)); 283: servo3.write(90 + Offset + .1 * (360 - counter)*rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 3 * lag)); 284: servo4.write(90 + Offset + .1 * (360 - counter)*rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 2 * lag)); 285: servo5.write(90 + Offset + .1 * (360 - counter)*rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 1 * lag)); 286: servo6.write(90 + Offset + .1 * (360 - counter)*rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 + 0 * lag)); 287: servo7.write(90 + Offset + .1 * (360 - counter)*rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 1 * lag)); 288: servo8.write(90 + Offset + .1 * (360 - counter)*rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 2 * lag)); 289: servo9.write(90 + Offset + .1 * (360 - counter)*rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 3 * lag)); 290: servo10.write(90 + Offset + .1 * (360 - counter)*rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 4 * lag)); 291: servo11.write(90 + Offset + .1 * (360 - counter)*rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 5 * lag)); 292: servo12.write(90 + Offset + .1 * (360 - counter)*rightOffset + amplitude * cos(frequency * counter * 3.14159 / 180 - 6 * lag)); 293: } 264:} 295: 296:void Calibrate() { // サーボモータの90度設定用 297: servo1.write(90); 298: servo2.write(90); 299: servo3.write(90); 300: servo4.write(90); 301: servo5.write(90); 302: servo6.write(90); 303: servo7.write(90); 304: servo8.write(90); 305: servo9.write(90); 306: servo10.write(90); 307: servo11.write(90); 308: servo12.write(90); 309:} 310: 311:void Autonomous_Mode() { // Autonomous mode IRセンサーによる自律行動を行います。 312: servo_pan.write(170); 313: Left_distance = 0; 314: delay(500); 315: Wire.beginTransmission(SENSOR_ADRS) ; 316: Wire.write(DISTANCE_ADRS) ; 317: Right_distance = Wire.endTransmission() ; 318: if (Left_distance == 0) { 319: Left_distance = Wire.requestFrom(SENSOR_ADRS, 2) ; 320: des[0] = Wire.read() ; 321: des[1] = Wire.read() ; 322: Left_distance = ((des[0] * 16 + des[1]) / 16) / 4 ; // 左側の測距値 323: Serial.print("Left_distance = ") ; 324: Serial.print(Left_distance) ; 325: Serial.println("cm") ; 326: } else { 327: Serial.print("ERROR NO.=") ; 328: } 329: delay(500); 330: servo_pan.write(10); 331: Right_distance = 0; 332: delay(500); 333: Wire.beginTransmission(SENSOR_ADRS) ; 334: Wire.write(DISTANCE_ADRS) ; 335: Right_distance = Wire.endTransmission() ; 336: if (Right_distance == 0) { 337: Right_distance = Wire.requestFrom(SENSOR_ADRS, 2) ; 338: des[0] = Wire.read() ; 339: des[1] = Wire.read() ; 340: Right_distance = ((des[0] * 16 + des[1]) / 16) / 4 ; // 右側の測距値 341: Serial.print("Right_distance = ") ; 342: Serial.print(Right_distance) ; 343: Serial.println("cm") ; 344: } else { 345: Serial.print("ERROR NO.=") ; 346: } 347: delay(500); 348: servo_pan.write(90); 349: if (Left_distance > Right_distance ) { // 左側測距値より右側測距値が小さい場合 350: Backward(); // 1モーション後退 351: delay(200) ; 352: Turnleft(); // 1モーション左旋回 353 Serial.println("Turnleft"); 354: } 355: else if (Left_distance <= Right_distance ) { // 左側測距値より右側測距値が大きい場合 356: Backward();// 1モーション後退 357: delay(200); 358: Turnright(); // 1モーション右旋回 359: Serial.println("Turnright"); 360: } 361: else { 362: Forward(); // 障害物が無い場合は前進 363: Serial.println("Forward"); 364: } 365: delay(100) ; 366:} 367: 368:void loop() { // メインループ 369: Blynk.run(); 370: if (button2 == 1) { 371: Serial.print("Forward\n"); 372: Forward(); 373: } 374: if (button3 == 1) { 375: Serial.print("Turnright\n"); 376: Turnright(); 377: } 378: if (button4 == 1) { 379: Serial.print("Backward\n"); 380: Backward(); 381: } 382: if (button5 == 1) { 383: Serial.print("Turnleft\n"); 384: Turnleft(); 385: } 386: 387: if (Mode == 1) { // Autonomous Mode 自律行動ON 388: Forward(); 389: servo_pan.write(90); 390: delay(200); 391: // Read basic measurement 392: Wire.beginTransmission(SENSOR_ADRS) ; 393: Wire.write(DISTANCE_ADRS) ; 394: Front_distance = Wire.endTransmission() ; 395: if (Front_distance == 0) { 396: Front_distance = Wire.requestFrom(SENSOR_ADRS, 2) ; 397: des[0] = Wire.read() ; 398: des[1] = Wire.read() ; 399: Front_distance = ((des[0] * 16 + des[1]) / 16) / 4 ; \\ 前方の測距値 400: Serial.print("Front_distance = ") ; 401: Serial.print(Front_distance) ; 402: Serial.println("cm") ; 403: } else { 404: Serial.print("ERROR NO.=") ; 405: } 406: delay(100); 407: if (Front_distance < wallDistanceTolerance) { // 30センチ以内に障害物が有れば自律モードに移ります。 408: Autonomous_Mode(); 409: } 410: } 411:} |
最後までご覧頂きまして有難うございます。
皆様の参考になれば幸いです。
by Paradise
スポンサーサイト
Snake Robot No.2 White Snake その2

Blynk APPをスマートフォンにインストールしてSnake Robotをコントロール

スネークロボットのコントロールに必要なBLEに対応したAPPがなく、Blynkを使って簡単なコントローラを作りました。
Blynkを使うもう一つの理由は、AndroidとiPhoneの両方がサポートされているからです。
先ずは、Android版はGoogle PlayにてBlynkを、iPhone版はAPP StoreにてBlynk - IoT for Arduino, ESP32を
夫々ダウンロードとインストールを行います。
Android版とiPhone版ではON/OFFスイッチの位置が逆など画面の構成や操作方法に僅かなが異なりますが、
ここではAndroid版を使った説明を行います。
Blynk APPの設定方法
次回はBlynkを使ったソースファイルを掲載予定です。 皆様の参考になれば幸いです。 by Paradise |
Snake Robot No.2 White Snake その1
White Snake 2021年3月26日
今回の作品はコントロール部のマイコンにESP32 devkitを使ったので部品点数が減りました。
また、コントローラーにスマホアプリBlynkを使いBluetooth(BLE)にてコントロールします。
(Blynkは低電力のBLEが使え、AndroidとiPhoneの両方をサポートしているのが特徴です)。
1号機と比べ、サーボモータの取り付け方法変更とサーボモータを2個減らしています。

SG90サーボモータについて
1号機を作る際に手持ちのSG90サーボモータを採寸して設計したのですが、届いたSG90は新しいタイプで
寸法が異なるので少し手を加えて使いました。 外見からの見分け方は、裏蓋の止めネジが対角に2本で、
旧タイプが四隅に4本です。 サーボホーンも厚みが有り、勘合するシャフトも少し太いのが特徴です。
裏蓋を外すと画像のようにモータやポテンションメータの接続が配線ではなく全て基板化されていました。
今回も同じ販売店に注文したのですが、届いた品は旧タイプだったので1号機と2号機のサーボモータを
入れ替えて使うことにしました。 このために掲載が少し遅くなりました。
これから作られる方は現物に合わせた設計と加工が必要なので注意して下さい。

アクリル板の加工とユニットの組み立て
今回もアクリル板をCNC加工して構成部品を作りました。

組立図
先の1号機ではサーボモータをフレームに固定してたが、今回はサーボモータ固定台をフレームに対して±約5度の
遊びを持たせ、路面に少し凹凸があっても車輪が接地するように工夫しました。

1ユニットの構成部品と組み立てた1ユニット
サーボモータと固定台はネジ止めで、下部サポート台のみ両面テープで貼り付けています。

コントロール部

DOIT社のESP32 devkit V1を組み込み、測距センサー用サーボモータの下側からmicro USBプラグを抜き差し
出来るようにしています。

DOIT社のESP32 devkit V1は固定用の穴が四隅に開いているので、ピンヘッダを上向きに付け替えて固定します。
同等のセカンドソース品が安く出回っていてAmazonにて購入できます。

GPIO機能表
30Pinと38pinの2タイプありますが、30pinの方が少し小さいのでこれを使いました。

回路図
コントロール部の電源は3.7Vのリポバッテリー出力をDC/DCコンバータにより、5Vに昇圧してdevkitに供給しています。
駆動用サーボモータの電源は別電源として単4型ニッケル水素充電池を4個直列にして使いました。
測距センサーはシャープ製GP2Y0E03をI2C接続にて使用、センサー用サーボモータの電源はコントロール電源を使用。

電源部
単4型充電池を使ったサーボモータの電源部、容量の目安にデジタル電圧計を付けてみました。
また、既製品の電池ボックス・リード線が細いのでAWG20に交換して使っています。

1号機のBlack Snakeと合わせて2台となりました。(1号機もサーボモータ取り換えに際し少し改良しています)。

次回はスマホアプリBlynkを使ったコントローラーを掲載の予定です。
皆様の参考になれば幸いです。
by Paradise
今回の作品はコントロール部のマイコンにESP32 devkitを使ったので部品点数が減りました。
また、コントローラーにスマホアプリBlynkを使いBluetooth(BLE)にてコントロールします。
(Blynkは低電力のBLEが使え、AndroidとiPhoneの両方をサポートしているのが特徴です)。
1号機と比べ、サーボモータの取り付け方法変更とサーボモータを2個減らしています。

SG90サーボモータについて
1号機を作る際に手持ちのSG90サーボモータを採寸して設計したのですが、届いたSG90は新しいタイプで
寸法が異なるので少し手を加えて使いました。 外見からの見分け方は、裏蓋の止めネジが対角に2本で、
旧タイプが四隅に4本です。 サーボホーンも厚みが有り、勘合するシャフトも少し太いのが特徴です。
裏蓋を外すと画像のようにモータやポテンションメータの接続が配線ではなく全て基板化されていました。
今回も同じ販売店に注文したのですが、届いた品は旧タイプだったので1号機と2号機のサーボモータを
入れ替えて使うことにしました。 このために掲載が少し遅くなりました。
これから作られる方は現物に合わせた設計と加工が必要なので注意して下さい。

アクリル板の加工とユニットの組み立て
今回もアクリル板をCNC加工して構成部品を作りました。

組立図
先の1号機ではサーボモータをフレームに固定してたが、今回はサーボモータ固定台をフレームに対して±約5度の
遊びを持たせ、路面に少し凹凸があっても車輪が接地するように工夫しました。

1ユニットの構成部品と組み立てた1ユニット
サーボモータと固定台はネジ止めで、下部サポート台のみ両面テープで貼り付けています。

コントロール部

DOIT社のESP32 devkit V1を組み込み、測距センサー用サーボモータの下側からmicro USBプラグを抜き差し
出来るようにしています。

DOIT社のESP32 devkit V1は固定用の穴が四隅に開いているので、ピンヘッダを上向きに付け替えて固定します。
同等のセカンドソース品が安く出回っていてAmazonにて購入できます。

GPIO機能表
30Pinと38pinの2タイプありますが、30pinの方が少し小さいのでこれを使いました。

回路図
コントロール部の電源は3.7Vのリポバッテリー出力をDC/DCコンバータにより、5Vに昇圧してdevkitに供給しています。
駆動用サーボモータの電源は別電源として単4型ニッケル水素充電池を4個直列にして使いました。
測距センサーはシャープ製GP2Y0E03をI2C接続にて使用、センサー用サーボモータの電源はコントロール電源を使用。

電源部
単4型充電池を使ったサーボモータの電源部、容量の目安にデジタル電圧計を付けてみました。
また、既製品の電池ボックス・リード線が細いのでAWG20に交換して使っています。

1号機のBlack Snakeと合わせて2台となりました。(1号機もサーボモータ取り換えに際し少し改良しています)。

次回はスマホアプリBlynkを使ったコントローラーを掲載の予定です。
皆様の参考になれば幸いです。
by Paradise
White Snake
Snake Robot 蛇型ロボット その6
その6 ソフトウェア編 2021年3月9日
その1で紹介しましたが、このSnakeRobotの原型は2017年4月23日にjoes氏がinsutructables circuitsに
公開された作品です。 それを小型で入手可能な部品に置き換えてリメークしたのがこの作品です。
ioes氏のinstructables SnakeRobotページを開きます。
製作される方は、上記ページにハード面、ソフト面共に詳しく説明されていますので熟読して下さい。

主な使用部品と構成の変更
マイコン : ATmega2560 → ESP32
リモコン : RFリモコン一対(技適認証なし、日本国内使用不可) → ESP32内蔵Bluetooth LE
サーボモータ : ASM-RGS-13を12個使用 → マイクロサーボSG90を14個使用
測距センサー : Sharp GP2Y0A41SK0F(analog出力) → Sharp GP2Y0E02( I2C出力を使用)
SnakeRobot Paradise Modelのファームウェア
使用部品と構成の違いに合わせ、元のファームウェアから大きく変更しています。
また、このファームウェアは前回その5に掲載の専用BLEリモコンとセットで使うことを前提にしています。
現在製作中の2号機「White Snake」では、Androidスマホ用フリーアプリ「Bluetooth Serial Controller」にて
コントロールしています。 スマホでコントロールをお望みの方は次回の2号機掲載をお待ちください。
このブログではArduinoIDEのinoファイルを直接添付できません。
右のtxtファイルを開き、コピーしてArduino IDEに貼り付けて下さい。 SnakeRobot_BLE_A.txt
ファームウェアの変更ポイント
説明用行番号付きHTMLファイルにて順次説明します。 SnakeRobot_BLE_A.html
1)ライブラリーの取得とアドレスの設定005-010行
#include "BLEDevice.h" // Ver.1.01 Ver1.01の取得先URL
#include "ESP32Servo.h" // Ver.0.90 Ver.0.90の取得先URL
#include "Wire.h" // I2c 標準ライブラリー
#define ADDRESS 0x40 // SHARP GP2Y0E03 is a distance measuring sensor unit,I2C address.
#define SHIFT_ADDR 0x35
#define DISTANCE_ADDR 0x5E
2)各変数の宣言
012-042行:サーボモータ名とGPIOの指定
043-045行:ESP32ServoLibrarieの設定値(SG90対応)
046-052行:SnakeRobotの制御設定値。私は測距センサーをコントロールボード側に配置して前後を逆に設定。
このため、rightOffse、lestOffsetが±を反対に設定しています。defaultは5
053-067行:各サーボモータの角度補正値(実測にて要補正)
068-069行:delayTimeの設定、068行のdelayTimeを変えると蛇行する振幅が変わります。defaultは7
070-073行:ModeLEDのGPIOの設定、CAL_SWのGPIOの設定
074-082行:測距センサーの設定値関係、74行wallDistanceTolerance = 30は30㎝に設定してます。
3)BLE通信UUIDとDevice名 084-141行
BluetoothLEに必要なUUIDを入力しますが、BLE専用リモコンに合わせて同じコードを入力します。
Device名(ここではesp32_BLE)も同様です。
4)void setup 143-218行
Arduinoのservo.hと違ってESP32Servo.hは設定項目が多いですが、SG90サーボモータを問題なく動かせます。
181-196行:全てのサーボモータの角度を初期状態にします。
198-206行:測距センサーアドレスの初期設定です。
209-217行:BLEの初期設定。
5)void Forward(),void Backward(),void Turnleft(),void Turnright() 220-370
上記はloop内の行数が多くなるのでサブルーチンとして外に出しました。
これらはロボットを前後左右に動かすためのルーチンで、手動操作の場合はリモコンのボタンを1回押すと
1行程進みます。 この1行程の開始と終わりの姿勢は同じで、ボタンを押し続けると連続した動きとなります。
6)void Calibrate() 372-387行
CALスイッチをONにした状態において一直線ではなく左右何れかへ湾曲する場合、052行のoffset = 0を
±に少し値を変えて一直線となるように補正します。一直線でない場合は直進時に左右何れかへカーブします。
7)void Autonomous_Mode() 389-448行
自律モードの場合、IR測距センサーをサーボモータで左右に振り、障害物を検知します。
390-408行:170度の位置にサーボモータを振り、この時の障害物迄の距離を測定します。
410-428行:10度の位置にサーボモータを振り、この時の障害物迄の距離を測定します。
430-447行:左右の測定値を比較して進行方向を求め、一行程後退した後に左又は右に2行程旋回します。
8)void Received_Code() 450-494行
ペアリングしたBluetoothLEリモコンからの信号を受信してキャラクターコードに変換します。
変換したキャラクターコードにより選択された操作信号を出力します。
9)void loop() 496-528行
497行:Received_Code()を実行
498行:CAL_SWがONの場合はCalibrate()を実行
503行:自律操作モードの場合は前方の障害物を検知し、30㎝以内に障害物がある場合には
Autonomous_Mode()を実行し、障害物がない場合は1行程前進して同じ動作を繰り返します。
ファームウェアは改良のためにその都度変更を行います。その場合はこのページに追加掲載しますが、
この記事を参考に作られる方は、独自に改良してお楽しみください。
最後にもう一度動画をご覧下さい。
次回から改良型の2号機「White Snake編」を順次掲載の予定です。
皆様の参考になれば幸いです。
by Paradise
その1で紹介しましたが、このSnakeRobotの原型は2017年4月23日にjoes氏がinsutructables circuitsに
公開された作品です。 それを小型で入手可能な部品に置き換えてリメークしたのがこの作品です。
ioes氏のinstructables SnakeRobotページを開きます。
製作される方は、上記ページにハード面、ソフト面共に詳しく説明されていますので熟読して下さい。

主な使用部品と構成の変更
マイコン : ATmega2560 → ESP32
リモコン : RFリモコン一対(技適認証なし、日本国内使用不可) → ESP32内蔵Bluetooth LE
サーボモータ : ASM-RGS-13を12個使用 → マイクロサーボSG90を14個使用
測距センサー : Sharp GP2Y0A41SK0F(analog出力) → Sharp GP2Y0E02( I2C出力を使用)
SnakeRobot Paradise Modelのファームウェア
使用部品と構成の違いに合わせ、元のファームウェアから大きく変更しています。
また、このファームウェアは前回その5に掲載の専用BLEリモコンとセットで使うことを前提にしています。
現在製作中の2号機「White Snake」では、Androidスマホ用フリーアプリ「Bluetooth Serial Controller」にて
コントロールしています。 スマホでコントロールをお望みの方は次回の2号機掲載をお待ちください。
このブログではArduinoIDEのinoファイルを直接添付できません。
右のtxtファイルを開き、コピーしてArduino IDEに貼り付けて下さい。 SnakeRobot_BLE_A.txt
ファームウェアの変更ポイント
説明用行番号付きHTMLファイルにて順次説明します。 SnakeRobot_BLE_A.html
1)ライブラリーの取得とアドレスの設定005-010行
#include "BLEDevice.h" // Ver.1.01 Ver1.01の取得先URL
#include "ESP32Servo.h" // Ver.0.90 Ver.0.90の取得先URL
#include "Wire.h" // I2c 標準ライブラリー
#define ADDRESS 0x40 // SHARP GP2Y0E03 is a distance measuring sensor unit,I2C address.
#define SHIFT_ADDR 0x35
#define DISTANCE_ADDR 0x5E
2)各変数の宣言
012-042行:サーボモータ名とGPIOの指定
043-045行:ESP32ServoLibrarieの設定値(SG90対応)
046-052行:SnakeRobotの制御設定値。私は測距センサーをコントロールボード側に配置して前後を逆に設定。
このため、rightOffse、lestOffsetが±を反対に設定しています。defaultは5
053-067行:各サーボモータの角度補正値(実測にて要補正)
068-069行:delayTimeの設定、068行のdelayTimeを変えると蛇行する振幅が変わります。defaultは7
070-073行:ModeLEDのGPIOの設定、CAL_SWのGPIOの設定
074-082行:測距センサーの設定値関係、74行wallDistanceTolerance = 30は30㎝に設定してます。
3)BLE通信UUIDとDevice名 084-141行
BluetoothLEに必要なUUIDを入力しますが、BLE専用リモコンに合わせて同じコードを入力します。
Device名(ここではesp32_BLE)も同様です。
4)void setup 143-218行
Arduinoのservo.hと違ってESP32Servo.hは設定項目が多いですが、SG90サーボモータを問題なく動かせます。
181-196行:全てのサーボモータの角度を初期状態にします。
198-206行:測距センサーアドレスの初期設定です。
209-217行:BLEの初期設定。
5)void Forward(),void Backward(),void Turnleft(),void Turnright() 220-370
上記はloop内の行数が多くなるのでサブルーチンとして外に出しました。
これらはロボットを前後左右に動かすためのルーチンで、手動操作の場合はリモコンのボタンを1回押すと
1行程進みます。 この1行程の開始と終わりの姿勢は同じで、ボタンを押し続けると連続した動きとなります。
6)void Calibrate() 372-387行
CALスイッチをONにした状態において一直線ではなく左右何れかへ湾曲する場合、052行のoffset = 0を
±に少し値を変えて一直線となるように補正します。一直線でない場合は直進時に左右何れかへカーブします。
7)void Autonomous_Mode() 389-448行
自律モードの場合、IR測距センサーをサーボモータで左右に振り、障害物を検知します。
390-408行:170度の位置にサーボモータを振り、この時の障害物迄の距離を測定します。
410-428行:10度の位置にサーボモータを振り、この時の障害物迄の距離を測定します。
430-447行:左右の測定値を比較して進行方向を求め、一行程後退した後に左又は右に2行程旋回します。
8)void Received_Code() 450-494行
ペアリングしたBluetoothLEリモコンからの信号を受信してキャラクターコードに変換します。
変換したキャラクターコードにより選択された操作信号を出力します。
9)void loop() 496-528行
497行:Received_Code()を実行
498行:CAL_SWがONの場合はCalibrate()を実行
503行:自律操作モードの場合は前方の障害物を検知し、30㎝以内に障害物がある場合には
Autonomous_Mode()を実行し、障害物がない場合は1行程前進して同じ動作を繰り返します。
ファームウェアは改良のためにその都度変更を行います。その場合はこのページに追加掲載しますが、
この記事を参考に作られる方は、独自に改良してお楽しみください。
最後にもう一度動画をご覧下さい。
次回から改良型の2号機「White Snake編」を順次掲載の予定です。
皆様の参考になれば幸いです。
by Paradise
Snake Robot 蛇型ロボット その5
その5 リモコン部の製作 2021年3月3日
手動操作やモード切替を行うリモコンにはBluetooth(BLE)を使った専用リモコンを使います。
このリモコンは昨年5月30日に「Theo Jansen Mechanism series No.9 その5」に掲載と同じものです。

BLE(Bluetooth Low Energy)専用リモコン
リモコンにはスマホも使えますが、幼児でも操作が出来る専用リモコンを作りました。
Bluetoothのスマホアプリを使う方法は続編の2号機(White Snake)にて説明の予定です。
専用リモコンの回路図

タクトスイッチを使ったクロスキーボード
下がユニバーサル基板にタクトスイッチとシーソー式の十字板を組み合わせた物です。右側のシーソーボタンは
手動モードと自律モードの切替に使います。 シーソー板は大きく見えますが短辺8㎜、長辺24㎜です。

リモコンの内部
ケースは5㎜厚のアクリル板をCNC加工して作りました。ケースの中身は3.7Vリチウムイオン電池と
ESP32だけですが、 プログラムの書き換え用ソケットを設けています。

オリジナルBLEリモコンのソースファイル
UUIDはhttps://www.uuidgenerator.net/version4のOnline UUID Generatorにて取得し、受信側と同一コードとします。
UUIDの取得
以下を コピーしてArduino IDEに貼り付けて下さい。
/*I referred BLE notify from the sketch example of ESP32 BLE Arduino.*/
#include "BLEDevice.h"
#include "BLE2902.h"
BLECharacteristic *pCharacteristic;
bool deviceConnected = false;
char val;
//Get your UUID from Online UUID Generator.
//https://www.uuidgenerator.net/version4
#define SERVICE_UUID "********-****-****-*****-************"
#define CHARACTERISTIC_UUID "********-****-****-*****-************"
#define DEVICE_NAME "esp32_BLE"
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
}
};
void setup() {
Serial.begin(115200);
pinMode(12, INPUT_PULLUP); //Forward_SW
pinMode(13, INPUT_PULLUP); //Backward_SW
pinMode(14, INPUT_PULLUP); //Left_SW
pinMode(25, INPUT_PULLUP); //Right_SW
pinMode(26, INPUT_PULLUP); //SW_1(Autonomous operation)
pinMode(27, INPUT_PULLUP); //SW_2(Manual operation)
BLEDevice::init(DEVICE_NAME);
BLEServer *pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
BLEService *pService = pServer->createService(SERVICE_UUID);
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE
);
pCharacteristic->addDescriptor(new BLE2902());
pService->start();
pServer->getAdvertising()->start();
Serial.println("Waiting a client connection to notify...");
}
void loop() {
int val_F = digitalRead(12);//Forward
if (val_F == 0) {
val = 'F';
}
int val_B = digitalRead(13);//Backward
if (val_B == 0) {
val = 'B';
}
int val_L = digitalRead(14);//Left
if (val_L == 0) {
val = 'L';
}
int val_R = digitalRead(25);//Right
if (val_R == 0) {
val = 'R';
}
int val_1 = digitalRead(26);//Autonomous operation
if (val_1 == 0) {
val = '1';
}
int val_2 = digitalRead(27);//Manual operation
if (val_2 == 0) {
val = '2';
}
int val_S;
if ((val_L & val_R & val_F & val_B & val_1 & val_2) == 1) {
val = 'S';
}
if (deviceConnected) {
Serial.printf("*** NOTIFY: %d ***\n", val);
char buffer[10];
sprintf(buffer, "%d", val );
pCharacteristic->setValue(buffer);
pCharacteristic->notify();
}
delay(200);
}
次回はその6 本体のソフトウエアーについて掲載予定です。
皆様の参考になれば幸いです。
by Paradise
手動操作やモード切替を行うリモコンにはBluetooth(BLE)を使った専用リモコンを使います。
このリモコンは昨年5月30日に「Theo Jansen Mechanism series No.9 その5」に掲載と同じものです。

BLE(Bluetooth Low Energy)専用リモコン
リモコンにはスマホも使えますが、幼児でも操作が出来る専用リモコンを作りました。
Bluetoothのスマホアプリを使う方法は続編の2号機(White Snake)にて説明の予定です。
専用リモコンの回路図

タクトスイッチを使ったクロスキーボード
下がユニバーサル基板にタクトスイッチとシーソー式の十字板を組み合わせた物です。右側のシーソーボタンは
手動モードと自律モードの切替に使います。 シーソー板は大きく見えますが短辺8㎜、長辺24㎜です。

リモコンの内部
ケースは5㎜厚のアクリル板をCNC加工して作りました。ケースの中身は3.7Vリチウムイオン電池と
ESP32だけですが、 プログラムの書き換え用ソケットを設けています。

オリジナルBLEリモコンのソースファイル
UUIDはhttps://www.uuidgenerator.net/version4のOnline UUID Generatorにて取得し、受信側と同一コードとします。
UUIDの取得
以下を コピーしてArduino IDEに貼り付けて下さい。
/*I referred BLE notify from the sketch example of ESP32 BLE Arduino.*/
#include "BLEDevice.h"
#include "BLE2902.h"
BLECharacteristic *pCharacteristic;
bool deviceConnected = false;
char val;
//Get your UUID from Online UUID Generator.
//https://www.uuidgenerator.net/version4
#define SERVICE_UUID "********-****-****-*****-************"
#define CHARACTERISTIC_UUID "********-****-****-*****-************"
#define DEVICE_NAME "esp32_BLE"
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
}
};
void setup() {
Serial.begin(115200);
pinMode(12, INPUT_PULLUP); //Forward_SW
pinMode(13, INPUT_PULLUP); //Backward_SW
pinMode(14, INPUT_PULLUP); //Left_SW
pinMode(25, INPUT_PULLUP); //Right_SW
pinMode(26, INPUT_PULLUP); //SW_1(Autonomous operation)
pinMode(27, INPUT_PULLUP); //SW_2(Manual operation)
BLEDevice::init(DEVICE_NAME);
BLEServer *pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
BLEService *pService = pServer->createService(SERVICE_UUID);
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE
);
pCharacteristic->addDescriptor(new BLE2902());
pService->start();
pServer->getAdvertising()->start();
Serial.println("Waiting a client connection to notify...");
}
void loop() {
int val_F = digitalRead(12);//Forward
if (val_F == 0) {
val = 'F';
}
int val_B = digitalRead(13);//Backward
if (val_B == 0) {
val = 'B';
}
int val_L = digitalRead(14);//Left
if (val_L == 0) {
val = 'L';
}
int val_R = digitalRead(25);//Right
if (val_R == 0) {
val = 'R';
}
int val_1 = digitalRead(26);//Autonomous operation
if (val_1 == 0) {
val = '1';
}
int val_2 = digitalRead(27);//Manual operation
if (val_2 == 0) {
val = '2';
}
int val_S;
if ((val_L & val_R & val_F & val_B & val_1 & val_2) == 1) {
val = 'S';
}
if (deviceConnected) {
Serial.printf("*** NOTIFY: %d ***\n", val);
char buffer[10];
sprintf(buffer, "%d", val );
pCharacteristic->setValue(buffer);
pCharacteristic->notify();
}
delay(200);
}
次回はその6 本体のソフトウエアーについて掲載予定です。
皆様の参考になれば幸いです。
by Paradise
Snake Robot 蛇型ロボット その4
その4:コントロールボードの製作 2021年2月26日
各画像はクリックすると拡大します。
WiFiとBluetoothが搭載されたESP32チップを裏返し、両面テープでユニバーサル基板に貼り付けて手配線した
簡単なコントロールボードですが、これで複雑な蛇の蛇行運動を実現します。
右端のスイッチは、蛇行運動用サーボモータの出力を90度にセットするために設けました。90度に揃っていないと
左右どちらかへ弓なり、蛇が直進しません。その場合は一直線に揃うように補正値を修正する必要が有ります。
補正の詳細はソフト編で述べます。

参考にさせて頂いた元記事では、13個のサーボモーターをコントロールするために必要なPWM on GPIOを14個備えた
Arduino ATmega2560を使っていました。 しかし、その大きさと別途無線モジュールが必要なことなどが理由で小型で
安価なESP32を使うことにしました。 ここでは無線コントロールにBLE(Bluetooth Low Energy)を使用します。
尚、ESP32の場合PWMが使用可能なGPIOが18個搭載されていますが、、ここでは15個のGPIOを使用しています。
補足:最近ではATmega2560のミニタイプATmega2560 pro MINIが販売されていますがまだまだ高価です。
また、当初はPCA9685 16channel PWM Driverを使う計画でしたが、このドライバーボードが結構大きいので諦めました。

コントロールボードの回路図
下の回路図のようにESP32チップを裏返しに張り付けてあるので各GPIO名が判りやすく、インターフェースピンへの
手配線も間違わずに出来ると思います。 但し、ピンピッチが狭いので先の細い半田ごてが必要です。
プログラムの書き込み部はUSBシリアルモジュールを使った外付け回路としました。

部品配置図
私の例ですが参考にして下さい。手配線が嫌な方は、DOIT ESP32 DevKit V1等に置き換えて使うことが可能です。
クリックすると拡大します。

サーボモータへの配線方法について
各配線は本体中央部にまとめること、サーボモータの角度が90度の場合に配線距離が一番長く、0度又は180度側に
振ると外側へたわみます。そのため。配線をきつく束ねるとこの動きが重くなるので注意が必要です。
①蛇行運動用サーボモータへの配線は、コントロールボードのピンヘッダーからPWM信号線とGNDを配線します。
②+VとGNDの電源線は、最後尾のバッテリーから容量のある配線を用いて先頭のサーボモータまで配線します。
③各サーボモータの取り付けパネル上部にユニバーサル基板を加工した分岐基板をビス止めします。
④次に各々のサーボモータ近くで柔らかい配線を使って分岐して分岐基板へ配線します。ここでPWM信号線と一緒に
モーターリードと接続します。
⑤各配線を分岐基板へ直接半田付けすると、配線の先端が疲労切断する恐れがあるので、下記画像のように
ユニバーサル基板の穴を配線が通るように拡大し、一度その穴に通してから基板裏側で半田付けすると改善できます。

先頭(コントロール部)
測距センサー用サーボモータの電源はコントロール用電源を使用。DC/DCコンバータ(MT3608)はリポバッテリーの
裏側に取り付けています。

最後尾(バッテリー部)
蛇行運動用サーボモータの電源部で単4型充電池4個を搭載しています。

最後に動画をご覧下さい。
次回はその5 リモコン部を掲載の予定です。
皆様の参考になれば幸いです。
by Paradise
各画像はクリックすると拡大します。
WiFiとBluetoothが搭載されたESP32チップを裏返し、両面テープでユニバーサル基板に貼り付けて手配線した
簡単なコントロールボードですが、これで複雑な蛇の蛇行運動を実現します。
右端のスイッチは、蛇行運動用サーボモータの出力を90度にセットするために設けました。90度に揃っていないと
左右どちらかへ弓なり、蛇が直進しません。その場合は一直線に揃うように補正値を修正する必要が有ります。
補正の詳細はソフト編で述べます。

参考にさせて頂いた元記事では、13個のサーボモーターをコントロールするために必要なPWM on GPIOを14個備えた
Arduino ATmega2560を使っていました。 しかし、その大きさと別途無線モジュールが必要なことなどが理由で小型で
安価なESP32を使うことにしました。 ここでは無線コントロールにBLE(Bluetooth Low Energy)を使用します。
尚、ESP32の場合PWMが使用可能なGPIOが18個搭載されていますが、、ここでは15個のGPIOを使用しています。
補足:最近ではATmega2560のミニタイプATmega2560 pro MINIが販売されていますがまだまだ高価です。
また、当初はPCA9685 16channel PWM Driverを使う計画でしたが、このドライバーボードが結構大きいので諦めました。

コントロールボードの回路図
下の回路図のようにESP32チップを裏返しに張り付けてあるので各GPIO名が判りやすく、インターフェースピンへの
手配線も間違わずに出来ると思います。 但し、ピンピッチが狭いので先の細い半田ごてが必要です。
プログラムの書き込み部はUSBシリアルモジュールを使った外付け回路としました。

部品配置図
私の例ですが参考にして下さい。手配線が嫌な方は、DOIT ESP32 DevKit V1等に置き換えて使うことが可能です。
クリックすると拡大します。

サーボモータへの配線方法について
各配線は本体中央部にまとめること、サーボモータの角度が90度の場合に配線距離が一番長く、0度又は180度側に
振ると外側へたわみます。そのため。配線をきつく束ねるとこの動きが重くなるので注意が必要です。
①蛇行運動用サーボモータへの配線は、コントロールボードのピンヘッダーからPWM信号線とGNDを配線します。
②+VとGNDの電源線は、最後尾のバッテリーから容量のある配線を用いて先頭のサーボモータまで配線します。
③各サーボモータの取り付けパネル上部にユニバーサル基板を加工した分岐基板をビス止めします。
④次に各々のサーボモータ近くで柔らかい配線を使って分岐して分岐基板へ配線します。ここでPWM信号線と一緒に
モーターリードと接続します。
⑤各配線を分岐基板へ直接半田付けすると、配線の先端が疲労切断する恐れがあるので、下記画像のように
ユニバーサル基板の穴を配線が通るように拡大し、一度その穴に通してから基板裏側で半田付けすると改善できます。

先頭(コントロール部)
測距センサー用サーボモータの電源はコントロール用電源を使用。DC/DCコンバータ(MT3608)はリポバッテリーの
裏側に取り付けています。

最後尾(バッテリー部)
蛇行運動用サーボモータの電源部で単4型充電池4個を搭載しています。

最後に動画をご覧下さい。
次回はその5 リモコン部を掲載の予定です。
皆様の参考になれば幸いです。
by Paradise
Snake Robot 蛇型ロボット その3
その3:ボディーの製作 2020年2月17日
このSnakeRobotの原型は2017年4月にjoesinstructablesがinsutructables circuitsに発表された作品を参考に
安価で入手が容易な小型サーボモータを15個使い、コントロールボードを小型化した作品です。
興味のある方は、次のURを参考にして下さい。https://www.instructables.com/Snake-Robot-1/SnakeRobot
参考:組み立てキットや完成品も販売されていますが、US$683~746と大変高価です。
https://nevemtech.com/Paymentfinal.aspx?id=N459&stat=h&cnt=Japan
タイトルに100ドル以下で作ると書きましたが、新規購入費用は半分の5000円ほどで済みました。
手前はBluetooth(BLE)を使った自作コントローラーです。

主な部品リスト(制御部のパーツを含む)
サーボモータは到着までに20日ほど掛かりますが、AliExpressが断然安かった!送料込みで1個 100円余し!
1)ボディー部の製作
SG90型サーボモータを組み込む為のパーツをCNC加工して作ります。
使用する材料はアクリル板は、厚さ2㎜、3㎜、5㎜の3種類で、下図が各パーツと組み立て略図です。
私はサーボモータを14個使ったので同じパーツを14組作り、それを超強力両面テープを使って連結しました。
下図の斜線部分が超強力両面テープでサーボモータを固定した部分です。図はクリックすると大きく見れます。

1ユニットの組み立て
下の画像は加工した1ユニット分のパーツですが、サーボモータを挟み込むプレートが抜けていました。
左側のパーツを組み立てると右側のように1ユニットが出来上がります。
サーボモータが届く前に設計図を作ったら、以前日本で購入した物と細部が違い一部変更が必要でした。

このように連結して組み立てます。
サーボモーターに貼ってある数字のラベルは、各モーターに90度信号入力テスト時の補正値です。

先頭部(ESP32を使って小さくまとめたコントロール部)
先頭のサーボモータはIR測距センサーを左右に振るためのモータです。測距センサーGP2Y0E02はI2Cにて接続。
私はユニバーサル基板にESP32モジュールを組み込んで使いましたが、ESP32 Devkitでも使えます。
コントロール部は別電源とし、3.7V500mAhのリポバッテリーをDC/DCコンバータで5Vに昇圧しています。

最後部(電源部)
サーボモータ14台分の電源を賄うバッテリーに単4充電池1.2Vを4個使用してます。
14個のサーボモータを1度に動かすのでモーター電源の供給は、バッテリーから末端まで太い配線を使い、
夫々のモータの近くで分岐して供給する方法を採用、端末のモータも電圧降下が少なく元気に動きます。

最後に動画をご覧下さい。
次回はその4 制御部を掲載の予定です。
皆様の参考になれば幸いです。
by Paradise
このSnakeRobotの原型は2017年4月にjoesinstructablesがinsutructables circuitsに発表された作品を参考に
安価で入手が容易な小型サーボモータを15個使い、コントロールボードを小型化した作品です。
興味のある方は、次のURを参考にして下さい。https://www.instructables.com/Snake-Robot-1/SnakeRobot
参考:組み立てキットや完成品も販売されていますが、US$683~746と大変高価です。
https://nevemtech.com/Paymentfinal.aspx?id=N459&stat=h&cnt=Japan
タイトルに100ドル以下で作ると書きましたが、新規購入費用は半分の5000円ほどで済みました。
手前はBluetooth(BLE)を使った自作コントローラーです。

主な部品リスト(制御部のパーツを含む)
サーボモータは到着までに20日ほど掛かりますが、AliExpressが断然安かった!送料込みで1個 100円余し!
部品名 | 型番又は商品名 | 個数 | 参考 |
アクリル板 | 2㎜、3㎜、5㎜ | 100X300 | 端材数枚 手持ち品 |
強力両面テープ | スコッチ 超強力両面テープ | 1個 | ヨドバシカメラ |
タイヤ用Oリング | 1A-P9 [Oリング(10個入り)] | 32個(4 set) | ヨドバシ 1set 248円 |
サーボモータ | SG90 | 15個 | AliExpress 1個 1US$ |
マイコンモジュール | ESP32 WROOM 32D | 1個 | 秋月電子 1個 460円 |
測距モジュール | GP2Y0E02 I2C&analog | 1個 | 秋月電子 1個 680円 |
ユニバーサル基板 | 片面ガラス基板 Cタイプ | 3枚 | 秋月電子 1枚 60円 |
ピンヘッダー | 1x40(40P) | 1個 | 秋月電子 1個 35円 |
分割ピンソケット | 1x42(42P) | 1個 | 秋月電子 1個 80円 |
電池ケース | 単4 2個タイプ | 2個 | 秋月電子 1個 50円 |
その他 | 配線材・ビス類 | 多少 | 手持ち品 |
1)ボディー部の製作
SG90型サーボモータを組み込む為のパーツをCNC加工して作ります。
使用する材料はアクリル板は、厚さ2㎜、3㎜、5㎜の3種類で、下図が各パーツと組み立て略図です。
私はサーボモータを14個使ったので同じパーツを14組作り、それを超強力両面テープを使って連結しました。
下図の斜線部分が超強力両面テープでサーボモータを固定した部分です。図はクリックすると大きく見れます。

1ユニットの組み立て
下の画像は加工した1ユニット分のパーツですが、サーボモータを挟み込むプレートが抜けていました。
左側のパーツを組み立てると右側のように1ユニットが出来上がります。
サーボモータが届く前に設計図を作ったら、以前日本で購入した物と細部が違い一部変更が必要でした。

このように連結して組み立てます。
サーボモーターに貼ってある数字のラベルは、各モーターに90度信号入力テスト時の補正値です。

先頭部(ESP32を使って小さくまとめたコントロール部)
先頭のサーボモータはIR測距センサーを左右に振るためのモータです。測距センサーGP2Y0E02はI2Cにて接続。
私はユニバーサル基板にESP32モジュールを組み込んで使いましたが、ESP32 Devkitでも使えます。
コントロール部は別電源とし、3.7V500mAhのリポバッテリーをDC/DCコンバータで5Vに昇圧しています。

最後部(電源部)
サーボモータ14台分の電源を賄うバッテリーに単4充電池1.2Vを4個使用してます。
14個のサーボモータを1度に動かすのでモーター電源の供給は、バッテリーから末端まで太い配線を使い、
夫々のモータの近くで分岐して供給する方法を採用、端末のモータも電圧降下が少なく元気に動きます。

最後に動画をご覧下さい。
次回はその4 制御部を掲載の予定です。
皆様の参考になれば幸いです。
by Paradise
Snake Robot 蛇型ロボット その2
動画が出来ましたので先に掲載します。
その1でも述べましたが、この蛇型ロボットには車輪が付いていますが、全てフリーホイールです。
動く原理は、実際の蛇と同様にくねくねと蛇行する運動によって進みます。
この蛇行運動は、14個のサーボモータをコントロールするマイコンにより実現しています。
次回から製作編、ソフト編を順次掲載の予定です。
皆様の参考になれば幸いです。
by Paradise
その1でも述べましたが、この蛇型ロボットには車輪が付いていますが、全てフリーホイールです。
動く原理は、実際の蛇と同様にくねくねと蛇行する運動によって進みます。
この蛇行運動は、14個のサーボモータをコントロールするマイコンにより実現しています。
次回から製作編、ソフト編を順次掲載の予定です。
皆様の参考になれば幸いです。
by Paradise
Snake Robot 蛇型ロボット その1
Snake Robot 蛇型ロボット その1 2021年2月8日
先にお知らせしていました蛇型ロボットが出来上がりました。
今回も自作のCNCマシンを使い、アクリル板を切削して各パーツを作りました。
このSnakeRobotの原型は2017年にjoesinstructablesがinsutructables circuitsに発表された作品を
参考に、安価で入手が容易な小型サーボモーターや超小型のマイコンボードを自作して小さく纏めてみました。
興味のある方は、次のURを参考にして下さい。https://www.instructables.com/Snake-Robot-1/SnakeRobot
動作については動画を見て頂くのが1番ですが、未だ準備が出来ず、後日撮影の予定です。
完成したSnakeRobot本体 原形ではサーボ―モータを12個連結ですが、2個増やして14個としました。
また、距離センサーをコントロール部に直結する為に原形とは進行方向を逆向きしています。

このSnakeRobotの特徴は、車輪で動くのではなく、実際の蛇と同様にくねくね運動によって進みます。
車輪は蛇の鱗の代わりに胴体方向には転がりますが、斜めや横方向にはグリップが掛かり、進行方向へ
押し出す力となります。

先頭部には自律歩行の為の赤外線距離センサーとコントロール部を配置しました。
コントロール部の電源には3.7V500mAhのリポバッテリーを使用。サーボモータのバッテリーは最後部に搭載。

原形ではコントロール部に大きなATmega2560を使ってますが、小さく作ることが目標なのでESP32チップを
ユニバーサル基板に組み込んで小さく作りました。 また、ESP32を使うことにより、WiFiやBluetoothが使え、
リモコンモジュールの追加が不要となります。

次回から製作編、ソフト編を順次掲載の予定です。
皆様の参考になれば幸いです。
by Paradise
先にお知らせしていました蛇型ロボットが出来上がりました。
今回も自作のCNCマシンを使い、アクリル板を切削して各パーツを作りました。
このSnakeRobotの原型は2017年にjoesinstructablesがinsutructables circuitsに発表された作品を
参考に、安価で入手が容易な小型サーボモーターや超小型のマイコンボードを自作して小さく纏めてみました。
興味のある方は、次のURを参考にして下さい。https://www.instructables.com/Snake-Robot-1/SnakeRobot
動作については動画を見て頂くのが1番ですが、未だ準備が出来ず、後日撮影の予定です。
完成したSnakeRobot本体 原形ではサーボ―モータを12個連結ですが、2個増やして14個としました。
また、距離センサーをコントロール部に直結する為に原形とは進行方向を逆向きしています。

このSnakeRobotの特徴は、車輪で動くのではなく、実際の蛇と同様にくねくね運動によって進みます。
車輪は蛇の鱗の代わりに胴体方向には転がりますが、斜めや横方向にはグリップが掛かり、進行方向へ
押し出す力となります。

先頭部には自律歩行の為の赤外線距離センサーとコントロール部を配置しました。
コントロール部の電源には3.7V500mAhのリポバッテリーを使用。サーボモータのバッテリーは最後部に搭載。

原形ではコントロール部に大きなATmega2560を使ってますが、小さく作ることが目標なのでESP32チップを
ユニバーサル基板に組み込んで小さく作りました。 また、ESP32を使うことにより、WiFiやBluetoothが使え、
リモコンモジュールの追加が不要となります。

次回から製作編、ソフト編を順次掲載の予定です。
皆様の参考になれば幸いです。
by Paradise