Gemini

로봇, Physical AI — Gemini Robotics-ER 1.6 공간추론·기기판독 개발 가이드

cell-devlog 2026. 6. 12. 11:01
반응형

 


한줄요약: Gemini Robotics-ER 1.6은 공간추론·오브젝트 포인팅·멀티뷰 성공감지·기기판독(게이지·사이트글라스)을 지원하는 구글 DeepMind의 로봇 전용 임베디드 추론 모델로, Gemini API를 통해 개발자에게 공개됐습니다.


ER 모델이 뭔가요? — 전략가 vs 실행가

구글 DeepMind의 로봇 AI 스택은 두 모델로 나뉩니다. 이 구분을 먼저 이해해야 ER 1.6이 어디에 쓰이는지 명확해집니다.

Gemini Robotics 1.5 (VLA 모델)
  → 비전-언어-액션 모델
  → 카메라 입력을 받아 모터 명령을 직접 생성
  → "실행가" — 팔을 어디로 움직일지 결정

Gemini Robotics-ER 1.6 (ER 모델)
  → Embodied Reasoning 모델
  → 공간 이해·계획·성공 판단을 담당
  → "전략가" — 무엇을 해야 할지, 잘 됐는지 판단

ER 1.6은 VLA에 명령을 내리는 상위 레이어입니다. Google Search, 사용자 정의 함수, VLA 모델을 네이티브 툴로 호출할 수 있는 에이전틱 구조로 설계됐습니다.


ER 1.6에서 뭐가 달라졌나요?

1.5 대비 개선된 수치부터 보겠습니다.

능력                       ER 1.5   ER 1.6   비고
──────────────────────────────────────────────────────
포인팅·카운팅 정확도        61%      80%      +19%p
멀티뷰 성공감지             -        84%      신규 측정
기기판독 (기본)             23%      86%      +63%p
기기판독 (Agentic Vision)   미지원   93%      ER 1.5 불가
안전 지시 준수              기준     +6%p     텍스트 위험 인식
부상 위험 영상 인식         기준     +10%p    Gemini 3.0 Flash 대비

세 가지 신기능이 핵심입니다.

1. 기기판독 (Instrument Reading) — 압력계, 온도계, 사이트글라스를 수치로 읽는 기능. Boston Dynamics와의 협업에서 발견된 실용 케이스입니다. Agentic Vision(시각 추론 + 코드 실행 조합)을 켜면 93%까지 올라갑니다.

2. 멀티뷰 성공감지 — 오버헤드 카메라와 손목 카메라 등 여러 앵글을 동시에 합성해서 "작업이 정말 완료됐는가"를 판단합니다. 가림막(occlusion) 상황에서도 교차 확인이 가능합니다.

3. 향상된 네거티브 포인팅 — 없는 물체를 가리키지 않습니다. "휠바로를 가리켜"라고 했을 때 없으면 없다고 답합니다. 로봇이 엉뚱한 물체를 집는 오작동을 방지하는 핵심 능력입니다.


설치 및 기본 오브젝트 포인팅

pip install google-genai
export GEMINI_API_KEY="your-api-key"

가장 기본적인 태스크입니다. 이미지를 넘기면 씬 안의 오브젝트 위치를 정규화된 2D 좌표로 반환합니다.

from google import genai
from google.genai import types
import json

client = genai.Client()

# 포인팅 프롬프트 — [y, x] 형식, 0~1000 정규화
POINTING_PROMPT = """
이미지에서 최대 10개의 물체를 가리켜주세요.
반드시 아래 JSON 형식으로만 답하세요:
[{"point": [y, x], "label": "물체이름"}, ...]
좌표는 0~1000으로 정규화된 [y, x] 형식입니다.
이미지에 없는 물체는 절대 포함하지 마세요.
"""

with open("workspace.png", "rb") as f:
    image_bytes = f.read()

response = client.models.generate_content(
    model="gemini-robotics-er-1.6-preview",
    contents=[
        types.Part.from_bytes(data=image_bytes, mime_type="image/png"),
        POINTING_PROMPT
    ],
    config=types.GenerateContentConfig(
        temperature=1.0,
        thinking_config=types.ThinkingConfig(thinking_budget=0)
        # 단순 포인팅은 thinking 불필요 → 레이턴시 절약
    )
)

objects = json.loads(response.text)
for obj in objects:
    y, x = obj["point"]
    print(f"{obj['label']}: ({x/10:.1f}%, {y/10:.1f}%)")

출력 예시:

[
  {"point": [376, 508], "label": "압력계"},
  {"point": [287, 609], "label": "밸브 핸들"},
  {"point": [223, 303], "label": "파이프 연결부"},
  {"point": [435, 172], "label": "온도 센서"}
]

공간 추론 — 관계형 논리와 조건부 포인팅

단순히 "뭐가 있냐"가 아니라 "어떤 조건을 만족하는 물체"를 찾는 쿼리입니다.

