아들과 함께 만들어보는 인공지능(LLM) 로봇 만들기 프로젝트 — EP 9. 4개월 회고, 그리고 다음은 레고

아들과 함께 만들어보는 인공지능(LLM) 로봇 만들기 프로젝트 — EP 9. 4개월 회고, 그리고 다음은 레고 EP 0를 쓰던 날, 책상 위에 반쯤 조립된 아크릴 샤시가 있었다. 한쪽 바퀴가 반대로 돌고 있었다. 지금은 그 로봇이 주방까지 혼자 걸어가서 물컵을 찾는다. 가져오지는 못하지만. 4개월이 걸렸다. 잘 된 것들 에이전트 하네스 방식이 효과 있었다 CLAUDE.md 기반으로 도메인 지식을 주입하는 방식이 생각보다 효과적이었다. 매번 컨텍스트를 설명하는 피로가 없어졌고, AI가 프로젝트 규칙을 지키면서 코드를 짰다. 처음에 10줄로 시작한 파일이 120줄이 됐는데, 그게 프로젝트 자체의 성장 기록이기도 했다. 아들이 8번의 프롬프트로 동작 하나를 완성했다 EP 5에서 쓴 그 장면이 이 프로젝트 전체에서 가장 기억에 남는다. 아들이 혼자 앉아서 여덟 번 프롬프트를 다듬어서 장애물 회피 동작을 완성한 날. 코드를 한 줄도 안 쳤다. 근데 "내가 짠 거야"가 틀리지 않았다. Apple Silicon 로컬 LLM 조합이 맞았다 M4 Pro에서 112 tok/s. 클라우드 API 지연 없이 LAN 안에서 0.5초 이내 응답. 이게 실제로 작동한다는 걸 확인했다. 집에서 로컬 LLM 서버를 운영하는 게 생각보다 현실적이었다. 예상과 달랐던 것들 아두이노 → Pi 전환이 생각보다 빨리 왔다 EP 6에서 노트북 브릿지를 쓰는 순간, "이건 임시방편"이라는 게 너무 명확했다. 처음 설계에서는 EP 7이 Pi 전환이지만, 솔직히 EP 4~5 시점부터 이미 전환하고 싶었다. 한 편에 하나씩 넣는 시리즈 구조 때문에 참았다. 비전 LLM이 생각보다 잘 됐다 EP 8에서 "주방 가서 물컵 찾아줘"가 세 번째 시도에 됐을 때 솔직히 놀랐다. 하드코딩 없이, 지도 없이, 카메라 하나로 공간을 인식하고 목표를 찾아가는 것—이게 7B 파라미터 모델에서 된다는 게 2년 전이었으면 믿기 어려운 일이었을 거...

아들과 함께 만들어보는 인공지능(LLM) 로봇 만들기 프로젝트 — EP 8. 아들이 AI 로봇에게 첫 명령을 내렸다

아들과 함께 만들어보는 인공지능(LLM) 로봇 만들기 프로젝트 — EP 8. 아들이 AI 로봇에게 첫 명령을 내렸다 EP 6에서 LLM 서버와 연결했고, EP 7에서 Pi로 전환했다. 이번엔 카메라가 합류한다. Qwen2.5-VL-7B를 LLM 서버에 올렸다. 텍스트만 받던 모델 대신 이미지도 받을 수 있는 멀티모달 모델이다. 로봇 카메라에서 프레임을 캡처해서 "지금 뭐가 보여? 어디로 가야 해?"라고 물으면 모델이 이미지를 보고 판단을 내린다. 카메라 + 센서 + LLM + 로봇이 처음으로 한 번에 붙는 날이었다. Qwen2.5-VL 교체 텍스트 전용 Qwen2.5-7B에서 Qwen2.5-VL-7B로 교체했다. 같은 Qwen 계열이라 하네스는 거의 그대로였다. 바뀐 건 세 가지다. CLAUDE.md에 비전 입력 섹션 추가: ## 비전 입력 - 카메라 해상도: 640×480 - 전송 형식: JPEG (quality 70) - 프레임 전송: 명령 요청 시점에만 (연속 스트리밍 아님) - 이미지 + 센서 데이터를 함께 전달 ## LLM 입력 형식 (비전 모드) { "image": "<base64 encoded JPEG>", "sensor": "dist:45", "instruction": "사용자 명령" } 서버 래퍼 코드가 이미지 base64 인코딩을 받아서 llama.cpp에 multimodal 형식으로 전달하도록 수정했다. tok/s가 조금 떨어졌다. 텍스트 전용 112 tok/s에서 이미지 포함 시 78 tok/s로. TTFT도 늘었다. 이미지 인코딩 처리 시간 때문이다. 전체 응답 시간이 430ms에서 820ms로 늘었다. 0.8초. 느려졌지만 쓸 만하다. 첫 번째 시도 아들이 "주방 가서 물 가져와"라고 말했다. 실제로 입력한 건 이거였다. "...

