4축 로봇암(우노에 서보 네 개)에 키보드 텔레옵을 붙이는 날이었어요. 배선은 멀쩡한데 실기에서 문제가 여덟 번 났고, 그중 다섯이 시리얼 명령과 액추에이터를 다루는 쪽이었어요.
고치고 보니 하나하나가 임베디드 시리얼 통신에서 반복되는 전형적인 함정이었어요.
다섯 함정을 한눈에
텔레옵이 아예 작동 안 함
PermissionError 13으로 죽어 있었어요목표각이 0으로 찍히며 관절이 0도로 꺾임
Serial.parseInt()는 기본 타임아웃 1초 안에 숫자가 안 오면 0을 반환해요큰 숫자를 넣으면 또 0도
int는 16비트라 atoi("40000")이 음수로 감기고, constrain을 거쳐 0이 돼요atol로 교체. 버퍼 길이상 최대 6자리라 이 부류가 통째로 사라져요포트를 열 때마다 팔이 90도로 튐
연속 명령·시퀀스 재생에서 동작이 씹힘
숫자가 늦게 오면 0이 돼요
관절을 고르는 글자만 먼저 도착하고 숫자가 늦으면, 명령이 반쯤 도착한 상태가 “0도로 가라”로 해석돼요. 문자 단위로 스트림을 해석하는 한 이런 부분 입력 상태는 피할 수 없어요.
수정은 줄 단위 버퍼링이에요. 개행이 와서 한 줄이 완성됐을 때만 해석하고, 숫자 자리에 숫자가 아닌 문자가 섞이면 그 명령을 통째로 무시해요. 절반만 온 명령을 어떻게든 해석해 주는 게 아니라, 완성 안 된 입력은 없는 셈 치는 쪽이 견고해요.
같은 계열의 버그가 코드 리뷰에서 하나 더 나왔어요. AVR의 int는 16비트라 atoi("40000")이 음수로 감기고, 그 값이 constrain을 거치면 역시 0도가 돼요. 32비트 long을 반환하는 atol로 바꾸면 버퍼 길이상 들어올 수 있는 최대 6자리 숫자로는 절대 안 넘쳐요.
개루프 서보는 부팅 순간이 위험해요
감속 없는 급점프는 기어에도 프레임에도 부담이에요. 해결은 마지막 자세를 EEPROM에 기억시키는 거였어요. EEPROM은 전원이 꺼져도 내용이 남는 저장 공간인데, 우노에 1킬로바이트가 있고 셀당 약 10만 회의 쓰기 수명이 있어요.
쓰기 수명이 유한하니 아무 때나 쓰면 안 되는데, EEPROM.update()는 값이 바뀐 바이트만 실제로 써서 수명을 아껴줘요.
개루프 액추에이터의 “지금 어디인가”를 하드웨어가 못 주니, 소프트웨어가 마지막으로 보낸 값을 대신 기억하는 구조예요.
관절 넷이 같이 출발해서 같이 도착하게
관절마다 같은 속도로 움직이면 적게 도는 관절이 먼저 멈춰서 동작이 삐걱거려요.
전 관절이 동시에 출발해서 동시에 도착해요. 산업 로봇 컨트롤러가 하는 관절 공간 보간의 가장 단순한 형태이고, 정수 나눗셈으로 계산해도 종점 오차는 0이에요.
진실의 원본을 어디에 둘까
연속 명령이나 시퀀스 재생에서 동작이 씹히는 증상은 통신 유실이 아니라 상태 어긋남이 원인이었어요. 호스트가 믿는 각도와 실제 각도가 달라지면, 그 상태에서 보내는 절대각 명령은 엉뚱한 기준에서 출발한 점프가 돼요.
수정은 응답 기반 동기화예요. 펌웨어가 이동을 마칠 때마다 Base=90 Shoulder=45 같은 형식으로 실제 자세를 한 줄로 응답하고, 호스트는 그걸 파싱해 자기 상태를 덮어써요.
| 진실의 원본 | 어긋나는 경우 | 회복 |
|---|---|---|
| 호스트의 기억 | 우노 리셋 · 명령 거부 | 없음 — 계속 어긋난 채 |
| 실제로 움직인 쪽의 보고 | 같은 상황 | 다음 응답 한 줄로 복구 |
재생 중에 누른 키가 재생이 끝난 뒤 뒤늦게 실행되는 유령 입력은 재생 종료 시 키 버퍼를 비워서 막았고, 물리적으로 움직이는 물건에는 중단 수단이 기본이어야 해서 ESC 즉시 중단도 넣었어요.
남는 기준
다섯 문제의 공통 교훈이 하나예요. 임베디드 통신의 견고함은 정상 입력을 잘 처리하는 데서가 아니라, 비정상 입력을 통째로 무시하는 데서 나와요. 형식이 어긋난 명령을 버려도 다음 명령이 곧 다시 오지만, 어긋난 명령을 어떻게든 해석해 버리면 그게 물리적 움직임이 되니까요. 버리는 쪽이 언제나 싸요.
진단 습관도 하나 남았어요. “안 움직인다”는 증상에서 바로 배선과 하드웨어를 의심하기 쉬운데, 포트를 누가 잡고 있는지 프로세스부터 보는 쪽이 훨씬 빨랐어요.
지난주 좌우 바퀴 편차 보정 — 같은 지령으로 직진하지 않는 이유가 모터 쪽 이야기였다면, 이번엔 모터에 도착하기 전 단계인 통신과 상태 관리 이야기예요.