Skip to main content
Summary

속도 제어가 '얼마나 빠르게 돌릴 것인가'를 다루었다면, 위치 제어는 한 걸음 더 나아가 '어디까지 움직일 것인가'를 다룬다. 로봇에게 단순히 앞으로 가라고 말하면 로봇은 계속 움직인다. 하지만 '앞으로 50cm 이동해', '제자리에서 왼쪽으로 90도 돌아', '책상 끝에서 10cm 앞에 멈춰' 같은 명령을 내리려면 이동한 양을 알아야 한다.

이때 가장 기본이 되는 감각이 엔코더다. 엔코더는 바퀴가 얼마나 회전했는지 알려준다. 바퀴가 얼마나 돌았는지 알면, 바퀴 둘레를 이용해 로봇이 대략 얼마나 이동했는지 계산할 수 있다. 이번 장에서는 이 원리를 이용해 바퀴 로봇이 목표 거리까지 이동하고, 목표 지점 근처에서 부드럽게 감속해 멈추는 방법을 배운다.

위치 제어

위치 제어는 속도 제어의 다음 단계다

속도 제어에서는 일정 시간 동안 엔코더 카운트가 얼마나 증가했는지를 보았다. 예를 들어 0.1초 동안 엔코더가 30카운트 증가했다면, 그 값을 바탕으로 현재 속도를 계산하고 PWM을 조절했다.

위치 제어에서는 시간당 변화량보다 누적된 카운트가 중요하다. 출발할 때 엔코더 값을 0으로 맞춘 뒤, 목표 카운트까지 얼마나 남았는지 계속 확인한다. 현재 카운트가 목표에 가까워지면 모터 출력을 줄이고, 목표에 도달하면 멈춘다.

구조는 제어의 기본 공식과 똑같다.

오차 = 목표 위치 - 현재 위치

현재 위치가 목표보다 한참 뒤에 있으면 오차가 크다. 이때는 모터를 비교적 강하게 돌려도 된다. 반대로 목표 위치에 가까워지면 오차가 작아진다. 이때도 계속 강한 PWM을 주면 로봇은 목표를 지나쳐 버린다. 그래서 위치 제어에서는 오차가 줄어들수록 출력도 줄이는 방식이 자연스럽다.

로봇을 역에 세우는 기차라고 생각해 보자. 역이 멀리 있을 때는 빠르게 달려도 된다. 하지만 플랫폼이 눈앞에 있는데도 계속 전속력으로 달리면 정확히 멈출 수 없다. 위치 제어의 핵심은 '목표까지 남은 거리'를 보며 속도를 조절하는 것이다.

이동거리를 엔코더 카운트로 바꾸기

로봇에게 50cm 이동하라고 명령하려면 먼저 50cm가 엔코더 카운트로 몇 카운트인지 계산해야 한다. 계산에 필요한 값은 두 가지다. 하나는 바퀴 둘레이고, 다른 하나는 바퀴가 한 바퀴 돌 때 엔코더가 몇 카운트를 만들어 내는지다.

바퀴 둘레는 다음처럼 계산한다.

바퀴 둘레 = 바퀴 지름 × 3.14159

예를 들어 바퀴 지름이 65mm라면 바퀴 둘레는 약 204mm다. 바퀴가 한 바퀴 굴러가면 로봇은 이론적으로 약 204mm 이동한다. 물론 실제로는 타이어 미끄러짐, 바닥 재질, 로봇 무게 때문에 조금 달라질 수 있다. 그래도 첫 계산에는 이 값이 훌륭한 출발점이 된다.

목표 이동거리를 엔코더 카운트로 바꾸는 공식은 다음과 같다.

목표 카운트 = 목표 이동거리 ÷ 바퀴 둘레 × 바퀴 1회전당 카운트

예를 들어 바퀴 지름이 65mm이고, 바퀴 한 바퀴에 엔코더가 600카운트 증가한다고 하자. 로봇을 500mm, 즉 50cm 이동시키고 싶다면 다음과 같이 계산한다.

목표 카운트 = 500 ÷ 204 × 600 ≈ 1471

따라서 출발할 때 엔코더를 0으로 맞추고, 좌우 바퀴의 평균 카운트가 약 1471에 도달하면 50cm 정도 이동했다고 볼 수 있다.

