02참가자 기본예상 약 25분
MuJoCo에서 집고 옮기고 쌓기
실제 접촉으로 생성한 기록을 재생하고 성공·실패를 물리 상태로 확인합니다.
이 장의 예상 시간은 약 25분입니다. 서버·화면 확인, 단일 물체·쌓기, 실패 실험과 근거 확인을 진행하며 실습 반복과 질문에 맞춰 시간을 조정합니다. 아직 Bedrock은 호출하지 않습니다. 규칙 기반 계획과 실제 물리 계산을 먼저 확인합니다.
1. 두 프로세스 시작
EC2 터미널 A와 B에서 각각 01장의 환경 변수 블록을 실행합니다.
프로세스는 전경에서 실행해 둡니다. 멈출 때는 해당 터미널에서 Ctrl+C를 사용합니다.
터미널 A에서 참가자 전용 MuJoCo 서비스를 시작합니다.
cd "$ROBOT_LAB"
MUJOCO_GL=disable .venv/bin/python -m uvicorn simulation.service:app \
--host 127.0.0.1 --port "$ROBOT_SIM_PORT"
터미널 B에서 API와 빌드된 화면을 시작합니다.
포트 미리보기용 HTTPS 주소를 쓰는 경우 ROBOT_BROWSER_ORIGIN을 먼저 설정합니다.
cd "$ROBOT_LAB"
NODE_ENV=development HOST=127.0.0.1 PORT="$ROBOT_API_PORT" \
PUBLIC_ORIGIN="$ROBOT_BROWSER_ORIGIN" \
SIMULATION_URL="http://127.0.0.1:$ROBOT_SIM_PORT" \
LOCAL_RUNS_DIR=.local/runs MAX_ACTIVE_RUNS=1 \
MODEL_ID="" AGENTCORE_RUNTIME_ARN="" RUNS_TABLE="" REPLAY_BUCKET="" \
ORIGIN_GATE="" SESSION_SECRET="" \
npm run server
기동 로그의 planner는 rules, storage는 local입니다.
클라우드 관련 변수를 비워 이 프로세스가 로컬 실습 설정을 사용하도록 지정했습니다.
운영 파일을 복사하지 않고 참가자 프로세스의 환경 변수로 설정합니다.
터미널 C에서도 01장의 환경 변수를 불러온 뒤 연결을 확인합니다.
curl --fail --silent --show-error "http://127.0.0.1:$ROBOT_SIM_PORT/health"
curl --fail --silent --show-error "$ROBOT_API_URL/healthz"
curl --fail --silent --show-error "$ROBOT_API_URL/readyz"
각각 engine: "MuJoCo", status: "ok", simulation: "ready"를 확인합니다.
healthz는 Node 프로세스의 생존 확인이며, 물리 엔진 연결은 readyz로 따로 검사합니다.
둘 다 성공해도 아직 새로운 물리 작업의 성공을 증명한 것은 아닙니다.
2. 기록 재생과 새 실행 구분
브라우저에서 참가자 API의 전달된 주소를 엽니다. 시연 시작으로 기존 기록을 재생하고, 일시정지·시간 탐색·상단 시점을 확인합니다. 이 기록의 배치 번호와 다음에 생성하는 배치 번호를 구분합니다.
이어서 AI에게 작업 지시를 열고 다음을 입력합니다.
빨간 상자만 오른쪽 트레이로 옮겨줘
지금 표시되어야 하는 제공자는 규칙 기반 · AI 모델 미연결입니다.
계획에 빨간 상자 한 개만 있는지 확인한 뒤 초기 배치 번호를 445로 바꾸고 실행합니다.
완료 후 결과 재생하기, 검증 결과 보기를 차례로 확인합니다.
기대 결과는 빨간 상자 1 / 1개 배치입니다.
파란·주황 상자는 출발 영역에 남아 있어야 합니다.
작업이 끝나기 전에 다음 실행을 누르지 않습니다.
API는 한 세션의 실행과 시뮬레이터의 동시 실행을 제한합니다.
3. CLI로 물리 기록 생성
다음은 같은 물리 엔진을 웹 계획 없이 직접 호출하는 실험입니다.
터미널 C의 참가자 폴더에서 실행합니다.
파일 경로는 기존 공개 시연 파일과 분리된 .local/evidence/를 사용합니다.
cd "$ROBOT_LAB"
mkdir -p .local/evidence
.venv/bin/python -m simulation.cli \
--scenario transfer --seed 42 \
--actions '[{"objectId":"red","target":"tray"}]' \
--output .local/evidence/red-only-42.json
.venv/bin/python -m simulation.cli \
--scenario stack --seed 2026 \
--actions '[{"objectId":"blue","target":"tower"},{"objectId":"red","target":"tower"},{"objectId":"orange","target":"tower"}]' \
--output .local/evidence/blue-first-2026.json
두 명령 모두 종료 코드가 0이어야 합니다.
첫 기록은 placed: 1, total: 1, 둘째 기록은 placed: 3, total: 3입니다.
status: "succeeded", metrics.success: true, 모든 checks[].passed: true를 함께 확인합니다.
generationSeconds는 계산에 걸린 실제 시간이며 duration은 기록 속 시뮬레이션 시간입니다.
두 값이 같을 필요는 없습니다.
아래 검사는 결과 라벨과 별개로 기록된 위치도 확인합니다.
.venv/bin/python - <<'PY'
import json
import math
from pathlib import Path
root = Path(".local/evidence")
red = json.loads((root / "red-only-42.json").read_text())
tower = json.loads((root / "blue-first-2026.json").read_text())
for replay, count in ((red, 1), (tower, 3)):
assert replay["engine"] == "MuJoCo"
assert replay["status"] == "succeeded", replay["checks"]
assert replay["metrics"]["success"] is True
assert replay["metrics"]["placed"] == replay["metrics"]["total"] == count
assert all(check["passed"] for check in replay["checks"]), replay["checks"]
assert replay["provenance"]["learning"] == "none"
assert replay["metrics"]["physicsSteps"] > 1000
print(replay["scenario"], replay["seed"], replay["metrics"])
first, last = red["frames"][0]["bodies"], red["frames"][-1]["bodies"]
assert last["red"]["position"][1] < -0.10
for name in ("blue", "orange"):
assert last[name]["position"][1] > 0.17
assert math.dist(first[name]["position"], last[name]["position"]) < 0.008
final = tower["frames"][-1]["bodies"]
assert [step["objectId"] for step in tower["steps"]] == ["blue", "red", "orange"]
for name, height in (("blue", 0.025), ("red", 0.075), ("orange", 0.125)):
x, y, z = final[name]["position"]
assert abs(x - 0.50) < 0.008 and abs(y + 0.20) < 0.008
assert abs(z - height) < 0.003, (name, z, height)
print("단일 물체 이동과 파랑→빨강→주황 타워의 위치 확인")
PY
높이는 물체 중심의 Z 좌표이며 단위는 미터입니다.
5cm 상자 세 개의 중심 높이는 약 0.025, 0.075, 0.125m입니다.
계획 순서만 바꾸고 기본 빨강부터 쌓기 기록을 보여주면 이 검사를 통과할 수 없습니다.
4. 실패를 실패로 남기는 실험
물체가 움직였다는 화면만으로 접촉에 의한 잡기가 증명되지는 않습니다. 이번에는 그리퍼 힘을 0으로 만들고 같은 빨간 상자를 집도록 합니다. CLI가 실패 기록을 저장한 뒤 종료 코드 1을 반환하는 것이 기대 결과입니다.
cd "$ROBOT_LAB"
(
set -eu
if [ -e .local/evidence/no-grip-42.json ]; then
printf '%s\n' '기존 실패 기록을 먼저 확인하세요. 재실행하려면 이 블록의 출력·읽기 경로를 새 파일명으로 함께 바꾸세요.'
exit 1
fi
ROBOT_NEGATIVE_STATUS=0
.venv/bin/python -m simulation.cli \
--scenario transfer --seed 42 --grip-force 0 \
--actions '[{"objectId":"red","target":"tray"}]' \
--output .local/evidence/no-grip-42.json \
|| ROBOT_NEGATIVE_STATUS=$?
test "$ROBOT_NEGATIVE_STATUS" -eq 1
.venv/bin/python - <<'PY'
import json
from pathlib import Path
replay = json.loads(Path(".local/evidence/no-grip-42.json").read_text())
assert replay["status"] == "failed"
assert replay["metrics"]["success"] is False
assert replay["metrics"]["placed"] == 0
assert replay["metrics"]["total"] == 1
failed = [check for check in replay["checks"] if not check["passed"]]
assert failed, "실패 근거가 있어야 합니다."
assert replay["frames"][-1]["bodies"]["red"]["position"][1] > 0.17
print("실패를 올바르게 검출:", json.dumps(failed, ensure_ascii=False))
PY
)
종료 코드만 1이고 JSON이 없으면 import 오류나 실행 오류일 수 있습니다.
실제 실패 기록과 physical grasp·released stable placement 검사를 함께 읽습니다.
기존 파일을 새 실행 결과로 오인하지 않도록 이 블록은 같은 이름의 기록이 있으면 멈춥니다.
이 실험을 통과시키려고 물체를 손에 붙이거나 좌표를 덮어쓰지 않습니다.
5. 기존 물리 회귀 검사
참가자별 검사를 한 번 실행합니다. 여러 팀이 CPU를 공유하면 동시에 시작하지 않도록 조정합니다.
cd "$ROBOT_LAB"
.venv/bin/python -m unittest discover -s tests/simulation -v
검사에는 여러 배치의 이동·쌓기, 색상 분류, 파랑부터 쌓기, 그리퍼 힘 0,
손가락 마찰 0, 놓은 뒤 외력으로 목표를 벗어난 경우, HTTP 입력과 GLB 구성이 포함됩니다.
OK와 실제 검사 수를 기록합니다. 이 검사는 Bedrock을 호출하지 않습니다.
물리 성공을 읽는 방법
| 검사 | 읽을 내용 |
|---|---|
physical grasp |
두 손가락의 하중 접촉과 실제 들어 올림 |
released stable placement |
목표 오차, 손과의 분리, 바닥·아래 상자의 지지, 직립·정지 |
solver remained healthy |
MuJoCo solver 경고 유무 |
metrics.positionError |
최종 중심 위치의 최대 오차, 미터 단위 |
provenance |
사용 엔진·모델·제어기, learning: "none" |
놓기 검사는 8mm 미만의 목표 오차뿐 아니라 마지막 정착 구간의 흔들림과 속도도 확인합니다. 검사 문자열은 원인 조사에 활용하고, 성공 여부는 실제 Boolean 필드와 위치 기록으로 판단합니다.
막혔을 때
| 증상 | 확인과 조치 |
|---|---|
readyz가 503 |
터미널 A 로그, 참가자 시뮬레이터 포트, B의 SIMULATION_URL 확인 |
| 포트 사용 중 | 해당 포트의 본인 프로세스인지 확인. 다른 앱을 종료하지 않음 |
origin_rejected 또는 CSRF 오류 |
브라우저 주소와 B의 PUBLIC_ORIGIN을 맞추고 새로고침. 보호 검사 제거 금지 |
| “서버 응답을 읽지 못했습니다” | 브라우저 Network의 /api/plan 상태·Content-Type 확인. HTML 응답이면 포트 전달 경로부터 조사 |
| 실행 중 429 | 현재 작업 종료를 기다림. 중복 클릭이나 동시 실행 수 증가로 우회하지 않음 |
| 성공 실험이 실패 | 실패 JSON·배치·명령·호스트 부하를 보존하고 조사. 위치·접촉 검사를 완화하지 않음 |
선택한 AI CLI의 대화 입력창에 붙여넣으세요. 세 도구가 같은 프롬프트를 사용합니다.
.local/evidence의 빨간 상자 단독 이동, 파랑부터 쌓기, 그리퍼 힘 0 결과를 비교해 주세요.
status만 읽지 말고 checks, 최종 물체 위치, provenance를 근거로 설명하세요.
실행 시간을 시뮬레이션 기록 길이와 구분하고, 실패 결과를 성공으로 바꾸는 수정은 하지 마세요.
마칠 때 확인
- 화면에서 규칙 기반 제공자를 확인하고 배치 445의 새 단일 물체 실행을 만들었다.
- CLI 기록에서 빨간 상자만 이동하고 파란·주황 상자가 출발 영역에 남는 것을 확인했다.
- 파랑→빨강→주황 타워의 실제 높이와 놓기 검사를 확인했다.
- 힘 0 실험의 종료 코드 1과 실패 JSON을 함께 확인했다.
- 물리 회귀 검사 결과를 기록했다.
프롬프트: 물리 근거 읽기
이전: 01. 환경과 참가자 작업 공간 · 다음: 03. 작업 계획 검증
AI CLI 프롬프트 카드
아래 프롬프트를 복사해 실습 EC2에서 선택한 Codex, Kiro CLI, Claude Code의 대화 입력창에 붙여넣습니다. Markdown 카드 다운로드
선택한 AI CLI의 대화 입력창에 붙여넣으세요. 세 도구가 같은 프롬프트를 사용합니다.
현재 참가자 폴더의 다음 파일을 비교해 주세요.
- .local/evidence/red-only-42.json
- .local/evidence/blue-first-2026.json
- .local/evidence/no-grip-42.json
필드의 뜻은 shared/contracts.ts, simulation/runtime.py, simulation/scene.py에서 확인하세요.
1. engine, seed, status, metrics.placed/total, checks, provenance를 읽으세요.
2. 빨간 상자 단독 이동에서는 파란·주황 상자의 최초·최종 위치를 비교하세요.
3. 파랑부터 쌓기에서는 steps 순서뿐 아니라 최종 Z 중심 높이와 지지·분리·안정성 검사를 확인하세요.
4. 힘 0 실험의 실제 실패 근거를 읽고, 실패 기록이 없다면 실행 오류와 물리 실패를 구분하세요.
5. 위치 단위는 미터이며, generationSeconds와 duration은 각각 계산 소요 시간과 가상 기록 길이임을 설명하세요.
성공 여부를 status 하나나 화면 캡처만으로 판단하지 마세요.
접촉과 최종 상태를 통과하지 못한 결과를 성공으로 바꾸지 마세요.
물체를 손에 붙이는 제약, 좌표 덮어쓰기, 미리 정한 애니메이션을 추가하지 마세요.
공개 기본 시연 파일로 이번 기록을 대신하지 마세요.
실행하지 않은 검사·모델 호출·Isaac Sim 결과는 미확인으로 적으세요.
결과별로 근거 필드와 관측값을 표로 적고, 차이가 있으면 원인 조사에 필요한 파일만 제안하세요.
이 요청에서는 코드와 기록을 수정하지 않습니다.
실패가 발견되면 다음 순서로 추가 조사하세요.
먼저 실패한 물리 검사가 무엇인지 확인해 주세요.
초기 배치, 물체 순서, 제어 설정, 검사 이름과 실제 값으로 원인을 좁히세요.
tests/simulation/test_physics.py의 힘 0·마찰 0·놓은 뒤 외력 검사를 읽어 비교하세요.
검사 기준 완화나 실패 숨김 없이, 재현할 수 있는 가장 작은 명령을 제안해 주세요.
읽기 기록
읽음 표시는 이 브라우저에만 저장됩니다.
실제 실습 결과는 터미널에서 확인하세요.