Devin.KR

바퀴 속도와 몸체 속도

65분 안팎

학습 목표

바퀴 반지름과 간격으로 선속도·각속도를 계산합니다.

개념

동작을 단위 계약으로 확인합니다

관제에서 목표를 접수해도 바퀴가 어떤 방향으로 돌아야 하는지는 아직 정하지 않았습니다. 앞 모듈의 PWM 0.2는 이동 인터페이스를 시험하는 고정값이었습니다. 이번에는 바퀴 반지름과 중심 간격을 사용해 왼쪽·오른쪽 회전 속도를 몸체의 선속도와 각속도로 바꿉니다. 계산 결과를 직접 검산할 수 있어야 다음 레슨의 위치 갱신을 믿을 수 있습니다.

실습 범위를 정합니다

이 모듈은 Python 3 표준 라이브러리와 gcc·bash를 사용하며 별도 패키지 설치가 없습니다. 브라우저는 표준 입력 한 행을 하나의 사례로 처리하고 계산 답만 표준 출력에 씁니다. 로컬 starter.zip은 별도 폴더에 풀어 bash check.sh로 검사합니다. 가상 시간은 반복 횟수와 dt의 곱입니다. 실제 모터나 ROS 2를 실행하지 않으며 C 공유 라이브러리도 PC 안의 가상 장치입니다. 출력은 숫자 서식을 맞춰 비교하고 디버깅 메시지는 제거합니다.

기준과 양의 방향

로봇의 정면을 몸체 x축, 왼쪽을 y축으로 정합니다. 평면에서 반시계 회전이 양의 각속도입니다. 바퀴의 양의 회전은 각각 로봇을 앞으로 미는 방향으로 정의합니다. 좌우 모터 설치 방향이 거울 대칭이어도 이 계약의 양의 방향은 같습니다. 실물 드라이버의 부호 보정은 HAL 경계에서 해야 하며 식을 상황마다 바꾸면 검사가 의미를 잃습니다.

회전 속도를 접선 속도로 바꿉니다

반지름 r의 단위는 m이고 바퀴 회전 속도 q의 단위는 rad/s입니다. 접선 속도는 r·q이며 단위는 m/s입니다. 이번 가상 로봇의 r은 0.05m입니다. 두 바퀴가 각각 2rad/s라면 접선 속도는 각 0.10m/s가 됩니다. rad를 도로 해석하거나 반지름 대신 지름 0.10을 넣으면 거리 배율이 틀어집니다. 작은 직선 사례 하나가 이 두 오류를 빠르게 드러냅니다.

몸체 속도 두 값을 계산합니다

몸체 중심의 선속도 v는 좌우 접선 속도의 평균입니다. 각속도 w는 오른쪽 접선 속도에서 왼쪽 접선 속도를 뺀 값을 중심 간격 L로 나눈 값입니다. 따라서 v=r(q_left+q_right)/2, w=r(q_right−q_left)/L입니다. L=0.30m는 몸체 외곽 폭이 아닙니다. 오른쪽이 더 빠르면 왼쪽으로 도는지, 두 속도가 같으면 회전이 없는지 식의 부호를 물리 그림과 비교합니다.

직선·제자리 회전·원호를 분리합니다

좌우 값이 같으면 속도 차가 0이므로 직선입니다. 크기가 같고 부호가 반대이면 평균이 0이므로 중심은 이동하지 않고 회전합니다. 나머지는 이동과 회전이 함께 생기는 원호입니다. 왼쪽 −2, 오른쪽 2rad/s이면 v=0.0m/s이고 w는 약 0.666667rad/s입니다. 선속도 0을 모든 동작의 정지로 해석하면 제자리 회전을 놓치므로 두 출력을 함께 확인합니다.

원하는 몸체 명령을 역으로 변환합니다

제어기는 보통 v와 w를 정하고 HAL은 바퀴 명령을 받습니다. q_left=(v−wL/2)/r, q_right=(v+wL/2)/r로 역변환합니다. 이 결과를 다시 정기구학에 넣으면 원래 v와 w가 복원되어야 합니다. 왕복 검사만으로 좌우 부호 오류를 전부 찾지는 못하므로 오른쪽만 빠르게 만드는 독립 회전 사례도 둡니다. 같은 오류를 정변환과 역변환에 넣으면 왕복 결과가 맞을 수 있습니다.

PWM과 회전 속도는 다른 값입니다

PWM은 무차원 듀티이고 rad/s는 운동량의 시간 변화입니다. 이번 로컬 이상 모델에서만 PWM 1.0을 10rad/s로 대응시킵니다. 회전 속도를 10으로 나눈 값이 가상 PWM입니다. 이는 배터리·부하·마찰을 무시한 교육용 약속입니다. 실제 모터에서는 같은 듀티라도 속도가 달라지므로 엔코더 측정과 속도 제어가 필요합니다. 브라우저 기구학은 PWM 입력을 받지 않습니다.

오류를 계산 전에 차단합니다