여기서 중요한 점이 있다. 엔코더의 '한 바퀴당 카운트'는 데이터시트에 적힌 값과 실제 코드에서 세는 값이 다를 수 있다. 어떤 엔코더는 한 채널의 펄스 수만 표시하고, 어떤 경우에는 A상과 B상의 변화 네 가지를 모두 세어 카운트가 4배가 되기도 한다. 기어드 모터라면 모터 축 기준인지 바퀴 축 기준인지도 확인해야 한다. 가장 확실한 방법은 바퀴를 직접 한 바퀴 돌려 보고 코드에서 카운트가 얼마나 증가하는지 확인하는 것이다.

현재 위치를 어떻게 읽을까

두 바퀴 로봇에서 앞으로 이동한 거리는 보통 왼쪽 바퀴와 오른쪽 바퀴 이동량의 평균으로 계산한다.

현재 위치 = 왼쪽 엔코더 카운트와 오른쪽 엔코더 카운트의 평균

왼쪽 바퀴가 1000카운트, 오른쪽 바퀴가 980카운트 움직였다면 로봇의 전진 위치는 대략 990카운트라고 볼 수 있다. 두 바퀴의 값이 완전히 같지 않은 것은 자연스러운 일이다. 모터 특성이 다르고, 타이어 마찰도 조금씩 다르며, 배터리 상태에 따라서도 차이가 생긴다.

위치 제어를 시작할 때는 엔코더 카운트를 0으로 초기화하는 습관을 들이는 것이 좋다. 그래야 이번 명령에서 얼마나 이동했는지 계산하기 쉽다. 전체 주행 경로에서 누적 위치를 관리하는 방식도 있지만, 처음에는 '명령 하나를 시작할 때 0으로 만들고 목표 카운트까지 이동하기'가 이해하기 쉽고 디버깅도 편하다.

엔코더 카운트에는 방향도 포함하는 것이 좋다. 앞으로 움직일 때 카운트가 증가하고, 뒤로 움직일 때 카운트가 감소하도록 만들면 목표 거리를 양수와 음수로 표현할 수 있다. 예를 들어 moveDistance(300)은 앞으로 300mm, moveDistance(-300)은 뒤로 300mm처럼 사용할 수 있다.

오차로 모터 출력을 만든다

가장 단순한 위치 제어는 P 제어와 거의 같다. 목표 카운트와 현재 카운트의 차이를 구하고, 그 오차에 비례해 PWM을 만든다. 오차가 크면 PWM이 커지고, 오차가 작아지면 PWM도 작아진다.

다음 코드는 이동거리를 엔코더 카운트로 바꾸고, 위치 오차에서 PWM을 계산하는 기본 예다. 실제 프로젝트에서는 resetEncoders, getLeftCount, getRightCount, setMotor, stopMotors 같은 함수가 모터 드라이버와 엔코더 배선에 맞게 준비되어 있어야 한다.

const float WHEEL_DIAMETER_MM = 65.0;
const float WHEEL_CIRCUMFERENCE_MM = WHEEL_DIAMETER_MM * 3.14159;
const long COUNTS_PER_REV = 600;

const float KP_POS = 0.25;
const int MAX_PWM = 180;
const int MIN_PWM = 45;
const long STOP_TOLERANCE = 8;

long distanceToCounts(float distanceMm) {
  return (long)(distanceMm / WHEEL_CIRCUMFERENCE_MM * COUNTS_PER_REV);
}

int pwmFromError(long error) {
  long remaining = abs(error);

  if (remaining <= STOP_TOLERANCE) {
    return 0;
  }

  int pwm = (int)(remaining * KP_POS);

  if (pwm > MAX_PWM) pwm = MAX_PWM;
  if (pwm < MIN_PWM) pwm = MIN_PWM;

  if (error < 0) pwm = -pwm;
  return pwm;
}

KP_POS는 위치 오차를 PWM으로 바꾸는 비례 상수다. 값이 너무 작으면 로봇이 느리게 반응하고, 너무 크면 목표를 지나치기 쉽다. MAX_PWM은 너무 빠르게 달리지 않도록 제한하는 값이다. MIN_PWM은 모터가 실제로 움직이기 시작하는 최소 출력에 가깝다. DC 모터는 PWM이 너무 낮으면 소리만 나고 돌지 않을 수 있기 때문에, 목표에 아직 도달하지 않았다면 어느 정도 이상의 PWM을 주어야 한다.

이제 이 함수를 이용해 지정한 거리만큼 이동하는 코드를 만들 수 있다.

