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

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

이 튜토리얼에서는 이전 영상에서 만든 메카넘 휠 로봇 플랫폼과 3D 프린팅 로봇 팔(이전 아두이노 프로젝트)을 연동해 자동으로 작동하게 만드는 방법을 보여드립니다.

다음 영상을 시청하거나 아래 작성된 튜토리얼을 읽어보세요.

개요

메카넘 휠 로봇은 이전 영상에서 설명한 것과 같은 커스텀 안드로이드 앱으로 제어합니다. 여기에 로봇 팔 제어 버튼이 추가됐습니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

원래 로봇 팔 제어 앱은 슬라이더로 관절을 제어했는데, 팔 안정성에 문제가 있었습니다. 이번 방식이 훨씬 잘 작동하므로, 업데이트된 로봇 팔 제어 앱과 아두이노 코드를 원래 로봇 팔 프로젝트에도 제공하겠습니다.

이 로봇의 가장 멋진 기능은 움직임을 저장했다가 자동으로 반복 실행하는 기능입니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

Save 버튼으로 각 단계의 모터 위치를 저장합니다. Run 버튼을 누르면 저장된 움직임을 반복 실행합니다.

아두이노 로봇 제작

메카넘 휠 플랫폼은 이미 조립된 상태입니다(자세한 내용은 이전 영상 참고).

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

3D 프린팅된 로봇 팔 부품과 서보 모터가 준비됐으니 조립해보겠습니다. 이 프로젝트의 3D 모델입니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

Thangs에서 3D 모델을 찾아 다운로드하거나 브라우저에서 탐색할 수 있습니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

Thangs에서 3D 모델 다운로드.

STL 파일(3D 프린팅용):

로봇 팔의 첫 번째 서보(요 축)는 메카넘 휠 플랫폼의 상단 커버에 직접 장착합니다.

위치를 표시하고 10mm 드릴로 여러 구멍을 뚫었습니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

줄로 구멍을 뚫고 서보 장착용 개구부를 다듬었습니다. M3 볼트와 너트 4개로 서보를 상단 플레이트에 고정했습니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

이 서보의 출력 샤프트에 서보 액세서리로 제공되는 원형 혼을 이용해 로봇 팔의 허리(waist) 파트를 연결합니다. 그런데 이대로면 파트가 플레이트에서 약 8mm 위에 떠 있게 됩니다. 그래서 8mm MDF 보드 두 조각을 대 허리 파트가 그 위를 슬라이드하며 관절이 더 안정되게 했습니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

원형 혼은 서보와 함께 제공되는 탭핑 나사로 허리 파트에 고정하고, 서보 샤프트에는 제공되는 볼트로 고정합니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

다음은 어깨(shoulder) 서보입니다. 제자리에 놓고 3D 프린팅 파트에 탭핑 나사로 고정합니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

원형 혼을 다음 파트에 끼우고, 두 파트를 서보 출력 샤프트의 볼트로 서로 고정합니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

파트 고정 전에 서보가 전체 동작 범위를 가질 수 있는지 확인해야 합니다. 여기에 고무줄을 추가해 어깨 관절을 보조했습니다. 이 서보가 나머지 팔과 페이로드 무게를 지탱해야 하기 때문입니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

비슷한 방식으로 나머지 로봇 팔을 조립합니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

그리퍼(gripper) 메커니즘을 조립합니다. SG90 서보 모터로 제어하며, 먼저 커스텀 설계된 기어 링크를 서보에 장착합니다. 반대쪽에 또 다른 기어 링크를 짝지어 M4 볼트와 너트로 고정합니다. 사실 모든 링크는 M4 볼트와 너트로 연결됩니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

그리퍼 3D 모델의 구멍이 원래 3mm인데 M3 볼트가 부족해 4mm 드릴로 확장해 M4 볼트를 사용했습니다.

그리퍼 메커니즘 조립 완료 후 마지막 서보에 고정하면 로봇 팔 완성입니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

케이블 정리를 했습니다. 서보 선들을 로봇 팔의 전용 홀들을 통과시켰습니다. 상단 플레이트에 10mm 드릴로 구멍을 뚫어 선들이 통과하게 했습니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

케이블 타이로 모든 선을 묶고, 이제 아두이노 보드에 연결만 하면 됩니다.

아두이노 로봇 회로도

