로봇은 왜 마음대로 움직이지 않을까
지금까지 우리는 로봇을 이루는 많은 부품을 살펴보았다. DC 모터를 돌리고, 서보 모터의 각도를 바꾸고, 거리 센서로 앞을 보고, 엔코더로 바퀴 회전을 읽고, IMU로 기울기를 감지했다. 통신 장에서는 라즈베리파이와 ESP32, 아두이노가 서로 명령을 주고받는 방법도 배웠다.
이제 한 단계 더 나아가야 한다. 부품을 연결하고 값을 읽는 것만으로는 로봇이 안정적으로 움직이지 않는다. 로봇에게 정말 필요한 것은 "원하는 동작을 계속 유지하는 능력"이다.
예를 들어 보자. 두 바퀴 로봇의 왼쪽 모터와 오른쪽 모터에 똑같이 PWM 150을 주었다. 그러면 로봇은 똑바로 갈까? 처음에는 그렇게 기대하기 쉽다. 하지만 실제로는 한쪽으로 조금씩 휘어질 가능성이 높다. 모터마다 힘이 다르고, 바퀴 마찰이 다르고, 바닥 상태가 다르고, 배터리 전압도 조금씩 변하기 때문이다.
서보 모터도 마찬가지다. 90도로 이동하라고 명령했다고 해서 항상 완벽히 같은 속도로, 같은 경로로 움직이는 것은 아니다. 로봇팔 끝에 무거운 물체를 달면 움직임이 느려지고, 바퀴 로봇이 카펫 위로 올라가면 속도가 떨어진다. 같은 명령이라도 상황에 따라 결과가 달라진다.
제어는 바로 이 차이를 다루는 기술이다. 로봇에게 단순히 "이만큼 출력해"라고 말하는 데서 끝나는 것이 아니라, "내가 원하는 상태와 지금 상태가 얼마나 다른지 보고, 그 차이를 줄이도록 출력을 조절해"라고 말하는 방식이다.
이 장에서는 PID 같은 복잡한 공식으로 바로 들어가지 않는다. 그 전에 반드시 익혀야 할 사고방식이 있다. 목표값, 현재값, 오차, 출력, 보정. 이 다섯 단어를 이해하면 이후의 속도 제어, 위치 제어, PID 제어가 훨씬 쉬워진다.

