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

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

이 튜토리얼에서는 어떤 방향으로든 이동할 수 있는 아두이노 메카넘 휠 로봇을 만드는 방법을 배웁니다. 이 독특한 이동성은 메카넘 휠(Mecanum Wheels)이라는 특수 휠을 사용해 달성합니다.

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

개요

이 휠들은 구매하려면 좀 비싸서 직접 설계하고 3D 프린팅했습니다. 꽤 잘 작동하며 이 로봇 플랫폼을 운전하는 건 정말 재미있습니다. NRF24L01 무선 트랜시버 모듈로 무선 제어하거나, 제 경우 이전 영상에서 만든 DIY RC 송신기로 제어합니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

스마트폰으로 블루투스 통신 제어도 가능하게 했습니다. 커스텀 안드로이드 앱으로 메카넘 휠 로봇을 어떤 방향으로든 움직일 수 있고, 앱의 슬라이더로 이동 속도도 조절할 수 있습니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

이 로봇 플랫폼의 두뇌는 아두이노 메가 보드로, 각 휠을 개별 제어합니다. 각 휠은 NEMA 17 스테퍼 모터에 연결되며, 스테퍼 모터를 정밀 제어할 수 있다는 점을 이용해 앱에 자동 이동 프로그래밍 기능을 추가했습니다. Save 버튼으로 각 위치/단계를 저장하면 로봇이 자동으로 이 단계들을 반복 실행합니다. 같은 버튼으로 일시정지, 리셋/삭제 후 새 단계 저장이 가능합니다.

메카넘 휠 로봇 3D 모델

3D 모델링 소프트웨어로 이 메카넘 휠 로봇을 설계했습니다. 로봇의 베이스 플랫폼은 8mm 두께 MDF 보드로 만들 간단한 박스 형태입니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

네 개의 스테퍼 모터가 이 플랫폼에 장착되고 메카넘 휠이 모터 샤프트에 연결됩니다.

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

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

Thangs에서 어셈블리 3D 모델 다운로드.

3D 프린팅용 STL 파일:

메카넘 휠 작동 원리

메카넘 휠은 휠 둘레에 롤러가 달린 휠입니다. 이 롤러들은 휠 회전축에 대해 대각선(45도) 방향으로 배치됩니다. 이로 인해 휠이 전진/후진할 때 대각선 방향으로 힘을 가하게 됩니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

휠들을 특정 패턴으로 회전시키면 이 대각선 힘들을 이용해 로봇이 어떤 방향으로든 이동할 수 있습니다.

왼손/오른손 메카넘 휠 두 종류가 필요합니다. 롤러 방향이 다르며 로봇의 특정 위치에 장착해야 합니다. 각 휠의 상단 롤러 회전축이 로봇 중심을 향해야 합니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

휠 회전 방향에 따른 로봇 이동 데모:

  • 네 휠 모두 전진 → 로봇 전진 (모두 후진 → 후진)
  • 우측 휠은 안쪽(로봇 중앙 방향), 좌측 휠은 바깥쪽으로 회전 → 로봇 우측 이동 (좌측 이동은 반대)
  • 두 휠만 회전시켜 대각선 이동도 가능

메카넘 휠 로봇 제작

이제 로봇 플랫폼 제작 과정을 보여드리겠습니다. 베이스는 8mm MDF 보드로 만듭니다. 테이블 톱으로 3D 모델 치수대로 모든 조각을 자릅니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

3mm 드릴과 25mm 포스트너 비트로 사이드 패널에 스테퍼 모터 장착용 구멍을 뚫습니다. 조각들이 준비되면 조립합니다. 목공 본드와 나사로 고정합니다. 가장 중요한 건 모터 장착 구멍을 정밀하게 만들어 나중에 네 휠이 지면과 고르게 접촉하게 하는 것입니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

