산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

MeArm 로봇용 정밀 조이스틱 컨트롤러 - 아두이노 우노로 좌표 기록 및 재생

부품 및 소모품

부품수량
아두이노 UNO1
아날로그 조이스틱 (일반)2
저항 10k 옴2
저항 680 옴1
5mm 적색 LED1
마이크로 서보 SG904

앱 및 온라인 서비스

프로젝트 개요

로봇 팔(MeArm®)용 아두이노 우노 조이스틱 컨트롤러로 좌표 기록 기능을 제공합니다. 버튼 한 번 클릭으로 기록된 좌표를 한 번 재생하거나 반복 재생할 수 있습니다. 추가 보드 없이 아두이노 우노 기본 보드만 사용하며 USB 연결(2A) 또는 6V/2A 전원 공급이 필요합니다. SG90 서보 4개로 테스트 완료.

조작 방법

참고: 저항과 LED가 필요 없는 단순화 버전도 있습니다: MeArm Robot - Recording of Coordinates (Minimalized Version). MeArm 조이스틱 보드가 있는 경우: MeArm 1.6.1 Robot Joystick Board Recording Movements (IR)

소스 코드

아두이노 조이스틱 컨트롤러 for MeArm® 로봇 (버전 1.3.1)

/* meArm 아날로그 조이스틱 버전 1.3.1 - UtilStudio.com Dec 2018
 * 두 개의 아날로그 조이스틱과 네 개의 서보 사용
 * 버전 1.3에서 좌표 기록 기능 개선, 일부 버그 수정
 * 첫 번째 조이스틱: 그리퍼 전진/후진, 좌/우 이동, 버튼으로 기록 시작/정지
 * 두 번째 조이스틱: 그리퍼 상/하 이동, 열기/닫기, 버튼으로 재생 시작/정지
 * 버튼 2초 누름: 자동 재생
 * 핀 연결:
 * Arduino  Stick1  Stick2  Base  Shoulder  Elbow  Gripper  Record/Autoplay
 * GND      GND     GND     Brown Brown     Brown  Brown
 * 5V       VCC     VCC     Red   Red       Red    Red
 * A0       HOR     A1      VER
 * A2       HOR     A3      VER
 * PD2      BUTT    A4      (권장: A4는 추가 조이스틱용으로 예약)
 * PD3      BUTT    A5      (권장: A5는 추가 조이스틱용으로 예약)
 * 11       Yellow                     Base 서보
 * 10       Yellow                     Shoulder 서보
 * 9        Yellow                     Elbow 서보
 * 6        Yellow                     Gripper 서보
 * PD4      X                          LED 표시
 */
#include <Servo.h>

bool repeatePlaying = false;  // 기록된 사이클 반복 실행
int delayBetweenCycles = 2000; // 사이클 간 지연(ms)

int basePin = 11;
int shoulderPin = 10;
int elbowPin = 9;
int gripperPin = 6;

int xdirPin = 0;  // Base - 조이스틱1
int ydirPin = 1;  // Shoulder - 조이스틱1
int zdirPin = 3;  // Elbow - 조이스틱2
int gdirPin = 2;  // Gripper - 조이스틱2

int pinRecord = 2;  // 기록 버튼 (PD2 권장)
int pinPlay = 3;    // 재생 버튼 (PD3 권장)
int pinLedRecord = 4; // 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);
  
  // 재생 버튼 2초 이상 누름 감지 -> 자동 반복 재생
  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;
  
  // 조이스틱 값 읽기 및 데드존 적용
  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);
}

회로도

MeArm® 로봇용 조이스틱 컨트롤러 회로도 (좌표 기록 기능 포함)

테스트 보드: 서보 2개만 연결된 상태. 4개 서보 연결은 회로도 참조.

제조공정

  1. DIY 아두이노 USB 게임 컨트롤러 - 나만의 고성능 게임패드 만들기
  2. MeArm 로봇 팔 V1.0 – 아두이노 & 라즈베리파이로 만드는 로봇 암
  3. 낡은 RC카를 아두이노와 엑스박스 컨트롤러로 조종하는 차량으로 개조하기
  4. 아두이노 우노로 자율 주행 라인 트레이서 로봇 만들기
  5. 아두이노 우노로 기초 계산기 만들기 - 쉬운 프로젝트
  6. 아두이노 나노로 만드는 음성 제어 로봇 - 블루투스와 안드로이드 앱 활용
  7. 아두이노 우노와 ATtiny85로 만드는 휴대용 POV(잔상) 디스플레이
  8. 게이밍용 커스텀 아두이노 조이스틱 스티어링 휠 만들기
  9. Arduino DMX-512 테스터 컨트롤러 – 안정적인 조명 제어를 위한 풀 파츠 키트
  10. 아두이노 프로젝트를 위한 스마트 실내 내비게이션 로봇 키트