제어의 기본 용어
제어를 가장 짧게 말하면 "목표값과 현재값의 차이를 줄이는 과정"이다. 로봇에게 원하는 상태가 있고, 센서로 읽은 실제 상태가 있다. 둘 사이에는 차이가 생긴다. 제어기는 그 차이를 계산해서 모터나 서보 같은 액추에이터의 출력을 바꾼다.
가장 기본적인 관계는 다음과 같다.
오차 = 목표값 - 현재값여기서 목표값은 로봇이 도달하거나 유지하고 싶은 값이다. 현재값은 센서로 측정한 실제 값이다. 오차는 둘 사이의 차이다. 출력은 모터 PWM, 서보 각도 명령, 히터 출력처럼 실제 장치를 움직이기 위해 보내는 값이다. 보정은 오차를 줄이기 위해 출력을 바꾸는 행동이다.
로봇 예제로 바꿔 보면 훨씬 쉽다.
바퀴 속도 제어를 생각해 보자. 목표 속도가 초당 20cm인데 엔코더로 계산한 현재 속도가 초당 16cm라면 오차는 4cm/s이다. 로봇이 원하는 것보다 느리게 가고 있으므로 PWM을 조금 올려야 한다. 반대로 현재 속도가 초당 23cm라면 오차는 -3cm/s이다. 너무 빠르므로 PWM을 낮춰야 한다.
목표 속도: 20 cm/s
현재 속도: 16 cm/s
오차: 20 - 16 = 4 cm/s
동작: PWM을 올린다이번에는 라인트레이서 로봇을 생각해 보자. 로봇이 검은 선의 중앙을 따라가야 한다면 목표값은 "라인이 센서 배열의 중앙에 있는 상태"다. 그런데 라인이 왼쪽 센서 쪽으로 치우쳐 감지되면 로봇은 선의 오른쪽에 있다는 뜻일 수 있다. 이때 로봇은 왼쪽으로 방향을 틀어야 한다. 센서값을 보고 좌우 모터 속도를 다르게 주는 것이 보정이다.
제어에서 중요한 점은 출력 자체가 목적이 아니라는 것이다. PWM 150을 주는 것이 목적이 아니다. 목표 속도로 달리는 것이 목적이다. 왼쪽 모터와 오른쪽 모터에 같은 값을 주는 것이 목적이 아니다. 로봇이 똑바로 가는 것이 목적이다.
이 차이를 이해하면 로봇을 바라보는 눈이 달라진다. 초보 단계에서는 "모터에 몇을 줘야 하지?"라고 묻는다. 제어를 이해하기 시작하면 "현재 로봇이 목표에서 얼마나 벗어났고, 그 차이를 줄이려면 출력을 어떻게 바꿔야 하지?"라고 묻는다.
목표값, 현재값, 오차, 출력, 보정
제어 용어를 하나씩 로봇 상황에 연결해 보자.
목표값은 로봇에게 주는 희망 사항이다. 바퀴 로봇이라면 목표 속도, 목표 거리, 목표 방향이 될 수 있다. 로봇팔이라면 목표 각도나 목표 위치가 될 수 있다. 거리 센서를 사용하는 장애물 회피 로봇이라면 "앞 장애물과 최소 20cm 이상 떨어져 있기"가 목표가 될 수도 있다.
현재값은 센서가 알려주는 실제 상태다. 엔코더는 바퀴가 얼마나 돌았는지 알려주고, 거리 센서는 장애물까지의 거리를 알려준다. IMU는 기울기와 회전 변화를 알려주며, 라인 센서는 선이 어느 쪽에 있는지 알려준다. 현재값이 없으면 로봇은 자신이 잘하고 있는지 확인할 방법이 없다.
오차는 목표값과 현재값의 차이다. 오차가 크면 로봇이 목표에서 많이 벗어난 것이다. 오차가 작으면 목표에 가까워진 것이다. 오차가 0에 가까워질수록 제어는 성공에 가까워진다.
출력은 로봇이 실제로 내보내는 힘이다. DC 모터에서는 PWM 값이 대표적이다. 서보 모터에서는 각도 명령이 출력처럼 사용된다. 스텝 모터에서는 스텝 속도나 이동 스텝 수가 출력이 된다. 출력은 로봇이 세상에 영향을 주는 방법이다.
보정은 오차를 줄이기 위해 출력을 바꾸는 행동이다. 느리면 더 세게 밀고, 빠르면 줄인다. 왼쪽으로 치우치면 오른쪽 모터와 왼쪽 모터의 속도 차이를 바꾼다. 목표에 가까워지면 천천히 감속한다.
정리하면 제어 루프는 다음과 같은 흐름으로 움직인다.
목표값 설정
→ 현재값 측정
→ 오차 계산
→ 출력 계산
→ 모터 구동
→ 다시 현재값 측정여기서 핵심은 마지막 줄이다. 한 번 출력하고 끝나는 것이 아니라 다시 측정한다. 다시 측정하기 때문에 로봇은 자기 행동의 결과를 확인할 수 있다. 그리고 확인한 결과를 바탕으로 다시 출력을 바꾼다. 이 반복이 제어의 심장이다.
개방 루프: 센서 없이 밀어붙이는 제어
가장 단순한 방식은 센서값을 보지 않고 정해진 출력만 주는 것이다. 이를 개방 루프 제어라고 부른다. 영어로는 open-loop control이라고 한다. 루프라는 말은 반복 흐름을 뜻하는데, 개방 루프에서는 출력 결과가 다시 입력으로 돌아오지 않는다.
예를 들어 다음과 같은 로봇 코드를 떠올려 보자.
analogWrite(leftMotorPWM, 150);
analogWrite(rightMotorPWM, 150);
delay(2000);
stopMotors();이 코드는 왼쪽과 오른쪽 모터에 PWM 150을 주고 2초 동안 달린 뒤 멈춘다. 매우 이해하기 쉽고 만들기도 쉽다. 평평한 바닥에서 가벼운 로봇을 천천히 움직이는 정도라면 꽤 잘 작동할 수도 있다.
하지만 이 방식에는 큰 약점이 있다. 로봇이 실제로 얼마나 이동했는지 모른다. 바퀴가 미끄러졌는지 모른다. 배터리 전압이 낮아져 속도가 떨어졌는지도 모른다. 오른쪽 모터가 왼쪽보다 조금 더 빠른지도 모른다. 즉, 로봇은 눈을 감고 달리는 셈이다.
개방 루프가 항상 나쁜 것은 아니다. 센서가 없어도 되는 단순한 장치, 정밀도가 중요하지 않은 동작, 짧은 시간 동안의 단순 출력에는 유용하다. LED를 1초 동안 켜거나, 부저를 짧게 울리거나, 서보를 대략적인 위치로 움직이는 작업은 개방 루프로도 충분할 때가 많다.
하지만 로봇이 바닥 위를 달리고, 장애물을 피하고, 라인을 따라가고, 목표 위치에 멈추어야 한다면 이야기가 달라진다. 현실 세계는 매번 조금씩 다르다. 그래서 움직이는 로봇에는 현재 상태를 확인하는 감각이 필요하다.
폐쇄 루프: 센서값으로 스스로 고치는 제어
폐쇄 루프 제어는 출력 결과를 센서로 다시 확인하고, 그 값을 이용해 다음 출력을 조절하는 방식이다. 영어로는 closed-loop control이라고 한다. 제어에서 말하는 피드백 제어가 대부분 이 구조를 가진다.
피드백이라는 말은 결과를 다시 되돌려 준다는 뜻이다. 로봇이 모터를 돌린다. 엔코더가 실제 회전 속도를 알려준다. 제어기는 목표 속도와 실제 속도를 비교한다. 차이가 있으면 PWM을 조정한다. 조정된 PWM으로 모터가 다시 돈다. 엔코더가 다시 측정한다. 이 흐름이 계속 반복된다.
개방 루프와 폐쇄 루프를 비교하면 다음과 같다.
| 구분 | 개방 루프 | 폐쇄 루프 |
|---|---|---|
| 센서 사용 | 보통 사용하지 않음 | 현재값 측정에 사용 |
| 출력 방식 | 정해진 값을 그대로 출력 | 오차에 따라 출력 변경 |
| 장점 | 단순하고 구현이 쉬움 | 환경 변화에 강함 |
| 단점 | 실제 결과를 확인하지 못함 | 센서와 계산이 필요함 |
| 로봇 예 | PWM 150으로 2초 전진 | 엔코더 속도를 보며 PWM 조정 |
폐쇄 루프의 장점은 로봇이 자기 실수를 조금씩 고칠 수 있다는 것이다. 물론 완벽해진다는 뜻은 아니다. 센서에도 오차가 있고, 모터 반응도 늦고, 바퀴가 심하게 미끄러지면 엔코더만으로는 실제 이동을 정확히 알기 어렵다. 그래도 현재값을 읽고 보정하는 구조가 있으면 로봇은 훨씬 안정적으로 움직인다.
좋은 로봇 제어는 한 번에 정답을 맞히는 것이 아니다. 계속 보고, 계속 비교하고, 계속 조금씩 고치는 것이다. 마치 자전거를 탈 때 몸이 끊임없이 균형을 잡는 것과 같다. 우리는 자전거 핸들을 한 번만 꺾고 그대로 두지 않는다. 몸이 왼쪽으로 기울면 오른쪽으로 살짝 보정하고, 오른쪽으로 기울면 다시 왼쪽으로 보정한다. 로봇도 마찬가지다.
바퀴 속도 제어에서 보는 피드백 루프
가장 대표적인 예는 바퀴 속도 제어다. 두 바퀴 로봇이 똑바로 가려면 왼쪽 바퀴와 오른쪽 바퀴의 속도가 비슷해야 한다. 그런데 같은 PWM을 주어도 실제 속도는 다를 수 있다. 이 문제를 해결하려면 엔코더가 필요하다.
왼쪽 바퀴의 목표 속도를 20cm/s로 정했다고 하자. 엔코더로 계산한 현재 속도가 17cm/s라면 왼쪽 모터는 목표보다 느리다. PWM을 조금 올린다. 다음 측정에서 19cm/s가 되었다면 아직 조금 느리지만 많이 가까워졌다. PWM을 조금 더 올릴 수 있다. 현재 속도가 21cm/s가 되면 목표보다 빠르므로 PWM을 낮춘다.
이 흐름을 간단한 의사 코드로 쓰면 다음과 같다.
targetSpeed = 20.0; // 목표 속도
currentSpeed = readEncoderSpeed();
error = targetSpeed - currentSpeed;
pwm = pwm + error * 2.0; // 오차만큼 PWM 보정
pwm = constrain(pwm, 0, 255);
analogWrite(motorPWM, pwm);이 코드는 아직 완성된 PID 제어가 아니다. 하지만 피드백 제어의 기본 모양을 이미 갖고 있다. 목표값을 정하고, 현재값을 읽고, 오차를 계산하고, 출력 PWM을 바꾼다.
여기서 재미있는 점은 제어기가 모터의 정확한 내부 구조를 몰라도 된다는 것이다. 모터 코일이 어떤 자속을 만들고, 기어박스 마찰이 얼마인지 전부 계산하지 않아도 된다. 현재 속도가 느리면 올리고, 빠르면 내리는 단순한 원리만으로도 꽤 유용한 제어가 가능하다.
물론 실제 로봇에서는 왼쪽과 오른쪽을 함께 봐야 한다. 두 바퀴의 목표 속도를 각각 정하고, 각 바퀴의 엔코더 속도를 읽고, 각자의 오차를 계산해 PWM을 따로 조절한다. 그러면 한쪽 모터가 조금 약하더라도 제어기가 더 큰 PWM을 보내 균형을 맞출 수 있다.
로봇 위치 제어에서 보는 피드백 루프
속도 제어가 "얼마나 빠르게 움직일 것인가"의 문제라면, 위치 제어는 "어디까지 움직일 것인가"의 문제다. 로봇이 1m 앞으로 이동한 뒤 멈춰야 한다고 해 보자. 개방 루프라면 PWM 150으로 몇 초 동안 달린 뒤 멈추게 만들 수 있다. 하지만 배터리 상태와 바닥 마찰에 따라 실제 이동거리가 달라진다.
폐쇄 루프에서는 엔코더로 이동거리를 추정한다. 목표 위치가 100cm이고 현재 위치가 40cm라면 오차는 60cm다. 아직 많이 남았으므로 계속 전진한다. 현재 위치가 90cm라면 오차는 10cm다. 목표에 가까워졌으므로 속도를 줄이는 것이 좋다. 현재 위치가 100cm 근처에 도달하면 멈춘다.
목표 위치: 100 cm
현재 위치: 40 cm
오차: 60 cm → 빠르게 전진
목표 위치: 100 cm
현재 위치: 90 cm
오차: 10 cm → 천천히 전진
목표 위치: 100 cm
현재 위치: 100 cm
오차: 0 cm → 정지위치 제어에서 중요한 것은 "목표에 가까워질수록 출력을 줄이는 감각"이다. 로봇이 목표 지점까지 빠르게 달리다가 바로 멈추려고 하면 관성 때문에 지나칠 수 있다. 작은 바퀴 로봇도 급정지하면 앞으로 밀리거나 바퀴가 미끄러질 수 있다. 그래서 목표와의 거리가 줄어들수록 속도를 낮추는 방식이 자주 사용된다.
이 사고방식은 로봇팔에도 그대로 적용된다. 목표 각도가 90도인데 현재 각도가 30도라면 빠르게 움직여도 된다. 현재 각도가 85도라면 속도를 줄여야 부드럽게 멈출 수 있다. 제어는 단지 목표에 도달하는 것만이 아니라, 어떻게 도달할지도 다룬다.
라인 중앙 추적에서 보는 피드백 루프
라인트레이서 로봇은 피드백 제어를 이해하기에 아주 좋은 예다. 로봇의 목표는 검은 선을 센서 배열의 중앙에 두는 것이다. 현재값은 라인 센서 배열이 읽은 선의 위치다. 오차는 중앙에서 얼마나 벗어났는지다.
센서 배열의 중앙을 0이라고 하자. 라인이 왼쪽에 있으면 현재 위치를 -값, 오른쪽에 있으면 +값으로 표현할 수 있다.
목표 라인 위치: 0
현재 라인 위치: -0.4
오차: 0 - (-0.4) = +0.4
동작: 왼쪽으로 회전하도록 좌우 모터 속도 차이 조정라인이 왼쪽에 있다는 것은 로봇이 선의 오른쪽으로 벗어났다는 뜻일 수 있다. 따라서 로봇은 왼쪽으로 돌아와야 한다. 이때 왼쪽 모터를 느리게 하고 오른쪽 모터를 빠르게 하면 로봇은 왼쪽으로 회전한다. 반대로 라인이 오른쪽에 있으면 오른쪽 모터를 느리게 하고 왼쪽 모터를 빠르게 한다.
라인트레이서의 기본 구조는 다음과 같이 생각할 수 있다.
baseSpeed = 120;
linePosition = readLineSensor(); // 중앙이면 0, 왼쪽이면 음수, 오른쪽이면 양수
error = 0 - linePosition;
correction = error * 40;
leftPWM = baseSpeed - correction;
rightPWM = baseSpeed + correction;
setMotor(leftPWM, rightPWM);여기서 baseSpeed는 기본 전진 속도이고, correction은 방향을 고치기 위한 보정값이다. 보정값이 작으면 부드럽게 움직이지만 곡선을 따라가는 힘이 부족할 수 있다. 보정값이 너무 크면 로봇이 좌우로 심하게 흔들릴 수 있다.
이 흔들림은 제어에서 매우 중요한 신호다. 로봇이 좌우로 계속 지그재그를 그린다면 "보정을 너무 세게 하고 있구나"라고 생각할 수 있다. 반대로 라인을 놓친다면 "보정이 약하거나 속도가 너무 빠르구나"라고 의심할 수 있다. 제어를 배운다는 것은 로봇의 움직임을 보고 원인을 추리하는 능력을 키우는 일이기도 하다.
제어 주기: 얼마나 자주 보고 고칠 것인가
피드백 제어는 반복이다. 그렇다면 얼마나 자주 반복해야 할까? 이것을 제어 주기라고 한다. 제어 주기는 센서를 읽고, 오차를 계산하고, 출력을 갱신하는 시간 간격이다.
예를 들어 제어 주기가 20ms라면 로봇은 0.02초마다 한 번씩 현재 상태를 확인하고 출력을 바꾼다. 1초에 50번 판단하는 셈이다. 제어 주기가 100ms라면 1초에 10번 판단한다.
제어 주기가 너무 느리면 로봇이 늦게 반응한다. 라인트레이서가 선을 벗어나고 있는데 0.5초 뒤에야 보정한다면 이미 늦을 수 있다. 장애물 회피 로봇이 가까운 벽을 보고도 늦게 멈추면 부딪힐 수 있다. 속도 제어에서도 현재 속도 변화에 늦게 반응하면 목표 속도 주변에서 크게 출렁일 수 있다.
반대로 제어 주기가 무조건 빠르다고 좋은 것도 아니다. 센서값이 아직 변하지 않았는데 너무 자주 읽으면 거의 같은 값을 반복해서 처리할 수 있다. 노이즈가 많은 센서를 너무 빠르게 읽으면 작은 흔들림에 지나치게 민감하게 반응할 수도 있다. 또 보드가 센서 읽기, 통신, 모터 출력, 디버깅 출력을 모두 처리해야 한다면 너무 짧은 제어 주기는 프로그램을 불안정하게 만들 수 있다.
간단한 바퀴 로봇이나 라인트레이서에서는 먼저 10ms에서 50ms 정도의 범위에서 실험해 볼 수 있다. 중요한 것은 정답 숫자를 외우는 것이 아니라, 로봇의 움직임을 보며 조정하는 것이다. 반응이 늦고 크게 벗어나면 주기를 줄여 더 자주 제어해 본다. 출력이 지나치게 자주 흔들리고 센서 노이즈에 민감하면 주기를 조금 늘리거나 필터를 적용해 본다.
아두이노 스타일 코드에서는 delay()로 제어 주기를 만들 수도 있지만, 로봇이 여러 일을 동시에 해야 한다면 millis()를 이용하는 편이 좋다.
unsigned long lastControlTime = 0;
const unsigned long controlInterval = 20; // 20ms
void loop() {
unsigned long now = millis();
if (now - lastControlTime >= controlInterval) {
lastControlTime = now;
readSensors();
computeError();
updateMotors();
}
checkSerialCommand();
updateStatusLED();
}이 구조에서는 제어 루프가 20ms마다 실행되면서도, 그 사이에 시리얼 명령 확인이나 LED 상태 표시 같은 다른 일을 처리할 수 있다. 실제 로봇 프로그램은 하나의 긴 delay()보다 이런 식의 작은 반복 구조로 만드는 것이 다루기 쉽다.
센서 노이즈: 현재값은 항상 조금 흔들린다
제어에서 현재값은 센서가 알려준다. 그런데 센서값은 항상 완벽하지 않다. 초음파 거리 센서는 같은 위치에서도 몇 cm씩 흔들릴 수 있고, 적외선 센서는 물체 색과 주변 빛의 영향을 받는다. IMU는 진동에 민감하고, 라인 센서는 바닥 재질과 조명에 따라 값이 달라질 수 있다. 엔코더도 빠른 회전이나 잘못된 배선에서는 카운트가 튈 수 있다.
센서값이 흔들리면 오차도 흔들린다. 오차가 흔들리면 출력도 흔들린다. 출력이 흔들리면 로봇 움직임도 흔들린다. 그래서 피드백 제어에서는 "측정값을 얼마나 믿을 것인가"가 중요하다.
가장 간단한 방법은 여러 번 읽어 평균을 내는 것이다. 거리 센서를 3번 또는 5번 읽고 평균을 사용하면 갑자기 튀는 값을 줄일 수 있다. 다만 평균을 많이 낼수록 반응은 느려진다. 부드러워지는 대신 늦어진다.
또 다른 방법은 작은 오차를 무시하는 것이다. 이것을 데드밴드라고 부를 수 있다. 예를 들어 목표 속도와 현재 속도의 차이가 아주 작다면 굳이 PWM을 바꾸지 않는다. 이렇게 하면 목표 주변에서 출력이 계속 잘게 떨리는 현상을 줄일 수 있다.
if (abs(error) < 0.5) {
error = 0;
}라인 센서처럼 빠른 반응이 중요한 경우에는 너무 강한 평균 필터가 오히려 문제를 만들 수 있다. 로봇이 이미 선을 벗어나고 있는데 평균값이 천천히 변하면 보정이 늦어진다. 반대로 거리 센서로 장애물까지의 대략적인 거리를 보는 경우에는 약간의 평균이 안정성에 도움이 된다.
센서 노이즈를 다룰 때 기억할 원칙은 간단하다. 빠른 반응이 중요한 센서는 너무 둔하게 만들지 말고, 흔들림이 문제인 센서는 적당히 부드럽게 만든다. 제어는 늘 균형의 문제다.
모터 응답 지연: 출력한다고 바로 움직이지 않는다
제어를 처음 배울 때 자주 놓치는 부분이 있다. 프로그램에서 PWM 값을 바꾸면 모터가 즉시 그 속도로 변할 것 같지만 실제로는 그렇지 않다. 모터와 로봇 몸체에는 관성이 있다. 멈춰 있던 바퀴는 서서히 빨라지고, 빠르게 돌던 바퀴는 서서히 느려진다. 기어박스 마찰, 바퀴와 바닥의 접촉, 로봇 무게도 모두 영향을 준다.
이 지연 때문에 제어가 어려워진다. 예를 들어 로봇이 목표 속도보다 느리다고 판단해 PWM을 크게 올렸다고 하자. 그런데 모터는 조금 늦게 반응한다. 제어기는 아직도 느리다고 생각하고 PWM을 더 올릴 수 있다. 잠시 뒤 모터가 뒤늦게 빨라지면 이번에는 목표보다 너무 빨라진다. 그러면 제어기는 다시 PWM을 확 낮춘다. 이런 과정이 반복되면 속도가 출렁인다.
라인트레이서에서도 비슷한 일이 생긴다. 로봇이 선에서 벗어나서 급하게 보정했는데, 로봇 몸체는 관성 때문에 조금 더 나아간다. 뒤늦게 방향이 바뀌면서 이번에는 반대쪽으로 지나친다. 그래서 로봇이 좌우로 흔들린다.
모터 응답 지연을 줄이거나 다루기 위해서는 몇 가지 습관이 도움이 된다.
출력을 한 번에 너무 크게 바꾸지 않고 조금씩 바꾼다.
목표에 가까워질수록 속도를 줄인다.
로봇의 기계 구조를 단단히 만들고 바퀴 미끄러짐을 줄인다.
제어 주기를 일정하게 유지한다.
센서값과 출력값을 시리얼 모니터로 기록해 움직임과 숫자를 함께 본다.
특히 마지막 습관이 중요하다. 로봇이 흔들릴 때 단순히 "이상하다"라고 느끼는 것과, 오차가 어떻게 변하고 PWM이 어떻게 바뀌는지 보는 것은 완전히 다르다. 제어는 눈으로 보는 움직임과 숫자로 보는 데이터를 함께 읽어야 빨리 늘어난다.
출력 제한과 안전한 제어
제어기는 오차를 줄이려고 출력을 바꾼다. 하지만 출력에는 한계가 있다. 아두이노의 PWM은 보통 0에서 255 사이 값을 사용한다. 모터 드라이버에는 허용 전류가 있고, 배터리에는 공급할 수 있는 전류 한계가 있다. 서보 모터도 각도 범위를 벗어나 무리하게 밀면 소음이 나거나 뜨거워질 수 있다.
그래서 제어 코드에는 출력 제한이 반드시 필요하다.
pwm = constrain(pwm, 0, 255);이 한 줄은 단순해 보이지만 로봇을 안전하게 지켜 준다. 계산 결과가 300이 나오더라도 실제 출력은 255를 넘지 않게 막는다. 반대로 음수 PWM이 의미 없는 상황이라면 0 아래로 내려가지 않게 막을 수 있다.
좌우 모터를 제어할 때도 제한이 필요하다.
leftPWM = constrain(leftPWM, 0, 255);
rightPWM = constrain(rightPWM, 0, 255);제어가 강해질수록 로봇은 더 적극적으로 움직인다. 하지만 너무 강한 출력은 바퀴 미끄러짐, 배터리 전압 강하, 모터 드라이버 발열을 만들 수 있다. 앞의 전원 장에서 배웠듯이 모터는 순간적으로 큰 전류를 요구한다. 제어 코드가 갑자기 최대 출력을 반복해서 보내면 전원도 함께 흔들릴 수 있다.
좋은 제어는 목표에 빨리 도달하는 것만을 뜻하지 않는다. 안정적으로, 부품에 무리를 주지 않고, 예측 가능한 방식으로 움직이는 것이 좋은 제어다.
피드백 제어를 설계하는 순서
새로운 로봇 동작을 만들 때는 처음부터 복잡한 알고리즘을 넣기보다 제어 구조를 차근차근 세우는 것이 좋다. 예를 들어 "로봇이 일정한 속도로 직진하게 만들기"라는 목표가 있다면 먼저 목표값과 현재값을 명확히 정해야 한다.
목표값은 왼쪽 바퀴와 오른쪽 바퀴의 목표 속도다. 현재값은 엔코더로 계산한 실제 속도다. 오차는 목표 속도에서 실제 속도를 뺀 값이다. 출력은 각 모터에 보내는 PWM이다. 보정은 오차에 따라 PWM을 올리거나 내리는 것이다.
이렇게 정리하면 제어 문제가 훨씬 단순해진다. 막연히 "로봇이 똑바로 안 가요"가 아니라 "오른쪽 바퀴의 현재 속도가 목표보다 높으니 오른쪽 PWM을 낮추거나 왼쪽 PWM을 올려야 한다"로 바뀐다.
제어를 설계할 때는 다음 질문을 순서대로 던져 보자.
로봇이 유지하거나 도달해야 할 목표값은 무엇인가?
그 목표와 비교할 현재값을 어떤 센서로 측정할 것인가?
오차는 어떤 식으로 계산할 것인가?
오차가 양수일 때와 음수일 때 출력은 어떻게 바뀌어야 하는가?
출력의 최소값과 최대값은 어디까지 허용할 것인가?
제어 주기는 몇 ms로 시작해 볼 것인가?
센서 노이즈나 모터 지연 때문에 흔들리면 무엇을 조정할 것인가?
이 질문에 답할 수 있으면 이미 제어의 절반은 설계한 것이다. 나머지 절반은 실제 로봇을 움직이며 숫자를 조정하는 과정이다.
제어는 로봇의 태도를 만드는 일이다
같은 센서와 같은 모터를 사용해도 제어 방식에 따라 로봇의 성격은 완전히 달라진다. 어떤 로봇은 느리지만 안정적으로 움직인다. 어떤 로봇은 빠르지만 자주 흔들린다. 어떤 로봇은 장애물을 보자마자 급정지하고, 어떤 로봇은 부드럽게 감속한다. 하드웨어가 몸이라면 제어는 태도에 가깝다.
초보 로봇은 명령을 그대로 실행한다. "PWM 150으로 가라"고 하면 그냥 간다. 중급 로봇은 자기 상태를 본다. "내가 정말 목표 속도로 가고 있나?"를 확인한다. 더 나은 로봇은 상황에 맞춰 행동을 바꾼다. "목표에 가까워졌으니 감속하자", "라인에서 많이 벗어났으니 더 강하게 회전하자", "센서값이 이상하니 잠시 멈추자"라고 판단한다.
이 장에서 배운 핵심은 단순하다. 제어는 목표값과 현재값을 비교해 오차를 줄이는 반복 과정이다. 개방 루프는 정해진 출력만 보내지만, 폐쇄 루프는 센서값을 이용해 스스로 보정한다. 바퀴 속도, 로봇 위치, 라인 중앙 추적은 모두 같은 구조로 설명할 수 있다. 목표를 정하고, 현재를 측정하고, 차이를 계산하고, 출력을 조정한다.
다음 장부터는 이 생각을 실제 모터 속도 제어에 적용한다. 엔코더로 현재 속도를 읽고, 목표 속도와 비교하고, PWM을 조정하는 과정을 더 구체적으로 다룰 것이다. 그다음에는 제어에서 가장 널리 쓰이는 PID 제어로 넘어간다. PID는 어려운 주문이 아니다. 오늘 배운 "오차를 줄이는 반복"을 조금 더 정교하게 만든 방법이다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.