MeArmロボット用精密ジョイスティックコントローラー - 座標の記録・再生機能付き
必要な部品と材料
| 部品 | 数量 |
|---|---|
| Arduino UNO | 1 |
| アナログジョイスティック(汎用) | 2 |
| 抵抗 10kΩ | 2 |
| 抵抗 680Ω | 1 |
| 5mm赤色LED | 1 |
| マイクロサーボ SG90 | 4 |
使用するアプリとオンラインサービス
- Arduino IDE
プロジェクト概要
Arduino UnoベースのMeArm®ロボットアーム用ジョイスティックコントローラーで、座標の記録・再生機能を搭載しています。追加基板なしでArduino Uno単体で動作します。USB接続(2A)または6V/2A電源が必要です。SG90サーボ4個で動作確認済みです。
操作方法
- ジョイスティック1のボタン押下: 座標記録の開始/停止
- ジョイスティック2のボタン押下: 記録済み座標の再生開始/停止
- ジョイスティック2のボタン長押し(2秒): 記録済み座標を自動ループ再生
メモ: 追加の抵抗とLEDを不要としたシンプル版「MeArm Robot - Recording of Coordinates (Minimalized Version)」も公開しています。MeArmジョイスティックボードをお持ちの場合は、「MeArm 1.6.1 Robot Joystick Board Recording Movements (IR)」版をご利用ください。
コード
Arduinoジョイスティックコントローラー for MeArm® ロボット(バージョン 1.3.1)
Arduino IDEでArduino UNOベースボードをUSBポート経由でプログラミングしてください。
/* meArm analog joysticks version 1.3.1 - UtilStudio.com Dec 2018
* 2つのアナログジョイスティックと4つのサーボを使用
* v1.3で座標記録機能を改善、いくつかのバグを修正
*
* ジョイスティック1: グリッパーの前後左右移動、ボタンで記録開始/停止
* ジョイスティック2: グリッパーの上下移動と開閉、ボタンで再生開始/停止
* ボタン2秒長押しで自動再生
*
* ピン配置:
* Arduino Stick1 Stick2 Base Shoulder Elbow Gripper Record/
* GND GND GND Brn Brn Brn Brn Auto play
* 5V VCC VCC Red Red Red Red
* A0 HOR - - - - -
* A1 VER - - - - -
* PD2 BUTT - - - - -
* A2 - HOR - - - -
* A3 - VER - - - -
* PD3 - BUTT - - - -
* 11 - - Yellow - - -
* 10 - - - Yellow - -
* 9 - - - - Yellow -
* 6 - - - - - Yellow
* PD4 - - - - - - X (LED)
*/
#include <Servo.h>
bool repeatePlaying = false; // 記録サイクルを繰り返し再生
int delayBetweenCycles = 2000; // サイクル間ディレイ
int basePin = 11; // ベースサーボ
int shoulderPin = 10; // ショルダーサーボ
int elbowPin = 9; // エルボーサーボ
int gripperPin = 6; // グリッパーサーボ
int xdirPin = 0; // ベース - ジョイスティック1
int ydirPin = 1; // ショルダー - ジョイスティック1
int zdirPin = 3; // エルボー - ジョイスティック2
int gdirPin = 2; // グリッパー - ジョイスティック2
int pinRecord = PD2; // 記録ボタン - 推奨(A4は非推奨、追加ジョイスティック用に予約)
int pinPlay = PD3; // 再生ボタン - 推奨(A5は非推奨、追加ジョイスティック用に予約)
int pinLedRecord = PD4; // LED - 記録中点灯、自動再生モードで点滅
bool useInternalPullUpResistors = false;
const int buffSize = 512; // 録音バッファサイズ
// サーボ初期位置
int startBase = 90, startShoulder = 90, startElbow = 90, startGripper = 0;
int posBase = 90, posShoulder = 90, posElbow = 90, posGripper = 0;
int lastBase = 90, lastShoulder = 90, lastElbow = 90, lastGripper = 90;
// サーボ可動範囲
int minBase = 0, maxBase = 150;
int minShoulder = 0, maxShoulder = 150;
int minElbow = 0, maxElbow = 150;
int minGripper = 0, maxGripper = 150;
const int countServo = 4;
int buff[buffSize];
int buffAdd[countServo];
int recPos = 0, playPos = 0;
int buttonRecord = HIGH, buttonPlay = HIGH;
int buttonRecordLast = LOW, buttonPlayLast = LOW;
bool record = false, play = false, debug = false;
String command = "Manual";
int printPos = 0;
int buttonPlayDelay = 20, buttonPlayCount = 0;
bool ledLight = false;
Servo servoBase, servoShoulder, servoElbow, servoGripper;
void setup() {
Serial.begin(9600);
if (useInternalPullUpResistors) {
pinMode(pinRecord, INPUT_PULLUP);
pinMode(pinPlay, INPUT_PULLUP);
} else {
pinMode(pinRecord, INPUT);
pinMode(pinPlay, INPUT);
}
pinMode(xdirPin, INPUT);
pinMode(ydirPin, INPUT);
pinMode(zdirPin, INPUT);
pinMode(gdirPin, INPUT);
pinMode(pinLedRecord, OUTPUT);
servoBase.attach(basePin);
servoShoulder.attach(shoulderPin);
servoElbow.attach(elbowPin);
servoGripper.attach(gripperPin);
StartPosition();
digitalWrite(pinLedRecord, HIGH);
delay(1000);
digitalWrite(pinLedRecord, LOW);
}
void loop() {
buttonRecord = digitalRead(pinRecord);
buttonPlay = digitalRead(pinPlay);
// 再生ボタン長押し検出(自動ループ再生)
if (buttonPlay == LOW) {
buttonPlayCount++;
if (buttonPlayCount >= buttonPlayDelay) repeatePlaying = true;
} else buttonPlayCount = 0;
// 再生ボタン状態変化
if (buttonPlay != buttonPlayLast) {
if (record) record = false;
if (buttonPlay == LOW) {
play = !play;
repeatePlaying = false;
if (play) StartPosition();
}
}
// 記録ボタン状態変化
if (buttonRecord != buttonRecordLast) {
if (buttonRecord == LOW) {
record = !record;
if (record) {
play = false; repeatePlaying = false; recPos = 0;
} else if (debug) PrintBuffer();
}
}
buttonPlayLast = buttonPlay;
buttonRecordLast = buttonRecord;
// ジョイスティック入力読み取り(-5.0〜5.0にマッピング、デッドゾーン±1.5)
float dx = map(analogRead(xdirPin), 0, 1023, -5.0, 5.0);
float dy = map(analogRead(ydirPin), 0, 1023, 5.0, -5.0);
float dz = map(analogRead(zdirPin), 0, 1023, 5.0, -5.0);
float dg = map(analogRead(gdirPin), 0, 1023, 5.0, -5.0);
if (abs(dx) < 1.5) dx = 0;
if (abs(dy) < 1.5) dy = 0;
if (abs(dz) < 1.5) dz = 0;
if (abs(dg) < 1.5) dg = 0;
posBase += dx;
posShoulder += dy;
posElbow += dz;
posGripper += dg;
// 再生モード処理
if (play) {
if (playPos >= recPos) {
playPos = 0;
if (repeatePlaying) {
delay(delayBetweenCycles);
StartPosition();
} else play = false;
}
bool endOfData = false;
while (!endOfData) {
if (playPos >= buffSize - 1) break;
if (playPos >= recPos) break;
int data = buff[playPos];
int angle = data & 0xFFF;
int servoNumber = data & 0x3000;
endOfData = data & 0x4000;
switch (servoNumber) {
case 0x0000: posBase = angle; break;
case 0x1000: posShoulder = angle; break;
case 0x2000: posElbow = angle; break;
case 0x3000: posGripper = angle; dg = posGripper - lastGripper; break;
}
playPos++;
}
}
// 可動範囲制限
posBase = constrain(posBase, minBase, maxBase);
posShoulder = constrain(posShoulder, minShoulder, maxShoulder);
posElbow = constrain(posElbow, minElbow, maxElbow);
posGripper = constrain(posGripper, minGripper, maxGripper);
servoBase.write(posBase);
servoShoulder.write(posShoulder);
servoElbow.write(posElbow);
bool waitGripper = false;
if (dg < 0) { posGripper = minGripper; waitGripper = true; }
else if (dg > 0) { posGripper = maxGripper; waitGripper = true; }
servoGripper.write(posGripper);
if (play && waitGripper) delay(1000);
// 位置変化時の記録処理とシリアル出力
if (lastBase != posBase || lastShoulder != posShoulder ||
lastElbow != posElbow || lastGripper != posGripper) {
if (record && recPos < buffSize - countServo) {
int buffPos = 0;
if (lastBase != posBase) { buffAdd[buffPos] = posBase; buffPos++; }
if (lastShoulder != posShoulder) { buffAdd[buffPos] = posShoulder | 0x1000; buffPos++; }
if (lastElbow != posElbow) { buffAdd[buffPos] = posElbow | 0x2000; buffPos++; }
if (lastGripper != posGripper) { buffAdd[buffPos] = posGripper | 0x3000; buffPos++; }
buffAdd[buffPos - 1] |= 0x4000; // 終端フラグ
for (int i = 0; i < buffPos; i++) buff[recPos + i] = buffAdd[i];
recPos += buffPos;
}
command = "Manual"; printPos = 0;
if (play) { command = "Play"; printPos = playPos; }
else if (record) { command = "Record"; printPos = recPos; }
Serial.print(command); Serial.print("\t");
Serial.print(printPos); Serial.print("\t");
Serial.print(posBase); Serial.print("\t");
Serial.print(posShoulder); Serial.print("\t");
Serial.print(posElbow); Serial.print("\t");
Serial.print(posGripper); Serial.print("\t");
Serial.print(record); Serial.print("\t");
Serial.println(play);
}
lastBase = posBase; lastShoulder = posShoulder;
lastElbow = posElbow; lastGripper = posGripper;
// LED制御:自動再生中は点滅、記録中は点灯
if (repeatePlaying) ledLight = !ledLight;
else { if (ledLight) ledLight = false; if (record) ledLight = true; }
digitalWrite(pinLedRecord, ledLight);
delay(50);
}
void PrintBuffer() {
for (int i = 0; i < recPos; i++) {
int data = buff[i];
int angle = data & 0xFFF;
int servoNumber = data & 0x3000;
bool endOfData = data & 0x4000;
Serial.print("Servo="); Serial.print(servoNumber);
Serial.print("\tAngle="); Serial.print(angle);
Serial.print("\tEnd="); Serial.print(endOfData);
Serial.print("\tData="); Serial.println(data, BIN);
}
}
void StartPosition() {
int angleBase = servoBase.read();
int angleShoulder = servoShoulder.read();
int angleElbow = servoElbow.read();
int angleGripper = servoGripper.read();
Serial.print(angleBase); Serial.print("\t");
Serial.print(angleShoulder); Serial.print("\t");
Serial.print(angleElbow); Serial.print("\t");
Serial.println(angleGripper);
posBase = startBase; posShoulder = startShoulder;
posElbow = startElbow; posGripper = startGripper;
servoBase.write(posBase);
servoShoulder.write(posShoulder);
servoElbow.write(posElbow);
servoGripper.write(posGripper);
}
製造プロセス
- DIY Arduino USBゲームコントローラー - 高性能ゲームパッドを自作
- MeArmロボットアーム V1.0 – Arduino/Raspberry Piで作るロボットアーム
- 古いラジコンカーをXboxコントローラーで操作可能に - ArduinoとUSBホストシールドで実現
- Arduino UNOで作る完全自律ライントラッカーロボット ─ 入門に最適な自作キット
- Arduino UNOで作るシンプル電卓 - シリアルモニターで四則演算
- Arduino Nanoで音声制御ロボットを自作
- Arduino UNOとATtiny85で作る携帯型POV(残像)ディスプレイ
- ゲーム用カスタムArduinoジョイスティックハンドルの製作
- ESP32-CAMで作るAI顔認識ドアロックシステム:ローカル処理でプライバシー保護
- ArduinoとBluetoothでスマートフォン温度監視システムを構築
- Arduino向けスマート屋内ナビゲーションロボットキット