# 파란 컵 안에 들어갈 수 있는 물체만 가리키기
SPATIAL_PROMPT = """
파란 컵 안에 들어갈 수 있을 만큼 작은 물체들만 모두 가리켜주세요.
JSON 형식: [{"point": [y, x], "label": "이름"}, ...]
"""

# X에서 Y로 이동 궤적 계획
TRAJECTORY_PROMPT = """
빨간 블록에서 초록 바구니까지의 이동 경로를 계획하세요.
경유 웨이포인트를 3~5개 JSON 포인트로 반환하세요.
JSON 형식: [{"point": [y, x], "label": "waypoint_N"}, ...]
"""

# 카운팅 — thinking_budget을 높이면 정확도 향상
COUNT_PROMPT = """
작업대 위에 있는 렌치를 모두 카운트하고 각각 가리켜주세요.
JSON 형식: {"count": N, "points": [{"point": [y, x], "label": "렌치_N"}, ...]}
"""

response = client.models.generate_content(
    model="gemini-robotics-er-1.6-preview",
    contents=[
        types.Part.from_bytes(data=image_bytes, mime_type="image/png"),
        COUNT_PROMPT
    ],
    config=types.GenerateContentConfig(
        temperature=1.0,
        thinking_config=types.ThinkingConfig(thinking_budget=1024)
        # 카운팅·무게 추정 등 복잡한 추론은 thinking_budget을 높임
    )
)

기기판독 — Agentic Vision으로 압력계 읽기

ER 1.6의 하이라이트 기능입니다. 모델이 게이지 이미지를 줌인하고, 눈금 간격과 바늘 위치를 코드로 계산해서 정확한 수치를 반환합니다. Agentic Vision은 코드 실행 툴을 함께 켜야 합니다.

from google import genai
from google.genai import types

client = genai.Client()

INSTRUMENT_PROMPT = """
이미지의 압력계를 읽어주세요.

다음 단계로 진행하세요:
1. 게이지 전체를 식별하고 눈금 범위를 파악하세요
2. 바늘이 가리키는 위치를 정밀하게 특정하세요
3. 눈금 간격을 코드로 계산해서 정확한 수치를 산출하세요
4. 단위(PSI, bar, kPa 등)를 포함해서 최종값을 반환하세요

응답 형식:
{
  "value": 숫자,
  "unit": "단위",
  "range": {"min": 최솟값, "max": 최댓값},
  "confidence": "high/medium/low",
  "notes": "이상 여부 또는 특이사항"
}
"""

with open("pressure_gauge.jpg", "rb") as f:
    gauge_image = f.read()

response = client.models.generate_content(
    model="gemini-robotics-er-1.6-preview",
    contents=[
        types.Part.from_bytes(data=gauge_image, mime_type="image/jpeg"),
        INSTRUMENT_PROMPT
    ],
    config=types.GenerateContentConfig(
        temperature=1.0,
        tools=[types.Tool(code_execution=types.ToolCodeExecution())],
        # Agentic Vision = code_execution 툴 활성화가 핵심
        thinking_config=types.ThinkingConfig(thinking_budget=2048)
    )
)

import json
result = json.loads(response.text)
print(f"측정값: {result['value']} {result['unit']}")
print(f"신뢰도: {result['confidence']}")
if result.get("notes"):
    print(f"비고: {result['notes']}")

벤치마크 재확인:

기기판독 성공률
  ER 1.5 기본:          23%
  Gemini 3.0 Flash:     67%
  ER 1.6 기본:          86%
  ER 1.6 + Agentic Vision: 93%  ← code_execution 툴 필수

멀티뷰 성공감지 — 여러 카메라로 작업 완료 확인

로봇 손이 물체를 가린 상황에서도 오버헤드 카메라와 교차 확인해서 "집었는가"를 판단합니다.

# 여러 앵글 이미지를 동시에 전달
with open("overhead_view.png", "rb") as f:
    overhead = f.read()
with open("wrist_camera.png", "rb") as f:
    wrist = f.read()

SUCCESS_PROMPT = """
두 개의 카메라 뷰를 분석해주세요.
첫 번째는 오버헤드 뷰, 두 번째는 손목 카메라 뷰입니다.

태스크: 로봇이 빨간 블록을 파란 바구니에 넣는 작업

이 작업이 성공적으로 완료됐는지 판단하세요.
JSON 형식:
{
  "success": true/false,
  "confidence": 0.0~1.0,
  "evidence": ["근거1", "근거2"],
  "next_action": "다음 수행할 액션 또는 null"
}
"""

response = client.models.generate_content(
    model="gemini-robotics-er-1.6-preview",
    contents=[
        types.Part.from_bytes(data=overhead, mime_type="image/png"),
        types.Part.from_bytes(data=wrist, mime_type="image/png"),
        SUCCESS_PROMPT
    ],
    config=types.GenerateContentConfig(
        temperature=1.0,
        thinking_config=types.ThinkingConfig(thinking_budget=1024)
    )
)

