전원은 3S 리포 배터리(약 12V)를 사용합니다. 리포 배터리는 대용량 전류 공급이 가능해 모든 서보가 풀 로드로 작동 시 약 10A 전류 소모에도 적합합니다. 하지만 서보 동작 전압은 4.8~7.2V로 제한되므로 DC-DC 벅 컨버터로 12V를 5V로 강압해야 합니다. 2S 리포(약 7.4~8.4V)를 써도 벅 컨버터는 필요합니다. 제가 사용한 벅 컨버터는 8A까지 지원하지만 10~15A 제품을 추천합니다(발열 방지). 실제 로봇 동작 시 최대 전류는 약 6A였습니다.
필요한 부품
MG996R 서보 모터
SG90 마이크로 서보 모터
HC-05 블루투스 모듈
아두이노 메가 보드
3S 리포 배터리
DC-DC 벅 컨버터
아두이노 헥사포드용 PCB 설계
많은 서보 연결로 배선 정리가 필수라 EasyEDA로 맞춤 PCB를 설계했습니다. 이 PCB는 아두이노 메가 실드 역할을 해 메가 보드 위에 직접 꽂을 수 있습니다. 서보 연결 단자를 서로 가깝게 배치하고 큰 커패시터 2개로 전압을 안정화했습니다. 무선 제어용 NRF24L01 트랜시버 연결단자, 여러 디지털/아날로그 핀, 5V/그라운드, LED 2개, 배터리 전압 모니터링 단자도 포함했습니다. 12V 배터리 전압은 R1, R2 저항 분압기로 5V 이하로 낮춰 아날로그 핀으로 안전하게 읽어 배터리 잔량을 확인합니다.
거버 파일 업로드 후 뷰어로 검토하고, 아두이노 보드 색상과 맞추려 파란색 PCB로 주문했습니다. 첫 주문 시 10장 $2 혜택도 있습니다.
며칠 후 도착한 PCB입니다. 품질이 훌륭하고 설계와 정확히 일치합니다.
PCB 조립
PCB를 조립합니다. 아두이노 보드 연결용 남성 핀 헤더를 PCB에 납땜합니다. 핀 헤더를 하단에 놓고 판으로 고정한 뒤 뒤집어 모두 납땜합니다.
서보 연결용 남성 핀 헤더도 같은 방식으로 모두 납땜합니다. 마지막에 저항, 커패시터, 터미널 블록을 납땜하면 아두이노 메가 실드 완성입니다. 아두이노 보드에 꽂기만 하면 됩니다.
벅 컨버터 출력 전압을 5V로 맞춥니다(포텐쇼미터 조절). 벅 컨버터 입력 측에 파워 스위치를 달고 배터리 12V를 PCB의 12V 핀(배터리 모니터링용)에 연결했습니다. PCB 메인 입력은 반드시 5V여야 합니다.
이제 두 플레이트 사이에 전자부품을 넣습니다. 좀 꽉 끼지만 모두 들어갑니다. 배터리를 테이프로 고정하고, 그 위에 아두이노+PCB를 올립니다. NRF24L01이나 블루투스 모듈 중 사용할 통신 방식에 맞게 연결합니다. 배터리 충전 표시용 LED도 장착(전압 11V 미만 시 점등). 마지막으로 모든 서보를 서보 핀에 연결합니다(각 서보가 어떤 핀 번호인지 기록 필수). 곡면 커버를 상단 플레이트에 올리면 프로젝트 완성입니다.
아두이노 헥사포드 코드
코드가 길어 섹션별로 나누어 설명하고, 마지막에 전체 코드를 제공합니다.
서보 제어용 기본 Servo 라이브러리와 블루투스 통신용 SoftwareSerial 라이브러리를 포함합니다. 모든 서보 객체와 필요한 변수를 정의합니다.
setup에서 블루투스 통신 초기화, 초음파 센서/LED 핀 모드 설정, 서보 연결 핀을 정의합니다.
void setup() {
Serial.begin(38400);
Bluetooth.begin(38400); // 블루투스 기본 보드레이트
Bluetooth.setTimeout(1);
delay(20);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(ledB, OUTPUT);
// 머리
s15.attach(36, 600, 2400);
s14.attach(35, 600, 2400);
s13.attach(34, 600, 2400); // 대걸이
// 꼬리
s5.attach(26, 600, 2400);
// 다리 4
s10.attach(31, 600, 2400);
s11.attach(32, 600, 2400);
s12.attach(33, 600, 2400); // 회전
// 다리 5
s7.attach(28, 600, 2400);
s8.attach(29, 600, 2400);
s9.attach(30, 600, 2400); // 회전
// 다리 6
s1.attach(22, 600, 2400);
s2.attach(23, 600, 2400);
s3.attach(24, 600, 2400); // 회전
// 다리 1
s18.attach(39, 600, 2400);
s17.attach(38, 600, 2400);
s16.attach(37, 600, 2400); // 회전
// 다리 2
s21.attach(42, 600, 2400);
s20.attach(41, 600, 2400);
s19.attach(40, 600, 2400); // 회전
// 다리 3
s24.attach(45, 600, 2400);
s23.attach(44, 600, 2400);
s22.attach(43, 600, 2400); // 회전
}
write() 함수로 서보를 초기 위치로 이동시킵니다. 여기서 서보 캘리브레이션을 합니다. 조립 시 각 서보를 정확한 위치에 맞추기 어렵지만, 여기서 초기값을 조정해 로봇 움직임을 프로그래밍합니다.
// 초기 위치로 이동
// 머리
s15.write(72); s14.write(50); s13.write(90); // 대걸이
// 꼬리
s5.write(65);
// 다리 4
s10.write(65); s11.write(35); s12.write(40);
// 다리 5
s7.write(80); s8.write(50); s9.write(25);
// 다리 6
s1.write(90); s2.write(45); s3.write(60);
// 다리 1
s18.write(60); s17.write(90); s16.write(100);
// 다리 2
s21.write(50); s20.write(85); s19.write(75);
// 다리 3
s24.write(50); s23.write(80); s22.write(80);
헥사포드 걷기 프로그래밍
헥사포드 걷기 프로그래밍에는 순/역기구학(Forward/Inverse Kinematics) 등 여러 방법이 있습니다. 이는 원하는 몸체 최종 위치에 기반해 각 관절 위치를 계산하는 수학적 방법입니다. 하지만 저는 더 단순하게 만들었습니다. 사용한 서보가 저가형 MG996R이라 토크가 부족하고 정확한 위치 제어가 안 되기 때문입니다.
각 다리 움직임을 위한 커스텀 함수를 만들었습니다. 다리 사이클은 스윙(Swing) 페이즈와 스탠스(Stance) 페이즈 두 단계입니다. 스윙 페이즈에서는 다리가 공중에서 초기 위치에서 최종 위치로 이동하고, 스탠스 페이즈에서는 다리 끝이 바닥에 닿은 상태에서 최종 위치에서 초기 위치로 이동하며 몸체를 앞으로 밉니다.
메인 루프와 카운터를 사용해 각 서보 위치를 수동으로 프로그래밍해 이 움직임을 구현했습니다.
void moveLeg1() {
// 스윙 페이즈 - 공중에서 초기에서 최종 위치로
// 다리 들어올리기
if (i1L1 <= 10) {
s18.write(60 - i1L1 * 2);
s17.write(90 - i1L1 * 3);
i1L1++;
}
// 다리 회전
if (i2L1 <= 30) {
s16.write(100 - i2L1);
i2L1++;
}
// 바닥에 닿게 내리기
if (i2L1 > 20 && i3L1 <= 10) {
s18.write(40 + i3L1 * 2);
s17.write(60 + i3L1 * 3);
i3L1++;
}
// 스탠스 페이즈 - 바닥에 닿은 상태에서 이동
// 초기 위치로 회전 복귀
if (i2L1 >= 30) {
s16.write(70 + i4L1);
i4L1++;
l1status = HIGH;
}
// 카운터 리셋(반복용)
if (i4L1 >= 30) {
i1L1 = 0; i2L1 = 0; i3L1 = 0; i4L1 = 0; i5L1 = 0;
}
// 각 반복/스텝은 메인 루프에서 실행되며 딜레이로 속도 제어
}
먼저 바깥쪽 두 서보로 다리를 들어 올리고, 몸체 연결된 세 번째 서보가 특정 방향으로 회전을 시작합니다. 세 번째 서보가 멈출 10 스텝 전부터 바깥쪽 두 서보를 뒤로 이동시켜 바닥에 닿게 합니다. 이게 스윙 페이즈입니다. 이후 세 번째 서보를 초기 위치로 되돌리면 스탠스 페이즈 완료. 다리 한 사이클 후 카운터 리셋해 반복. 메인 루프에서 각 스텝 실행 시 딜레이로 서보 속도 제어. 다른 다리들도 같은 방식, 반대 방향 움직임(후진, 좌/우 회전)용 함수도 만들었습니다. 카운터로 스텝을 추적하며 머리, 꼬리, 대걸이 등 나머지 기능도 비슷하게 구현했습니다.
전진하려면 6개 moveLeg() 함수를 메인 루프에서 계속 호출합니다.
// 전진
if (m == 2) {
moveLeg1(); moveLeg3(); moveLeg5();
if (l1status == HIGH) {
moveLeg2(); moveLeg4(); moveLeg6();
}
}
3개 다리씩 오프셋되어 다리 1,3,5가 스윙 페이즈일 때 2,4,6은 스탠스 페이즈입니다. 좌회전 시엔 moveLeft() 함수들을 호출합니다.
이 명령들은 블루투스 모듈, 즉 스마트폰의 맞춤형 안드로이드 앱에서 옵니다.
// 수신 데이터 확인
if (Bluetooth.available() > 0) {
dataIn = Bluetooth.read(); // 데이터 읽기
}
아두이노 개미 로봇 안드로이드 앱
앱이 아두이노로 어떤 데이터를 보내는지 봅니다. MIT App Inventor로 만들었습니다.
앱 그래픽은 직접 만든 이미지를 버튼으로 사용했습니다. 하단에 속도 조절 슬라이더, 상단에 블루투스 연결 버튼이 있습니다.
예를 들어 전진 버튼 클릭 시 'if' 블록 실행: 아두이노로 숫자 2 전송(전진 함수 실행), 버튼 이미지를 하이라이트 버전으로 변경. 다시 누르면 'else' 블록: 숫자 0 전송(초기 위치로 리셋), 버튼 이미지 원복. 다른 버튼도 같은 원리입니다.
// 배터리 전압 모니터링
int sensorValue = analogRead(A3);
float voltage = sensorValue * (5.00 / 1023.00) * 2.9; // 5V 읽기를 12V 스케일로 변환
Serial.println(voltage);
// 11V 미만 시 LED 점등
if (voltage < 11) {
digitalWrite(ledB, HIGH);
} else {
digitalWrite(ledB, LOW);
}
배터리 전압이 11V 미만이면 LED 점등. 초음파 센서가 40cm 이내 물체 감지 시 공격 준비.
// 초음파 센서 거리 측정
if (getDistance() > 40) { att = 0; }
if (getDistance() <= 40) { att = 1; dataIn = 99; }
물체가 멀어지면 공격 취소, 계속 가까이 있으면 공격 실행.
// 물체 감지 시 공격 준비
if (att == 1) {
prepareAttack();
if (aStatus == HIGH) {
while (a == 0) { delay(2000); a = 1; }
if (getDistance() > 30) { att = 2; a = 0; aStatus = LOW; initialPosHead(); }
if (getDistance() < 30) { att = 3; a = 0; aStatus = LOW; initialPosHead(); }
}
}
// 물체 사라지면 공격 취소
if (att == 2) {
dismissAttack();
if (aStatus == HIGH) { dataIn = 0; att = 0; }
}
// 가까이 있으면 공격
if (att == 3) {
attack();
if (attStatus == HIGH) {
while (aa == 0) { delay(2000); aa = 1; }
attStatus = LOW;
}
if (aStatus == HIGH) {
while (a == 0) { delay(2000); a = 1; }
dataIn = 0; att = 0; initialPosHead();
}
}