api.py, subgoal_predictor.py, eval.py, run_api_mem_one_episode.sh 4개 파일 — +92 / -18 linesApiMemSubgoalPredictor + HybridApiMemLoRACoordPredictor 동일 처리
기존 ApiMem get_subgoal은 매 K-tick마다 현재 프레임 1장만 VLM에 넘겼다.
BinFill 같이 이미 수행된 동작(arm이 무언가를 집었다, bin 가까이 이동했다)이 다음 subgoal 결정에 중요한 태스크에서,
단일 정적 프레임만으로는 "이번 tick까지 뭘 했는지"를 VLM이 볼 방법이 없었다.
mt에만 의존해야 했음.
이 변경은 tool-calling 설계(260510)에서 ApiMemModel.get_subgoal(frames)에 frame list를 넘기는 인터페이스가 필요해진 것과도 맞물려 진행됨.
즉 frames_per_call 자체로도 유용하고, 이후 tool-calling 구조의 전제 조건이기도 함.
subgoal_predictor.py에 추가된 함수. frame_buffer[last_tick_buf_idx + 1:] 구간에서
균등 간격(even-spaced)으로 N장을 추출한다. 마지막 프레임이 항상 current frame = frames[-1]이 되도록 보장.
기존: get_subgoal(image: np.ndarray) → 현재: get_subgoal(frames: list[np.ndarray] | np.ndarray).
단일 ndarray 입력도 [image]로 자동 wrap → 하위 호환 유지.
Detection은 frames[-1] (최신 프레임)에만 수행 — 비용 절감.
subgoal_predictor.py::HybridApiMemLoRACoordPredictor.get_subgoal도 같은 multi-frame 플럼빙 추가.
LoRA coord swap 로직은 frames[-1] 기반으로 그대로 작동.
구현 단계 — SR 측정 실험은 아직 미실행. 기존 runs (8-run BinFill, hybrid 9951)은 모두 frames_per_call=1로 돌아가므로 수치 변화 없음.
| 변경 항목 | Before | After | Notes |
|---|---|---|---|
get_subgoal 시그니처 | image: np.ndarray | frames: list | ndarray | 하위 호환 유지 |
| detection 대상 | current frame | frames[-1] only | 비용 불변 |
| image attach (VLM) | 1장 | N장 (frames_per_call) | N=1 = 기존 동작 |
| HybridPredictor | 미지원 | 동일 적용 | coord swap 무영향 |
ApiMem VLM이 드디어 "이번 tick까지 arm이 어떻게 움직였는지"를 직접 볼 수 있게 됐다.
기존 text-only mt가 담기 어려운 공간적 변화(arm이 container 쪽으로 이동 중, gripper가 열렸다 닫혔다)를
시각적으로 전달 가능.
또한 이 변경은 tool-calling 설계에서 ApiMemModel.get_subgoal(frames, full_frame_buffer=None)
시그니처가 필요한 전제 조건이기도 하다 — tool-calling P2 구현 시 이 인터페이스를 그대로 확장.
N=1(baseline) vs N=3 vs N=5 on BinFill (50 ep each) 로 실제 SR 변화 측정 필요. VideoUnmaskSwap tool-calling sweep (P4) 시 frames_per_call=3 병행 제출 권장.
get_subgoal(frames, full_frame_buffer=None)로 확장 예정.
full_frame_buffer는 track_objects tool이 keyframe-to-now 슬라이싱에 사용.
현재 frames_per_call 구현이 이 확장의 기반.