이 프로젝트의 회로도와 연결 방법입니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

필요한 부품:

  • 스테퍼 모터 – NEMA 17
  • DRV8825 스테퍼 드라이버
  • MG996R 서보 모터
  • SG90 마이크로 서보 모터
  • HC-05 블루투스 모듈
  • 리튬폴리머 배터리
  • 아두이노 메가 보드

이전 튜토리얼에서 메카넘 휠 로봇 부분의 작동 원리와 커스텀 PCB 제작 과정을 설명했습니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

이 PCB에 5V 전압 레귤레이터(LM350, 최대 3A)를 포함시켰습니다. 서보 모터가 5V로 동작하므로 이를 위해 추가했습니다. 로봇 팔의 6개 서보가 2~3A 전류를 소모할 수 있는데, 레귤레이터가 감당은 하지만 매우 뜨거워집니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

그래서 방열판을 달고, 방열판만으로는 부족해 소형 12V DC 팬으로 강제 냉각했습니다.

서보 신호선은 아두이노 디지털 핀 5~10번에 연결하고, 전원은 PCB의 5V 핀 헤더에서 공급했습니다. 모든 선을 플랫폼 내부로 밀어 넣고 상단 플레이트를 너트 두 개로 고정했습니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

이제 프로젝트 조립이 완료됐습니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

아두이노 코드

아두이노 코드와 안드로이드 앱 작동 원리를 살펴보겠습니다. 코드가 길어 이해를 돕기 위해 섹션별로 나누어 설명하고, 마지막에 전체 소스 코드를 제공합니다.

먼저 6개 서보, 4개 스테퍼 모터, 블루투스 통신을 정의하고 프로그램에 필요한 변수들을 선언합니다. setup에서 스테퍼 최대 속도 설정, 서보 연결 핀 정의, 블루투스 통신 시작, 로봇 팔 초기 위치 설정 등을 합니다.

초기 설정 코드

#include <SoftwareSerial.h>
#include <AccelStepper.h>
#include <Servo.h>

Servo servo01; Servo servo02; Servo servo03;
Servo servo04; Servo servo05; Servo servo06;

SoftwareSerial Bluetooth(A8, 38); // 아두이노(RX, TX) - HC-05 블루투스(TX, RX)

// 스테퍼 모터 및 핀 정의
AccelStepper LeftBackWheel(1, 42, 43);   // (Type:driver, STEP, DIR) - Stepper1
AccelStepper LeftFrontWheel(1, 40, 41);  // Stepper2
AccelStepper RightBackWheel(1, 44, 45);  // Stepper3
AccelStepper RightFrontWheel(1, 46, 47); // Stepper4

#define led 14
int wheelSpeed = 1500;
int lbw[50], lfw[50], rbw[50], rfw[50]; // 위치/스텝 저장용 배열
int servo1Pos, servo2Pos, servo3Pos, servo4Pos, servo5Pos, servo6Pos; // 현재 위치
int servo1PPos, servo2PPos, servo3PPos, servo4PPos, servo5PPos, servo6PPos; // 이전 위치
int servo01SP[50], servo02SP[50], servo03SP[50], servo04SP[50], servo05SP[50], servo06SP[50]; // 저장용
int speedDelay = 20;
int index = 0;
int dataIn;
int m = 0;

void setup() {
  // 스테퍼 초기 최대 속도 설정
  LeftFrontWheel.setMaxSpeed(3000);
  LeftBackWheel.setMaxSpeed(3000);
  RightFrontWheel.setMaxSpeed(3000);
  RightBackWheel.setMaxSpeed(3000);
  
  pinMode(led, OUTPUT);
  servo01.attach(5); servo02.attach(6); servo03.attach(7);
  servo04.attach(8); servo05.attach(9); servo06.attach(10);
  
  Bluetooth.begin(38400); // 블루투스 모듈 기본 보드레이트
  Bluetooth.setTimeout(5);
  delay(20);
  Serial.begin(38400);
  
  // 로봇 팔 초기 위치로 이동
  servo1PPos = 90;  servo01.write(servo1PPos);
  servo2PPos = 100; servo02.write(servo2PPos);
  servo3PPos = 120; servo03.write(servo3PPos);
  servo4PPos = 95;  servo04.write(servo4PPos);
  servo5PPos = 60;  servo05.write(servo5PPos);
  servo6PPos = 110; servo06.write(servo6PPos);
}

