이 튜토리얼에서는 맞춤 제작한 안드로이드 애플리케이션으로 무선 제어 및 프로그래밍 가능한 아두이노 로봇 팔을 만드는 방법을 배웁니다. 로봇 부품 설계 및 3D 프린팅, 전자 부품 연결, 아두이노 프로그래밍, 로봇 팔 제어용 자체 안드로이드 앱 개발까지 전체 과정을 보여드리겠습니다.
개요
앱의 슬라이더를 사용하여 각 서보 또는 로봇 팔 축의 움직임을 수동으로 제어할 수 있습니다. 또한 "저장(Save)" 버튼을 사용하여 각 위치나 단계를 기록하면 로봇 팔이 자동으로 이 단계들을 실행하고 반복할 수 있습니다. 같은 버튼으로 자동 동작을 일시정지하거나 모든 단계를 초기화/삭제하여 새로운 단계를 기록할 수도 있습니다.
아두이노 로봇 팔 3D 모델
Solidworks 3D 모델링 소프트웨어를 사용하여 로봇 팔을 설계했습니다. 팔은 5자유도(5 DOF)를 가집니다.
첫 3축(허리, 어깨, 팔꿈치)에는 MG996R 서보를, 나머지 2축(손목 롤, 손목 피치) 및 그리퍼에는 더 작은 SG90 마이크로 서보를 사용했습니다.
이 3D 모델은 Thangs에서 찾아 다운로드하거나 브라우저에서 탐색할 수 있습니다:
Thangs에서 어셈블리 3D 모델 다운로드.
이 튜토리얼을 지원해준 Thangs에 감사드립니다.
3D 프린팅용 STL 파일:
아두이노 로봇 팔 STL 파일
로봇 팔 3D 프린팅
새 3D 프린터인 Creality CR-10을 사용하여 아두이노 로봇 팔의 모든 부품을 3D 프린팅했습니다.
이 멋진 3D 프린터를 제공해준 Banggood.com에 감사를 전합니다. Creality CR-10은 가격 대비 놀라운 출력 품질을 보여주며, 90% 가까이 사전 조립되어 있어 장점이 있습니다.
조립을 완료하려면 볼트와 브라켓으로 상하 프레임을 연결하고, 제공된 케이블로 전자 부품을 컨트롤 박스에 연결하기만 하면 됩니다.
시작하기 전에 롤러 휠이 충분히 조여졌는지 확인하는 것이 좋습니다. 조여지지 않았다면 이심 너트를 사용하여 조일 수 있습니다. 그리고 3D 프린팅 베드 레벨링 후 여러분의 3D 창작물을 현실로 만들 준비가 됩니다.
이제 로봇 팔을 조립할 준비가 되었습니다. 베이스에 포함된 나사로 첫 번째 서보 모터를 부착하는 것으로 시작했습니다. 서보의 출력 샤프트에 원형 혼(round horn)을 볼트로 고정했습니다.
그 위에 상단 부품을 놓고 두 나사로 고정했습니다.
여기서도 서보, 다음 부품에 원형 혼, 출력 샤프트의 볼트로 서로 고정하는 방식입니다.
어깨 축에서는 서보가 팔 나머지 부분과 페이로드 무게 전체를 지탱해야 하므로 스프링이나 고무줄 같은 보조 장치를 추가하는 것이 좋습니다. 제 경우 고무줄을 사용했습니다.
비슷한 방식으로 로봇 팔의 나머지를 계속 조립했습니다. 그리퍼 메커니즘에는 4mm 볼트와 너트를 사용했습니다.
마지막으로 그리퍼 메커니즘을 마지막 서보에 부착하고 아두이노 로봇 팔이 완성되었습니다.
아두이노 로봇 팔 회로도
다음 단계는 전자 부품 연결입니다. 이 프로젝트의 회로도는 사실 꽤 단순합니다. 아두이노 보드와 스마트폰과의 통신용 HC-05 블루투스 모듈만 있으면 됩니다. 6개 서보 모터의 제어 핀을 아두이노 보드의 6개 디지털 핀에 연결합니다.
서보 전원용 5V가 필요하지만, 모든 서보가 소모할 수 있는 전류량을 아두이노가 감당할 수 없으므로 외부 전원에서 공급해야 합니다. 전원은 최소 2A 전류를 처리할 수 있어야 합니다. 모든 것을 연결했으면 아두이노 프로그래밍과 안드로이드 앱 제작으로 넘어갑니다.
필요한 부품
MG996R 서보 모터
SG90 마이크로 서보 모터
HC-05 블루투스 모듈
아두이노 보드
5V 2A DC 전원 공급 장치
아두이노 로봇 팔 코드
코드가 좀 길어서 이해를 돕기 위해 섹션별로 나누어 설명하고, 글 마지막에 전체 소스 코드를 올리겠습니다.
먼저 블루투스 모듈의 시리얼 통신용 SoftwareSerial 라이브러리와 서보 라이브러리를 포함합니다. 둘 다 아두이노 IDE에 포함되어 있어 외부 설치가 필요 없습니다. 그런 다음 6개 서보, HC-05 블루투스 모듈, 서보의 현재/이전 위치 저장용 변수, 자동 모드용 위치/단계 저장 배열들을 정의합니다.
#include <SoftwareSerial.h>
#include <Servo.h>
Servo servo01;
Servo servo02;
Servo servo03;
Servo servo04;
Servo servo05;
Servo servo06;
SoftwareSerial Bluetooth(3, 4); // 아두이노(RX, TX) - HC-05 블루투스(TX, RX)
int servo1Pos, servo2Pos, servo3Pos, servo4Pos, servo5Pos, servo6Pos; // 현재 위치
int servo1PPos, servo2PPos, servo3PPos, servo4PPos, servo5PPos, servo6PPos; // 이전 위치
int servo01SP[50], servo02SP[50], servo03SP[50], servo04SP[50], servo05SP[50], servo06SP[50]; // 위치/단계 저장용
int speedDelay = 20;
int index = 0;
String dataIn = "";
setup 섹션에서 서보들과 블루투스 모듈을 초기화하고 로봇 팔을 초기 위치로 이동시킵니다. write() 함수로 서보를 0~180도 아무 위치로나 이동시킵니다.
void setup() {
servo01.attach(5);
servo02.attach(6);
servo03.attach(7);
servo04.attach(8);
servo05.attach(9);
servo06.attach(10);
Bluetooth.begin(38400); // 블루투스 모듈 기본 보드레이트
Bluetooth.setTimeout(1);
delay(20);
// 로봇 팔 초기 위치
servo1PPos = 90; servo01.write(servo1PPos);
servo2PPos = 150; servo02.write(servo2PPos);
servo3PPos = 35; servo03.write(servo3PPos);
servo4PPos = 140; servo04.write(servo4PPos);
servo5PPos = 85; servo05.write(servo5PPos);
servo6PPos = 80; servo06.write(servo6PPos);
}
loop 섹션에서 Bluetooth.available() 함수로 스마트폰으로부터 들어오는 데이터가 있는지 계속 확인합니다. 데이터가 있으면 readString() 함수로 문자열로 읽어 dataIn 변수에 저장합니다. 도착한 데이터에 따라 로봇 팔에 무엇을 할지 지시합니다.
// 들어오는 데이터 확인
if (Bluetooth.available() > 0) {
dataIn = Bluetooth.readString(); // 데이터를 문자열로 읽기
제어 안드로이드 앱
이제 안드로이드 앱을 보고 실제로 아두이노로 어떤 데이터를 보내는지 살펴보겠습니다.
MIT App Inventor 온라인 애플리케이션으로 앱을 만들었습니다. 작동 방식은 다음과 같습니다. 상단에 HC-05 블루투스 모듈과 스마트폰을 연결하는 두 버튼이 있습니다. 왼쪽에는 로봇 팔 이미지, 오른쪽에는 6개 서보 제어용 슬라이더와 속도 제어용 슬라이더 하나가 있습니다.
각 슬라이더는 로봇 팔 관절에 맞는 서로 다른 초기값, 최소/최대값을 가집니다. 앱 하단에는 SAVE, RUN, RESET 세 버튼이 있어 로봇 팔을 프로그래밍하여 자동 실행할 수 있습니다. 저장된 단계 수를 보여주는 레이블도 있습니다. MIT App Inventor로 이런 앱 만드는 방법은 별도 상세 튜토리얼을 참조하세요.
앱 뒤의 프로그램이나 블록을 살펴보겠습니다. 왼쪽에는 블루투스 모듈과 스마트폰 연결용 블록들이 있습니다.
그다음 서보 위치 제어용 슬라이더 블록들과 로봇 팔 프로그래밍용 버튼 블록들이 있습니다. 슬라이더 위치가 변경되면 블루투스 .SendText 함수로 아두이노에 텍스트를 보냅니다. 이 텍스트는 어떤 슬라이더가 변경되었는지 나타내는 접두사와 슬라이더의 현재 값으로 구성됩니다.
위 MIT App Inventor 프로젝트 파일과 스마트폰에 설치할 준비된 안드로이드 앱 다운로드 파일:
아두이노 로봇 팔 제어 MIT App Inventor 프로젝트 파일아두이노 로봇 팔 제어 안드로이드 앱
따라서 아두이노에서 startsWith() 함수로 들어오는 데이터의 접두사를 확인하여 다음에 무엇을 할지 알 수 있습니다. 예를 들어 접두사가 "s1"이면 1번 서보를 이동해야 함을 압니다. substring() 함수로 나머지 텍스트, 즉 위치 값을 추출하고 정수로 변환하여 해당 위치로 서보를 이동시킵니다.
// "허리(Waist)" 슬라이더 값 변경 시 - 서보 1을 해당 위치로 이동
if (dataIn.startsWith("s1")) {
String dataInS = dataIn.substring(2, dataIn.length()); // 숫자만 추출. 예: "s1120" -> "120"
servo1Pos = dataInS.toInt(); // 문자열을 정수로 변환
// 서보 속도 제어를 위해 for 루프 사용
// 이전 위치가 현재 위치보다 큰 경우
if (servo1PPos > servo1Pos) {
for (int j = servo1PPos; j >= servo1Pos; j--) {
servo01.write(j); // 서보 내리기
delay(20); // 서보 회전 속도 정의
}
}
// 이전 위치가 현재 위치보다 작은 경우
if (servo1PPos < servo1Pos) {
for (int j = servo1PPos; j <= servo1Pos; j++) {
servo01.write(j); // 서보 올리기
delay(20);
}
}
servo1PPos = servo1Pos; // 현재 위치를 이전 위치로 설정
}
단순히 write() 함수를 호출하면 서보가 최대 속도로 움직여 로봇 팔에는 너무 빠릅니다. 대신 FOR 루프로 이전 위치에서 현재 위치로 점진적으로 이동시키며 각 반복 사이에 지연 시간을 두어 서보 속도를 제어합니다. 지연 시간을 변경하여 서보 속도를 조절할 수 있습니다.
같은 방법으로 로봇 팔의 각 축을 구동합니다.
아래는 SAVE 버튼입니다. SAVE 버튼을 누르면 각 서보 모터의 위치가 배열에 저장됩니다. 누를 때마다 인덱스가 증가하여 배열이 단계별로 채워집니다.
// "SAVE" 버튼 눌림
if (dataIn.startsWith("SAVE")) {
servo01SP[index] = servo1PPos; // 위치를 배열에 저장
servo02SP[index] = servo2PPos;
servo03SP[index] = servo3PPos;
servo04SP[index] = servo4PPos;
servo05SP[index] = servo5PPos;
servo06SP[index] = servo6PPos;
index++; // 배열 인덱스 증가
}
RUN 버튼을 누르면 저장된 단계들을 실행하는 runservo() 사용자 정의 함수를 호출합니다. 이 함수를 살펴보겠습니다. RESET 버튼이 눌릴 때까지 저장된 단계들을 반복 실행합니다. FOR 루프로 배열에 저장된 모든 위치를 실행하면서 동시에 스마트폰으로부터 들어오는 데이터가 있는지 확인합니다. 이 데이터는 RUN/일시정지 버튼일 수 있으며, 로봇을 일시정지하고 다시 클릭하면 자동 움직임을 계속합니다. 속도 슬라이더 위치가 변경되면 그 값을 FOR 루프의 반복 간 지연 시간 변경에 사용하여 서보 모터 속도를 변경합니다.
// 자동 모드 사용자 정의 함수 - 저장된 단계 실행
void runservo() {
while (dataIn != "RESET") { // "RESET" 버튼 눌릴 때까지 단계 반복 실행
for (int i = 0; i <= index - 2; i++) { // 모든 단계(index) 실행
if (Bluetooth.available() > 0) { // 들어오는 데이터 확인
dataIn = Bluetooth.readString();
if (dataIn == "PAUSE") { // "PAUSE" 버튼 눌림
while (dataIn != "RUN") { // "RUN" 다시 눌릴 때까지 대기
if (Bluetooth.available() > 0) {
dataIn = Bluetooth.readString();
if (dataIn == "RESET") { break; }
}
}
}
// 속도 슬라이더 변경 시
if (dataIn.startsWith("ss")) {
String dataInS = dataIn.substring(2, dataIn.length());
speedDelay = dataInS.toInt(); // 서보 속도(지연 시간) 변경
}
}
// 서보 1
if (servo01SP[i] == servo01SP[i + 1]) { }
if (servo01SP[i] > servo01SP[i + 1]) {
for (int j = servo01SP[i]; j >= servo01SP[i + 1]; j--) {
servo01.write(j);
delay(speedDelay);
}
}
if (servo01SP[i] < servo01SP[i + 1]) {
for (int j = servo01SP[i]; j <= servo01SP[i + 1]; j++) {
servo01.write(j);
delay(speedDelay);
}
}
// ... 서보 2~6도 동일한 방식
}
}
}
앞서 설명한 IF 문과 FOR 루프를 비슷한 방식으로 사용하여 서보들을 다음 위치로 이동시킵니다. 마지막으로 RESET 버튼을 누르면 배열의 모든 데이터를 0으로 지우고 인덱스도 0으로 리셋하여 새로운 움직임으로 로봇 팔을 재프로그래밍할 수 있습니다.
// "RESET" 버튼 눌림
if (dataIn == "RESET") {
memset(servo01SP, 0, sizeof(servo01SP)); // 배열 데이터 0으로 초기화
memset(servo02SP, 0, sizeof(servo02SP));
memset(servo03SP, 0, sizeof(servo03SP));
memset(servo04SP, 0, sizeof(servo04SP));
memset(servo05SP, 0, sizeof(servo05SP));
memset(servo06SP, 0, sizeof(servo06SP));
index = 0; // 인덱스 0으로
}
이제 로봇 팔을 즐기고 재미있게 놀 수 있습니다.
아두이노 로봇 팔 전체 코드:
/*
DIY 아두이노 로봇 팔 스마트폰 제어
by Dejan, www.HowToMechatronics.com
*/
#include <SoftwareSerial.h>
#include <Servo.h>
Servo servo01;
Servo servo02;
Servo servo03;
Servo servo04;
Servo servo05;
Servo servo06;
SoftwareSerial Bluetooth(3, 4); // 아두이노(RX, TX) - HC-05 블루투스(TX, RX)
int servo1Pos, servo2Pos, servo3Pos, servo4Pos, servo5Pos, servo6Pos;
int servo1PPos, servo2PPos, servo3PPos, servo4PPos, servo5PPos, servo6PPos;
int servo01SP[50], servo02SP[50], servo03SP[50], servo04SP[50], servo05SP[50], servo06SP[50];
int speedDelay = 20;
int index = 0;
String dataIn = "";
void setup() {
servo01.attach(5);
servo02.attach(6);
servo03.attach(7);
servo04.attach(8);
servo05.attach(9);
servo06.attach(10);
Bluetooth.begin(38400);
Bluetooth.setTimeout(1);
delay(20);
// 로봇 팔 초기 위치
servo1PPos = 90; servo01.write(servo1PPos);
servo2PPos = 150; servo02.write(servo2PPos);
servo3PPos = 35; servo03.write(servo3PPos);
servo4PPos = 140; servo04.write(servo4PPos);
servo5PPos = 85; servo05.write(servo5PPos);
servo6PPos = 80; servo06.write(servo6PPos);
}
void loop() {
if (Bluetooth.available() > 0) {
dataIn = Bluetooth.readString();
// 서보 1~6 제어 (s1~s6 접두사)
if (dataIn.startsWith("s1")) { /* 서보 1 제어 코드 */ }
if (dataIn.startsWith("s2")) { /* 서보 2 제어 코드 */ }
if (dataIn.startsWith("s3")) { /* 서보 3 제어 코드 */ }
if (dataIn.startsWith("s4")) { /* 서보 4 제어 코드 */ }
if (dataIn.startsWith("s5")) { /* 서보 5 제어 코드 */ }
if (dataIn.startsWith("s6")) { /* 서보 6 제어 코드 */ }
// SAVE 버튼
if (dataIn.startsWith("SAVE")) {
servo01SP[index] = servo1PPos;
servo02SP[index] = servo2PPos;
servo03SP[index] = servo3PPos;
servo04SP[index] = servo4PPos;
servo05SP[index] = servo5PPos;
servo06SP[index] = servo6PPos;
index++;
}
// RUN 버튼
if (dataIn.startsWith("RUN")) {
runservo();
}
// RESET 버튼
if (dataIn == "RESET") {
memset(servo01SP, 0, sizeof(servo01SP));
memset(servo02SP, 0, sizeof(servo02SP));
memset(servo03SP, 0, sizeof(servo03SP));
memset(servo04SP, 0, sizeof(servo04SP));
memset(servo05SP, 0, sizeof(servo05SP));
memset(servo06SP, 0, sizeof(servo06SP));
index = 0;
}
}
}
void runservo() {
while (dataIn != "RESET") {
for (int i = 0; i <= index - 2; i++) {
if (Bluetooth.available() > 0) {
dataIn = Bluetooth.readString();
if (dataIn == "PAUSE") {
while (dataIn != "RUN") {
if (Bluetooth.available() > 0) {
dataIn = Bluetooth.readString();
if (dataIn == "RESET") { break; }
}
}
}
if (dataIn.startsWith("ss")) {
String dataInS = dataIn.substring(2, dataIn.length());
speedDelay = dataInS.toInt();
}
}
// 서보 1~6 각각 위치 이동 (생략 - 위와 동일 패턴)
}
}
}
이 영상이 마음에 들고 새로운 것을 배웠기를 바랍니다. 아래 댓글 섹션에서 자유롭게 질문해 주시고 아두이노 프로젝트 모음도 확인해 보세요.