빌드 가이드 — 화면 보고, LLM이 판단하고, 피코가 누른다
맥(두뇌)이 화면을 보고 LLM으로 무엇을 할지 판단한 뒤, 라즈베리파이 피코(가짜 키보드)가 사람다운 타이밍으로 실제 키를 눌러 윈도우를 조작한다. 캡처보드 없이 맥 한 대에서 개념을 익힌 뒤 분리하는 단계별 로드맵.
화면을 눈으로 보고, 머리로 판단하고, 손으로 키보드를 누르는 일 — 사람이 하는 반복작업을 그대로 기계에게 옮기는 파이프라인을 만든다. 목표는 맥 노트북(두뇌) 이 화면을 보고 LLM으로 판단한 뒤, 라즈베리파이 피코(가짜 키보드) 를 통해 윈도우 데스크톱(대상) 을 조작하는 것.
용도는 내가 만든 게임 테스트, 엑셀 등 내 화면의 반복작업 자동화다. (온라인 게임 안티치트 회피 목적이 아니다 — 내가 만든 게임·내 화면에 한정한다.)
이 문서는 이 프로젝트의 빌드 가이드 겸 첫 정리본이다. 처음엔 캡처보드 없이 맥 한 대 안에서 전 과정을 돌려 개념을 익히고, 익숙해지면 대상 PC를 물리적으로 분리한다.
0. 큰 그림