아들과 함께 만들어보는 인공지능(LLM) 로봇 만들기 프로젝트 — EP 7. 아두이노에서 라즈베리파이로, 로봇 두뇌 교체 과정

아들과 함께 만들어보는 인공지능(LLM) 로봇 만들기 프로젝트 — EP 7. 아두이노에서 라즈베리파이로, 로봇 두뇌 교체 과정 아두이노로는 한계가 왔다. EP 6에서 만든 구조—아두이노 + Python 브릿지 노트북—는 작동했지만 매번 노트북을 연결해야 했다. 자율적으로 돌아다니는 로봇이 USB 케이블로 노트북에 묶여 있는 건 이상했다. Pi로 전환하면 브릿지와 ROS2 노드를 Pi 안에 올릴 수 있고, 로봇이 독립적으로 동작한다. 그리고 솔직히 아들이 이 전환을 기다리고 있었다. "언제 진짜 컴퓨터 달아?"가 몇 주째 들어오던 말이었다. Pi 4 vs Pi 5 vs Banana Pi 세 가지 선택지를 비교했다. 기준은 하나였다. LLM은 Pi에 올리지 않는다. Pi는 ROS2 노드 실행, 카메라 파이프라인, 브릿지 역할만 한다. 무거운 추론은 Mac LLM 서버가 한다. 기기 가격(대략) RAM USB 3.0 발열 특이사항 Raspberry Pi 4B 4GB 55달러 4GB 2포트 심함 구하기 쉬움 Raspberry Pi 5 4GB 60달러 4GB 2포트 개선됨 재고 들쭉날쭉 Banana Pi M5 45달러 4GB 4포트 보통 ROS2 지원 불확실 Pi 5가 Pi 4보다 CPU 성능이 2~3배 높다. 카메라 파이프라인(640×480 30fps JPEG 인코딩)을 Pi 4에서 돌리면 CPU가 40~60% 차지한다. Pi 5에서는 20% 이하다. ROS2 노드 여러 개 동시에 돌리면 Pi 4에서 버벅임이 생기는 지점이 있었다. Pi 5를 사고 싶었다. 재고 문제 때문에 잠깐 망설이다가 결국 Pi 4B 4GB로 갔다. 지금 당장 살 수 있었고, Pi 4도 우리 용도에 충분하다고 판단했다. Pi 5는 다음 버전에서. Banana Pi는 ROS2를 직접 설치해서 테스트해봤는데, 커뮤니티 지원이 적어서 의존성 충돌이 몇 개 생겼다. 이걸 해결하는 데 시간을 쓰는 게 비...

아들과 함께 만들어보는 인공지능(LLM) 로봇 만들기 프로젝트 — EP 6. AI 로봇과 로컬 LLM 서버 LAN 통신 연결