베이스 플랫폼을 3D 프린팅으로 대체할 수도 있으니 웹사이트 기사에 3D 파일을 포함하겠습니다. 마지막으로 베이스와 커버를 흰색 스프레이 페인트로 도색했습니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

이제 메카넘 휠 제작입니다. 비싸서 직접 설계해 3D 프린팅했습니다. 휠은 외부/내부 두 파트로 구성되며 M4 볼트/너트로 고정합니다. 각각 10개 롤러와 NEMA 17 스테퍼 모터에 맞는 샤프트 커플러가 있습니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드
아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

Creality CR-10 3D 프린터로 모든 메카넘 휠 부품을 프린팅했습니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

3D 프린팅 부품이 준비되면 롤러용 샤프트를 만듭니다. 3mm 강철 와이어를 사용하며, 길이는 약 40mm로 로터리 툴로 잘랐습니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

메카넘 휠 조립 시작: 양쪽 측면과 샤프트 커플러를 M4 볼트/너트 4개로 고정합니다(볼트 길이 45mm).

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

롤러 장착: 내부 측면 둘레 구멍에 샤프트를 살짝 끼웁니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

작은 M3 와셔 → 롤러 → 샤프트를 외부 측면 슬롯까지 끝까지 밀어 넣습니다. 반대편 와셔 넣을 공간이 없어 와셔 하나만 사용했습니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

10개 롤러 모두 같은 과정으로 장착합니다. 꽤 쉽고 재미있는 작업입니다. 중요한 건 롤러가 자유롭게 회전해야 한다는 점입니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

마지막으로 내부 구멍에 순간접착제 몇 방울 떨어뜨려 샤프트가 풀리지 않게 합니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

휠이 완성됐으니 로봇 전체 조립으로 넘어갑니다. 먼저 스테퍼 모터를 베이스 플랫폼에 장착합니다. M3 볼트(길이 12mm)로 고정합니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

휠을 모터 샤프트에 장착합니다. 제가 만든 샤프트 커플러에 M3 너트 슬롯이 있어 M3 볼트로 휠을 샤프트에 고정합니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

상단 커버 고정을 위해 베이스 두 모서리에 나사봉을 심고 커버에도 같은 위치에 구멍을 뚫어 쉽게 끼우고 고정할 수 있게 했습니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

베이스 후면에 전원 스위치용 20mm 구멍과 LED용 5mm 구멍을 뚫었습니다.

회로도

이제 전자 부품 연결입니다. 전체 회로도입니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

네 개의 NEMA 17 스테퍼 모터를 DRV8825 스테퍼 드라이버 4개로 제어합니다(A4988도 가능). 스테퍼와 로봇 전체 전원에는 12V 전원 공급, 제 경우 3S 리튬폴리머 배터리(약 12V)를 사용합니다. 무선 통신용 NRF24L01 모듈, 블루투스 통신용 HC-05 모듈, 배터리 전압 모니터링용 전압 분배기, 배터리 전압 11V 미만 시 점등될 LED를 포함했습니다.

향후 로봇 팔 프로젝트와 결합할 계획이라 서보 모터용 5V 공급을 위해 3A 용량의 전용 5V 전압 레귤레이터도 포함시켰습니다(선택 사항).

필요한 부품:

  • 스테퍼 모터 – NEMA 17
  • DRV8825 스테퍼 드라이버
  • NRF24L01 트랜시버 모듈
  • HC-05 블루투스 모듈
  • 리튬폴리머 배터리
  • 아두이노 메가 보드

PCB 설계

전자 부품을 정리하고 배선 난잡함을 없애기 위해 EasyEDA로 커스텀 PCB를 설계했습니다. 이 PCB는 아두이노 메가 쉴드 역할을 해 아두이노 메가 위에 직접 꽂을 수 있습니다. 탑/보텀 레이어 모두 배선에 사용했습니다. 사용하지 않은 아두이노 핀들도 핀 헤더로 빼두어 향후 확장 가능하게 했습니다. 12V, 5V, GND 연결 핀과 드라이버 스테핑 분해능 선택 핀도 포함했습니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

