이 프로젝트의 목적은 PID 제어를 사용하는 라인 팔로워 로봇을 만드는 것입니다. 또한 안드로이드 기기를 이용해 주요 제어 파라미터를 쉽게 설정하고 빠르게 튜닝할 수 있게 합니다.
이 프로젝트는 라인 팔로워 로봇의 잠재성을 탐구하는 2부작 프로젝트의 첫 번째 파트입니다. 2부에서는 미로 해결 로봇(Maze Solver Robot)을 아두이노와 간단한 인공지능 기법으로 구현할 예정입니다.
1단계: 자재 목록 (BOM)
필요한 자재는 매우 단순하며 최종 로봇 비용은 약 $75(약 10만 원) 수준으로 저렴합니다:
바디 (필요에 맞게 변경 가능):
나무 판 2개 (80x80mm)
바인더 클립 3개
나무 바퀴 2개 (지름 50mm)
볼 캐스터 1개
고무줄 9개
3M 커맨드 프레임 스트립
센서 고정용 플라스틱 조인트
브레드보드 및 배선
4xNi-MH 배터리 2세트 (각 5V)
SM-S4303R 연속 회전 서보 2개
아두이노 나노
HC-06 블루투스 모듈
라인 센서 5개 (TCRT5000 4채널 모듈 + 개별 센서 1개)
LED 1개
버튼 1개
2단계: 모터 설정
모터로 2개의 연속 회전 서보(SM-S4303R)를 사용했습니다. 3M 커맨드 스트립, 글루, 양면 테이프 등으로 단단하게 결합해 하나의 블록처럼 만듭니다. 이 서보들은 데이터 입력으로 받는 펄스 폭에 따라 정해진 속도로 회전합니다. 이 서보의 펄스 폭 범위는 1.0ms(1,000µs) ~ 2.0ms(2,000µs)입니다. 다른 서보는 다를 수 있습니다.
1.5ms 펄스: 중립 위치 = "정지"
1.0ms 펄스: 한 방향 최대 속도 (약 70 RPM)
2.0ms 펄스: 반대 방향 최대 속도
1.0~1.5ms 또는 1.5~2.0ms 사이: 비례 속도
두 서보를 물리적으로 연결한 후, 회로도에 따라 외부 5V/6V로 전원 공급하고 아두이노 신호를 연결합니다:
좌측 서보: 아두이노 핀 5
우측 서보: 아두이노 핀 3
연결 후 가장 먼저 1.5ms 펄스를 보내 모터가 "정지"하는지 확인하세요. 안 되면 서보 하단의 노란 볼트(조정 나사)로 완전 정지 위치 조정하세요.
참고: 물리적 조정이 없는 서보라면 코드 내 1500 마이크로초 값을 위아래로 조정해 완전 정지를 찾으세요.
핀 13에 외부 LED 추가 (신호/테스트용, 내부 LED는 케이블 사이에 가려 보기 어려움). 핀 9에 버튼 연결 (테스트 및 로봇 시작용으로 유용).
while(digitalRead(buttonPin)) { } // 버튼 누를 때까지 대기
motorTurn(LEFT, 500);
motorTurn(RIGHT, 500);
아래 코드로 모터 종합 테스트 가능 (전진, 후진, 정지, 좌회전, 우회전). 필요시 딜레이 조정으로 회전 각도 보정. 좌우 펄스 값을 약간 다르게 해 모터 불균형 보정도 가능.
4단계: 블루투스 모듈 (선택 사항)
HC-06 블루투스 모듈을 브레드보드에 장착. 아두이노 라이브러리 SoftwareSerial 사용.
Tx 핀 → 아두이노 핀 10 (Rx)
Rx 핀 → 아두이노 핀 11 (Tx)
VCC/GND → 아두이노 5V/GND
로봇은 블루투스 유무와 상관없이 작동합니다. 코드는 BT를 활성화하지 않으면 기본 파라미터를 사용하도록 작성되어 있습니다. 이 튜토리얼 마지막 부분에서 안드로이드 앱으로 로봇 파라미터 튜닝 및 수동 제어하는 방법을 다룹니다. 블루투스와 앱 사용은 선택 사항입니다 (예: 라인 팔로워 대회 출전 시).
5단계: 라인 센서 장착
아두이노 핀 연결:
센서 0 = 핀 12
센서 1 = 핀 18 (A4)
센서 2 = 핀 17 (A3)
센서 3 = 핀 16 (A2)
센서 4 = 핀 19 (A5)
5개 센서를 사진과 같이 플라스틱 바에 고정
테스트용으로 센서에 라벨 붙이기 권장. 왼쪽부터 "0" ~ 오른쪽 "4"
케이블을 프레임 아래로 고무줄로 고정. 바퀴나 캐스터에 걸리지 않게 주의
두 번째 5V 배터리 세트 장착 후 아두이노 Vin에 연결
본 프로젝트에서는 4채널 통합 모듈 + 개별 센서 1개 조합을 사용했습니다. 모든 센서 호환됩니다. 단순화를 위해 회로도에는 5개 개별 센서로 표시했지만 최종 결과는 동일합니다.
6단계: IR 센서 로직 구현
IR 센서는 개별 IR LED와 IR 포토다이오드로 구성. LED에서 방출된 IR 빛이 표면에 반사되어 포토다이오드로 돌아옵니다. 포토다이오드는 표면 반사율에 비례하는 출력 전압 생성 (밝은 표면 = 높은 값, 검정/어두운 표면 = 낮은 값).
사용한 센서 모듈의 내장 IC는 단순 디지털 신호 출력 (HIGH: 검정/어두움, LOW: 밝음). 모듈의 포텐쇼미터로 '검정'/'밝음' 기준 임계값 조정. 검정/어두운 색 반사 시 HIGH("1"), 그 외 밝은 색은 LOW("0") 출력.
5개 센서 배열은 검은 선 중앙에 하나의 센서만 오면 그 센서만 HIGH 출력되도록 장착. 센서 간격은 2개 센서가 검은 선 폭을 동시에 커버해 둘 다 HIGH 출력되도록 계산.
const int lineFollowSensor0 = 12;
const int lineFollowSensor1 = 18;
const int lineFollowSensor2 = 17;
const int lineFollowSensor3 = 16;
const int lineFollowSensor4 = 19;
반대 상황 (error=-1): 우측 1,800µs(빨라짐), 좌측 1,300µs(느려짐)
치우침이 클수록 error 커지고 중앙 복귀 속도 빨라짐. 이것이 비례 제어(P)이며, 더 복잡한 PID 제어의 P 구성 요소입니다.
8단계: PID 제어 (선택 사항)
이 부분은 건너뛰셔도 됩니다. 앞서 설명한 비례 제어만으로도 충분히 잘 작동합니다. 더 복잡한 제어 시스템을 원한다면 계속 읽으세요.
PID(비례, 적분, 미분)는 가장 일반적인 제어 방식 중 하나입니다. 대부분의 산업 제어 루프가 PID 변형을 사용합니다. PID 루프 튜닝 방법은 여러 가지가 있으며, 여기서는 수동 튜닝 기법을 사용합니다.
PID를 스프링으로 비유: 스프링은 원래 길이가 있고, 늘어나거나 압축되면 가능한 빨리 원래 길이로 돌아가려 함. 마찬가지로 PID 알고리즘은 제어 대상 물리량의 설정값(set point)을 가지며, 이것이 변경되면 시스템이 다른 특성을 제어해 최단 시간에 설정값으로 복귀시킴. 크루즈 컨트롤, 로봇, 온도/전압 조절기 등 물리량 제어가 필요한 곳 어디든 사용.
PID 작동 원리
시스템은 센서로 현재 물리량 값을 측정해 설정값과의 '오차(error)' 또는 '편차(deviation)' 계산. 이 오차를 최소화해 이상적으로 0으로 만들어야 함. 가능한 빨리. 이상적으로 시스템 응답에 지연이 없어야 함.
PID 구현
i) 오차 항 (e): 설정값과 현재 값의 차이. Error = set_point - current_value (우리 경우는 로봇의 선 위 위치에서 구한 error 변수)
ii) 비례 항 (P): 오차에 비례. P = error. 설정값 도달 위해 필요한 물리량 변화 크기 결정. 제어 루프 상승 시간(얼마나 빨리 설정값 도달) 결정.
iii) 적분 항 (I): 이전 모든 오차 값의 합. I = I + error. 설정값으로부터의 변화에 대한 시스템 반응 속도 담당. 비례 항이 필요로 하는 정상 상태 오차 제거용. 보통 소형 로봇에서는 정상 상태 오차 관심 적고 루프 튜닝 복잡해져 적분 항 생략.
iv) 미분 항 (D): 현재 순간 오차와 이전 순간 오차의 차이. D = error - previousError. 물리량이 설정값에 가까워질 때 변화율 늦춤. 오버슈트(과도 보정) 감소용.
방정식:
PIDvalue = (Kp*P) + (Ki*I) + (Kd*D)
상수:
Kp: 설정값 도달 위해 필요한 변화 크기 조절
Ki: 설정값 도달 위한 변화가 일어나는 비율/속도 조절
Kd: 시스템 안정성 조절
튜닝 접근법 (시행착오법):
Kd = 0으로 두고 Kp만 먼저 튜닝. Kp = 25가 좋은 시작점. 앞서 Kp=50이 내 로봇에 잘 작동했음.
로봇 반응이 적당해지면 미분 항(Kd) 튜닝. Kp와 Kd 각각을 Kp의 절반으로 시작. 예: Kp=50으로 잘 작동하면 Kp=25, Kd=25로 시작.
Kd(미분 게인) 증가 → 오버슈트 감소. 로봇 불안정해지면 Kd 감소.
로봇 반응 너무 느리면 값 증가, 너무 빨라 불안정하면 값 감소.
또 다른 요소: 실제 샘플/루프 속도. 코드의 delay 문으로 설정. 이 파라미터를 빠르게/느리게 조정해도 성능에 큰 차이. 역시 시행착오로 최적값 탐색.
위 접근법 기반 구현 함수:
void calculatePID() {
P = error;
I = I + error;
D = error - previousError;
PIDvalue = (Kp*P) + (Ki*I) + (Kd*D);
previousError = error;
}