



IB 디플로마를 이수하는 학생으로서, 저희는 주자들이 트랙을 달리는 동안 일정한 페이스를 유지하도록 도와줄 수 있는 자율 페이서 로봇(1/10 스케일 모터 구동 자동차)을 제작하기 위해 설계, 출력 및 코딩을 진행했습니다. 이 프로젝트는 시간 제약으로 완료하지 못한 CAS 프로젝트였다는 점을 참고해 주세요. 프로젝트 중 한 명이 다른 곳으로 이주하면서 실제 트랙 테스트에 사용할 수 있는 시간이 제한되었습니다. 이 때문에 전자 부품용 마운트를 최종 설계에 포함하지 못했으며, 부품은 녹색 PLA로 출력되었습니다. 또한 이 문서는 참고 및 학습 목적으로만 작성된 것으로, 저희가 어디까지 진행했으며 그 과정에서 무엇을 배웠는지를 보여주기 위한 자료입니다.
하드웨어:
소프트웨어:
프로젝트 목표
주요 사양


설계의 기계적 부분은 조향, 서스펜션, 구동계의 세 가지 주요 시스템으로 구성됩니다. 모든 부품은 Fusion 360으로 설계했으며 Bambu Lab A1 프린터를 사용해 PLA로 출력했습니다.







앞바퀴는 서스펜션을 위한 여유 공간을 확보할 수 있는 볼 조인트가 포함된 타이 로드/스티어링 링크를 통해 연결했으며, 서보를 링크에 연결하는 서보 혼 연결용 캡도 설계했습니다. 초기 테스트에서는 MG90S 마이크로 서보를 사용했지만, 부하가 걸린 상태에서 앞바퀴를 안정적으로 돌리기에는 토크가 부족했습니다. 따라서 훨씬 더 큰 토크를 제공하는 MG995로 업그레이드했습니다.
MG995 서보에는 전원, 접지, Arduino에서 오는 신호의 세 가지 연결이 필요합니다.
서보 신호는 Arduino Nano의 D3에 연결했습니다. 서보는 Arduino 출력 핀이 안전하게 공급할 수 있는 것보다 훨씬 큰 전류를 소비하므로 Arduino GPIO 핀에서 직접 전원을 공급해서는 안 됩니다. 따라서 전원 시스템에는 안정화된 전원 공급 장치를 사용했으며, 서보와 Arduino는 공통 접지를 공유했습니다.
Arduino의 제어 신호가 서보와 동일한 전압 기준을 사용해야 하므로 공통 접지는 중요합니다.
조향 각도 제어에는 Arduino Servo 라이브러리를 사용했습니다. 테스트 중에는 다른 시스템과 조향을 결합하기 전에 먼저 별도의 서보 프로그램을 업로드했습니다.
단순화한 버전은 다음과 같습니다:
#include <Servo.h>
Servo steering;
const int SERVO_PIN = 3;
const int CENTER_ANGLE = 90;
void setup() {
steering.attach(SERVO_PIN);
steering.write(CENTER_ANGLE);
}
void loop() {
}
정확한 중앙, 최대 좌회전, 최대 우회전 값은 실제 조향 메커니즘에 맞게 보정해야 합니다. 90도를 전송한다고 해서 휠이 반드시 기계적으로 중앙에 위치하는 것은 아닙니다.
IR 센서를 추가한 후에는 흰색 선이 감지된 위치에 따라 조향 각도를 변경할 수 있었습니다. 서보가 미리 정해진 각도 사이에서 단순히 움직이는 대신, 센서 판독값을 조향 방향을 결정하는 입력으로 사용했습니다.





평평한 트랙에서는 트랙이 비교적 평평하고 장애물이 없기 때문에 완전한 서스펜션이 우리 상황에 비해 과도하게 설계된 것이었지만, 엄밀히 말해 반드시 필요한 것은 아니더라도 엔지니어링 과제와 학습 가치를 위해 설계에 포함했습니다.
앞뒤 서스펜션은 모두 Fusion 360으로 설계했습니다. 앞쪽 서스펜션 암을 스티어링 링크와 연결된 베어링 홀더에 연결하는 볼 조인트는 적절하게 맞도록 설계하기가 특히 어려웠습니다. 이 볼 조인트는 회전 기능을 사용해 설계했으며, Engineer3D의 튜토리얼을 따랐습니다.
처음 사용한 쇼크 업소버는 전자 장치와 배터리의 무게를 지탱하기에 너무 약한 것으로 곧 드러났습니다. 그래서 더 큰 제품(72-100mm)을 구매해야 했고, 서스펜션 홀더도 다시 설계해야 했습니다.