PCB 프로젝트 파일 링크입니다. 설계 완료 후 제조용 거버 파일을 생성했습니다.

거버 파일:

JLCPCB(이 영상 후원사)에 PCB를 주문했습니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

거버 파일을 드래그 앤 드롭해 업로드하면 거버 뷰어에서 검토 가능합니다. 문제 없으면 원하는 PCB 속성 선택 후 주문합니다. 이번엔 아두이노 보드 색상과 맞추려 PCB 색상을 파란색으로 선택했습니다. 합리적인 가격에 주문 가능합니다. JLCPCB 첫 주문 시 최대 10장까지 $2입니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

며칠 후 PCB 도착. 품질 훌륭하고 설계와 정확히 일치합니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

PCB 조립

PCB 조립 시작. 작은 부품부터 납땜: 저항, 커패시터. 아두이노 연결용 수 핀 헤더를 PCB에 꽂아 납땜했습니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

암 핀 헤더들도 제자리에 놓고 납땜. 스테퍼 모터 연결 핀들과 스테핑 분해능 선택 핀들은 수 핀 헤더를 사용했습니다. 이렇게 하면 모터를 PCB에 직접 연결하고 점퍼로 분해능 선택 가능합니다. 단자대, 트리머, 전압 레귤레이터도 납땜했습니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

PCB 완성. 드라이버 장착과 모터 연결로 넘어갑니다. 먼저 드라이버 스테핑 분해능 선택 점퍼를 꽂습니다. MS3 핀을 5V에 연결해 1/16 스텝 분해능을 선택했습니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

그 위에 DRV8825 드라이버들을 올리고, NRF24L01 모듈과 HC-05 블루투스 모듈도 연결합니다. 이제 PCB를 아두이노 보드에 꽂기만 하면 됩니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

배터리를 적절한 단자대에 연결하고 베이스 플랫폼에 넣습니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

전원 스위치를 제자리에 끼우고 다른 단자대에 연결했습니다. 전원 스위치 바로 위에 배터리 표시 LED도 장착했습니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

모터를 PCB에 연결합니다. 주의: 반대편 모터 연결 시 커넥터도 반대 방향으로 연결해야 합니다. 나중에 프로그래밍할 때, 예를 들어 전진 명령 시 두 모터가 같은 방향으로 돌게 하기 위함입니다(실제로는 뒤집혀 있어 한 쪽은 시계 방향, 다른 쪽은 반시계 방향).

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

마지막으로 상단 커버를 끼우면 메카넘 휠 로봇 프로젝트 완료입니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

메카넘 휠 로봇 아두이노 코드

아두이노 코드를 살펴볼 차례입니다. 실제로는 두 가지 별도 코드가 있습니다. 하나는 NRF24L01 모듈로 제어하는 코드, 다른 하나는 스마트폰으로 제어하는 코드입니다.

NRF24L01 무선 제어용 아두이노 코드

/* === Arduino Mecanum Wheels Robot === Radio control with NRF24L01 by Dejan, www.HowToMechatronics.com Libraries: RF24, https://github.com/tmrh20/RF24/ AccelStepper by Mike McCauley: https://www.airspayce.com/mikem/arduino/AccelStepper/index.html */
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <AccelStepper.h>

RF24 radio(48, 49); // nRF24L01 (CE, CSN)
const byte address[6] = "00001";
unsigned long lastReceiveTime = 0;
unsigned long currentTime = 0;

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

int wheelSpeed = 1500;

struct Data_Package {
  byte j1PotX; byte j1PotY; byte j1Button;
  byte j2PotX; byte j2PotY; byte j2Button;
  byte pot1; byte pot2;
  byte tSwitch1; byte tSwitch2;
  byte button1; byte button2; byte button3; byte button4;
};

Data_Package data;

