아두이노와 MPU-6050으로 만드는 제스처 제어 로봇
부품 및 소모품
| 부품 | 수량 |
| 아두이노 우노 (Arduino UNO) | 1 |
| 아두이노 나노 R3 (Arduino Nano R3) | 1 |
| HC-05 블루투스 모듈 | 2 |
| MPU-6050 3축 가속도/자이로 센서 (SparkFun) | 1 |
| DC 모터 12V | 2 |
| 고무 바퀴 | 1세트 |
| L293D 듀얼 H-브리지 모터 드라이버 (TI) | 1 |
| 9V 배터리 (Generic) | 2 |
필요한 도구 및 기계
- 납땜 인두 (Generic)
- 무연 납땜 와이어
- 폼 테이프
- 멀티툴, 드라이버
앱 및 온라인 서비스
프로젝트 소개
MPU-6050 3축 자이로스코프/가속도계를 활용해 제스처로 제어하는 자동차를 직접 만드는 방법입니다. 이 센서의 사용법, 아두이노와의 인터페이스, 블루투스 모듈을 통한 데이터 전송을 이해하면 더 많은 응용이 가능합니다. 이 글에서는 두 개의 HC-05 블루투스 모듈 간 통신에 초점을 맞춥니다.
로봇 본체 제작과 연결은 영상을 참고하세요.
블루투스 모듈 설정
HC-05는 출하 시 슬레이브 모드로 설정되어 있어 모바일 기기에서 데이터를 보내기만 하면 됩니다(기본 비밀번호 1234/0000). 하지만 이 모듈로 다른 모듈이나 모바일 기기에 데이터를 보내려면 마스터 모드로 설정해야 합니다.
이 프로젝트에서는 MPU-6050이 수집한 데이터를 블루투스 모듈을 통해 다른 블루투스 모듈로 전송합니다. 두 모듈이 전원 켜짐과 동시에 자동으로 페어링되도록 설정해야 합니다. 첫 번째 모듈(자동차 장착)은 슬레이브로, 두 번째 모듈(송신기)은 마스터로 설정합니다.
슬레이브 모듈 설정
아래 배선도대로 아두이노에 연결하고 'configure' 코드 업로드:
#include <SoftwareSerial.h>
SoftwareSerial BTSerial(10, 11); // RX | TX
void setup() {
Serial.begin(9600);
Serial.println("Enter AT commands:");
BTSerial.begin(38400); // HC-05 AT 명령 모드 기본 속도
}
void loop() {
if (BTSerial.available()) Serial.write(BTSerial.read());
if (Serial.available()) BTSerial.write(Serial.read());
}
모듈 분리 → 모듈의 키 버튼 누른 상태에서 다시 연결 → LED가 2초마다 한 번씩 느리게 깜빡이면 AT 명령 모드 진입. 시리얼 모니터 보드레이트 9600, 줄바꿈 NL & CR 설정. AT 전송 → OK 응답 확인.
순서대로 AT 명령 입력:
AT+ORGL - 공장 초기화
AT+RMAAD - 이전 페어링 해제
AT+UART? - 현재 보드레이트 확인
AT+UART=38400,0,0 - 보드레이트 38400 설정
AT+ROLE? - 역할 확인 (0: 슬레이브, 1: 마스터)
AT+ROLE=0 - 슬레이브로 설정
AT+ADDR? - 모듈 주소 확인 → 이 주소 기록 필수
마스터 모듈 설정
두 번째 모듈을 아두이노에 연결하고 위와 동일하게 AT 모드 진입 후:
AT+ORGL
AT+RMAAD
AT+UART?
AT+UART=38400,0,0
AT+ROLE?
AT+ROLE=1 - 마스터로 설정
AT+CMODE=0 - 단일 기기만 연결 (기본값 0)
AT+BIND="슬레이브 모듈 주소" - 슬레이브와 바인딩
라이브러리 설치 및 소스코드
MPU-6050용 I2C 라이브러리 필요. 라이브러리와 소스코드 다운로드: MediaFire 링크 (이미 설치되어 있으면 생략)
자동차 유닛 코드 업로드
자동차 유닛을 USB로 PC 연결, 올바른 COM 포트와 보드 타입 선택, Gesture_controled_Robot__car_unit_ 프로그램 업로드. 업로드 시 배터리와 블루투스 모듈은 분리하세요.
// Shubham Shinganapure, 3-10-2019
// Gesture controlled Robotic Car (자동차 유닛)
int lm1=8, lm2=9, rm1=10, rm2=11;
char d=0;
void setup() {
pinMode(lm1,OUTPUT); pinMode(lm2,OUTPUT);
pinMode(rm1,OUTPUT); pinMode(rm2,OUTPUT);
Serial.begin(38400);
sTOP();
}
void loop() {
if(Serial.available()>0) {
d=Serial.read();
if(d=='F') ForWard();
else if(d=='B') BackWard();
else if(d=='L') Left();
else if(d=='R') Right();
else if(d=='S') sTOP();
}
}
void ForWard() { digitalWrite(lm1,HIGH); digitalWrite(lm2,LOW); digitalWrite(rm1,HIGH); digitalWrite(rm2,LOW); }
void BackWard() { digitalWrite(lm1,LOW); digitalWrite(lm2,HIGH); digitalWrite(rm1,LOW); digitalWrite(rm2,HIGH); }
void Left() { digitalWrite(lm1,LOW); digitalWrite(lm2,HIGH); digitalWrite(rm1,HIGH); digitalWrite(rm2,LOW); }
void Right() { digitalWrite(lm1,HIGH); digitalWrite(lm2,LOW); digitalWrite(rm1,LOW); digitalWrite(rm2,HIGH); }
void sTOP() { digitalWrite(lm1,LOW); digitalWrite(lm2,LOW); digitalWrite(rm1,LOW); digitalWrite(rm2,LOW); }
리모트 유닛 코드 업로드
동일하게 remote 프로그램 업로드:
// Shubham Shinganapure, 3/10/19 수정
// Gesture controlled Robotic Car (리모트 유닛)
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
MPU6050 mpu;
#define OUTPUT_READABLE_YAWPITCHROLL
bool dmpReady = false;
uint8_t mpuIntStatus, devStatus;
uint16_t packetSize, fifoCount;
uint8_t fifoBuffer[64];
VectorFloat gravity;
Quaternion q;
float ypr[3];
uint8_t teapotPacket[14] = {'$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n'};
volatile bool mpuInterrupt = false;
void dmpDataReady() { mpuInterrupt = true; }
#include <SoftwareSerial.h>
SoftwareSerial BTSerial(10, 11); // RX | TX
int bt=8; int x=1;
void setup() {
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin(); TWBR = 24;
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
Serial.begin(115200);
BTSerial.begin(38400);
Serial.println(F("Initializing I2C devices..."));
mpu.initialize();
Serial.println(F("Testing device connections..."));
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
Serial.println(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
mpu.setXGyroOffset(220); mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85); mpu.setZAccelOffset(1788);
if (devStatus == 0) {
Serial.println(F("Enabling DMP...")); mpu.setDMPEnabled(true);
Serial.println(F("Enabling interrupt detection..."));
attachInterrupt(0, dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
Serial.println(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;
packetSize = mpu.dmpGetFIFOPacketSize();
} else {
Serial.print(F("DMP Initialization failed (code ")); Serial.print(devStatus); Serial.println(F(")"));
}
pinMode(bt, INPUT);
}
void loop() {
if(digitalRead(bt)==HIGH) { x++; delay(150); }
if((x%2)==0) {
if (!dmpReady) return;
while (!mpuInterrupt && fifoCount < packetSize);
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
fifoCount = mpu.getFIFOCount();
if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
mpu.resetFIFO(); Serial.println(F("FIFO overflow!"));
} else if (mpuIntStatus & 0x02) {
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
mpu.getFIFOBytes(fifoBuffer, packetSize);
fifoCount -= packetSize;
#ifdef OUTPUT_READABLE_YAWPITCHROLL
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
Serial.print("ypr\t");
Serial.print(ypr[0] * 180/M_PI); Serial.print("\t");
Serial.print(ypr[1] * 180/M_PI); Serial.print("\t");
Serial.println(ypr[2] * 180/M_PI);
if((ypr[1] * 180/M_PI) <= -25) { BTSerial.write('F'); }
else if((ypr[1] * 180/M_PI) >= 25) { BTSerial.write('B'); }
else if((ypr[2] * 180/M_PI) <= -25) { BTSerial.write('L'); }
else if((ypr[2] * 180/M_PI) >= 20) { BTSerial.write('R'); }
else { BTSerial.write('S'); }
#endif
}
} else { BTSerial.write('S'); }
}
슬레이브 블루투스 모듈은 자동차 유닛에, 마스터는 리모트 유닛에 장착하면 완성입니다. 전원 켜고 즐기세요!
코드
제스처 제어 로봇 (리모트 유닛) - Arduino
위 리모트 유닛 코드 참조
회로도
회로도 이미지 참조