문법 드릴만으로는 실제 데이터의 형태를 알기 어려워서, 뒤 커리큘럼에서 다룰 로봇 시연 데이터를 미리 열어 검증 드릴 12문제를 만들었어요. 그 과정에서 교과서 예제로는 안 드러나는 두 지점, 부동소수점 비교와 파이썬 import의 실행 모델을 정리하게 됐어요.
데이터 구조는 실물로 확인해요
HuggingFace에 공개된 lerobot/pusht를 썼어요. 로봇이 T자 블록을 미는 시연(demonstration)을 모은 데이터셋이고, 각 프레임은 관측(observation)과 행동(action)의 쌍이에요. 모방학습은 이 쌍에서 “이 관측이면 이 행동”을 지도학습으로 배워요.
참고 자료는 에피소드마다 Parquet 파일이 하나씩 있는 구조라고 했는데, 실제 데이터셋은 v3.0으로 개편되면서 청크 파일 하나에 206개가 통합돼 있었어요. 오픈소스 로보틱스 스택은 개정이 잦아 몇 달 전 자료도 어긋나기 쉬우니, 구조는 문서로 익히되 최종 확인은 실물을 열어서 해요. 문서와 데이터가 다르면 데이터가 맞으니까요.
float32 타임스탬프의 == 비교 오류
10 fps 데이터니까 인접 프레임의 타임스탬프 간격은 0.1초여야 하는데, 간격 == 0.1로 검증했더니 전부 실패했어요.
timestamp는 float32(가수 23비트)라 0.1이 0.10000000149…가 됐고, 파이썬 리터럴 0.1은 float64(가수 52비트)라 더 촘촘한 다른 근사값이에요. 두 값이 다르니 ==가 깨져요.
import math
gap == 0.1 # 실패: 정확 비교
math.isclose(gap, 0.1, abs_tol=1e-6) # 수정: 허용 오차abs_tol은 절대 허용 오차예요. isclose는 기본적으로 상대 오차(rel_tol)로 비교하는데, 0에 가까운 값에서는 그것만으로 부족해서 절대 오차를 함께 줘요.
import는 모듈의 최상위 코드를 실행해요
모듈을 하나 만들고 아래쪽에 테스트용 print를 뒀는데, 다른 파일에서 import하는 순간 그 print가 실행됐어요.
__name__은 파일을 python x.py로 직접 실행하면 '__main__', 다른 파일이 import x로 부르면 모듈 이름 'x'가 돼요. 그래서 가드 안의 코드는 직접 실행에서만 돌고 import에서는 건너뛰어요.
if __name__ == '__main__':
# 파일을 직접 실행할 때만 도는 코드조용한 실패는 한 얼굴이에요
드릴에서 반복해 걸린 실수들의 원인이 하나로 모여요. 파이썬은 오류로 멈추지 않고 조용히 다른 값을 돌려준다는 점이에요.
| 쓴 것 | 기대한 결과 | 실제 결과 |
|---|---|---|
return print(...) | 값이 반환됨 | print는 값을 반환하지 않아 None |
min(xs, ys) | 각 리스트의 최솟값 | 두 시퀀스를 사전순 비교한 결과 |
컴프리헨션에 append 혼용 | 리스트가 채워짐 | 세 자리를 지키면 append가 아예 불필요 |
gap == 0.1 | 타임스탬프 검증 통과 | float32/float64 근사가 달라 전부 실패 |
모듈 최상위 print | 직접 실행할 때만 출력 | import만 해도 실행 |
정적 타입 언어라면 컴파일 단계에서 걸렸을 것들이 파이썬에서는 런타임에 조용히 통과하니, __main__ 가드나 assert 셀프체크 같은 방어 관용구가 자주 쓰여요.
드릴 구성과 재현 장치
데이터 검증(관측 키 추출, 프레임 수 집계, 타임스탬프 간격 검증, 좌표 범위 확인)과 문법 연습(컴프리헨션·Counter·deque)을 합쳐 12문제를 만들었어요. 재현을 위해 데이터셋 리비전(커밋 해시)을 고정했고, 문제마다 붙인 assert가 실행 즉시 통과·실패를 찍어요. 전체 드릴(58문제 + 답안)은 robotics-python-drills에 공개해 뒀어요.
전날 로봇의 몸(모터·실시간 신호)을 배운 기록은 1kHz 제어 주기와 지터 — 로봇 제어가 1ms를 다투는 이유에 있어요.