프로젝트를 위해 가장 먼저 한 일은 배송에 가장 오래 걸리는 부품을 구매하는 것이었고, 그 부품은 모터였습니다. 서둘러 구매한 결과, 회전 속도를 13,000 RPM (최대)에서 휠 기준 약 833 RPM으로 낮춰야 했으며, 이에 따라 1:15.6 감속비가 필요했습니다. 처음부터 필요한 RPM을 계산했더라면 기어박스를 더 단순하게 만들 수 있었을 것입니다. 시간이 부족했기 때문에 3단 헤링본 기어박스를 사용하기로 했습니다(더 높은 토크와 강도를 위해). 이 기어박스는 전부 직접 설계하고 출력했습니다. (POWER STROKE의 튜토리얼)
후방 서스펜션이 작동하도록 최종 기어에 유니버설 조인트 구동축을 연결하여 상하 운동과 회전/구동 운동을 모두 가능하게 했습니다.
테스트 중 배운 중요한 교훈은 다음과 같습니다. 모터를 높은 스로틀로 작동하면 PID 컨트롤러가 속도를 보정할 여지가 거의 남지 않습니다. 기존 기어비 1:15.6에서는 로봇이 15 km/h에 도달하려면 거의 95% 스로틀로 작동해야 했습니다.
휠을 장착하고 초기 속도 테스트를 진행한 후, 2단을 12T/30T (1:2.5)에서 14T/28T (1:2.0)로 변경하여 전체 기어비를 1:12.5로 조정했습니다. 이에 따라 작동 스로틀이 약 80%로 낮아져 PID가 양방향(더 느리게 또는 더 빠르게)으로 충분한 여유를 확보할 수 있었습니다. 중요한 점은 두 기어 쌍이 동일한 중심 거리(모듈 1.0에서 21mm)를 공유하므로 기어박스 하우징을 변경할 필요가 없었다는 것입니다.
RS-550은 Arduino Nano가 공급할 수 있는 것보다 훨씬 많은 전류를 끌어올 수 있으므로, 모터는 BTS7960 모터 드라이버를 통해 제어했습니다.
제어 연결은 다음과 같았습니다:
RPWM → Arduino D9
LPWM → Arduino D10
REN → Arduino D7
LEN → Arduino D8
모터는 BTS7960의 모터 출력에 연결했고, 7.4V LiPo 배터리가 고전류 모터 전원을 공급했습니다.
Arduino는 BTS7960에 제어 신호만 보냅니다. 모터 구동에 필요한 전력을 직접 공급하지는 않습니다.
간단한 모터 테스트 코드는 다음과 같습니다:
const int RPWM = 9;
const int LPWM = 10;
const int REN = 7;
const int LEN = 8;
void setup() {
pinMode(RPWM, OUTPUT);
pinMode(LPWM, OUTPUT);
pinMode(REN, OUTPUT);
pinMode(LEN, OUTPUT);
digitalWrite(REN, HIGH);
digitalWrite(LEN, HIGH);
}
void loop() {
analogWrite(RPWM, 140);
analogWrite(LPWM, 0);
}
PWM 값의 범위는 0부터 255까지입니다. PWM을 높이면 모터에 공급되는 평균 전력이 증가합니다.
그러나 PWM을 정확한 속도 설정값으로 간주해서는 안 됩니다. 예를 들어 PWM 값이 140이라고 해서 차량이 항상 같은 속도로 주행한다는 보장은 없습니다. 배터리 전압, 마찰, 노면 상태 및 부하에 따라 실제 속도가 달라질 수 있습니다. 이것이 엔코더와 PID 속도 제어를 추가한 이유입니다.