loop 섹션에서는 수신 데이터가 있는지 확인하는 것으로 시작합니다.

// 수신 데이터 확인
if (Bluetooth.available() > 0) {
  dataIn = Bluetooth.read(); // 데이터 읽기

이 데이터는 스마트폰/안드로이드 앱에서 옵니다. 앱이 어떤 데이터를 보내는지 봅시다. 안드로이드 앱은 MIT App Inventor로 만들었습니다. 적절한 이미지를 배경으로 한 단순 버튼들로 구성됩니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

앱 블록을 보면 버튼 클릭 시 1바이트 숫자를 전송하는 것만 합니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

클릭된 버튼에 따라 아두이노에 무엇을 할지 지시합니다. 예를 들어 '2'를 수신하면 moveForward() 커스텀 함수를 호출해 메카넘 휠 플랫폼을 전진시킵니다.

if (dataIn == 2) { m = 2; }
if (m == 2) { moveForward(); }

이 커스텀 함수는 네 개 스테퍼 모터를 모두 정방향으로 회전시킵니다.

void moveForward() {
  LeftFrontWheel.setSpeed(wheelSpeed);
  LeftBackWheel.setSpeed(wheelSpeed);
  RightFrontWheel.setSpeed(wheelSpeed);
  RightBackWheel.setSpeed(wheelSpeed);
}

다른 방향으로 이동하려면 휠을 적절한 방향으로 회전시키면 됩니다.

로봇 팔 제어도 같은 방식입니다. 앱에 버튼이 있고, 버튼을 누르고 있으면 해당 관절이 특정 방향으로 움직입니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

앞서 언급했듯 원래 로봇 팔 제어 앱에서는 서보 위치를 슬라이더로 제어했는데, 이때 텍스트를 아두이노로 전송해야 했습니다. 아두이노가 앱에서 오는 텍스트를 가끔 놓쳐 에러가 나거나 로봇 팔이 떨리고 비정상 동작하는 문제가 있었습니다.

이번 방식에서는 특정 버튼이 터치 다운될 때 1바이트 숫자만 간단히 보냅니다.

메카넘 휠 플랫폼과 로봇 팔의 완벽한 연동 제어

아두이노 코드는 해당 숫자의 while 루프에 진입해 버튼 터치 업(숫자 0 전송 = 아무 동작 안 함)될 때까지 머뭅니다.

// 서보 1 양방향 이동
while (m == 16) {
  if (Bluetooth.available() > 0) { m = Bluetooth.read(); }
  servo01.write(servo1PPos);
  servo1PPos++;
  delay(speedDelay);
}
while (m == 17) {
  if (Bluetooth.available() > 0) { m = Bluetooth.read(); }
  servo01.write(servo1PPos);
  servo1PPos--;
  delay(speedDelay);
}

터치된 버튼에 따라 서보가 양/음 방향으로 움직입니다. 같은 원리가 모든 서보에 적용됩니다. 이동 속도 변경은 슬라이더 값(100~250)을 사용합니다.

// 팔 속도 슬라이더 변경 시
if (dataIn > 101 & dataIn < 250) {
  speedDelay = dataIn / 10; // 서보 속도(지연 시간) 변경
}

10으로 나누면 10~25 값이 나와 while 루프의 서보 구동 지연 시간(마이크로초)으로 쓰입니다.

로봇 움직임 저장은 Save 버튼 클릭 시 현재 서보/스테퍼 위치를 배열에 저장하는 방식입니다.

// "SAVE" 버튼 눌림
if (m == 12) {
  // 최초 저장 시 스테퍼 위치를 0으로 초기화
  if (index == 0) {
    LeftBackWheel.setCurrentPosition(0);
    LeftFrontWheel.setCurrentPosition(0);
    RightBackWheel.setCurrentPosition(0);
    RightFrontWheel.setCurrentPosition(0);
  }
  lbw[index] = LeftBackWheel.currentPosition();
  lfw[index] = LeftFrontWheel.currentPosition();
  rbw[index] = RightBackWheel.currentPosition();
  rfw[index] = RightFrontWheel.currentPosition();
  servo01SP[index] = servo1PPos;
  servo02SP[index] = servo2PPos;
  servo03SP[index] = servo3PPos;
  servo04SP[index] = servo4PPos;
  servo05SP[index] = servo5PPos;
  servo06SP[index] = servo6PPos;
  index++;
  m = 0;
}

Run 버튼을 누르면 runSteps() 커스텀 함수를 호출합니다. 이 함수는 for/while 루프로 저장된 모든 단계를 실행합니다.

if (m == 14) {
  runSteps();
  // "RESET" 버튼 눌림
  if (dataIn != 14) {
    stopMoving();
    memset(lbw, 0, sizeof(lbw));
    memset(lfw, 0, sizeof(lfw));
    memset(rbw, 0, sizeof(rbw));
    memset(rfw, 0, sizeof(rfw));
    memset(servo01SP, 0, sizeof(servo01SP));
    memset(servo02SP, 0, sizeof(servo02SP));
    memset(servo03SP, 0, sizeof(servo03SP));
    memset(servo04SP, 0, sizeof(servo04SP));
    memset(servo05SP, 0, sizeof(servo05SP));
    memset(servo06SP, 0, sizeof(servo06SP));
    index = 0;
  }
}

첫 위치부터 마지막 위치까지 실행하고 반복합니다. 따라서 단계를 저장할 때 첫 단계와 마지막 단계 위치가 같도록 로봇을 위치시켜야 합니다. 실행 중에도 플랫폼과 로봇 팔 속도 변경, 일시정지, 전체 단계 리셋이 가능합니다.

앱과 편집 가능한 프로젝트 파일 다운로드:

이 아두이노 로봇 프로젝트의 전체 코드:

/* Arduino Robot Arm and Mecanum Wheels Robot Smartphone Control via Bluetooth by Dejan, www.HowToMechatronics.com */
#include <SoftwareSerial.h>
#include <AccelStepper.h>
#include <Servo.h>

Servo servo01; Servo servo02; Servo servo03;
Servo servo04; Servo servo05; Servo servo06;

SoftwareSerial Bluetooth(A8, 38); // Arduino(RX, TX) - HC-05 Bluetooth (TX, RX)

AccelStepper LeftBackWheel(1, 42, 43);
AccelStepper LeftFrontWheel(1, 40, 41);
AccelStepper RightBackWheel(1, 44, 45);
AccelStepper RightFrontWheel(1, 46, 47);

#define led 14
int wheelSpeed = 1500;
int lbw[50], lfw[50], rbw[50], rfw[50];
int servo1Pos, servo2Pos, servo3Pos, servo4Pos, servo5Pos, servo6Pos;
int servo1PPos, servo2PPos, servo3PPos, servo4PPos, servo5PPos, servo6PPos;
int servo01SP[50], servo02SP[50], servo03SP[50], servo04SP[50], servo05SP[50], servo06SP[50];
int speedDelay = 20;
int index = 0;
int dataIn;
int m = 0;

void setup() {
  LeftFrontWheel.setMaxSpeed(3000);
  LeftBackWheel.setMaxSpeed(3000);
  RightFrontWheel.setMaxSpeed(3000);
  RightBackWheel.setMaxSpeed(3000);
  pinMode(led, OUTPUT);
  servo01.attach(5); servo02.attach(6); servo03.attach(7);
  servo04.attach(8); servo05.attach(9); servo06.attach(10);
  Bluetooth.begin(38400);
  Bluetooth.setTimeout(5);
  delay(20);
  Serial.begin(38400);
  servo1PPos = 90;  servo01.write(servo1PPos);
  servo2PPos = 100; servo02.write(servo2PPos);
  servo3PPos = 120; servo03.write(servo3PPos);
  servo4PPos = 95;  servo04.write(servo4PPos);
  servo5PPos = 60;  servo05.write(servo5PPos);
  servo6PPos = 110; servo06.write(servo6PPos);
}

void loop() {
  if (Bluetooth.available() > 0) {
    dataIn = Bluetooth.read();
    if (dataIn == 0) { m = 0; }
    if (dataIn == 1) { m = 1; }
    if (dataIn == 2) { m = 2; }
    if (dataIn == 3) { m = 3; }
    if (dataIn == 4) { m = 4; }
    if (dataIn == 5) { m = 5; }
    if (dataIn == 6) { m = 6; }
    if (dataIn == 7) { m = 7; }
    if (dataIn == 8) { m = 8; }
    if (dataIn == 9) { m = 9; }
    if (dataIn == 10) { m = 10; }
    if (dataIn == 11) { m = 11; }
    if (dataIn == 12) { m = 12; }
    if (dataIn == 14) { m = 14; }
    if (dataIn == 16) { m = 16; }
    if (dataIn == 17) { m = 17; }
    if (dataIn == 18) { m = 18; }
    if (dataIn == 19) { m = 19; }
    if (dataIn == 20) { m = 20; }
    if (dataIn == 21) { m = 21; }
    if (dataIn == 22) { m = 22; }
    if (dataIn == 23) { m = 23; }
    if (dataIn == 24) { m = 24; }
    if (dataIn == 25) { m = 25; }
    if (dataIn == 26) { m = 26; }
    if (dataIn == 27) { m = 27; }

  // 메카넘 휠 플랫폼 이동
  if (m == 4) { moveSidewaysLeft(); }
  if (m == 5) { moveSidewaysRight(); }
  if (m == 2) { moveForward(); }
  if (m == 7) { moveBackward(); }
  if (m == 3) { moveRightForward(); }
  if (m == 1) { moveLeftForward(); }
  if (m == 8) { moveRightBackward(); }
  if (m == 6) { moveLeftBackward(); }
  if (m == 9) { rotateLeft(); }
  if (m == 10) { rotateRight(); }
  if (m == 0) { stopMoving(); }

  // 메카넘 휠 속도
  if (dataIn > 30 & dataIn < 100) { wheelSpeed = dataIn * 20; }

  // 로봇 팔 이동
  while (m == 16) { if (Bluetooth.available() > 0) { m = Bluetooth.read(); } servo01.write(servo1PPos); servo1PPos++; delay(speedDelay); }
  while (m == 17) { if (Bluetooth.available() > 0) { m = Bluetooth.read(); } servo01.write(servo1PPos); servo1PPos--; delay(speedDelay); }
  while (m == 19) { if (Bluetooth.available() > 0) { m = Bluetooth.read(); } servo02.write(servo2PPos); servo2PPos++; delay(speedDelay); }
  while (m == 18) { if (Bluetooth.available() > 0) { m = Bluetooth.read(); } servo02.write(servo2PPos); servo2PPos--; delay(speedDelay); }
  while (m == 20) { if (Bluetooth.available() > 0) { m = Bluetooth.read(); } servo03.write(servo3PPos); servo3PPos++; delay(speedDelay); }
  while (m == 21) { if (Bluetooth.available() > 0) { m = Bluetooth.read(); } servo03.write(servo3PPos); servo3PPos--; delay(speedDelay); }
  while (m == 23) { if (Bluetooth.available() > 0) { m = Bluetooth.read(); } servo04.write(servo4PPos); servo4PPos++; delay(speedDelay); }
  while (m == 22) { if (Bluetooth.available() > 0) { m = Bluetooth.read(); } servo04.write(servo4PPos); servo4PPos--; delay(speedDelay); }
  while (m == 25) { if (Bluetooth.available() > 0) { m = Bluetooth.read(); } servo05.write(servo5PPos); servo5PPos++; delay(speedDelay); }
  while (m == 24) { if (Bluetooth.available() > 0) { m = Bluetooth.read(); } servo05.write(servo5PPos); servo5PPos--; delay(speedDelay); }
  while (m == 26) { if (Bluetooth.available() > 0) { m = Bluetooth.read(); } servo06.write(servo6PPos); servo6PPos++; delay(speedDelay); }
  while (m == 27) { if (Bluetooth.available() > 0) { m = Bluetooth.read(); } servo06.write(servo6PPos); servo6PPos--; delay(speedDelay); }

  // 팔 속도 슬라이더 변경
  if (dataIn > 101 & dataIn < 250) { speedDelay = dataIn / 10; }

  // SAVE 버튼
  if (m == 12) {
    if (index == 0) {
      LeftBackWheel.setCurrentPosition(0);
      LeftFrontWheel.setCurrentPosition(0);
      RightBackWheel.setCurrentPosition(0);
      RightFrontWheel.setCurrentPosition(0);
    }
    lbw[index] = LeftBackWheel.currentPosition();
    lfw[index] = LeftFrontWheel.currentPosition();
    rbw[index] = RightBackWheel.currentPosition();
    rfw[index] = RightFrontWheel.currentPosition();
    servo01SP[index] = servo1PPos;
    servo02SP[index] = servo2PPos;
    servo03SP[index] = servo3PPos;
    servo04SP[index] = servo4PPos;
    servo05SP[index] = servo5PPos;
    servo06SP[index] = servo6PPos;
    index++;
    m = 0;
  }

  // RUN 버튼
  if (m == 14) {
    runSteps();
    if (dataIn != 14) {
      stopMoving();
      memset(lbw, 0, sizeof(lbw));
      memset(lfw, 0, sizeof(lfw));
      memset(rbw, 0, sizeof(rbw));
      memset(rfw, 0, sizeof(rfw));
      memset(servo01SP, 0, sizeof(servo01SP));
      memset(servo02SP, 0, sizeof(servo02SP));
      memset(servo03SP, 0, sizeof(servo03SP));
      memset(servo04SP, 0, sizeof(servo04SP));
      memset(servo05SP, 0, sizeof(servo05SP));
      memset(servo06SP, 0, sizeof(servo06SP));
      index = 0;
    }
  }
  }

  LeftFrontWheel.runSpeed();
  LeftBackWheel.runSpeed();
  RightFrontWheel.runSpeed();
  RightBackWheel.runSpeed();

  // 배터리 전압 모니터링
  int sensorValue = analogRead(A0);
  float voltage = sensorValue * (5.0 / 1023.00) * 3;
  if (voltage < 11) { digitalWrite(led, HIGH); }
  else { digitalWrite(led, LOW); }
}

void moveForward() {
  LeftFrontWheel.setSpeed(wheelSpeed);
  LeftBackWheel.setSpeed(wheelSpeed);
  RightFrontWheel.setSpeed(wheelSpeed);
  RightBackWheel.setSpeed(wheelSpeed);
}
void moveBackward() {
  LeftFrontWheel.setSpeed(-wheelSpeed);
  LeftBackWheel.setSpeed(-wheelSpeed);
  RightFrontWheel.setSpeed(-wheelSpeed);
  RightBackWheel.setSpeed(-wheelSpeed);
}
void moveSidewaysRight() {
  LeftFrontWheel.setSpeed(wheelSpeed);
  LeftBackWheel.setSpeed(-wheelSpeed);
  RightFrontWheel.setSpeed(-wheelSpeed);
  RightBackWheel.setSpeed(wheelSpeed);
}
void moveSidewaysLeft() {
  LeftFrontWheel.setSpeed(-wheelSpeed);
  LeftBackWheel.setSpeed(wheelSpeed);
  RightFrontWheel.setSpeed(wheelSpeed);
  RightBackWheel.setSpeed(-wheelSpeed);
}
void rotateLeft() {
  LeftFrontWheel.setSpeed(-wheelSpeed);
  LeftBackWheel.setSpeed(-wheelSpeed);
  RightFrontWheel.setSpeed(wheelSpeed);
  RightBackWheel.setSpeed(wheelSpeed);
}
void rotateRight() {
  LeftFrontWheel.setSpeed(wheelSpeed);
  LeftBackWheel.setSpeed(wheelSpeed);
  RightFrontWheel.setSpeed(-wheelSpeed);
  RightBackWheel.setSpeed(-wheelSpeed);
}
void moveRightForward() {
  LeftFrontWheel.setSpeed(wheelSpeed);
  LeftBackWheel.setSpeed(0);
  RightFrontWheel.setSpeed(0);
  RightBackWheel.setSpeed(wheelSpeed);
}
void moveRightBackward() {
  LeftFrontWheel.setSpeed(0);
  LeftBackWheel.setSpeed(-wheelSpeed);
  RightFrontWheel.setSpeed(-wheelSpeed);
  RightBackWheel.setSpeed(0);
}
void moveLeftForward() {
  LeftFrontWheel.setSpeed(0);
  LeftBackWheel.setSpeed(wheelSpeed);
  RightFrontWheel.setSpeed(wheelSpeed);
  RightBackWheel.setSpeed(0);
}
void moveLeftBackward() {
  LeftFrontWheel.setSpeed(-wheelSpeed);
  LeftBackWheel.setSpeed(0);
  RightFrontWheel.setSpeed(0);
  RightBackWheel.setSpeed(-wheelSpeed);
}
void stopMoving() {
  LeftFrontWheel.setSpeed(0);
  LeftBackWheel.setSpeed(0);
  RightFrontWheel.setSpeed(0);
  RightBackWheel.setSpeed(0);
}

// 자동 모드: 저장된 단계 실행
void runSteps() {
  while (dataIn != 13) {
    for (int i = 0; i <= index - 2; i++) {
      if (Bluetooth.available() > 0) {
        dataIn = Bluetooth.read();
        if (dataIn == 15) { // PAUSE
          while (dataIn != 14) {
            if (Bluetooth.available() > 0) {
              dataIn = Bluetooth.read();
              if (dataIn == 13) { break; }
            }
          }
        }
        if (dataIn > 100 & dataIn < 150) { speedDelay = dataIn / 10; }
        if (dataIn > 30 & dataIn < 100) { wheelSpeed = dataIn * 10; dataIn = 14; }
      }
      LeftFrontWheel.moveTo(lfw[i]); LeftFrontWheel.setSpeed(wheelSpeed);
      LeftBackWheel.moveTo(lbw[i]); LeftBackWheel.setSpeed(wheelSpeed);
      RightFrontWheel.moveTo(rfw[i]); RightFrontWheel.setSpeed(wheelSpeed);
      RightBackWheel.moveTo(rbw[i]); RightBackWheel.setSpeed(wheelSpeed);
      while (LeftBackWheel.currentPosition() != lbw[i] & LeftFrontWheel.currentPosition() != lfw[i] & RightFrontWheel.currentPosition() != rfw[i] & RightBackWheel.currentPosition() != rbw[i]) {
        LeftFrontWheel.runSpeedToPosition();
        LeftBackWheel.runSpeedToPosition();
        RightFrontWheel.runSpeedToPosition();
        RightBackWheel.runSpeedToPosition();
      }
      // 서보 1~6 각도 보간 이동
      for (int s = 1; s <= 6; s++) {
        int *sp = (s==1)?servo01SP:(s==2)?servo02SP:(s==3)?servo03SP:(s==4)?servo04SP:(s==5)?servo05SP:servo06SP;
        Servo *sv = (s==1)?&servo01:(s==2)?&servo02:(s==3)?&servo03:(s==4)?&servo04:(s==5)?&servo05:&servo06;
        if (sp[i] == sp[i+1]) continue;
        if (sp[i] > sp[i+1]) {
          for (int j = sp[i]; j >= sp[i+1]; j--) { sv->write(j); delay(speedDelay); }
        } else {
          for (int j = sp[i]; j <= sp[i+1]; j++) { sv->write(j); delay(speedDelay); }
        }
      }
    }
  }
}

이 프로젝트는 잘 작동하지만 완벽하지는 않습니다. 메카넘 휠의 슬립과 저가 서보 모터의 성능 한계로 자동 움직임이 정밀하지 않을 수 있습니다. 저가 서보는 3D 프린팅 부품 무게를 지탱할 힘이 부족해 정지 시에도 떨림/지터가 발생할 수 있습니다.

이 튜토리얼이 즐겁고 유익했길 바랍니다. 질문은 댓글로 남겨주시고 아두이노 프로젝트 모음도 확인해보세요.

제조공정

  1. 아두이노 & 라즈베리 파이로 인터넷 제어 영상 스트리밍 로봇 만들기
  2. 심플 파이 로봇 – 라즈베리파이로 2WD 자율 로버 만들기
  3. 서보 모터 구동 장애물 회피 로봇: 아두이노 & HC-SR04로 제작하기
  4. 아두이노와 안드로이드로 룸바 크리에이트 2 제어하기: 단계별 가이드
  5. 아두이노 & MPU6050: SG90 & MG996R 서보 모터 정밀 제어
  6. 스마트 아두이노 제어 로봇 팔 제작: 쉬운 DIY 튜토리얼
  7. LittleArm 2C: 3D 프린팅 아두이노 로봇 팔 – STEM 교육용 4자유도 로봇 암
  8. DIY 손동작 제어 아두이노 로봇 팔 - 단계별 제작 가이드
  9. 아두이노 메가와 블루투스로 제어하는 프로그래머블 3축 로봇 암 키트
  10. KUKA KMR iiwa: 인더스트리 4.0 대응 모바일 협동로봇 암