아두이노로 만드는 소형 자율 주행 로버
부품 및 소모품
| 아두이노 나노 R3 |
1개 |
| 브레드보드 (일반) |
1개 |
| 점퍼 와이어 (일반) |
1세트 |
| 초음파 센서 HC-SR04 |
1개 |
| Adafruit 마이크로 연속 회전 서보 FS90R용 휠 |
2개 |
| Adafruit 연속 회전 마이크로 서보 FS90R |
2개 |
| LED (색상 무관, 로봇이 돌아다닐 때 깜빡이는 용도) |
2개 |
| 4xAA 배터리 홀더 (배럴 타입 사용함, 참고용) |
1개 |
| SparkFun 볼 캐스터 메탈 3/8인치 |
1개 |
| 플라스틱 용기 (저렴한 타파웨어나 500g 마가린 통 등) |
1개 |
| 스티커 (장식용) |
1세트 |
필요한 도구 및 장비
앱 및 온라인 서비스
프로젝트 소개
배경
조카들(9세, 7세)이 예전에 만든 장애물 회피 로봇 영상을 보고 난리가 났습니다. 그 로봇 튜토리얼은 하드디스크가 고장 나며 백업과 깃허브의 중요성을 깨닫기 전에 만든 거라 아쉽게도 공개할 수 없습니다. 그래서 교육적이면서도 재미있는, 최대한 단순화한 로버를 새로 만들기로 했습니다.
새시는 원래 게맛살이 들어있던 상자인데, 크기가 딱 맞았습니다(아빠가 상자째 드신 덕분에!). 마가린 통도 적당할 겁니다.
면책 사항: 여기서는 아이들이 거의 모든 작업을 직접 하게 했습니다. 저는 절대 권장하지 않습니다. 각 아이를 가장 잘 아는 부모/보호자/이모/삼촌/베이비시터가 작업 가능 여부를 판단해야 합니다. 부드러운 플라스틱이라 칼 사용 시 자신이나 신체 향하지 않게 주의시켰고, LED 구멍 뚫기는 소형 무선 드라이버로 했습니다. 응급 키트는 다행히 안 썼습니다. 하지만 다시 말하지만, 책임은 어른에게 있습니다!
회로
서보 두 개를 연결하는 방식을 좀 더 복잡하게 했습니다(아이들을 위해). 회로도 이미지를 A4 용지에 1:1 비율로 출력해 주고, 색상과 길이 맞춘 점퍼 와이어를 나눠줬습니다.
회로 조립 후 아두이노에 코드를 업로드하세요. 코드에 주석이 있어 수정하기 쉽습니다. 제대로 연결됐다면 서보가 돌고 LED가 깜빡입니다. 센서 앞에 손을 대면 한 서보가 멈추고 다른 서보가 잠시 반대 방향으로 돕니다. 준비 완료!
조립
이제 '새시'에 구멍을 뚫을 차례입니다.
- 전면: 센서 위치 표시 후 '눈' 구멍을 냅니다.
- 측면: 바퀴 위치 측정 후 상자 바닥 가장자리에 서보가 들어갈 만큼 구멍을 냅니다(고정 브라켓은 안 빠져나가게).
- 작은 드릴 비트로 LED 구멍 2개 뚫기.
- 바닥 뒤쪽에 캐스터 휠 나사로 고정(또는 핫글루).
모든 부품을 상자에 넣고 핫글루로 고정하세요. 글루가 마르면 서보에 바퀴를 장착합니다. 상자 꾸미기(사진 찍어 공유하면 최고!). 배터리를 넣고 방 안을 랜덤하게 돌아다니는 로버를 구경하세요. 배터리 다 될 때까지, 혹은 소음에 지칠 때까지, 개가 물어갈 때까지!
코드
#include <Servo.h>
/* 3핀 센서면 6번 줄 주석 해제, 7번 줄 주석 처리 */
//int distanceR = A0; // Trigger pin
int distance = 6; // Trigger pin
int pingDelay = 10; // 초음파 펄스 길이
int distanceR = A0; // Echo pin
int led1 = 8, led2 = 9; // LEDs
Servo leftPower;
Servo rightPower;
unsigned long flashTime;
unsigned long nextSensorTime;
float obstacleDist = 0.0f;
void setup() {
//Serial.begin(115200);
leftPower.attach(3);
rightPower.attach(5);
leftPower.write(90);
rightPower.write(90);
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
delay(1000);
}
void loop() {
LEDs();
/* 배터리 절약을 위해 초당 5회만 센서 체크. 숫자 늘리면 덜 체크하지만 벽에 부딪힐 수 있음 */
if(millis() - nextSensorTime > 200){
nextSensorTime = millis();
/* 센서가 가끔 엉뚱한 값 내놓아서 2회 읽음 */
float reading1 = TakeReading();
delay(100);
float reading2 = TakeReading();
Serial.println(reading1);
Serial.println(reading2);
// 더 큰 거리로 장애물 거리 결정
obstacleDist = reading1 >= reading2 ? reading1 : reading2;
// 10cm 미만이면 회전, 아니면 직진
if(obstacleDist < 10.0f){
//Serial.println("turn");
turn();
}else{
//Serial.println("forward");
forward();
}
}
}
void LEDs(){
if(millis() - flashTime > 1000) flashTime = millis();
if(millis() - flashTime < 500){
digitalWrite(led1, LOW);
digitalWrite(led2, HIGH);
}else if(millis() - flashTime > 500){
digitalWrite(led1, HIGH);
digitalWrite(led2, LOW);
}
}
void forward(){
/* 뒤로 가면 135와 45 바꿔보세요 */
leftPower.write(135);
rightPower.write(45);
}
void turn(){
/* 반대 방향 회전 원하면 92-93 주석 해제, 95-96 주석 처리 */
//leftPower.write(90);
//rightPower.write(135);
leftPower.write(45);
rightPower.write(90);
delay(350);
}
float TakeReading(){
unsigned long echo = 0;
float result = 0.0f;
pinMode(distance, OUTPUT);
digitalWrite(distance, LOW);
delayMicroseconds(2);
digitalWrite(distance, HIGH);
delayMicroseconds(pingDelay);
digitalWrite(distance, LOW);
pinMode(distanceR, INPUT);
digitalWrite(distanceR, HIGH);
echo = pulseIn(distanceR,HIGH,38000);
result = echo/58.138;
return result;
}
회로도
littlerover_87IypZOgwm.fzz (프리칭 파일)