브라우저 입력은 r L q_left q_right 순서의 유한 숫자 네 개입니다. r과 L은 양수이며 음수 바퀴 속도는 후진을 뜻하므로 허용합니다. L=0은 ZeroDivisionError가 생기기 전에 ERROR로 반환합니다. NaN은 비교 연산을 통과할 수 있으므로 math.isfinite로 검사합니다. 숫자 개수가 틀린 행과 빈 행도 ERROR이며 빈 파일은 아무 행도 출력하지 않습니다.

결과를 읽는 순서

첫 출력은 선속도, 두 번째는 각속도이며 둘 다 소수점 여섯 자리입니다. 먼저 부호와 영점 관계를 보고 다음으로 크기를 봅니다. 회전만 반대로 나오면 좌우 순서 또는 뺄셈 순서를 점검합니다. 두 값이 모두 두 배라면 반지름과 지름을 의심합니다. 회전 크기만 틀리면 L의 측정 기준을 확인합니다. 계산 중간값을 남길 때도 q_left와 r·q_left를 서로 다른 이름으로 씁니다.

실습을 완료하는 기준

calculate 함수의 두 식을 완성하고 직선·후진·회전·비대칭·잘못된 치수 검사를 통과합니다. 0 근처 출력은 0.000000으로 통일하도록 제공한 출력 부분을 유지합니다. 음수 선속도를 절댓값으로 바꾸면 후진 사례가 실패합니다. AssertionError 또는 채점 불일치는 입력 사례의 단위부터 읽고, 기대 출력만 바꿔 맞추지 않습니다. 추가로 v=0.2, w=0.5를 역변환한 뒤 다시 계산한 기록을 제출합니다.

모델의 적용 한계를 표시합니다

이 식은 평면에서 바퀴가 미끄러지지 않는 차동 구동 모델입니다. 측면 미끄러짐과 바퀴 변형은 포함하지 않습니다. 따라서 기구학 검사가 통과했다고 실제 위치 정확도가 확보됐다고 말할 수 없습니다. 자세한 유도와 오도메트리 누적 사례는 더 읽기의 차동 구동 장으로 이어갑니다. 공식 ros2_control 문서에서도 바퀴 반지름과 간격을 몸체 속도와 바퀴 명령의 변환에 사용합니다.

사실 확인 참고: ros2_control 차동 구동 제어기 공식 문서에서 반지름·간격의 역할을 확인합니다.

따라하기

접선 속도를 검산합니다

반지름 0.05m와 2rad/s를 곱합니다. 출력 단위는 m/s입니다.

r=0.05
q=2.0
print(f"tangent={r*q:.6f} m/s")

실행 결과

tangent=0.100000 m/s

세 운동을 비교합니다

왼쪽·오른쪽 순서를 유지합니다. 출력은 v m/s와 w rad/s입니다.

r,L=0.05,0.30
for left,right in [(2,2),(-2,2),(2,4)]:
    print(f"{r*(left+right)/2:.6f} {r*(right-left)/L:.6f}")

실행 결과

0.100000 0.000000
0.000000 0.666667
0.150000 0.333333

역변환을 복원합니다

원하는 v와 w를 바퀴 회전 속도로 만든 뒤 몸체 속도로 복원합니다.

r,L=0.05,0.30
v,w=0.2,0.5
left=(v-w*L/2)/r
right=(v+w*L/2)/r
print(f"wheels={left:.6f} {right:.6f} rad/s")
print(f"body={r*(left+right)/2:.6f} {r*(right-left)/L:.6f}")

실행 결과

wheels=2.500000 5.500000 rad/s
body=0.200000 0.500000

확인 문제

실습

한 행에 r L q_left q_right를 받습니다. 네 값은 유한한 숫자이며 r과 L은 양수입니다. calculate를 완성해 선속도 m/s와 각속도 rad/s를 여섯 자리로 출력합니다. 잘못된 행은 ERROR, 빈 파일은 출력 없음입니다. 절댓값 0.0000005 미만인 출력은 0.000000으로 통일합니다. 제공된 입력·출력 처리는 유지합니다.

모범 답안
import sys, math

def calculate(r, width, left, right):
    return r*(left+right)/2, r*(right-left)/width

for line in sys.stdin:
    try:
        values=list(map(float,line.split()))
        if len(values)!=4 or not all(math.isfinite(v) for v in values): raise ValueError()
        r,width,left,right=values
        if r<=0 or width<=0: raise ValueError()
        v,w=calculate(r,width,left,right)
        if not all(math.isfinite(a) for a in (v,w)): raise ValueError()
        v=0.0 if abs(v)<0.0000005 else v
        w=0.0 if abs(w)<0.0000005 else w
        print(f"{v:.6f} {w:.6f}")
    except (ValueError,OverflowError): print("ERROR")

더 읽기

면접 질문

  • 목표점에 가까워져도 로봇이 흔들리는 상황을 설명해 주시면 됩니다.