프로젝트 인원 : 2인
소요기간 : 4일
준비물
아두이노 2개
모터드라이버 L298N
DC모터 4개, 차체 프레임 타이어 등 (알리익스프레스 구입)
9V & 1.5V AA 건전지 & 3.7V 2000mah 리튬이온배터리
HC-06 블루투스 모듈(슬레이브)
HC-05 블루투스 모듈(마스터)
mpu9250 9축 기울기센서 모듈
회로도 1
RC카 제어를 위한 데이터를 송신하는 부분 (Master)
1.5V 건전지 4개를 직렬로 연결한 배터리 홀더를 사용해 아두이노의 vin에 전원을 공급한다.
자이로 센서로 측정한 데이터를 i2c통신을 통해 읽어와서 실시간 핸드 무브먼트를 확인한다.
AT명령어로 마스터 모드로 설정한 HC-05 블루투스모듈을 통해 현재의 핸드 제스처에 따라 RC카 컨트롤을 위한 데이터를 슬레이브로 전송한다.
1.5V AA 건전지 X 4개 | |
+ | Vin |
- | GND |
MPU-9250 | |
VCC | VCC |
GND | GND |
SCL | A5 |
SDA | A4 |
HC-05 | |
VCC | VCC |
GND | GND |
TXD | 4 |
RXD | 5 |
회로도 2
수신한 데이터로 RC카를 제어하는 부분 (Slave)
배럴잭이 연결된 9V 배터리 홀더를 사용해 아두이노에 전원을 공급한다.
아래 사진과는 좀 다른데 9V 건전지 + 6V 건전지 + 18650 리튬이온배터리(3.7V, 2000ma/h) 를 직렬로 연결했다. 멀티미터로 측정했을 때 모터 드라이버의 입력전압은 총 17.8V (L298N의 +12V +-+-+- L298N의 GND)
동시에 외부 전원과 아두이노의 GND를 같은 레벨로 묶어야 회로가 제대로 동작한다.
HC-06 블루투스 모듈로 수신한 데이터에 따라 모터드라이버를 통해 DC모터를 제어한다.
LED를 달아둔 건 제어신호가 제대로 나갔는지 확인하기 위해 달아 두었다. LED에 불이 들어왔는데도 RC카가 움직이지 않는다면 모터드라이버나 모터, 외부 전원 쪽에 문제가 있겠구나 하고 추측할 수 있다.
L298N | |
VCC | 외부전원의 + |
GND | 외부전원의 - |
OUT1 | 오른쪽앞바퀴 DC모터 하단 오른쪽뒷바퀴 DC모터 상단 |
OUT2 | 오른쪽앞바퀴 DC모터 상단 오른쪽뒷바퀴 DC모터 하단 |
OUT3 | 왼쪽앞바퀴 DC모터 하단 왼쪽뒷바퀴 DC모터 상단 |
OUT4 | 왼쪽앞바퀴 DC모터 상단 왼쪽뒷바퀴 DC모터 하단 |
ENA | 10 |
IN1 | 9 |
IN2 | 8 |
IN3 | 7 |
IN4 | 6 |
ENB | 5 |
HC-06 | |
VCC | VCC |
GND | GND |
TXD | 2 |
RXD | 3 |
LED, 220옴 저항 | |
+ | 11 |
- | GND |
1. 프레임 조립 및 모터 드라이버 회로 연결
사용한 모터는 DIY용으로 많이 사용하는 기어박스가 장착된 노란 DC모터이다. 스펙은 대강 아래와 같다.
소모 전류가 100mA인데 아두이노 출력핀에서는 40mA밖에 뽑아내지 못하니 외부전원 사용이 필수이다. 그리고 MCU와 모터 사이의 컨트롤러 역할을 하는 모터 드라이버를 사용하면 토크제어, 속도제어, 전압제어, 전류제어, 위치제어 등을 정확하게 할 수 있고, 모터 보호 기능도 할 수 있다.
우리가 사용한건 H-bridge 회로가 두 개(2채널) 들어있는 모터 드라이버 L298N이다.
L298N datasheet 확인하기 :
http://www.handsontec.com/dataspecs/L298N%20Motor%20Driver.pdf
H-Bridge 회로의 작동 방식
스위치를
1 2
3 4
라고 했을 때
1, 4번 스위치를 닫을 때와 2, 3번 스위치를 닫을 때 전압의 극성이 반대로 바뀌면서 이로인해 모터의 회전 방향을 제어할 수 있다.
회로가 개방되어 있거나 1, 3번 스위치만 닫는다거나 2, 4번 스위치만 닫았을 때는 모터가 회전하지 않고 멈춘다.
실제로는 스위치가 아니라 트랜지스터가달려있다.
- Motor Ouput A & Ouput B : DC 모터를 연결
- +12V power : 모터 구동을 위한 외부 전원 연결. 5V-35V
- GND : 공통 접지. 아두이노의 GND와 외부 전원의 -극이 같이 연결된다.
- 5V regulator jumper : 점퍼가 제자리에 있으면 밑의 GND옆에 있는 5V단자가 출력핀 역할을 해서 아두이노 Vin이나 기타 5V전원이 필요한 회로에 전원을 공급하는 데 쓸 수 있다. 점퍼를 빼면 레귤레이터가 비활성화되고 밑의 5V 단자에 별도로 5V를 공급해줘야 한다. 모터 전원이 12V 미만일 땐 점퍼를 제자리에 두는 것이 권장되고, 12V이상일 땐 점퍼를 뽑아야 레귤레이터가 손상되지 않는다.
- 5V 단자 : 위의 5V 레귤레이터 점퍼에 따라 출력으로도 쓰이고 입력으로도 쓰인다.
- ENA, ENB : 점퍼를 껴두면 항상 최대출력으로 작동하고 점퍼를 빼고 아두이노에서 ~표시가 붙은 PWM핀에 연결하면 속도를 제어할 수 있다.
- IN1, IN2 : 아두이노 디지털 포트에 연결한다. 핀 중 하나가 HIGH이고 다른 하나가 LOW 상태일 때 모터가 회전한다. 둘 다 HIGH이거나 LOW이면 모터가 정지한다. 위의 H-Bridge 회로를 참고.
- IN3, IN3 : 위의 설명과 같다. Motor B를 제어한다.
2채널 모터 드라이버 1개로 4개의 모터를 제어하기 위해 오른쪽의 모터 2개를 같이 물리고 왼쪽의 모터 두개를 같이 물렸다. 그리고 DC모터에 연결된 두 가닥의 +, - 선은 어떻게 연결하는냐에 따라 정방향 역방향이 결정되는데 앞바퀴와 뒷바퀴의 기어박스가 서로 마주보는 방향으로 배치되어 있으니 모터 상단의 가닥과 하단의 가닥을 지그재그로 물렸다.
속도제어
아두이노는 8 bit resolution(2^8=256)으로 analog 값을 표현하고 모터는 입력 전압에 따라 회전속도가 달라진다.
그러니 모터에 공급되는 전원이 예를들어 12V이고 analogWrite(pin, value) 함수에서 두번째 매개변수로 128을 준다고 했을 때 duty cycle이 50%가 되서 평균 입력 전압이 50% 수준인 6V가 되고 속도가 떨어진다.
2. 블루투스 통신을 위한 준비
두 개의 블루투스 모듈을 사용하였고 HC-05를 마스터로 HC-06을 슬레이브로 사용하였다.
둘은 AT Command mode 진입하는 방법도 다른데 HC-05는 전원을 공급하면서 모듈에 있는 버튼을 같이 누르면 LED가 느리게 깜빡이며 AT모드에 들어간다. 반면 HC-06은 key핀에 HIGH를 주면 AT모드로 진입하고 LOW를 주거나 연결하지 않았을 땐 normal mode로 사용한다.
그리고 HC-06은 펌웨어 버전에 따라 AT+VERSION 명령어를 쳤을 때 1.7이상이면 마스터 또는 슬레이브로 사용 가능하고 그 이하면 슬레이브로 역할이 고정된다고 한다.
HC-05 마스터 모드 설정을 위한 코드
#include <SoftwareSerial.h>
SoftwareSerial HC05(4,5); // 블루투스 모듈 TX, RX. 통신을 위한 객체 선언
void setup() {
Serial.begin(9600); // 아두이노-PC간 통신 속도
BTSerial.begin(38400); // 아두이노-블루투스모듈(HC-05) 간 통신 속도
}
void loop() {
if (BTSerial.available()) // HC05에서 입력된 데이터를 아두이노 시리얼로 전송
Serial.write(BTSerial.read());
if (Serial.available()) // 아두이노 시리얼로 입력된 데이터를 HC05로 전송
BTSerial.write(Serial.read());
}
마스터 쪽 아두이노에 소스코드를 업로드 후 시리얼 모니터를 연다. HC-05는 'Both NL or CR' 로 세팅해 문장 끝에 라인 피드와 캐리지 리턴을 포함해 전송해야 정상적으로 통신이 된다.
전원핀을 뽑고 모듈 하단에 있는 버튼을 누른 상태로 다시 전원을 꼽는다. 그럼 LED가 천천히 점멸할 것이고 시리얼 모니터에 AT라고 쳤을 때 OK라고 회신이 오면 성공적으로 AT 모드에 진입한 것이다.
시리얼 모니터에
AT+UART? 명령어로 UART 통신 보드레이트를 확인한다.
만약 38400 이 아니라면
AT+UART=38400,0,0
이라고 세팅해 준다.
그리고 AT+ROLE? 명령어로 현재 세팅 역할을 확인한다.
1이면 마스터 모드이고 0이면 슬레이브 모드이다. 1이 아니라면
AT+ROLE=1
명령어로 다시 세팅해준다.
다음은 슬레이브로 사용할 HC-06모듈의 고유 6 byte의 MAC Address를 확인해야 한다. HC-06 모듈에 전원과 GND만 연결해주면 빠르게 점멸하면서 페어링을 시도하는데 이 때 핸드폰으로 잡은 뒤 안드로이드 Play 스토어에서 Serial Bluetooth Terminal 어플을 다운 받아 확인하면 16진수 12자리의 MAC 주소를 알아낼 수 있다.
이 숫자를 다른 곳에 옮겨 적고 네자리,두자리,나머지여섯자리 사이를 콤마로 구분해준다.
그리고 HC-05가 AT모드인 상태에서 시리얼 모니터에
AT+BIND=아까 적어 둔 주소를 복붙한다.
AT+BIND? 라고 치면
현재 바인드된 맥 주소가 뜨는데 hexa code로 앞자리 0은 출력되지 않는다.
이제 HC-05를 normal 모드로 reboot하면 항상 해당 주소와 페어링을 시도하게 된다.
또한 AT+CMODE 명령어로 고정 주소와 페어링을 시도할 것인지(0), 모든 주소와 페어링을 시도할 것인지(1)도 설정할 수 있다.
다음은 슬레이브인 HC-06을 위의 회로도대로 연결해주면 서로 빠르게 점멸하면서 페어링을 시도하다가 페어링에 성공하면 점멸을 멈출것이다.
다만 주의할 것은 HC-06은 HC-05 모듈과 달리 셋업함수에서 아두이노-블루투스간 통신 보드레이트를 9600으로 설정해주어야하고 시리얼 모니터에서 'line ending없음'을 선택해야 정상적으로 데이터를 읽는다.
이제 마스터-슬레이브간 블루투스 통신을 위한 설정은 마쳤고, 마스터 쪽에서는 기울기 센서를 통해 얻어낸 각도 데이터에 따라 RC카를 제어를 위한 데이터를 HC05.write 함수로 슬레이브 쪽에 전송하고, 슬레이브 쪽에선 loop 문 내에서 HC06.read 함수로 읽어들인 데이터를 RC카를 제어하는 데 사용한다.
void loop(){
if(HC06.available()){ // HC06에 데이터가 들어오면
byte incomingData = HC06.read(); // 읽어들인 데이터 변수에 저장 후 활용
}
참고 : https://www.youtube.com/watch?v=DdkmpbTgxnU (심심한녹칸다)
3. 기울기센서 사용을 위한 준비
기울기 센서 사용을 위해 찾아보고 이해한 내용을 아래에 정리한다.
9축 기울기센서는 중력가속도 센서, 자이로(각속도) 센서, 지자기 센서로 3가지로 구성된다.
먼저 중력가속도(Acceleration)는 크기와 방향을 가지는 벡터량이며 자연상태에선 1g의 값을 갖는다. 센서를 바닥에 가만히 놔둔다고 했을 때 평면과 수직하는 z축의 중력가속도는 1g이며 x, y축 방향으로 작용하는 힘은 없으니 시리얼 모니터로 측정하면 x, y, z 가 각각 0, 0, 1 의 값이 나온다. 그런데 만약 센서가 x축 방향으로 약간 기울어졌다고 했을 때 위의 사진과 같이 중력가속도를 x축과 z축이 기울어진 각도에 비례해서 나눠가지고 삼각함수 arctan을 사용해 역으로 계산하면 각도를 알아낼 수 있다고한다. 다만 정지상태에선 비교적 정확하게 측정 가능하지만 움직이는 상태에선 이게 중력가속도에 의한것인지 움직임에 의한것인지 확인할 방법이 없으니 데이터가 불확실해져서 다른 데이터로 보정할 필요가 있다고 한다.
다음으로 자이로(Gyroscope)센서는 각속도 센서이며 측정단위는 degrees/s(초당 회전각도)이다. 가속도를 적분하면 속도이고 속도를 적분하면 거리이듯, 각속도를 적분하면 단위시간 당 이동한 각도를 알아낼 수 있다. 다만 적분상수에 의한 오차가 시간이 지날 수록 누적되는 문제가 있어 역시 다른 데이터로 보정할 필요가 있다. 그래서 지자기 센서를 사용하여 yaw값을 알아내 보정한다고 하는데 정확한 원리는 아직 모르겠다.
라이브러리 매니저에서 mpu 9250 을 검색하고 MPU9250_WE 라이브러리를 설치한다.
사용한 예제는 보정한 데이터로 x, y, z축의 angles 데이터를 얻어낼 수 있는 코드이다.
#include <MPU9250_WE.h>
#include <Wire.h>
#define MPU9250_ADDR 0x68
MPU9250_WE myMPU9250 = MPU9250_WE(MPU9250_ADDR);
void setup() {
Serial.begin(115200);
Wire.begin();
if(!myMPU9250.init()){
Serial.println("MPU9250 does not respond");
}
else{
Serial.println("MPU9250 is connected");
}
Serial.println("Position you MPU9250 flat and don't move it - calibrating...");
delay(1000);
myMPU9250.autoOffsets();
Serial.println("Done!");
myMPU9250.setAccRange(MPU9250_ACC_RANGE_2G);
myMPU9250.enableAccDLPF(true);
myMPU9250.setAccDLPF(MPU9250_DLPF_6);
}
void loop() {
xyzFloat gValue = myMPU9250.getGValues();
xyzFloat angle = myMPU9250.getAngles();
/* For g-values the corrected raws are used */
/*
Serial.print("g-x = ");
Serial.print(gValue.x);
Serial.print(" | g-y = ");
Serial.print(gValue.y);
Serial.print(" | g-z = ");
Serial.println(gValue.z);
*/
/* Angles are also based on the corrected raws. Angles are simply calculated by
angle = arcsin(g Value) */
Serial.print("Angle x = ");
Serial.print(angle.x);
Serial.print(" | Angle y = ");
Serial.print(angle.y);
Serial.print(" | Angle z = ");
Serial.println(angle.z);
Serial.print("Orientation of the module: ");
Serial.println(myMPU9250.getOrientationAsString());
Serial.println();
delay(1000);
}
적당히 딜레이를 짧게 조정해서 시리얼 플러터로 데이터를 확인해 보았다.
시리얼 플러터를 통해 여러 변수의 그래프를 동시에 확인하려면 확인하고자 하는 변수 중 마지막 것만 Serial.println 함수를 쓰면 된다.
그래프로 확인해보니 가장 정확도가 높은 것은 z각도 값이었다. 손등이 하늘을 향하고 있을 땐 z값이 90도이고, 앞뒤좌우 어디든 옆을 향하면 0도의 값을 갖는다. 손등을 거꾸로 하면 z가 -90도인데 제스처로 사용하지는 않았다. 기울기 센서를 사진과 같이 배치했을 때 손등을 앞을보게 꺾거나 뒤를 보게 꺾었을 때 y가 90도에서 -90도 값이 나왔고 손을 좌우로 움직였을 때 x가 90도에서 -90도 값이 나왔다.
또한 손이 꺾어진 정도에 따라 PWM 제어를 했는데, 손등이 앞으로 기울면 기울수록 전진 속도가 빨라지고 손등이 뒤로 젖혀질 수록 후진 속도가 빨라지는 식이다.
그리고 센서의 고장인지 x, y값이 제대로 안나오는 mpu-9250이 많아서 여러개 중에 제일 잘나오는 것을 사용했다.
4. 소스코드 작성
Master 부분
loop문 내에서는 if else 문을 사용하여 자이로센서를 통해 얻어진 각도값에 따라 RC카 제어를 위한 데이터를 블루투스통신을 통해 슬레이브에 전송했다. x, y, z 각도값을 모두 활용하였으며, 전진/후진은 각도수치를 그대로 보내서 PWM제어를 위해 사용하였다. 그리고 안정적인 조종을 위해 정지의 각도 범위는 크게 잡았다.
Slave 부분
아래와 같이 RC카의 동작을 함수화하고 블루투스통신을 통해 읽어온 데이터에 따라 if else문으로 각각의 함수를 호출한다.
void Go(byte speed){
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
analogWrite(ENA, speed);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
analogWrite(ENB, speed);
digitalWrite(LED,HIGH);
}
void Back(byte speed){
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
analogWrite(ENA, speed);
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
analogWrite(ENB, speed);
digitalWrite(LED,HIGH);
}
void Stop(){
analogWrite(ENA, 0);
analogWrite(ENB, 0);
digitalWrite(LED,LOW);
}
void LeftGo(){
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
analogWrite(ENA,255);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
analogWrite(ENB, 150);
digitalWrite(LED,HIGH);
}
void rightGo(){
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
analogWrite(ENA, 150);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
analogWrite(ENB, 255);
digitalWrite(LED,HIGH);
}
후기 & 느낀점
서보모터를 사용해 앞바퀴는 조항장치로 제어하고 싶었는데 못한게 아쉽다. 다음에는 조향장치가 있는 프레임을 구입하거나 3d프린터를 사용해 조향장치를 제어해보고 싶다.
후속작인 mpu 9250보다는 mpu 6050 의 자료와 라이브러리가 풍부한 것 같다.
기회가 된다면 다음 프로젝트에서 기울기 센서를 좀 더 디테일하게 사용해보고 싶다.