아들과 함께 만들어보는 인공지능(LLM) 로봇 만들기 프로젝트 — EP 6. AI 로봇과 로컬 LLM 서버 LAN 통신 연결 로봇과 LLM 서버를 연결하는 방법을 결정해야 했다. 지금까지는 로봇 본체에 코드를 올려서 단독으로 동작하는 것만 했다. HC-SR04로 거리 재고, 모터 제어하는 건 아두이노 혼자서 할 수 있다. 근데 이 프로젝트의 목적은 LLM이 판단하는 로봇이다. 카메라 프레임과 센서 데이터를 LLM 서버로 보내고, LLM이 명령을 내려서 로봇이 움직이는 구조가 필요하다. 로봇(엣지) ↔ LLM 서버(Mac)를 어떻게 연결할지가 이번 편의 주제다. 세 가지 선택지 WebSocket : 양방향 실시간 통신. 구현이 간단하고 HTTP 기반이라 방화벽 문제가 적다. 로봇에서 데이터를 보내고 서버에서 명령을 스트리밍으로 받는 구조에 적합하다. gRPC : Google이 만든 RPC 프레임워크. Protocol Buffers로 데이터를 직렬화해서 WebSocket보다 페이로드가 작다. 타입 안전성이 있고 스트리밍 지원도 된다. 근데 설정이 복잡하다. 클라이언트와 서버 양쪽에 Protobuf 스키마를 관리해야 한다. ROS2 over LAN : 로봇 전용 미들웨어. DDS(Data Distribution Service) 기반으로 토픽 pub/sub 구조를 쓴다. 로봇 프레임워크에 native하게 붙는다. 근데 로봇 본체가 아두이노인 동안은 직접 쓰기 어렵다. Pi로 전환 이후에 의미 있다. 지금 단계에서 아두이노가 엣지 디바이스다. 아두이노는 HTTP 클라이언트를 올리기 어렵다. WiFi 쉴드를 붙이거나 시리얼-WiFi 브릿지를 쓰는 방법이 있는데, 복잡도가 올라간다. 그래서 현재 구조를 다르게 잡았다. 실제 선택한 구조 아두이노는 USB 시리얼로 노트북과 연결한다. 노트북(얇은 Python 브릿지 스크립트)이 시리얼로 아두이노 센서 데이터를 읽고, WebSocket으로 Mac LLM 서버에 보낸다. LLM 서버가 명령을 내려주면 브릿...

아들과 함께 만들어보는 인공지능(LLM) 로봇 만들기 프로젝트 — EP 5. 12살이 AI로 로봇 코딩한 첫날 기록

아들과 함께 만들어보는 인공지능(LLM) 로봇 만들기 프로젝트 — EP 5. 12살이 AI로 로봇 코딩한 첫날 기록 아들이 혼자 해보겠다고 한 날이 왔다. 그동안은 내가 옆에 있거나, 같이 입력하거나, 에러가 나면 내가 보고 설명해줬다. 근데 이날은 "나 혼자 해볼게"였다. 주말 오후였다. 나는 다른 방에 있었다. 30분쯤 지나서 아들이 왔다. "아빠, 로봇이 왼쪽으로만 계속 돌아." 아들이 쓴 첫 프롬프트 나중에 아들이 그날 입력한 것들을 같이 봤다. 첫 번째로 입력한 게 이거였다. "로봇이 장애물 만나면 왼쪽으로 피해가게 해줘" Claude Code가 코드를 냈다. 아들이 올렸다. 로봇이 앞으로 가다가 장애물 감지하면 멈추고 왼쪽으로 돌았다. 여기까지는 맞았다. 근데 돌고 나서 다시 앞으로 안 갔다. 왼쪽으로 계속 돌기만 했다. 아들이 두 번째 입력: "장애물 피하고 나서 다시 앞으로 가게 해줘" 코드가 수정됐다. 다시 올렸다. 이번엔 장애물 앞에서 멈추고, 왼쪽으로 돌고, 다시 앞으로 갔다. 근데 매번 같은 각도로만 돌았다. 장애물이 벽이면 계속 같은 방향으로 돌다가 벽을 따라가게 됐다. 아들이 세 번째 입력: "한 번 피할 때마다 왼쪽으로 도는 시간을 좀 다르게 해줘" 그다음부터 좀 바뀌기 시작했다. Claude한테 물어본 지점 세 번째 코드를 올리고 나서 아들이 멈췄다. Serial 모니터를 켜니까 이런 출력이 나왔다. dist: 0 dist: 0 dist: 0 dist: 18 dist: 0 dist: 400 dist: 23 0이랑 400이 계속 섞였다. 초음파 센서 노이즈 문제였다. EP 2에서 언급했던 바로 그 문제였다. 0cm가 나오면 코드가 장애물로 인식해서 계속 멈췄다. 아들이 "왜 아무것도 없는데 멈춰"라고 했다. 코드 문제가 아니라 센서 값 문제인데, 아들 입장에서는 코드가 잘못...

아들과 함께 만들어보는 인공지능(LLM) 로봇 만들기 프로젝트 — EP 4. AI 로봇에 맞는 로컬 LLM 고르기, Qwen vs Llama vs Phi 비교