void setup() {
  LeftFrontWheel.setMaxSpeed(3000);
  LeftBackWheel.setMaxSpeed(3000);
  RightFrontWheel.setMaxSpeed(3000);
  RightBackWheel.setMaxSpeed(3000);
  radio.begin();
  radio.openReadingPipe(0, address);
  radio.setAutoAck(false);
  radio.setDataRate(RF24_250KBPS);
  radio.setPALevel(RF24_PA_LOW);
  radio.startListening();
  Serial.begin(115200);
}

void loop() {
  currentTime = millis();
  if (currentTime - lastReceiveTime > 1000) {
    resetData();
  }
  if (radio.available()) {
    radio.read(&data, sizeof(Data_Package));
    lastReceiveTime = millis();
  }

  // 속도 설정 - 좌측 포텐셔미터
  wheelSpeed = map(data.pot1, 0, 255, 100, 3000);

  // 조이스틱 입력에 따른 이동 방향 결정
  if (data.j1PotX > 150) { moveSidewaysLeft(); }
  else if (data.j1PotX < 100) { moveSidewaysRight(); }
  else if (data.j1PotY > 160) { moveForward(); }
  else if (data.j1PotY < 100) { moveBackward(); }
  else if (data.j2PotX < 100 & data.j2PotY > 160) { moveRightForward(); }
  else if (data.j2PotX > 160 & data.j2PotY > 160) { moveLeftForward(); }
  else if (data.j2PotX < 100 & data.j2PotY < 100) { moveRightBackward(); }
  else if (data.j2PotX > 160 & data.j2PotY < 100) { moveLeftBackward(); }
  else if (data.j2PotX < 100) { rotateRight(); }
  else if (data.j2PotX > 150) { rotateLeft(); }
  else { stopMoving(); }

  // 스테퍼 실행
  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 resetData() {
  data.j1PotX = 127; data.j1PotY = 127;
  data.j2PotX = 127; data.j2PotY = 127;
  data.j1Button = 1; data.j2Button = 1;
  data.pot1 = 1; data.pot2 = 1;
  data.tSwitch1 = 1; data.tSwitch2 = 1;
  data.button1 = 1; data.button2 = 1; data.button3 = 1; data.button4 = 1;
}

설명: RF24 라이브러리로 무선 통신, AccelStepper 라이브러리로 스테퍼 모터 제어. 핀 정의, 변수 선언, setup에서 스테퍼 최대 속도 설정과 라디오 통신 초기화.

loop에서 RC 송신기 데이터를 읽습니다. 송신기 코드와 통신 원리는 별도 튜토리얼 참고.

수신 데이터에 따라 이동 방향 결정. 예: 좌측 조이스틱 전진(값 > 160) 시 moveForward() 호출. 이 함수는 모든 모터 속도를 양수로 설정. 후진은 음수. 다른 방향도 휠 회전을 적절히 설정해 구현(초반 설명 참조).

loop에서 runSpeed()로 모든 스테퍼 실행. 배터리 전압도 전압 분배기 아날로그 입력으로 읽어 11V 미만 시 LED 점등.

스마트폰 제어용 아두이노 코드

/* === Arduino Mecanum Wheels Robot === Smartphone control via Bluetooth by Dejan, www.HowToMechatronics.com Libraries: RF24, https://github.com/tmrh20/RF24/ AccelStepper by Mike McCauley: https://www.airspayce.com/mikem/arduino/AccelStepper/index.html */
#include <SoftwareSerial.h>
#include <AccelStepper.h>

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 dataIn, m;
int lbw[50], lfw[50], rbw[50], rfw[50];
int index = 0;

void setup() {
  LeftFrontWheel.setMaxSpeed(3000);
  LeftBackWheel.setMaxSpeed(3000);
  RightFrontWheel.setMaxSpeed(3000);
  RightBackWheel.setMaxSpeed(3000);
  Serial.begin(38400);
  Bluetooth.begin(38400);
  Bluetooth.setTimeout(1);
  delay(20);
  pinMode(led, OUTPUT);
}

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) { wheelSpeed = dataIn * 10; Serial.println(wheelSpeed); }
  }

  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(); }

  // 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();
    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));
      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 runSteps() {
  for (int i = index - 1; i >= 0; i--) {
    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();
      if (Bluetooth.available() > 0) {
        dataIn = Bluetooth.read();
        if (dataIn == 15) { // PAUSE
          while (dataIn != 14) {
            if (Bluetooth.available() > 0) {
              dataIn = Bluetooth.read();
              if (dataIn == 13) { stopMoving(); break; }
            }
          }
        }
        if (dataIn >= 16) { wheelSpeed = dataIn * 10; dataIn = 14; }
        if (dataIn == 13) { break; }
      }
    }
  }
  // 역방향 실행
  for (int i = 1; i <= index - 1; i++) {
    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();
      if (Bluetooth.available() > 0) {
        dataIn = Bluetooth.read();
        if (dataIn == 15) {
          while (dataIn != 14) {
            if (Bluetooth.available() > 0) {
              dataIn = Bluetooth.read();
              if (dataIn == 13) { stopMoving(); break; }
            }
          }
        }
        if (dataIn >= 16) { wheelSpeed = dataIn * 10; dataIn = 14; }
        if (dataIn == 13) { break; }
      }
    }
  }
}