엔코더 — LM393 광학 엔코더와 함께 작동하도록 20슬롯 디스크를 설계하고 출력했습니다. 디스크가 기어축과 함께 회전하면 엔코더가 간극을 통과하는 슬롯을 세고, 이를 RPM과 휠 속도로 변환하여 PID 루프에 전달합니다.
IR 센서 — TCRT5000 3채널 IR 센서는 햇빛을 차단하기 위한 출력 차광 커버가 필요했습니다. 그렇지 않으면 센서가 흰색 선과 빨간색 트랙을 구분하지 못했습니다. 또한 트랙 표면에서 정확한 높이에 센서를 고정할 수 있도록 센서 마운트도 설계했습니다.
모터 마운트 — RS-550 모터 전용 모터 마운트를 설계하여 기어박스 입력부에 대해 정확한 위치에 모터를 섀시에 고정했습니다.
엔코더가 실제 속도에 대한 피드백을 제공할 수 있게 된 후, PID 제어를 사용하여 모터 PWM을 자동으로 조정했습니다. PID는 다음을 의미합니다: 비례(Proportional), 적분(Integral), 및 미분(Derivative). 이는 목표 속도와 차량이 실제로 주행하는 속도의 차이를 지속적으로 확인한 다음, 그 차이를 줄이도록 모터 PWM을 조정하는 피드백 제어 시스템의 한 유형입니다.
모터에 고정된 PWM 값을 지정한다고 해서 일정한 주행 속도가 보장되는 것은 아니므로 이것이 필요했습니다. 대신 배터리 전압, 마찰, 트랙 상태 또는 모터 부하의 변화로 인해 PWM이 동일하게 유지되더라도 차량이 약간 더 빠르거나 느리게 주행할 수 있습니다. 엔코더를 PID 제어와 함께 사용하면 프로그램이 이러한 변화를 감지하고 자동으로 보정할 수 있습니다.
비례(Proportional, P) 은 현재 속도 오차에 반응합니다. 차량이 너무 느리면 모터 출력을 높이고, 너무 빠르면 출력을 낮춥니다.
적분(Integral, I) 은 시간이 지나도 계속되는 오차에 반응합니다. 예를 들어 차량이 목표 속도보다 지속적으로 약간 느리게 유지되면 적분 항이 보정량을 점진적으로 증가시킵니다.
미분(Derivative, D) 은 오차가 변화하는 속도에 반응하며 컨트롤러가 과도하게 보정하는 것을 방지하는 데 도움이 됩니다.
그런 다음 PID 컨트롤러는 세 항을 결합합니다:
output = Kp(error) + Ki(sum of previous errors) + Kd(change in error)
여기서 Kp, Ki 및 Kd 값은 PID 컨트롤러의 각 부분이 모터에 미치는 영향의 강도를 결정합니다. 이러한 값은 실험적으로 조정해야 합니다.
속도 제어에 PID를 사용하려면 프로그램이 엔코더를 사용해 현재 속도를 반복적으로 측정하고, 목표 속도와 비교한 다음, 필요한 보정값을 계산하여 모터 PWM을 변경합니다.
기본 제어 루프는 다음과 같습니다:
목표 속도 → 측정 속도와 비교 → 오차 계산 → PID 컨트롤러 → 조정 → 모터 PWM → 속도 재측정 → 반복
오차는 다음과 같이 계산됩니다:
error = target speed - measured speed
양의 오차는 차량이 너무 느리게 주행하고 있음을 의미하며, 음의 오차는 너무 빠르게 주행하고 있음을 의미합니다.
그런 다음 PID 계산은 코드로 다음과 같이 나타낼 수 있습니다:
error = targetSpeed - measuredSpeed;
integral += error * dt;
derivative = (error - previousError) / dt;
correction =
Kp * error +
Ki * integral +
Kd * derivative;
motorPWM += correction;
motorPWM = constrain(motorPWM, 0, 255);
previousError = error;
계산된 보정값은 목표 속도와 측정 속도의 차이에 따라 모터 PWM을 변경합니다. 그런 다음 PWM은 0에서 255 사이로 제한되는데, 이는 Arduino에서 사용할 수 있는 PWM 범위입니다. 이 과정은 계속 반복되므로 차량은 주행 중 스스로 속도를 보정할 수 있습니다.
PID 속도 제어 시스템을 개발하면서 기어박스 비율을 1:15.6에서 1:12.5로 변경했을 때 얻는 숨은 이점 중 하나를 알 수 있었습니다. 15km/h의 속도를 유지하려면 스로틀의 약 95%가 필요하므로, 차량 속도가 목표 속도보다 낮아졌을 때 컨트롤러가 모터 출력을 높일 여지가 거의 없습니다. 기어비를 변경했기 때문에 약 80%에서 작동할 수 있었고, PID 컨트롤러가 보정할 때 추가 PWM을 사용할 여유가 생겼습니다.