핵심 설계 원칙 두 가지.
1) "무엇을(What)"과 "어떻게(How/When)"를 분리한다. LLM은 무엇을 할지(의미 판단)만 결정한다. 언제, 얼마나 사람같은 타이밍으로 누를지는 짧은 코드/피코가 담당한다. LLM에게 밀리초 타이밍을 맡기지 않는다 — 느리고 부정확하다.
2) "두뇌"는 갈아끼우는 부품이다. 판단 모듈을 인터페이스로 분리해두면, API(클로드·GPT)든 로컬 모델(Ollama)이든 같은 자리에 꽂을 수 있다.
1. 준비물
| 항목 | 추천 | 가격대 | 비고 |
|---|---|---|---|
| 마이크로컨트롤러 | Raspberry Pi Pico 2 (또는 Pico) | 6~9천원 | USB에 꽂으면 키보드로 인식. CircuitPython 사용 |
| USB 케이블 | Micro-USB (데이터 지원) | 있으면 생략 | 충전 전용 케이블 주의 |
| 두뇌 PC | 맥 노트북 | 보유 | 화면 캡처 + LLM + 명령 전송 |
| 대상 PC | 윈도우 데스크톱 | 보유 | 자작게임/엑셀 실행, 피코가 여기 꽂힘 |
| (3단계 이후) 캡처보드 | HDMI→USB 캡처 동글 | 1~3만원 | 대상 화면을 맥으로 넘김 |
| (선택) HDMI 분배기 | 1 in 2 out | 1만원대 | 내 모니터 + 캡처보드 동시 |
로컬 모델을 쓸 경우 맥 사양 참고(2026 기준).
- moondream 2 (~2B, 1.5~2GB): 초경량. 단순 인식·필터용. 빽빽한 엑셀 화면엔 부족.
- Qwen2.5-VL 7B: 표·차트·스크린샷·OCR에 강함. 화면 이해 올라운더. 8GB+ 권장.
- Llama 3.2 Vision 11B: 8~16GB VRAM/통합메모리에서 최고 품질.
Apple Silicon + 통합메모리 16GB 이상이면 Qwen2.5-VL 7B가 현실적인 출발점.
2. 단계별 로드맵
각 단계는 앞 단계 위에 한 조각씩 얹는다. 막히면 다음으로 넘어가지 말 것.
1단계 — 피코를 가짜 키보드로 만들기 (하드웨어 "손")
목표: 피코를 맥에 꽂으면 자동으로 hello를 타이핑한다.
- circuitpython.org에서 Pico용
.uf2다운로드. - 피코의 BOOTSEL 버튼을 누른 채 USB로 맥에 연결 →
RPI-RP2드라이브가 뜸 →.uf2파일을 복사 → 자동 재부팅되며CIRCUITPY드라이브로 바뀜. - Adafruit HID 라이브러리 필요: circuitpython.org/libraries에서 라이브러리 번들 받아
adafruithid폴더를CIRCUITPY/lib/에 복사. CIRCUITPY/code.py를 아래로 작성(저장하면 피코가 자동 실행).
# code.py — 피코가 실행. 꽂자마자 "hello" 타이핑
import time
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
time.sleep(1.0) # 호스트가 장치 인식할 시간
kbd = Keyboard(usb_hid.devices)
layout = KeyboardLayoutUS(kbd)
layout.write("hello")
✅ 성공하면: "피코 = HID 키보드" 개념이 손에 잡힌다.
2단계 — 명령 채널 만들기 (두뇌 ↔ 손 분리)
목표: 맥에서 시리얼로 "RIGHT" 같은 명령을 보내면 피코가 해당 키를 누른다. 여기서 판단(맥) 과 실행(피코) 이 분리된다. 사람다운 타이밍(지터)도 이 피코 쪽에 넣는다.
피코 쪽 (code.py) — 시리얼로 한 줄씩 명령을 읽어 키 입력 + 사람같은 지연.
# code.py — 명령 수신 → 키 입력 + humanize
import time, random, sys, supervisor
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
kbd = Keyboard(usb_hid.devices)
KEYMAP = {
"RIGHT": Keycode.RIGHT_ARROW,
"LEFT": Keycode.LEFT_ARROW,
"SPACE": Keycode.SPACE,
"ENTER": Keycode.ENTER,
}
def human_delay(mean=0.09, sigma=0.02):
# 정규분포로 사람같은 간격 (음수 방지)
return max(0.02, random.gauss(mean, sigma))
buf = ""
while True:
if supervisor.runtime.serial_bytes_available:
ch = sys.stdin.read(1)
if ch in ("\n", "\r"):
cmd = buf.strip().upper()
buf = ""
if cmd in KEYMAP:
time.sleep(human_delay()) # 누르기 전 지연
kbd.press(KEYMAP[cmd])
time.sleep(human_delay(0.05, 0.01)) # 누름 유지 시간
kbd.release_all()
else:
buf += ch
맥 쪽 (테스트용 send.py) — 피코로 명령 전송.
# send.py (맥) — pip install pyserial
import serial, time, glob
# 맥에서 피코 포트 찾기 (보통 /dev/tty.usbmodemXXXX)
port = sorted(glob.glob("/dev/tty.usbmodem*"))[0]
pico = serial.Serial(port, 115200, timeout=1)
time.sleep(2)
for cmd in ["RIGHT", "RIGHT", "SPACE", "LEFT"]:
pico.write((cmd + "\n").encode())
time.sleep(0.5)
⚠️ CircuitPython은 기본적으로 시리얼(REPL)로 명령을 받는다. 안정적으로 하려면
boot.py에서 USB CDC data 채널을 따로 켜고 그 포트로 통신하는 방식이 더 깔끔하다(심화). 처음엔 위 방식으로 개념부터.
✅ 성공하면: "두뇌가 명령 → 손이 실행 + 사람다운 타이밍" 완성. 여기까지가 하드웨어 매크로의 뼈대.
3단계 — 화면 보기 (두뇌의 "눈")
목표: 맥이 대상 화면을 캡처해서 이미지로 확보한다.
(a) 먼저 같은 맥에서 연습 — mss로 스크린샷.
# capture.py (맥) — pip install mss pillow
import mss, mss.tools
def grab(region=None, path="frame.png"):
with mss.mss() as sct:
mon = region or sct.monitors[1] # region = {"top","left","width","height"}
img = sct.grab(mon)
mss.tools.to_png(img.rgba, img.size, output=path)
return path
(b) 그다음 진짜 분리 구조 — 캡처보드 연결.
- 윈도우 데스크톱 HDMI → (HDMI 분배기) → ① 내 모니터 + ② 캡처보드
- 캡처보드 USB → 맥에 연결 → 맥에서는 웹캠처럼 인식됨
opencv-python으로 그 영상 프레임을 읽으면 됨.
# capture_card.py (맥) — pip install opencv-python
import cv2
cap = cv2.VideoCapture(0) # 캡처보드 인덱스 (여러 개면 0,1,2 시도)
ok, frame = cap.read()
if ok:
cv2.imwrite("frame.png", frame)
cap.release()
✅ 성공하면: 대상 화면이 맥으로 들어온다. "보는 쪽" 완성. 이 시점에서 대상 PC(윈도우)엔 캡처 소프트웨어가 전혀 없다 — 영상은 물리적으로(HDMI) 넘어왔기 때문.
4단계 — LLM 끼워넣기 (두뇌의 "판단")
목표: 캡처한 화면 + 목표 지시를 LLM에 주고, "무엇을 할지"를 받아 2단계 명령 채널로 피코에 전달한다.
두뇌를 교체 가능하게 — 공통 인터페이스.
# brain.py (맥)
from abc import ABC, abstractmethod
class Brain(ABC):
@abstractmethod
def decide(self, image_path: str, goal: str, allowed_actions: list[str]) -> str:
"""이미지+목표를 보고 allowed_actions 중 하나(예: 'RIGHT')를 반환"""
...
(A) API 두뇌 — 시작용 추천 (정확도 높고 설정 쉬움).
# brain_api.py (맥) — pip install anthropic
import base64, os
from anthropic import Anthropic
from brain import Brain
class ApiBrain(Brain):
def __init__(self, model="claude-sonnet-5"):
self.client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
self.model = model
def decide(self, image_path, goal, allowed_actions):
img = base64.standard_b64encode(open(image_path, "rb").read()).decode()
msg = self.client.messages.create(
model=self.model, max_tokens=20,
messages=[{"role": "user", "content": [
{"type": "image", "source": {"type": "base64",
"media_type": "image/png", "data": img}},
{"type": "text", "text":
f"목표: {goal}\n"
f"가능한 동작: {allowed_actions}\n"
f"다음 한 동작만 정확히 하나 출력(다른 말 금지):"}
]}])
return msg.content[0].text.strip().upper()
claude -p같은 CLI 헤드리스 호출로도 비슷하게 만들 수 있지만, 스크린샷(이미지) 입력은 위처럼 SDK로 보내는 게 깔끔하다.
(B) 로컬 두뇌 — 나중에 교체 (공짜·오프라인).
# brain_local.py (맥) — Ollama 설치 후: ollama pull qwen2.5-vl
# pip install ollama
import ollama
from brain import Brain
class LocalBrain(Brain):
def __init__(self, model="qwen2.5-vl"):
self.model = model
def decide(self, image_path, goal, allowed_actions):
res = ollama.chat(model=self.model, messages=[{
"role": "user",
"content": f"목표: {goal}\n가능한 동작: {allowed_actions}\n"
f"다음 한 동작만 하나 출력:",
"images": [image_path],
}])
return res["message"]["content"].strip().upper()
전체 루프 — 눈→판단→손.
# main.py (맥)
import time, serial, glob
from capture import grab
from brain_api import ApiBrain # 나중에 LocalBrain으로 한 줄만 교체
brain = ApiBrain()
pico = serial.Serial(sorted(glob.glob("/dev/tty.usbmodem*"))[0], 115200, timeout=1)
time.sleep(2)
GOAL = "장애물을 피해 캐릭터를 오른쪽 끝까지 이동시켜라"
ACTIONS = ["RIGHT", "LEFT", "SPACE"]
while True:
frame = grab() # ① 눈
action = brain.decide(frame, GOAL, ACTIONS) # ② 판단(무엇을)
if action in ACTIONS:
pico.write((action + "\n").encode()) # ④ 손(피코가 타이밍 입혀 실행)
time.sleep(0.3) # LLM 지연 고려
✅ 성공하면: 화면 보고 → 생각하고 → 물리 키보드로 실행하는 범용 로봇 완성.
3. 범용화 — 엔진 하나 + 작업 프로필 여러 개
같은 파이프라인으로 자작게임·엑셀 등 여러 대상을 다루려면, 바뀌는 건 "목표 지시"뿐이다. 프로필로 분리한다.
# profiles.py
PROFILES = {
"game": {
"goal": "장애물을 피해 오른쪽 끝까지 이동",
"actions": ["RIGHT", "LEFT", "SPACE"],
},
"excel": {
"goal": "빈 합계 셀을 찾아 SUM 수식을 입력",
"actions": ["RIGHT", "DOWN", "ENTER", "TYPE_SUM"],
},
}
실행 시 --profile game / --profile excel만 바꿔 같은 엔진을 재사용한다. LLM에 allowed_actions(행동 목록)를 명시해주면 엉뚱한 키를 상상하지 않아 훨씬 안정적이다.
엑셀 팁: 범용성 학습엔 "화면 보고 조작"이 좋지만, 실제 업무로 엑셀을 다룰 땐
openpyxl/pandas로.xlsx파일을 직접 읽고 쓰는 게 백배 빠르고 정확하다. 필요하면 이 지름길을 프로필에 얹어라.
4. 현실 체크 & 한계
- LLM은 느리다. 비전 호출 1회에 API 1~3초, 로컬은 하드웨어에 따라 더 걸릴 수 있다. 턴제·천천히 도는 상황(퍼즐게임, 전략 시뮬, 엑셀)엔 완벽하지만, FPS 같은 반사신경 실시간엔 부적합. 실시간이 필요하면 "빠른 반응은 단순 규칙, 큰 판단만 LLM" 식으로 계층을 또 나눈다.
- 로컬 vs API 품질차. 빽빽한 화면(엑셀 표, 작은 글자) 읽기는 API 프론티어 모델이 로컬 소형 모델보다 확실히 낫다. 로컬은 프라이버시·비용이 강점.
- 정규분포 humanize는 피코/타이밍 층에 둔다. LLM은 "무엇"만. 이 분리를 지키면 구조가 단순하고 튼튼하다.
- 분리 구조의 의미. 캡처보드로 화면을 물리적으로 넘기면 대상 PC엔 캡처 소프트웨어 흔적이 없다. 이 기술 자체는 하드웨어 테스트·자동화 등에 정당하게 쓰인다. 단, 온라인 게임에 적용하면 이용약관 위반이라 밴 사유 — 내가 만든 게임·내 화면 자동화에 한정할 것.
5. 추천 진행 순서 요약
- 피코 사서 1단계(hello 타이핑) — 하루면 됨.
- 2단계 명령 채널 + humanize — 여기까지가 하드웨어 매크로 뼈대.
- 3단계 (a) 맥 자기 화면 캡처로 눈 붙이기.
- 4단계 API 두뇌로 전체 루프 완성 (제일 쉬운 성공 경험).
- 익숙해지면 3단계 (b) 캡처보드 + 윈도우 데스크톱으로 진짜 분리.
- 마지막에 두뇌를 로컬(Ollama)로 교체 —
main.py한 줄만 바꾸면 됨.
참고 링크
- CircuitPython (Pico): https://circuitpython.org/board/raspberry_pi_pico/
- Adafruit HID 키보드/마우스: https://learn.adafruit.com/circuitpython-essentials/circuitpython-hid-keyboard-and-mouse
- CircuitPython 라이브러리 번들: https://circuitpython.org/libraries
- Pico를 HID로 쓰기(예제): https://blog.thestaticturtle.fr/getting-started-with-hid-and-the-pi-pico/
- Ollama 비전 모델: https://mljourney.com/how-to-use-ollama-vision-models-for-local-image-analysis/