result = json.loads(response.text)
print(f"성공 여부: {'완료' if result['success'] else '미완료'}")
print(f"신뢰도: {result['confidence']*100:.0f}%")
for e in result["evidence"]:
    print(f"  → {e}")

롱호라이즌 태스크 오케스트레이션

복잡한 자연어 명령을 받아 서브태스크로 분해하고, 로봇 함수를 직접 호출하는 에이전틱 패턴입니다.

# 로봇 컨트롤러 함수 정의
def move_to_point(y: float, x: float) -> str:
    """로봇 팔을 지정 좌표로 이동"""
    # 실제 로봇 SDK 호출
    return f"이동 완료: ({x:.1f}, {y:.1f})"

def pick_object(label: str) -> str:
    """지정 오브젝트 집기"""
    return f"{label} 집기 완료"

def place_at(container: str) -> str:
    """지정 컨테이너에 내려놓기"""
    return f"{container}에 놓기 완료"

# 함수 툴로 등록
tools = [
    types.Tool(function_declarations=[
        types.FunctionDeclaration(
            name="move_to_point",
            description="로봇 팔을 이미지의 특정 좌표로 이동",
            parameters=types.Schema(
                type=types.Type.OBJECT,
                properties={
                    "y": types.Schema(type=types.Type.NUMBER, description="y 좌표 (0-1000)"),
                    "x": types.Schema(type=types.Type.NUMBER, description="x 좌표 (0-1000)")
                }
            )
        ),
        types.FunctionDeclaration(
            name="pick_object",
            description="현재 위치의 오브젝트를 집기",
            parameters=types.Schema(
                type=types.Type.OBJECT,
                properties={"label": types.Schema(type=types.Type.STRING)}
            )
        ),
    ])
]

response = client.models.generate_content(
    model="gemini-robotics-er-1.6-preview",
    contents=[
        types.Part.from_bytes(data=image_bytes, mime_type="image/png"),
        "빨간 블록을 찾아서 파란 바구니에 넣어주세요."
    ],
    config=types.GenerateContentConfig(
        tools=tools,
        temperature=1.0,
    )
)

# 함수 호출 처리
for part in response.candidates[0].content.parts:
    if part.function_call:
        fn = part.function_call
        print(f"호출: {fn.name}({dict(fn.args)})")

thinking_budget 사용 가이드

태스크 유형                      권장 thinking_budget
───────────────────────────────────────────────────────
단순 포인팅·오브젝트 감지         0 (비활성, 레이턴시 최소화)
공간 관계 추론                   512~1024
오브젝트 카운팅·무게 추정         1024~2048
기기판독 (Agentic Vision)        2048
복잡한 롱호라이즌 플래닝          2048~4096

같은 프롬프트를 여러 번 실행하고 결과를 평균 내는 컨센서스 방식이 정밀도 중요 태스크에서 효과적입니다. ER 1.6에서 공식 권장하는 패턴입니다.

import numpy as np

def consensus_reading(image_bytes, prompt, n=3):
    """n회 실행 후 좌표 평균으로 정밀도 향상"""
    results = []
    for _ in range(n):
        response = client.models.generate_content(
            model="gemini-robotics-er-1.6-preview",
            contents=[
                types.Part.from_bytes(data=image_bytes, mime_type="image/png"),
                prompt
            ],
            config=types.GenerateContentConfig(
                temperature=1.0,
                thinking_config=types.ThinkingConfig(thinking_budget=1024)
            )
        )
        results.append(json.loads(response.text))

    # 좌표 평균 계산
    avg_y = np.mean([r["point"][0] for r in results])
    avg_x = np.mean([r["point"][1] for r in results])
    return {"point": [avg_y, avg_x]}

ER 1.5에서 마이그레이션

한 줄입니다.

# 변경 전
model="gemini-robotics-er-1.5-preview"

# 변경 후
model="gemini-robotics-er-1.6-preview"

ER 1.5는 2026년 4월 30일에 종료됐습니다. 아직 1.5를 쓰고 계시다면 이미 API가 중단된 상태입니다.


현재 제약사항

→ Preview 상태 — GA 전 API 변경 가능
→ 고해상도 입력·높은 thinking_budget은 레이턴시 증가
→ 할루시네이션 — 모호한 프롬프트나 분포 외 입력에서 오답 가능
→ 실제 물리 로봇 안전은 개발자 책임 (모델 오류로 인한 물리적 손상 위험)
→ 직접 모터 제어 불가 — VLA 모델 또는 커스텀 컨트롤러와 연동 필요

✅ 기기판독은 ER 1.6에서만 가능한 신기능입니다. code_execution 툴을 함께 켜야 Agentic Vision이 활성화되고 93% 정확도를 냅니다. 빼먹으면 86%로 떨어집니다.

❌ 로봇 팔을 직접 움직이는 코드가 아닙니다. ER 1.6은 "어디를, 무엇을, 어떻게"를 판단하는 뇌 역할이고, 실제 모터 제어는 VLA 모델이나 기존 로봇 컨트롤러에 전달해야 합니다.

 

반응형