트랙 선을 감지하기 위한 초기 방식은 TCS34725 RGB 컬러 센서를 사용하는 것이었습니다. 실내 테스트에서는 센서가 서로 다른 표면 색상을 상당히 잘 구분했습니다. 그러나 실외 테스트에서는 직사광선 때문에 문제가 발생했습니다. 햇빛으로 인해 밝은 빨간색 트랙과 색이 바랜 흰색 선을 안정적으로 구분하기가 훨씬 어려워졌습니다. 따라서 결국 3채널 TCRT5000 적외선 반사 센서로 변경했습니다.
TCRT5000은 RGB 컬러 센서와 다르게 작동합니다. 적외선을 방출하고 표면에서 반사되어 돌아오는 양을 측정합니다. 흰색 트랙 선은 일반적으로 주변의 빨간색 표면보다 더 많은 적외선을 반사합니다.
센서 3개를 사용하면 선의 위치에 대한 정보도 얻을 수 있습니다:
왼쪽 중앙 오른쪽
센서 센서 센서
↓ ↓ ↓
[ L ] [ C ] [ R ]
흰색 선
중앙 센서가 선을 감지하면 차량은 대략 직선으로 계속 주행합니다. 왼쪽 또는 오른쪽 센서가 선을 감지하면 프로그램이 조향 각도를 변경하여 차량을 다시 중앙 쪽으로 이동시킵니다.
센서를 조향 코드와 결합하기 전에 원시 센서 측정값을 별도로 확인했습니다.
예를 들면 다음과 같습니다:
const int LEFT_SENSOR = A0;
const int CENTER_SENSOR = A1;
const int RIGHT_SENSOR = A2;
void setup() {
Serial.begin(115200);
}
void loop() {
Serial.print("L: ");
Serial.print(analogRead(LEFT_SENSOR));
Serial.print(" C: ");
Serial.print(analogRead(CENTER_SENSOR));
Serial.print(" R: ");
Serial.println(analogRead(RIGHT_SENSOR));
delay(100);
}
사전에 고정된 임계값을 가정하는 대신 흰색 선과 빨간색 트랙 위에서 모두 측정값을 테스트했습니다.
또한 센서와 트랙 사이의 물리적 거리가 측정값에 큰 영향을 준다는 것도 확인했습니다. 따라서 센서 보정과 센서 장착을 함께 고려해야 했습니다.
햇빛으로 인한 간섭을 줄이기 위해 인쇄된 차광 커버도 설계했으며, 센서 마운트는 트랙 위의 높이를 일정하게 유지하도록 제작했습니다.
센서 3개와 서보가 각각 정상적으로 작동하면 이를 결합할 수 있습니다.
가장 간단한 제어 시스템은 다음과 같은 조건문을 사용합니다:
if (centerWhite) {
steering.write(CENTER);
}
else if (leftWhite) {
steering.write(LEFT);
}
else if (rightWhite) {
steering.write(RIGHT);
}
이 코드에서는 중앙 센서와 왼쪽 센서가 모두 라인을 감지하면 차량에 약간의 보정만 필요합니다. 가장 왼쪽 센서만 라인을 감지하는 경우에는 더 강한 보정을 적용할 수 있습니다.
이것이 단일 라인 센서보다 센서 3개가 더 적합했던 이유입니다. 센서 3개는 차량 아래에 흰색 라인이 존재하는지뿐만 아니라, 차량 중앙을 기준으로 라인이 대략 어디에 있는지도 알려 줍니다.
최종 전기 시스템은 세 가지 주요 제어 시스템을 결합했습니다:
TCRT5000 센서 → Arduino → 조향 서보
↓
엔코더 → Arduino → PID → BTS7960 → 모터
따라서 Arduino는 동시에 두 가지 주요 작업을 수행했습니다:
이 프로젝트를 개발하면서 연습한 가장 중요한 것 중 하나는 모든 부품을 처음부터 바로 결합하지 않는 것이었습니다. 대신 각 부품을 개별적으로 테스트하고 제대로 작동하는지 확인했습니다. 모터, 서보, 센서, 엔코더용 테스트 프로그램을 각각 따로 만들었습니다. 개별 부품이 작동한다는 것을 확인한 후에야 다른 부품과 결합하기 시작했습니다.
덕분에 문제를 훨씬 쉽게 해결할 수 있었습니다. 완성된 차량이 작동하지 않을 때는 배선, 전원, 센서 보정, 기계적 저항 또는 프로그래밍 등 가능한 원인이 많습니다. 각 하위 시스템을 개별적으로 테스트하니 실제 문제를 훨씬 쉽게 분리해 낼 수 있었습니다.
전체 코드는 GitHub에 업로드했습니다.
전반적으로 개인적인 사정이 복잡해 프로젝트를 완성하지는 못했지만, 학생으로서 우리가 진행한 과정을 공유할 가치가 있다고 생각했습니다.
이 프로젝트를 재현해 보시거나 앞으로 프로젝트를 어떻게 개선할 수 있을지 제안이 있다면 자유롭게 댓글을 남겨 주세요.
원문: https://www.instructables.com/Autonomous-Pacer-for-Track-Running-coded-110-Scale/