void moveDistance(float distanceMm) {
  resetEncoders();

  long target = distanceToCounts(distanceMm);

  while (true) {
    long left = getLeftCount();
    long right = getRightCount();

    long current = (left + right) / 2;
    long error = target - current;

    int pwm = pwmFromError(error);

    if (pwm == 0) {
      break;
    }

    setMotor(pwm, pwm);
    delay(10);
  }

  stopMotors();
}

이 코드는 위치 제어의 핵심 구조를 그대로 담고 있다. 목표 카운트를 정하고, 현재 카운트를 읽고, 오차를 계산하고, 오차에 맞춰 PWM을 넣는다. 10ms마다 이 과정을 반복하면서 로봇은 목표 위치에 다가간다.

하지만 이 코드에는 아직 부족한 점이 있다. 목표에 가까워질수록 PWM이 줄어들긴 하지만, MIN_PWM 때문에 마지막 구간에서 갑자기 튀듯 움직일 수 있다. 또한 좌우 바퀴가 같은 PWM을 받기 때문에 한쪽 모터가 더 빠른 로봇은 조금씩 휘어질 수 있다. 이제 이 문제들을 하나씩 고쳐 보자.

목표 근처에서 감속하지 않으면 왜 지나칠까

로봇은 명령을 받는 즉시 완벽하게 멈추지 않는다. 모터와 바퀴에는 관성이 있고, 타이어와 바닥 사이에는 미끄러짐이 있다. 제어 보드가 엔코더를 읽고 계산하는 데도 아주 짧지만 시간이 걸린다. 그래서 목표 카운트에 도달한 순간 PWM을 0으로 만들어도 로봇은 조금 더 굴러갈 수 있다.

특히 목표 바로 앞까지 최대 PWM으로 달리다가 갑자기 멈추려고 하면 지나침이 커진다. 지나친 뒤에는 오차가 반대 방향으로 생긴다. 그러면 로봇이 뒤로 살짝 움직이고, 다시 앞으로 움직이며 목표 주변에서 흔들릴 수 있다. 위치 제어를 처음 만들 때 흔히 보는 '덜컥덜컥' 움직임이다.

이를 줄이는 가장 쉬운 방법은 목표 근처에 감속 구간을 만드는 것이다. 목표까지 많이 남았을 때는 빠르게 움직이고, 일정 거리 안으로 들어오면 허용 PWM을 낮춘다. 이것은 사람이 자동차를 주차할 때와 비슷하다. 주차 공간이 멀리 있을 때는 조금 빠르게 접근하지만, 선에 가까워지면 발을 브레이크 위에 올리고 천천히 움직인다.

감속 구간을 추가한 위치 제어

다음 코드는 목표까지 남은 카운트가 SLOW_ZONE보다 작아지면 최대 PWM을 낮춘다. 이렇게 하면 목표 근처에서 로봇이 자연스럽게 느려진다.

const long SLOW_ZONE = 250;
const int SLOW_MAX_PWM = 90;

int pwmFromErrorWithSlowdown(long error) {
  long remaining = abs(error);

  if (remaining <= STOP_TOLERANCE) {
    return 0;
  }

  int speedLimit = MAX_PWM;

  if (remaining < SLOW_ZONE) {
    speedLimit = map(remaining, 0, SLOW_ZONE, MIN_PWM, SLOW_MAX_PWM);
  }

  int pwm = (int)(remaining * KP_POS);

  if (pwm > speedLimit) pwm = speedLimit;
  if (pwm < MIN_PWM) pwm = MIN_PWM;

  if (error < 0) pwm = -pwm;
  return pwm;
}

여기서 SLOW_ZONE은 감속을 시작할 거리다. 단위는 mm가 아니라 엔코더 카운트다. 예를 들어 SLOW_ZONE이 250이면 목표까지 250카운트 남은 지점부터 속도 제한이 낮아진다. 로봇이 목표를 자주 지나친다면 감속 구간을 더 크게 잡는다. 반대로 너무 일찍 느려져 답답하다면 조금 줄일 수 있다.

STOP_TOLERANCE도 중요하다. 실제 로봇은 목표 카운트에 정확히 1카운트 단위로 딱 멈추기 어렵다. 목표가 1471카운트인데 1468이나 1475에서 멈췄다면 충분히 잘 멈춘 것이다. 허용 오차를 0으로 잡으면 로봇이 목표 근처에서 계속 앞뒤로 떨 수 있다. 작은 오차는 받아들이는 것이 더 안정적이다.

