아들과 함께 만들어보는 인공지능(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이 카메라 프레임을 보고 "소파가 보임, 왼쪽으로 우회"라는 판단을 내렸다. 왼쪽으로 돌았다.
그다음이 문제였다. 왼쪽으로 돌고 나서 계속 왼쪽으로 돌았다. 원을 그리기 시작했다.
로그를 봤다.
[LLM] 이미지: 벽이 가까워 보임, 오른쪽에 통로 있음
[CMD] right, speed:120
[LLM] 이미지: 왼쪽에 공간 있음, 왼쪽으로 이동 추천
[CMD] left, speed:120
[LLM] 이미지: 오른쪽에 공간 있음
[CMD] right, speed:120
LLM이 왼쪽-오른쪽을 번갈아 가며 지시하고 있었다. 방향을 못 잡고 진동하는 상태였다.
두 번째 시도: 프롬프트 수정
이전 명령 이력을 LLM에게 같이 줬다. 최근 3개 명령을 컨텍스트에 포함시켰다.
context = {
"image": image_b64,
"sensor": sensor_data,
"instruction": user_instruction,
"history": last_3_commands # 추가
}
두 번째 시도. 로봇이 출발했다. 이번엔 왼쪽-오른쪽 진동이 줄었다. 소파를 피하고 주방 방향으로 가기 시작했다.
주방 입구에서 멈췄다.
냉장고가 앞에 보인다고 LLM이 판단했다. "장애물 감지, 멈춤"을 냈다. 냉장고는 장애물이 맞는데, 주방 안으로 들어가야 하는 상황에서 냉장고를 보자마자 멈춘 거다. 목표 위치에 대한 개념이 없었다.
멈췄다. 그 자리에서 계속 멈춰 있었다.
아들이 "왜 저기서 멈춰?"라고 했다. 냉장고 사진을 보고 장애물이라고 생각한 거라고 설명했다. "물컵을 찾으러 가는 거라고 알려줘야 하는 거야?" 맞다.
세 번째 시도: 목표 개념 추가
시스템 프롬프트에 목표 개념을 추가했다.
## 로봇 행동 지침
- 사용자가 목표를 주면 그 목표를 향해 계속 이동한다
- 장애물은 우회하되, 목표 방향으로 계속 진행한다
- 냉장고, 가전제품은 장애물이 아니라 환경 요소로 인식한다
- "도착"은 사용자가 말한 공간에 진입했을 때로 판단한다
세 번째 시도. 로봇이 출발했다. 소파를 피했다. 주방 입구에서 멈추지 않고 들어갔다.
주방 안에서 두리번거렸다(로봇이 천천히 회전했다). 카운터 위를 카메라로 훑었다. 물컵이 카운터 한쪽에 있었다. LLM이 "카운터 위에 투명한 컵 발견, 접근 중"이라는 텍스트를 냈다.
로봇이 카운터 쪽으로 접근했다. 카운터 다리에 20cm 거리에서 멈췄다. 안전 거리 제약이 걸린 거다.
가져오지는 못했다. 팔이 없으니까.
아들이 "물 가져왔어?"라고 기대하면서 봤다가, 카운터 앞에서 멈춘 걸 보고 "못 가져왔어?" 했다. "팔이 없어서." "팔도 달자." 나도 그러고 싶다.
부분 성공으로 기록한 이유
세 번째 시도를 "부분 성공"으로 기록한다.
가져오지는 못했다. 근데 로봇이 한 것들:
- 거실을 가로질러 주방까지 이동했다 (자율적으로)
- 소파를 피했다
- 주방 입구에서 멈추지 않고 들어갔다
- 카운터 위의 물컵을 인식했다
- 물컵 방향으로 접근했다
LLM이 카메라 이미지를 보고 환경을 인식하고 이동 명령을 내린 것이다. 하드코딩 없이. 사전에 주방 지도를 넣어준 것도 아니고, 물컵 위치를 알려준 것도 아니다. 그냥 "주방 가서 물컵 찾아줘"라는 명령 하나와 카메라 프레임뿐이었다.
이게 됐다는 게 중요하다.
아들의 반응
아들이 그날 저녁에 이걸 직접 해봤다. "나 혼자 해볼게."
입력한 명령: "거실에서 TV 찾아봐"
로봇이 거실을 돌아다니다가 TV 앞에서 멈췄다. TV를 향한 채로 멈춰 있었다. LLM 응답 로그에 "검은 직사각형 화면, TV로 판단, 도착"이 찍혔다.
아들이 "찾았다!" 했다.
다음 명령: "TV 옆에 뭐가 있어?"
로봇이 TV 왼쪽을 스캔했다. LLM 응답: "TV 왼쪽에 스피커 1개, 책 여러 권 있는 책꽂이 보임"
아들이 그 응답 텍스트를 읽더니 조용히 있었다. 한참 후에 "로봇이 진짜 보고 있는 거네"라고 했다.
맞다. 로봇이 카메라로 보고 있는 거다. 그걸 아들이 느낀 순간이었다.
아직 안 되는 것들
꽤 됐는데 안 되는 것도 많다.
조명이 어두우면 인식이 확 떨어진다. 저녁에 조명을 끄고 테스트하면 LLM이 "어두워서 식별 어려움"을 내놓는다.
같은 명령을 여러 번 반복해도 매번 다르게 행동한다. LLM이 temperature=0.1이어도 이미지 인식에서 비결정적인 부분이 생긴다. 같은 거실 같은 자리에서 어떤 날은 소파를 왼쪽으로 피하고 어떤 날은 오른쪽으로 피한다.
속도 조절이 아직 거칠다. LLM이 내리는 speed 값이 100이나 150으로 양분되고 중간이 없다. 거리에 따라 부드럽게 감속하는 로직을 LLM에게 직접 맡기기보다 별도 모터 제어 레이어에서 처리하는 게 나을 것 같다.
이것들은 다음 편에서 다룰 내용이기도 하고, 프로젝트가 계속되는 이유이기도 하다.
댓글
댓글 쓰기