화면→판단→피코 — 눈·두뇌·손을 나눈 범용 데스크톱 자동화 2026.07.25

빌드 가이드 — 화면 보고, 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 out1만원대내 모니터 + 캡처보드 동시

로컬 모델을 쓸 경우 맥 사양 참고(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를 타이핑한다.

  1. circuitpython.org에서 Pico용 .uf2 다운로드.
  2. 피코의 BOOTSEL 버튼을 누른 채 USB로 맥에 연결 → RPI-RP2 드라이브가 뜸 → .uf2 파일을 복사 → 자동 재부팅되며 CIRCUITPY 드라이브로 바뀜.
  3. Adafruit HID 라이브러리 필요: circuitpython.org/libraries에서 라이브러리 번들 받아 adafruithid 폴더를 CIRCUITPY/lib/에 복사.
  4. 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. 피코 사서 1단계(hello 타이핑) — 하루면 됨.
  2. 2단계 명령 채널 + humanize — 여기까지가 하드웨어 매크로 뼈대.
  3. 3단계 (a) 맥 자기 화면 캡처로 눈 붙이기.
  4. 4단계 API 두뇌로 전체 루프 완성 (제일 쉬운 성공 경험).
  5. 익숙해지면 3단계 (b) 캡처보드 + 윈도우 데스크톱으로 진짜 분리.
  6. 마지막에 두뇌를 로컬(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/

화면→판단→피코 — 눈·두뇌·손을 나눈 범용 데스크톱 자동화 프로젝트로