정밀한 장비에서는 더 복잡한 감속 프로파일을 쓰기도 한다. 예를 들어 서서히 가속하고, 일정 속도로 이동하고, 다시 서서히 감속하는 사다리꼴 속도 프로파일을 사용한다. 하지만 작은 바퀴 로봇을 처음 만들 때는 감속 구간과 정지 허용 오차만 잘 잡아도 움직임이 훨씬 부드러워진다.

직진은 두 바퀴의 약속이다

로봇에게 앞으로 50cm 가라고 했는데 왼쪽으로 슬금슬금 휘어진다면 위치 제어가 실패한 것처럼 보인다. 하지만 사실 이것은 매우 자연스러운 현상이다. 같은 PWM을 줘도 왼쪽 모터와 오른쪽 모터가 정확히 같은 속도로 돌지 않기 때문이다.

엔코더가 있다면 이 문제를 더 똑똑하게 해결할 수 있다. 왼쪽 바퀴와 오른쪽 바퀴가 같은 목표 카운트에 도달하도록 각각 제어하면 된다. 왼쪽 바퀴가 더 빨리 가서 목표에 가까워지면 왼쪽 PWM은 줄어든다. 오른쪽 바퀴가 뒤처지면 오른쪽 PWM은 상대적으로 더 유지된다. 이렇게 하면 로봇은 스스로 좌우 균형을 맞추며 앞으로 간다.

다음은 좌우 바퀴를 각각 목표 카운트로 보내는 구조다.

void moveWheelTargets(long leftTarget, long rightTarget) {
  resetEncoders();

  while (true) {
    long left = getLeftCount();
    long right = getRightCount();

    long leftError = leftTarget - left;
    long rightError = rightTarget - right;

    int leftPwm = pwmFromErrorWithSlowdown(leftError);
    int rightPwm = pwmFromErrorWithSlowdown(rightError);

    if (leftPwm == 0 && rightPwm == 0) {
      break;
    }

    setMotor(leftPwm, rightPwm);
    delay(10);
  }

  stopMotors();
}

void moveDistanceStraight(float distanceMm) {
  long target = distanceToCounts(distanceMm);
  moveWheelTargets(target, target);
}

이 방식은 단순히 평균 위치만 보는 방식보다 직진성이 좋다. 왼쪽과 오른쪽 바퀴가 각각 자기 목표를 따라가기 때문이다. 물론 완벽한 직진은 아니다. 바퀴가 미끄러지거나 바닥이 울퉁불퉁하면 엔코더 카운트만으로는 실제 자세를 정확히 알 수 없다. 그래도 작은 실내 로봇에서는 매우 실용적인 방법이다.

또 다른 방식은 좌우 엔코더 차이를 직접 보정값으로 사용하는 것이다. 왼쪽 카운트가 오른쪽보다 크다면 왼쪽 바퀴가 더 많이 간 것이다. 이때 왼쪽 PWM을 조금 줄이고 오른쪽 PWM을 조금 올리면 로봇이 다시 균형을 잡는다.

const float KP_STRAIGHT = 0.15;

void moveDistanceWithBalance(float distanceMm) {
  resetEncoders();

  long target = distanceToCounts(distanceMm);

  while (true) {
    long left = getLeftCount();
    long right = getRightCount();

    long position = (left + right) / 2;
    long positionError = target - position;

    int basePwm = pwmFromErrorWithSlowdown(positionError);

    if (basePwm == 0) {
      break;
    }

    long balanceError = left - right;
    int correction = (int)(balanceError * KP_STRAIGHT);

    int leftPwm = basePwm - correction;
    int rightPwm = basePwm + correction;

    leftPwm = constrain(leftPwm, -MAX_PWM, MAX_PWM);
    rightPwm = constrain(rightPwm, -MAX_PWM, MAX_PWM);

    setMotor(leftPwm, rightPwm);
    delay(10);
  }

  stopMotors();
}

왼쪽 바퀴가 더 많이 이동하면 balanceError가 양수가 된다. 그러면 왼쪽 PWM은 줄고 오른쪽 PWM은 커진다. 반대로 오른쪽 바퀴가 더 앞서면 보정 방향이 반대로 작동한다. 이 방법은 '앞으로 가는 위치 제어'와 '좌우 차이 보정'을 분리해서 생각할 수 있어 디버깅하기 좋다.

제자리 회전도 위치 제어로 만들 수 있다

