Devin.KR

인지·계획·제어 연결

100분 안팎

학습 목표

센서·변환·지도·계획·제어·HAL 구성과 주기·지연 예산을 통합 실행합니다.

개념

통합은 연결 계약을 확인하는 작업입니다

개별 테스트가 통과해도 센서의 단위와 제어기의 단위가 다르면 주행 결과는 틀립니다. 통합 담당자는 함수 이름을 모으기 전에 입력의 의미와 정지 책임을 확인합니다. 이번 레슨은 앞 모듈의 재생기를 유지하면서 구성 계약과 지연 예산을 검사합니다. 열린 지도와 우회 지도에서 실제 참값 도달을 확인하고, 입력 누락은 정상 도달과 분리합니다.

이 모듈의 로컬 실습은 Python 3 표준 라이브러리, gcc, bash를 사용합니다. 압축을 새 폴더에 풀고 bash check.sh를 실행합니다. 앞 모듈 미션 solution 전체를 포함하므로 이전 다운로드를 찾을 필요가 없습니다. starter에서는 새 기능의 TODO 때문에 일부 검사만 실패합니다. 미션은 모든 회귀를 실행하고 레슨 실습은 해당 주제의 새 검사를 실행합니다.

구성도를 코드의 실제 경계에 맞춥니다

입력 JSONL의 sensor 이벤트는 RangeGate와 History로 들어갑니다. RangeGate는 누락과 범위, 측정 시각의 나이를 검사하고 History는 해당 시각의 추정 자세를 제공합니다. SensorNavigation은 보호 조건과 이동 작업을 연결합니다. 센서 점을 map으로 투영하지만 점유 지도를 갱신하지는 않습니다. 인지라는 이름을 붙였다고 실제 장애물 인식 기능이 생기는 것은 아닙니다.

지도는 planning.py의 탐색과 차체 여유 검사에 전달됩니다. 탐색이 반환한 셀은 미터 단위 중간 목표로 바뀌고 NavigationActions의 제어기가 추정 자세와 목표의 차이로 명령을 만듭니다. MotionHal은 C 가상 구동기를 호출합니다. EncoderHal은 바퀴 이동량에서 추정 자세를 적분합니다. 이 흐름을 따라 각 경계에서 어떤 값을 읽는지 표시하면 교체 책임이 드러납니다.

truth_pose는 평가자가 아는 참값이며 제어기의 위치 입력이 아닙니다. 제어기에 참값을 넣으면 잡음이 있는 추정기가 실제보다 좋아 보입니다. 도달 판정은 액션 성공, 참값 목표 거리, 충돌 수를 함께 검사합니다. 통합 그림에도 추정 자세의 제어 피드백과 참값의 평가 화살표를 나누어 적습니다. 겉으로 비슷한 위치 변수라도 소비자가 다릅니다.

좌표와 설정의 출처를 고정합니다

이번 PC 모델에서는 map과 odom이 같은 기준을 공유하지만 base_link와 sensor는 장착 위치로 구분합니다. 실제 위치 보정이 있는 시스템에서 map과 odom이 항상 같다고 일반화하지 않습니다. 거리 m, 회전 rad, 기록 시각 us를 사용하고 초를 받는 기존 함수에 전달할 때만 환산합니다. 센서 장착 변환과 지도 원점은 서로 다른 이동값입니다.

motion_config.json은 제어 주기와 속도 한계를, planning_config.json은 지도 해상도와 원점, 차체 반경, 계획 여유를 소유합니다. 통합의 DEFAULT는 연결 검사에 쓸 예산 가정입니다. 기존 설정을 자동으로 측정해서 만든 값이 아니므로 값의 출처를 README에 함께 적습니다. 설정 파일 하나만 바꾸고 그림이나 예산표를 그대로 두면 검토자가 다른 시스템을 설명받습니다.

지연 예산은 정보가 늙는 경로를 셉니다

센서 주기 50000us, 전송 50000us, 계산 20000us, 다음 제어까지 대기 50000us를 보수적으로 더하면 170000us입니다. 이 합은 각 대기가 독립적으로 최대로 나타난다는 교육용 가정입니다. 허용 나이 200000us와 비교하면 30000us 여유가 남습니다. 실행이 빨리 끝났다는 벽시계 시간과 이 정보 나이 예산을 같은 지표로 보고하지 않습니다.

validate_stack은 frame, clock, units를 먼저 검사한 뒤 정수 시간 값을 확인합니다. bool은 Python에서 정수처럼 취급될 수 있으므로 type(value) is int로 구분합니다. 음수 대기와 0 주기는 거절합니다. 합이 한계와 같으면 허용하고 초과하면 AGE_BUDGET 오류를 냅니다. 테스트가 기대하는 경계는 계산 시간이 50000us일 때 통과하고 50001us일 때 실패하는 경우입니다.

0.20초 경계를 허용하는 기존 RangeGate와 예산 검사의 비교 방향이 맞아야 합니다. 예산을 통과했다고 실제 입력이 신선하다고 가정하지 않습니다. 실행 중에는 각 표본의 측정 시각을 현재 가상 시각과 비교해 다시 판단합니다. 예산은 구성 단계의 검토이며 RangeGate는 입력별 검사입니다. 계획상의 상한과 관측된 위반은 서로 다른 자료로 남깁니다.

정상과 실패의 연결을 함께 실행합니다

