fc2ブログ

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用で、新たに作られる方はそのロボットに合わせた変更が必要です。

以下に要点のみ注釈を付けました。
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_logo_diamond.png
Blynk APPをスマートフォンにインストールしてSnake Robotをコントロール
WhiteSnake4.jpg

スネークロボットのコントロールに必要なBLEに対応したAPPがなく、Blynkを使って簡単なコントローラを作りました。
Blynkを使うもう一つの理由は、AndroidiPhoneの両方がサポートされているからです。
先ずは、Android版はGoogle PlayにてBlynkを、iPhone版はAPP StoreにてBlynk - IoT for Arduino, ESP32
夫々ダウンロードとインストールを行います。
Android版とiPhone版ではON/OFFスイッチの位置が逆など画面の構成や操作方法に僅かなが異なりますが、
ここではAndroid版を使った説明を行います。

Blynk APPの設定方法
1.
Blynkをインストールしたら
最初にアカウントを作成します。
Facebook accountでログイン又
は新規アカウンを作成します。
更にBlynkアカウントをお持ち



2.
新規作成の場合、
e-mailとパスワードを
入力します。





3.
サインアップすると
Create New Projectが開きます。
プロジェクト名を入力
SNAKE ROBOT BLE
デバイス名
ESP32 Dev Boardを選択
コネクションタイプ
BLEを選択してCreateをタップ
Screenshot_Blynk_01.jpgScreenshot_Blynk_02.jpgScreenshot_Blynk_03.jpg
4. 
OKをタップすと登録した
メールアドレスにAuth Tokenが
送られてきます。
このTokenをソースファイルに
書き込むので大切にします。



5.
作成初期画面
六角形アイコンがNew Project
Create New Projecを開く
丸+アイコンがWidget Boxの
部品選択画面です。
三角形アイコンが完成画面を
開きます。
6.
Widget Box
ここで使用部品を選びます。
各部品に点数を表示していて
2000点迄は無料。
ここで1番上のButton200点を
6個選択します。合計1200点
なので無料で使えます。
Screenshot_Blynk_04.jpgScreenshot_Blynk_05.jpgScreenshot_Blynk_06.jpg
7. 選択した6個のButton
後で各Buttonを長押しと
スライドして配置換えします。


8. 次にWidget Boxを下げて
BLEアイコンを選択します。



9. 各ButtonとBLEアイコンを
長押しとスライドして
この画面のように配置を
換えます。
Screenshot_Blynk_07.jpgScreenshot_Blynk_08.jpgScreenshot_Blynk_09.jpg
10.
コントロールボタンの設定
左上Buttonをタップすると
ボタン設定画面①が開きます。
ボタン名にcontrolと入力
下側のOUTPUTをタップすると
次の設定面②が開きます。




11.
設定画面②
ここでVirtualとV0を選択して
Select PinをOKします。







12.
新しく開いた画面では
PinがV0に替わっています。
次に下側のMODEをPUSHから
SWITCH(トグル)に切り替えて
下側のOFFをManual>に
ONをAUTO>に書き換えます。
下側のFont Sizeを大きくします。
注意:iPhonではON/OFFが逆
でFont Sizeが固定です。
Screenshot_Blynk_10.jpgScreenshot_Blynk_11.jpgScreenshot_Blynk_12.jpg
13.
今度は右側のCALボタンを
設定します。
使ったV0にはUSEDとマーク
されています。
今度はV1を選択して
Select PinをOKします。
14.
V1の設定画面では
ボタン名称を90 angleとし、
SWITCH(トグル)を使用。
その他は画像を参考にします。
変更します。


15.
V2 Forwardボタンの設定
画像を参考にして各項目を
設定します。
PUSHスイッチを使います。
その他は画像を参考にします。


Screenshot_Blynk_13.jpgScreenshot_Blynk_14.jpgScreenshot_Blynk_15.jpg
16.
V3 Right Turnボタンの設定
画像を参考にして各項目を
設定します。
PUSHスイッチを使います。
その他は画像を参考にします。
17.
V4 Backwardボタンの設定
画像を参考にして各項目をL
設定します。
PUSHスイッチを使います。
その他は画像を参考にします。
18.
V5 Left Turnボタンの設定
画像を参考にして各項目をL
設定します。
PUSHスイッチを使います。
その他は画像を参考にします。
Screenshot_Blynk_16.jpgScreenshot_Blynk_17.jpgScreenshot_Blynk_18.jpg
19.
V1からV6のの設定完了後
画面がこのようになります。


右端の三角形アイコンをタップ
すると完成画面に替わります。




20.
完成画面では各ボタンが
OFF状態の名称を表示します。
ボタン色がON状態で反転表示
Controlと90angleボタンは
トグルスイッチで、タップ毎に
色が反転します。
デバイスに接続していない時は
アイコンに赤マークを表示。
21.
BLEのコネクト方法
Snake Robotの電源を入れ、
Connect BLE deviceをタップ
すると次の画面に替わります。