아들과 함께 만들어보는 인공지능(LLM) 로봇 만들기 프로젝트 — EP 4. AI 로봇에 맞는 로컬 LLM 고르기, Qwen vs Llama vs Phi 비교 모델을 골라야 했다. 로봇에 로컬 LLM을 붙이려면 어떤 모델을 쓸지 결정해야 한다. 클라우드 API를 쓰면 이 고민이 없다. GPT-4o나 Claude를 그냥 쓰면 된다. 하지만 우리 프로젝트는 LAN 내에서 로컬 LLM 서버를 돌리는 구조다. 어떤 오픈소스 모델이 이 용도에 맞는지 직접 테스트해야 했다. 평가 기준을 먼저 정했다. 하나는 도구 사용(tool use) 이다. 로봇에게 "앞으로 가", "멈춰" 같은 명령을 구조화된 형태로 내리려면 모델이 JSON 함수 호출을 지원해야 한다. 자연어로 답하다가 갑자기 "move_forward()" 같은 텍스트를 섞어서 내놓으면 파싱이 어렵다. 다른 하나는 한국어 능력 이다. 아들이 한국어로 지시하고, 나도 한국어로 디버깅 메시지를 보고 싶다. 영어로만 작동하는 모델은 쓰기 불편하다. 마지막은 비전(vision) 이다. 지금 당장은 아니지만, 나중에 카메라 프레임을 입력으로 받아서 판단하는 기능이 필요하다. 비전을 지원하는 모델이면 나중에 교체 없이 확장이 된다. 이 세 기준으로 네 모델을 평가했다. Qwen2.5-7B-Instruct, Llama 3.1 8B Instruct, Phi-3.5 Mini Instruct, Gemma 2 9B Instruct. 도구 사용 테스트 같은 함수 정의를 줬다. { "name" : "robot_command" , "description" : "로봇에게 이동 명령을 내린다" , "parameters" : { "action" : { "type" : "string" , ...

아들과 함께 만들어보는 인공지능(LLM) 로봇 만들기 프로젝트 — EP 3. Mac M1 M4 M4 Pro로 로컬 LLM 속도 직접 비교했다

아들과 함께 만들어보는 인공지능(LLM) 로봇 만들기 프로젝트 — EP 3. Mac M1 M4 M4 Pro로 로컬 LLM 속도 직접 비교했다 로컬 LLM을 처음 돌려본 날, Mac mini M1에서 Qwen2.5-7B를 올렸다가 토큰이 한 글자씩 찍히는 걸 보고 잠깐 멈췄다. 초당 8토큰 정도였다. 느리지는 않다. 근데 로봇 실시간 제어에 쓸 수 있는 속도인지는 다른 문제다. 로봇이 카메라 프레임을 보내고 LLM이 판단을 내려서 명령이 돌아오기까지 지연이 얼마나 되는지—그걸 실제로 측정해봐야 했다. 마침 집에 Mac이 세 대 있었다. Mac mini M1 16GB, Mac mini M4 24GB, MacBook Pro M4 Pro 14" 24GB. 같은 프롬프트, 같은 모델로 세 대를 비교해보기로 했다. 테스트 환경 모델은 Qwen2.5-7B-Instruct로 통일했다. Q4_K_M 양자화. mlx-lm과 llama.cpp Metal 백엔드 두 가지로 각각 측정했다. 측정 항목: - tok/s : 초당 생성 토큰 수 (generation speed) - TTFT : Time to First Token, 첫 토큰이 나오기까지 걸리는 시간 - 메모리 사용량 : 32K 컨텍스트 기준, 128K 컨텍스트 기준 - 발열 : 지속 부하 5분 후 CPU/GPU 온도 프롬프트는 세 종류를 썼다. 짧은 코드 생성(아두이노 50줄 수준), 중간 분석(센서 데이터 해석), 긴 문서 요약(ROS2 문서 일부). 각각 10회 측정해서 평균을 냈다. 결과: 숫자부터 기기 메모리 대역폭 tok/s (mlx) tok/s (llama.cpp) TTFT (32K) Mac mini M1 16GB 68.25 GB/s 31 28 1.2초 Mac mini M4 24GB 120 GB/s 58 52 0.7초 MacBook Pro M4 Pro 24GB 273 GB/s 112 98 0.4초 숫자만 보면 M4 P...