case 함수는 앞 모듈의 build_case와 run_case를 호출합니다. open은 기본 열린 지도, detour는 장애물 한 칸을 추가한 우회 지도입니다. 지도 파일을 바꾼 뒤 manifest의 map 해시도 새 실행에 맞춰 계산합니다. 과거 실패 묶음을 덮어쓰는 대신 새 출력 폴더를 사용합니다. 같은 목표에 대해 경로가 달라져도 도달과 충돌 기준은 유지합니다.

정상 두 지도에서는 reached가 참이고 collisions가 0이며 종료 PWM이 두 바퀴 모두 0이어야 합니다. 막힌 지도는 PATH_NOT_FOUND로 종료한 앞 모듈 회귀를 유지합니다. 정상 지도 테스트만 추가한 뒤 실패 회귀를 빼면 통합의 보호 기능이 사라질 수 있습니다. 미션 check.sh가 이전 check-m09.sh를 직접 실행하는 이유가 여기에 있습니다.

센서 누락 fixture는 500000us에 null 이벤트를 주입합니다. safe 정책은 SENSOR_MISSING으로 액션을 중단하고 PWM을 0으로 만듭니다. legacy는 센서 보호를 우회한 비교용 정책입니다. legacy에서 목표에 도달했다는 결과는 좋은 성능의 증거가 아니라 보호 조건 없이 계속 움직였다는 결함의 재현입니다. 코드의 기본 실행은 safe를 유지합니다.

오류를 읽고 고칠 위치를 정합니다

FRAME은 예상 map 대신 다른 기준을 전달한 구성 오류이며 좌표 숫자만 바꾸어 숨기지 않습니다. CLOCK은 virtual 계약 위반, UNITS는 필드 이름이 아니라 단위 집합의 불일치입니다. BUDGET_TYPE이면 bool, 실수, 음수를 찾아봅니다. AGE_BUDGET이면 어떤 대기 항목이 합을 늘렸는지 계산한 뒤 구조나 가정을 수정합니다. 한계만 키우는 수정은 별도의 근거가 필요합니다.

test_excess가 실패하고 ValueError가 발생하지 않았다고 나오면 초과 검사 TODO부터 봅니다. test_two_maps가 실패하면 reached, reason, collisions, pwm을 순서대로 확인합니다. gcc 오류는 Python 논리 검사에 도달하기 전의 빌드 문제이며 소스와 컴파일러 메시지를 읽습니다. 모든 오류를 같은 실행 실패로 묶으면 입력 계약 문제와 환경 문제를 구별하기 어렵습니다.

실행 스크립트의 방향도 통합 계약입니다

check.sh에서 check-m09.sh로, 그 안에서 check-m08.sh로 진행하는 방향을 유지합니다. unittest는 함수와 fixture를 직접 호출하며 check.sh를 다시 부르지 않습니다. 테스트가 전체 스크립트를 재호출하면 같은 테스트가 다시 시작될 수 있습니다. 이번 확장 테스트는 별도의 임시 폴더에서 case를 만들고 끝나면 파일만 정리합니다. 서버나 장기 실행 프로세스는 필요하지 않습니다.

실습 제출물에는 완성한 validate_stack, 경계 검사 결과, 구성도를 읽어 설명한 짧은 글을 포함합니다. 글에는 고정 거리 입력의 한계와 참값의 평가 전용 역할을 넣습니다. 더 읽기에서는 launch와 파라미터 계층을 살펴보고 실제 ROS 통합 때 확인할 항목을 넓힙니다. 여기서는 PC 실행의 연결 계약을 스스로 검사하고 실패 원인을 특정하는 능력을 검증합니다.

따라하기

기본 나이 예산을 계산합니다

다음 코드를 budget_demo.py에 저장하고 python3 budget_demo.py로 실행합니다. 가정한 대기를 더한 값이며 실행 속도 측정이 아닙니다.

period=50000
transport=50000
compute=20000
control=50000
age=period+transport+compute+control
print(f'age_budget_us={age}')
print(f'margin_us={200000-age}')

실행 결과

age_budget_us=170000
margin_us=30000

경계 비교를 확인합니다

새 Python 파일로 실행하여 같은 값과 초과를 구분합니다.

for compute in (50000,50001):
    age=50000+50000+compute+50000
    print(f'compute_us={compute} accepted={age<=200000}')

실행 결과

compute_us=50000 accepted=True
compute_us=50001 accepted=False

starter 계약을 완성합니다

실습 zip을 풀고 integration.py의 TODO를 완성한 뒤 bash check.sh를 실행합니다. test_excess는 초과 오류를, test_boundary는 같은 값의 허용을 검사합니다. test_two_maps는 열린 지도와 우회 지도에서 도달·충돌 0·정지 PWM을 검사합니다. 정상 완료 출력의 age_budget_us를 첫 계산과 비교합니다.

확인 문제

실습

integration.py의 나이 예산 초과 검사를 완성합니다. 같은 한계값은 허용하고 1us 초과는 AGE_BUDGET을 냅니다. bash check.sh로 타입·0 주기·단위·시간 기준·두 지도 도달을 검증합니다. 구성 그림의 평가 전용 참값 경로도 설명합니다.

시작 코드·테스트 내려받기

실행 명령

bash check.sh

기대 결과

새 계약 검사 9개 통과, age_budget_us=170000

모범 답안모범 답안 내려받기

더 읽기

면접 질문

  • 실험 기록을 재생할 때 함께 남겨야 할 정보를 설명해 주시면 됩니다.