바퀴 로봇의 멋진 점은 같은 원리로 직진과 회전을 모두 만들 수 있다는 것이다. 앞으로 가려면 두 바퀴가 같은 방향으로 같은 거리만큼 굴러가면 된다. 제자리에서 회전하려면 한쪽 바퀴는 앞으로, 다른 쪽 바퀴는 뒤로 굴러가면 된다.

제자리 회전에서 필요한 값은 로봇의 바퀴 간 거리다. 여기서 바퀴 간 거리는 왼쪽 바퀴와 오른쪽 바퀴가 바닥에 닿는 중심 사이의 거리다. 보통 TRACK_WIDTH 또는 윤거라고 부른다.

로봇이 제자리에서 360도 한 바퀴 돈다고 생각해 보자. 왼쪽 바퀴와 오른쪽 바퀴는 로봇 중심을 기준으로 원을 그리며 움직인다. 각 바퀴가 그리는 원의 반지름은 바퀴 간 거리의 절반이다. 그래서 특정 각도만큼 회전할 때 한 바퀴가 이동해야 하는 거리는 다음과 같다.

바퀴 이동거리 = 3.14159 × 바퀴 간 거리 × 회전각도 ÷ 360

예를 들어 바퀴 간 거리가 130mm인 로봇이 제자리에서 90도 회전한다고 하자.

바퀴 이동거리 = 3.14159 × 130 × 90 ÷ 360 ≈ 102mm

바퀴 지름이 65mm이고 한 바퀴에 600카운트라면, 102mm는 약 300카운트다. 즉 90도 제자리 회전을 하려면 한쪽 바퀴는 약 300카운트 앞으로, 다른 쪽 바퀴는 약 300카운트 뒤로 움직이면 된다.

const float TRACK_WIDTH_MM = 130.0;

long angleToTurnCounts(float angleDeg) {
  float wheelTravel = 3.14159 * TRACK_WIDTH_MM * abs(angleDeg) / 360.0;
  return distanceToCounts(wheelTravel);
}

void turnInPlace(float angleDeg) {
  long target = angleToTurnCounts(angleDeg);

  if (angleDeg > 0) {
    moveWheelTargets(-target, target);
  } else {
    moveWheelTargets(target, -target);
  }
}

여기서는 각도가 양수일 때 왼쪽으로 회전한다고 정했다. 즉 왼쪽 바퀴는 뒤로, 오른쪽 바퀴는 앞으로 움직인다. 만약 실제 로봇이 반대로 돈다면 좌우 부호를 바꾸면 된다. 로봇 제작에서 이런 일은 흔하다. 수식이 틀렸다기보다 모터 배선, 엔코더 방향, 좌표 규칙이 프로젝트마다 다르기 때문이다.

제자리 회전도 바닥 상태의 영향을 많이 받는다. 타이어가 살짝 미끄러지면 엔코더 카운트는 맞아도 실제 각도는 부족하거나 넘칠 수 있다. 처음에는 90도 회전 명령을 내려 보고 실제 회전 각도를 자로 그은 기준선이나 바닥 타일을 이용해 확인하자. 항상 85도 정도만 돈다면 TRACK_WIDTH_MM 값을 조금 조정하거나 회전 카운트에 보정 계수를 곱하면 된다.

튜닝은 작은 실험의 반복이다

위치 제어는 공식만으로 끝나지 않는다. 실제 로봇은 배터리 전압, 모터 상태, 바닥 재질, 바퀴 지름 오차에 따라 다르게 움직인다. 그래서 반드시 짧은 거리부터 테스트해야 한다. 처음부터 2m를 달리게 하기보다 10cm, 30cm, 50cm처럼 조금씩 늘려 가는 편이 안전하다.

튜닝할 때는 다음 순서로 확인하면 좋다.

  • 바퀴를 공중에 띄운 상태에서 앞으로 명령을 줄 때 엔코더 카운트가 증가하는지 확인한다.

  • 50cm 이동 명령을 내리고 실제 이동거리를 자로 측정해 바퀴 둘레 또는 카운트 보정값을 조정한다.

  • 목표를 자주 지나치면 KP_POS를 낮추거나 SLOW_ZONE을 키운다.

  • 목표 근처에서 모터가 떨면 STOP_TOLERANCE를 조금 키운다.

  • 로봇이 출발하지 못하고 웅웅거리면 MIN_PWM을 조금 올린다.

  • 출발과 정지가 너무 거칠면 MAX_PWM과 SLOW_MAX_PWM을 낮춘다.