// 이동 방향 함수들 (위 NRF24L01 코드와 동일)
void moveForward() { ... }
void moveBackward() { ... }
void moveSidewaysRight() { ... }
void moveSidewaysLeft() { ... }
void rotateLeft() { ... }
void rotateRight() { ... }
void moveRightForward() { ... }
void moveRightBackward() { ... }
void moveLeftForward() { ... }
void moveLeftBackward() { ... }
void stopMoving() { ... }

설명: 스마트폰 제어 코드도 매우 비슷하고 같은 방식으로 작동합니다. 라디오 모듈 대신 블루투스 모듈을 정의하고 setup에서 통신 초기화. 다시 스마트폰/앱에서 들어오는 데이터 읽고 로봇 이동 방향 지시.

안드로이드 앱을 보면 버튼 눌릴 때 블루투스로 0~15 숫자를 보냅니다.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

앱은 MIT App Inventor로 만들었으며 자세한 내용은 별도 튜토리얼 참고.

아두이노 메카넘 휠 로봇 제작: 360° 전방위 이동 DIY 가이드

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

이 앱으로 자동 로봇 이동 프로그래밍: "SAVE" 버튼 누르면 현재 스테퍼 모터 위치를 배열에 저장. "RUN" 버튼 누르면 runSteps() 커스텀 함수가 for/while 루프로 저장된 모든 단계 실행.

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

제조공정

  1. 블루투스 제어 라즈베리 파이 로봇 만들기 - 오디오 피드백 포함
  2. 서보 모터 구동 장애물 회피 로봇: 아두이노 & HC-SR04로 제작하기
  3. 아두이노 우노로 자율 주행 라인 트레이서 로봇 만들기
  4. 아두이노 나노로 만드는 음성 제어 로봇 - 블루투스와 안드로이드 앱 활용
  5. 피봇(PiBot): 아두이노 기반 88건반 피아노 연주 로봇
  6. LittleArm 2C: 3D 프린팅 아두이노 로봇 팔 – STEM 교육용 4자유도 로봇 암
  7. 자율 홈 어시스턴트 로봇 'O'nine' 제작 – ROS, Alexa, MoveIt, 내비게이션 완전 가이드
  8. 전용 안드로이드 앱으로 아두이노 로봇팔 제어하기
  9. 아두이노 프로젝트를 위한 스마트 실내 내비게이션 로봇 키트
  10. WiDC: Wi-Fi FPV 로봇 – 실시간 영상 원격 제어