Screenshot_Blynk_19.jpgScreenshot_Blynk_20.jpgScreenshot_Blynk_21.jpg
22.
Select BLE device画面
スキャンが実行され、コネクト
可能なデバイス名が表示
されます。
その中のBlynk選んでOKを
タップします。
23.
コネクト完了画面
コネクトするとデバイスの
アイコンに付いていた赤マーク
が消えます。
画像はControlボタンがONの
状態で表示色が反転中です。
24.
Project Settings画面にて
ショートカットアイコンが
作れます。
以上にてオリジナルBlynk
コントローラが完成です。


Screenshot_Blynk_22.jpgScreenshot_Blynk_23.jpgScreenshot_Blynk_24.jpg


次回は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個減らしています。
WhiteSnake5.jpg

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

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

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

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

コントロール部
WhiteSnake6.jpg

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

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

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

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

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

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


次回はスマホアプリBlynkを使ったコントローラーを掲載の予定です。

皆様の参考になれば幸いです。
by Paradise

テーマ : 電子工作
ジャンル : 趣味・実用

White Snake

次回から掲載のWhite Snakeについて
前回の作品は、コントローラーに専用BLEリモコンを使ってましたが、今回はスマートフォンを使います。
使用するアプリには、AndroidとiPhoneの両方がサポートされたBlynkを使います。
また、マイコンも前回のように自分でボードを組み立てるのではなく、汎用のESP32 devkitを使うので
このブログを参考に製作される方の負担が軽くなると思います。
WhiteSnake1.jpg


次回から改良型の2号機「White Snake編」を順次掲載の予定です。

皆様の参考になれば幸いです。
by Paradise

テーマ : 電子工作
ジャンル : 趣味・実用

Snake Robot 蛇型ロボット その6

その6 ソフトウェア編 2021年3月9日

その1で紹介しましたが、このSnakeRobotの原型は2017年4月23日にjoes氏がinsutructables circuitsに
公開された作品です。 それを小型で入手可能な部品に置き換えてリメークしたのがこの作品です。
ioes氏のinstructables SnakeRobotページを開きます。
製作される方は、上記ページにハード面、ソフト面共に詳しく説明されていますので熟読して下さい。
SnakeRobot_01a.jpg

主な使用部品と構成の変更
マイコン : 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」に掲載と同じものです。
ESP32_BLE_Controller.jpg

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

専用リモコンの回路図
TJ9_BLE_Control Circuit

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

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

オリジナル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度に揃っていないと
左右どちらかへ弓なり、蛇が直進しません。その場合は一直線に揃うように補正値を修正する必要が有ります。
補正の詳細はソフト編で述べます。
SnakeRobot_esp32.jpg

参考にさせて頂いた元記事では、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を使う計画でしたが、このドライバーボードが結構大きいので諦めました。
SnakeRobot_11.jpg

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

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

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

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

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

最後に動画をご覧下さい。



次回はその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)を使った自作コントローラーです。
SnakeRobot_Top.jpg

主な部品リスト(制御部のパーツを含む)
サーボモータは到着までに20日ほど掛かりますが、AliExpressが断然安かった!送料込みで1個 100円余し!
部品名型番又は商品名個数参考
アクリル板2㎜、3㎜、5㎜100X300 端材数枚 手持ち品
強力両面テープスコッチ 超強力両面テープ1個ヨドバシカメラ
タイヤ用Oリング1A-P9 [Oリング(10個入り)]32個(4 set)ヨドバシ 1set  248円
サーボモータSG9015個AliExpress 1個 1US$
マイコンモジュールESP32 WROOM 32D1個秋月電子 1個 460円
測距モジュールGP2Y0E02 I2C&analog1個秋月電子 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組作り、それを超強力両面テープを使って連結しました。
下図の斜線部分が超強力両面テープでサーボモータを固定した部分です。図はクリックすると大きく見れます。
SnakeRobot_parts.gif

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

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

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

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

最後に動画をご覧下さい。



次回はその4 制御部を掲載の予定です。

皆様の参考になれば幸いです。
by Paradise

テーマ : 電子工作
ジャンル : 趣味・実用

Snake Robot 蛇型ロボット その2

動画が出来ましたので先に掲載します。

その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_01a.jpg

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

SnakeRobot_02.jpg

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

SnakeRobot_03.jpg

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

SnakeRobot_05.jpg


次回から製作編、ソフト編を順次掲載の予定です。

皆様の参考になれば幸いです。
by Paradise

テーマ : 電子工作
ジャンル : 趣味・実用

プロフィール

CNC_Paradise

Author:CNC_Paradise
木工・彫刻・機械工作・電子工作が
大好きで自作のCNCを使って工作を
楽しんでます。

最新記事
最新コメント
月別アーカイブ
カテゴリ
何でも工作室
検索フォーム
RSSリンクの表示
リンク
QRコード
QR