튜닝은 실패를 줄이는 작업이 아니라 로봇의 성격을 알아가는 작업에 가깝다. 어떤 로봇은 가볍고 민첩해서 PWM을 조금만 줘도 튀어나간다. 어떤 로봇은 무겁고 바퀴 마찰이 커서 최소 PWM을 꽤 높게 잡아야 움직인다. 같은 코드라도 몸체가 바뀌면 값은 달라질 수 있다.

엔코더 위치 제어의 한계

엔코더는 바퀴가 얼마나 돌았는지를 알려준다. 하지만 로봇이 실제로 바닥 위에서 얼마나 이동했는지를 완벽하게 알려주는 것은 아니다. 바퀴가 헛돌면 엔코더 카운트는 증가하지만 로봇은 제자리에서 미끄러질 수 있다. 타이어가 눌리면 실제 바퀴 지름이 조금 작아지고, 카펫과 매끄러운 책상 위에서도 결과가 달라진다.

그래서 엔코더 기반 위치 제어는 '바퀴가 미끄러지지 않는다는 가정' 위에서 작동한다. 작은 실내 로봇에서는 이 가정이 꽤 잘 맞지만, 빠르게 출발하거나 급정지하거나 장애물에 부딪히면 오차가 커진다.

이 한계를 줄이는 방법은 여러 가지다. 급가속과 급정지를 피하고, 타이어 접지력을 높이고, 바닥 상태가 일정한 곳에서 테스트하면 오차가 줄어든다. 더 나아가 IMU로 회전 방향을 확인하거나, 라인 센서로 기준선을 따라가거나, 카메라로 위치 표시를 인식하면 엔코더만 사용할 때보다 더 안정적인 위치 추정이 가능하다.

하지만 모든 복잡한 로봇 제어도 출발점은 같다. 바퀴가 얼마나 돌았는지 알고, 목표와 현재의 차이를 계산하고, 그 차이를 줄이는 방향으로 출력을 조절한다. 이것이 위치 제어의 뼈대다.

작은 실습: 사각형을 그리며 달리기

위치 제어와 제자리 회전을 조합하면 로봇에게 간단한 경로를 줄 수 있다. 예를 들어 다음 동작을 네 번 반복하면 로봇은 바닥 위에 보이지 않는 사각형을 그리듯 움직인다.

for (int i = 0; i < 4; i++) {
  moveDistanceStraight(500);
  delay(300);
  turnInPlace(90);
  delay(300);
}

처음 실행하면 완벽한 사각형이 나오지 않을 가능성이 높다. 마지막 위치가 시작점에서 조금 어긋날 수 있고, 90도 회전이 조금 부족할 수도 있다. 괜찮다. 그 어긋남이 바로 로봇이 보내는 디버깅 메시지다. 직진 거리가 계속 짧다면 거리 변환 계수를 조정하고, 회전이 계속 부족하다면 바퀴 간 거리 값이나 회전 보정값을 조정하면 된다.

이 실습은 단순해 보이지만 중요한 의미가 있다. 로봇이 더 이상 '그냥 움직이는 장난감'이 아니라 '명령한 거리와 각도를 따라가려는 기계'가 되었기 때문이다. 원하는 위치로 이동하는 능력이 생기면 장애물 회피, 라인트레이싱, 카메라 기반 추적 같은 동작도 훨씬 체계적으로 만들 수 있다.

위치 제어가 로봇에 주는 변화

위치 제어를 배우면 로봇을 바라보는 관점이 달라진다. 이전에는 PWM 120을 주면 얼마나 갈지 눈으로 지켜봐야 했다. 이제는 300mm 이동, 90도 회전, 다시 200mm 이동처럼 명령을 거리와 각도로 표현할 수 있다.

물론 아직 완벽한 지도 기반 주행은 아니다. 엔코더 오차는 쌓이고, 바퀴는 미끄러지고, 바닥은 늘 일정하지 않다. 그래도 위치 제어는 모든 이동 로봇의 기초 체력이다. 목표값, 현재값, 오차, 출력이라는 제어의 기본 구조가 실제 바퀴 움직임으로 연결되는 순간이기 때문이다.

로봇이 원하는 거리만큼 가고, 목표 근처에서 천천히 줄이고, 좌우 바퀴 차이를 보정하며, 제자리에서 각도를 맞춰 회전할 수 있다면 이미 많은 일을 할 준비가 된 것이다. 이제 로봇은 단순히 앞으로 달리는 것이 아니라, 목적지를 향해 움직이기 시작한다.

책 목록

센서와 모터로 시작하는 실전 로봇 만들기

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기