아두이노 4족 보행 거미 로봇 만들기 – 단계별 튜토리얼
부품 및 소모품
| 부품 |
수량 |
| 아두이노 나노 R3 |
1 |
| 블루투스 저에너지(BLE) 모듈 (범용) |
1 |
| 어니언 코퍼레이션 OLED 확장 보드 |
1 |
| RGB 확산형 공통 캐소드 LED |
1 |
| JLCPCB 맞춤형 PCB |
1 |
| 서보 모터 (다리당 3개, 총 12개) |
12 |
| HC-06 블루투스 모듈 |
1 |
| OLED 디스플레이 화면 |
1 |
| 5mm RGB LED |
1 |
| 헤더 커넥터 |
여러 개 |
| 로봇 본체 부품 (3D 프린터 출력 필요) |
1세트 |
프로젝트 개요
여러분 안녕하세요! 놀라운 전자 프로젝트인 '크롤러 로봇', 일명 '거미 로봇' 또는 '4족 보행 로봇'을 단계별로 제작하는 튜토리얼을 준비했습니다.
로봇 공학 기술의 급속한 발전에 맞춰, 우리는 여러분을 로봇 제작의 더 높은 수준으로 안내하고자 합니다. 앞서 라인 팔로워 로봇 PICTO92 같은 기초 프로젝트를 통해 전자 부품에 익숙해지고 자신만의 프로젝트를 구상할 수 있도록 도왔습니다.
이제 한 단계 더 나아가, 개념은 기본적이지만 프로그래밍에 들어가면 꽤 복잡해지는 이 로봇을 제작해 보겠습니다. 시중에서 이러한 키트는 매우 비싸기 때문에, 단계별 가이드를 통해 여러분이 직접 스파이더봇을 만들 수 있도록 했습니다.
JLCPCB에서 주문한 맞춤형 PCB를 활용하면 제작이 훨씬 수월하며, 이 가이드에는 회로도와 코드 등 크롤러 로봇을 쉽게 제작할 수 있는 충분한 자료가 포함되어 있습니다.
이 프로젝트는 단 7일 만에 완성했습니다. 하드웨어 제작과 조립에 이틀, 코드 작성과 안드로이드 앱 개발(블루투스로 로봇 제어)에 닷새가 소요되었습니다. 본격적으로 시작하기 전에 주요 내용을 살펴보겠습니다.
배울 내용
- 프로젝트 기능에 맞는 적절한 부품 선택
- 선택한 모든 부품을 연결하는 회로 구성
- 프로젝트 모든 부품 조립
- 로봇 균형 조정 및 보정
- 안드로이드 앱으로 블루투스 연결 및 시스템 제어
1단계: '거미 로봇'이란?
이름에서 알 수 있듯, 우리 로봇은 거미의 움직임을 기본적으로 모방하지만 다리가 8개가 아닌 4개이므로 정확히 동일한 몸체 움직임은 수행하지 않습니다.
4족 보행(Quadruped) 로봇이라고도 불리며, 네 다리로 움직이고 각 다리의 움직임이 서로 연계되어 로봇 몸체의 위치를 파악하고 균형을 제어합니다.
다리 달린 로봇은 바퀴 달린 로봇보다 험난한 지형을 더 잘 주행하며, 동물적이고 다양한 움직임을 보입니다. 하지만 이로 인해 구조가 더 복잡해지고 많은 메이커에게는 진입 장벽이 높습니다. 또한 서보 모터나 스테퍼 모터 기반이라 DC 모터를 쓰는 바퀴 로봇보다 제작 비용이 훨씬 많이 듭니다.
4족 보행 로봇의 장점
자연계에 4족 동물이 많은 이유는 네 다리가 수동적 안정성, 즉 위치를 적극적으로 조정하지 않고도 서 있을 수 있는 능력을 제공하기 때문입니다. 로봇도 마찬가지입니다. 다리가 더 많은 로봇보다 저렴하고 단순하면서도 안정성을 확보할 수 있습니다.
2단계: 서보 모터가 핵심 구동기
위키피디아 정의에 따르면 서보모터는 각도나 선형 위치, 속도, 가속도를 정밀하게 제어할 수 있는 회전 또는 선형 액추에이터입니다. 위치 피드백용 센서와 결합된 적절한 모터로 구성되며, 서보모터 전용으로 설계된 정교한 컨트롤러가 필요합니다.
서보모터는 특정 모터 클래스가 아니며, 주로 폐루프 제어 시스템에 적합한 모터를 지칭하는 용어로 쓰입니다.
일반적으로 제어 신호는 사각파 펄스열입니다. 일반적인 제어 주파수는 44Hz, 50Hz, 400Hz입니다. 양의 펄스 폭이 서보 위치를 결정합니다. 약 0.5ms 펄스 폭은 서보 혼을 최대한 왼쪽(일반적으로 45~90도)으로 돌리고, 약 2.5~3.0ms는 최대한 오른쪽으로 돌립니다. 약 1.5ms는 0도 중립 위치를 유지합니다. 출력 고전압은 보통 2.5~10V(대표적 3V), 저전압은 -40mV~0V 범위입니다.
3단계: PCB 제작 (JLCPCB 생산)
JLCPCB 소개
JLCPCB(심천 지아리창 전자 기술 개발 유한회사)는 중국 최대 PCB 시제품 기업이자, 빠른 PCB 시제품 및 소량 생산을 전문으로 하는 하이테크 제조사입니다.
10년 이상의 PCB 제조 경험을 바탕으로 전 세계 20만 명 이상의 고객을 보유하고 있으며, 하루 8,000건 이상의 온라인 주문을 처리합니다. 연간 생산 능력은 20만 제곱미터로 1~다층 PCB를 생산합니다. 대규모 설비, 엄격한 관리, 우수한 품질을 자랑합니다.
프로젝트 적용
PCB 제작을 위해 여러 업체 가격을 비교한 결과, JLCPCB가 최상의 공급업체이자 가장 저렴한 제공업체로 선정되었습니다. 거버 파일을 업로드하고 PCB 두께, 색상, 수량 등 파라미터를 설정한 뒤 단 2달러만 지불하면 5일 만에 PCB를 받아볼 수 있습니다.
회로도 사진에서 볼 수 있듯 아두이노 나노로 전체 시스템을 제어하며, 로봇 거미 형태의 PCB를 설계해 외관을 크게 개선했습니다.
회로도(PDF) 파일은 여기에서 다운로드 가능합니다. 사진처럼 PCB가 매우 정교하게 제조되었으며, 설계한 거미 형태와 모든 라벨, 로고가 그대로 있어 납땜 작업을 안내합니다.
동일한 회로 설계로 주문하려면 거버 파일을 여기에서 다운로드하세요.
4단계: 필요한 부품
이 프로젝트에 필요한 구성품을 정리해 보겠습니다. 앞서 언급했듯 아두이노 나노로 로봇 네 다리의 12개 서보 모터를 모두 제어합니다. 또한 코즈모(Cozmo) 표정 표시용 OLED 디스플레이와 안드로이드 앱 제어용 블루투스 모듈이 포함됩니다.
- JLCPCB에서 주문한 PCB
- 서보 모터 12개 (다리당 3개) - 구매 링크
- 아두이노 나노 1개 - 구매 링크
- HC-06 블루투스 모듈 - 구매 링크
- OLED 디스플레이 화면 1개 - 구매 링크
- 5mm RGB LED - 구매 링크
- 헤더 커넥터 몇 개 - 구매 링크
- 로봇 본체 부품 (3D 프린터로 출력 필요) - STL 파일 여기
5단계: 로봇 조립
PCB 준비와 부품 납땜이 완료되면 로봇 본체를 조립합니다. 절차는 매우 간단하니 아래 단계를 따라 하세요.
먼저 각 다리를 개별적으로 준비합니다. 다리 하나당 관절용 서보 2개와 콕사(Coxa), 펨러(Femur), 티비아(Tibia) 출력 부품, 작은 연결 부품이 필요합니다.
로봇 본체 부품 STL 파일은 여기에서 다운로드하세요.
첫 번째 서보를 소켓에 장착하고 나사로 고정합니다. 서보 축을 나사 없이 180° 돌린 뒤, 펨러 부품을 연결하고 첫 번째 서보 관절 축과 연결 부품으로 티비아와 결합합니다. 마지막 단계로 두 번째 서보(두 번째 관절)를 장착해 콕사 부품을 고정하면 다리 하나가 완성됩니다.
같은 방식으로 네 다리 모두 완성하세요. 그 후 상부 섀시에 나머지 서보들을 소켓에 장착하고 각 다리를 해당 서보에 연결합니다. 마지막으로 하부 로봇 섀시에 회로 기판을 장착하면 조립이 끝납니다.
6단계: 안드로이드 앱
안드로이드 앱을 통해 블루투스로 로봇에 연결해 전진, 후진, 좌우 회전 제어가 가능합니다. 또한 컬러 휠에서 원하는 색상을 선택해 로봇 LED 색상을 실시간으로 변경할 수 있습니다.
안드로이드 앱은 여기에서 무료로 다운로드할 수 있습니다.
7단계: 아두이노 코드 및 테스트 검증
로봇이 거의 완성되었지만 먼저 관절 각도를 설정해야 합니다. 설정 코드를 업로드해 각 서보를 90도 위치에 고정한 뒤 부착하세요. 7V DC 배터리를 연결해 로봇을 구동해야 합니다.
다음으로 안드로이드 앱으로 로봇을 제어하는 메인 프로그램을 업로드합니다. 두 프로그램 다운로드 링크:
코드 업로드 후 OLED 디스플레이를 연결해 메인 코드에 구현한 코즈모 로봇 표정을 표시합니다.
사진에서 보듯 로봇이 스마트폰에서 보낸 모든 명령을 정확히 수행하며, 앞으로 더 개선할 여지가 남아 있습니다.
코드
거미 로봇 메인 코드
/************************************************************************************************************************************************************************
* - Author : BELKHIR Mohamed
* - Profession : (Electrical Engineer) MEGA DAS owner
* - Main purpose : Industrial Application
* - Copyright (c) holder : All rights reserved
* - License : BSD 2-Clause License
* - Date : 20/04/2017
***********************************************************************************************************************************************************************/
/*********************************** NOTE **************************************/
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED
/* */
#include <Servo.h> // 서보 정의 및 제어용
#include <FlexiTimer2.h> // 모든 서보 관리를 위한 타이머 설정
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
// OLED 디스플레이 TWI 주소
#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(-1);
/* 서보 --------------------------------------------------------------------*/
// 4개 다리용 12개 서보 정의
Servo servo[4][3];
// 서보 포트 정의
const int servo_pin[4][3] = {
{11, 12, 13},
{2, 4, 7},
{14, 15, 16},
{8, 9, 10}
};
/* 로봇 크기 ---------------------------------------------------------*/
const float length_a = 50;
const float length_b = 77.1;
const float length_c = 27.5;
const float length_side = 71;
const float z_absolute = -28;
/* 이동 상수 ----------------------------------------------------*/
const float z_default = -50, z_up = -30, z_boot = z_absolute;
const float x_default = 62, x_offset = 0;
const float y_start = 0, y_step = 40;
const float y_default = x_default;
/* 이동 변수 ----------------------------------------------------*/
volatile float site_now[4][3]; // 각 다리 끝의 실시간 좌표
volatile float site_expect[4][3]; // 각 다리 끝의 목표 좌표
float temp_speed[4][3]; // 각 축 속도, 이동 전 재계산 필요
float move_speed; // 이동 속도
float speed_multiple = 1; // 이동 속도 배율
const float spot_turn_speed = 4;
const float leg_move_speed = 8;
const float body_move_speed = 3;
const float stand_seat_speed = 1;
volatile int rest_counter; // +1/0.02초, 자동 휴식용
// 함수 파라미터
const float KEEP = 255; // 계산용 PI 정의
const float pi = 3.1415926;
/* 회전 상수 --------------------------------------------------------*/
// 임시 길이
const float temp_a = sqrt(pow(2 * x_default + length_side, 2) + pow(y_step, 2));
const float temp_b = 2 * (y_start + y_step) + length_side;
const float temp_c = sqrt(pow(2 * x_default + length_side, 2) + pow(2 * y_start + y_step + length_side, 2));
const float temp_alpha = acos((pow(temp_a, 2) + pow(temp_b, 2) - pow(temp_c, 2)) / 2 / temp_a / temp_b);
// 회전용 좌표
const float turn_x1 = (temp_a - length_side) / 2;
const float turn_y1 = y_start + y_step / 2;
const float turn_x0 = turn_x1 - temp_b * cos(temp_alpha);
const float turn_y0 = temp_b * sin(temp_alpha) - turn_y1 - length_side;
const int lightR = 3;
const int lightG = 5;
const int lightB = 6;
int LedR = 0;
int LedG = 0;
int LedB = 0;
char SerialData;
// 시리얼 포트로 수신된 각 문자를 읽기 위한 변수
String data = "";
void setup() {
Serial.begin(9600);
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
display.clearDisplay();
display.display();
delay(10000);
set_site(0, x_default - x_offset, y_start + y_step, z_boot);
set_site(1, x_default - x_offset, y_start + y_step, z_boot);
set_site(2, x_default + x_offset, y_start, z_boot);
set_site(3, x_default + x_offset, y_start, z_boot);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
site_now[i][j] = site_expect[i][j];
}
}
// 서보 서비스 시작
FlexiTimer2::set(20, servo_service);
FlexiTimer2::start();
// 서보 초기화
servo_attach();
stand();
// delay(3000);
// sit();
// delay(3000);
// stand();
// delay(3000);
happy();
delay(random(500, 1000));
cierra();
delay(150);
enfado();
delay(random(1000, 3000));
cierra();
delay(150);
entorna();
delay(random(1000, 3000));
cierra();
delay(150);
enfado1();
delay(random(1000, 3000));
cierra();
delay(150);
triste();
delay(random(1000, 3000));
cierra();
delay(150);
abre();
delay(random(500, 3000));
cierra();
delay(150);
happy();
delay(random(500, 1000));
}
void loop() {
while (Serial.available()) { // 시리얼 데이터가 있으면 저장
delay(10);
SerialData = Serial.read();
if (SerialData == 'b') LedR = Serial.parseInt();
else if (SerialData == 'g') LedG = Serial.parseInt();
else if (SerialData == 'r') LedB = Serial.parseInt();
else data += SerialData;
}
if (data == "f") { // 전진 명령
cierra();
delay(150);
happy();
step_forward(1);
}
if (data == "p") { // 후진 명령
cierra();
delay(150);
triste();
step_back(1);
}
if (data == "l") { // 좌회전 명령
cierra();
delay(150);
enfado1();
turn_left(1);
}
if (data == "m") { // 우회전 명령
cierra();
delay(150);
enfado();
turn_right(5);
}
data = "";
analogWrite(lightR, LedR);
analogWrite(lightG, LedG);
analogWrite(lightB, LedB);
}
// ... (이하 서보 제어, 이동 알고리즘, OLED 표정 표시 함수 등 전체 코드 생략 - 원본 참조) ...
커스텀 파트 및 인클로저
3D 프린터용 STL 파일은 본문 링크에서 다운로드 가능합니다.
회로도
회로도 파일은 본문 링크에서 확인하세요.