RB-Y1 skill planner (Qwen3.5-4B + LoRA) β€” μ–΄λŒ‘ν„°

이 λ¦¬ν¬λŠ” LoRA μ–΄λŒ‘ν„°λ§Œ λ‹΄κ³  μžˆμŠ΅λ‹ˆλ‹€(100 MB). vLLM으둜 λ°”λ‘œ μ„œλΉ™ν•˜λ €λ©΄ 병합본 napalna/rby1-skill-planner-qwen3.5-4bλ₯Ό μ“°κ³ , PEFT둜 직접 μ–ΉμœΌλ €λ©΄ 베이슀 Qwen/Qwen3.5-4B μœ„μ— 이 μ–΄λŒ‘ν„°λ₯Ό λ‘œλ“œν•˜μ„Έμš”.

머리 카메라 이미지 ν•œ μž₯κ³Ό μžμ—°μ–΄ μ§€μ‹œ(μ˜μ–΄/ν•œκ΅­μ–΄)λ₯Ό λ°›μ•„, μ–‘νŒ” λ‘œλ΄‡ RB-Y1이 μ‹€ν–‰ν•  μŠ€ν‚¬ μ‹œν€€μŠ€λ₯Ό JSON으둜 좜λ ₯ν•©λ‹ˆλ‹€.

μž…λ ₯: "λ°”λ‚˜λ‚˜ μ£Όμ„Έμš”" + 이미지
좜λ ₯: approach(banana, right) β†’ pick(banana, right) β†’ lift(right) β†’ handover(right)

1. νŒŒμΈνŠœλ‹ 방법

ν•­λͺ© λ‚΄μš©
베이슀 Qwen/Qwen3.5-4B (Apache-2.0)
방식 LoRA r=16, μ–Έμ–΄ λͺ¨λΈμ˜ q/k/v/o/gate/up/downμ—λ§Œ 적용, λΉ„μ „ νƒ€μ›Œ 동결
데이터 μ‹€μ œ ν”„λ ˆμž„ 7μž₯ β†’ λ°˜μ „Β·λ¬Όμ²΄ λΆ™μ—¬λ„£κΈ°Β·ν™”μ§ˆμ €ν•˜λ‘œ 63μž₯λ©΄ β†’ μ§€μ‹œλ¬Έ 5,503개 μžλ™ 생성 β†’ 1,600개둜 1 epoch
ν•™μŠ΅ μ‹œκ°„ A100 1μž₯, μ•½ 60λΆ„
좜λ ₯ κ°•μ œ vLLM ꡬ쑰화 좜λ ₯(json_schema). μŠ€ν‚¬ 이름과 νŒ”μ€ μŠ€ν‚€λ§ˆ enum이라 잘λͺ»λœ 값이 λ‚˜μ˜¬ 수 μ—†μŒ

μ§€μ‹œλ¬Έμ€ handover / pick-up / put-on / 손 μ§€μ • / 거절(μ—†λŠ” 물체, 미지원 λ™μž‘, λͺ¨ν˜Έ, μœ„ν—˜) / μ–‘νŒ” λ³΅ν•©μ˜ 6κ°€μ§€ μœ ν˜•, μ˜μ–΄Β·ν•œκ΅­μ–΄ λ°˜λ°˜μž…λ‹ˆλ‹€.

2. νŒŒμΈνŠœλ‹ μ „ν›„ 비ꡐ

ν•™μŠ΅μ— μ“°μ§€ μ•Šμ€ ν”„λ ˆμž„ 3μž₯, 170개 μ§€μ‹œλ¬Έ(영 85 / ν•œ 85). 같은 방·카메라·물체 μ„ΈνŠΈμ΄λ―€λ‘œ "이 μ‹œμ—° ν™˜κ²½" κΈ°μ€€ μ μˆ˜μž…λ‹ˆλ‹€.

λͺ¨λΈ μ™„μ „ 일치 arm 정확도 μ§€μ—° (p50)
Qwen3.5-4B, ν”„λ‘¬ν”„νŠΈλ§Œ 140 / 170 (82%) 0.87 1.3 s
Qwen3.5-9B, ν”„λ‘¬ν”„νŠΈλ§Œ 153 / 170 (90%) 0.95 2.0 s
Gemma 4 12B, ν”„λ‘¬ν”„νŠΈλ§Œ 152 / 170 (89%) 0.89 2.8 s
Qwen3.5-4B + LoRA SFT (이 λͺ¨λΈ) 169 / 170 (99%) 0.99 1.3 s
+ GRPO 170 / 170 1.00 1.2 s (SFT와 μ°¨μ΄λŠ” λ…Έμ΄μ¦ˆ μˆ˜μ€€)

ν”„λ‘¬ν”„νŠΈλ§ŒμœΌλ‘œλ„ handoverΒ·pick-up·손 지정은 거의 λ§Œμ μž…λ‹ˆλ‹€. νŒŒμΈνŠœλ‹μ΄ 고친 것은 거절 ν˜•μ‹, μ˜† 책상 물체 λ¬΄μ‹œ, "던져" 같은 미지원 λ™μž‘, λͺ¨ν˜Έμ„± νŒλ‹¨, μ–‘νŒ” 볡합 μ§€μ‹œμž…λ‹ˆλ‹€.

주의: μ‹œμ—°μ— λ‚˜μ˜¬ λ¬Όμ²΄λŠ” λ°˜λ“œμ‹œ μž‘μ—…λŒ€ μœ„μ— 놓인 μ‚¬μ§„μœΌλ‘œ ν•™μŠ΅ 데이터에 λ„£μ–΄μ•Ό ν•©λ‹ˆλ‹€. μ˜† 책상 λ°©ν•΄λ¬Όλ‘œλ§Œ λ“±μž₯ν•œ λ¬Όμ²΄λŠ” "항상 μ—†λ‹€"κ³  ν•™μŠ΅λ©λ‹ˆλ‹€(μ‚¬κ³Όμ—μ„œ μ‹€μ œλ‘œ λ°œμƒ, ν•©μ„± λ°μ΄ν„°λ‘œ μˆ˜μ •).

3. 좜λ ₯ ν˜•μ‹

{
  "visible_objects": [{"name": "banana", "nearest_arm": "right"}],
  "status": "ok",
  "reason": null,
  "skills": [
    {"skill": "approach", "target": "banana", "arm": "right"},
    {"skill": "pick",     "target": "banana", "arm": "right"},
    {"skill": "lift",     "target": null,     "arm": "right"},
    {"skill": "handover", "target": null,     "arm": "right"}
  ]
}
  • skill: approach | pick | lift | handover | put_on | ready_pose
  • target: approach/pick은 물체 이름(SAM3 ν”„λ‘¬ν”„νŠΈλ‘œ μ‚¬μš©), put_on은 λͺ©μ μ§€, λ‚˜λ¨Έμ§€λŠ” null
  • arm: left | right. 같은 νŒ”μ€ λ°°μ—΄ μˆœμ„œλŒ€λ‘œ, 두 νŒ”μ€ 병렬 μ‹€ν–‰
  • statusκ°€ okκ°€ μ•„λ‹ˆλ©΄(object_not_visible | unsupported_action | ambiguous | unsafe) skillsλŠ” 빈 배열이며 μ‹€ν–‰ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€

4. μ‹€ν–‰ 맀뉴얼

μ„œλ²„ (GPU λ¨Έμ‹ )

pip install "vllm>=0.27" 
vllm serve napalna/rby1-skill-planner-qwen3.5-4b --served-model-name planner-sft --port 8002 \
  --dtype bfloat16 --max-model-len 8192 --limit-mm-per-prompt '{"image": 1}' \
  --structured-outputs-config '{"backend": "xgrammar"}'
curl http://localhost:8002/health      # 200이면 μ€€λΉ„ μ™„λ£Œ (기동 μ•½ 3λΆ„, VRAM μ•½ 20 GB)

μƒμœ„ λͺ¨λ“ˆ β†’ ν”Œλž˜λ„ˆ 호좜 (λ‘œλ΄‡ 리포 plan_actions.py)

from plan_actions import plan_actions
plan = plan_actions("λ°”λ‚˜λ‚˜ μ£Όμ„Έμš”",
                    endpoint="http://<server>:8002/v1/chat/completions",
                    image=head_frame_bgr,   # numpy(BGR) / 파일 경둜 / None이면 ZED SHMμ—μ„œ μžλ™ 캑처
                    strict=True)            # 이상 응닡 μ‹œ μ˜ˆμ™Έ (κ·œμΉ™ planner둜 쑰용히 λ„˜μ–΄κ°€μ§€ μ•ŠμŒ)
plan.meta      # {'status': 'ok', 'reason': None, 'visible_objects': [...], 'problems': []}
plan.to_json() # ν•˜μœ„ λͺ¨λ“ˆ(TaskQueueManager.load_plan)에 λ„˜κΈ°λŠ” ν˜•μ‹

ν”Œλž˜λ„ˆ β†’ ν•˜μœ„ λͺ¨λ“ˆ: plan.to_json()의 skills 배열을 TaskQueueManager.load_plan()에 λ„˜κΈ°λ©΄ λ©λ‹ˆλ‹€. ν•˜μœ„ λͺ¨λ“ˆμ΄ μ½λŠ” ν•„λ“œλŠ” skill, target, arm μ…‹λΏμž…λ‹ˆλ‹€. plan.meta["status"] != "ok"μ΄κ±°λ‚˜ problemsκ°€ 있으면 μ‹€ν–‰ν•˜μ§€ λ§ˆμ„Έμš”.

파이썬 없이 HTTP둜 직접 호좜

import base64, json, requests
from plan_actions import SYSTEM_PROMPT, PLAN_JSON_SCHEMA
img = base64.b64encode(open("frame.jpg", "rb").read()).decode()
r = requests.post("http://<server>:8002/v1/chat/completions", json={
  "model": "planner-sft", "temperature": 0, "max_tokens": 512,
  "chat_template_kwargs": {"enable_thinking": False},
  "response_format": {"type": "json_schema", "json_schema": {"name": "plan", "schema": PLAN_JSON_SCHEMA, "strict": True}},
  "messages": [{"role": "system", "content": SYSTEM_PROMPT},
               {"role": "user", "content": [{"type": "image_url", "image_url": {"url": "data:image/jpeg;base64," + img}},
                                            {"type": "text", "text": "Instruction: λ°”λ‚˜λ‚˜ μ£Όμ„Έμš”"}]}]})
print(json.loads(r.json()["choices"][0]["message"]["content"]))

μ‹œμŠ€ν…œ ν”„λ‘¬ν”„νŠΈμ™€ μŠ€ν‚€λ§ˆλŠ” λ‘œλ΄‡ 리포의 plan_actions.py에 있으며, ν•™μŠ΅ λ•Œμ™€ 같은 ν”„λ‘¬ν”„νŠΈλ₯Ό 써야 ν•©λ‹ˆλ‹€.

5. 리포 ꡬ성

  • napalna/rby1-skill-planner-qwen3.5-4b β€” 병합 κ°€μ€‘μΉ˜ (vLLM으둜 λ°”λ‘œ μ„œλΉ™)
  • napalna/rby1-skill-planner-qwen3.5-4b-lora β€” LoRA μ–΄λŒ‘ν„°λ§Œ (100 MB, PEFT둜 베이슀 μœ„μ— λ‘œλ“œ)
  • λ²€μΉ˜λ§ˆν¬Β·ν•™μŠ΅ μ½”λ“œ: λ‘œλ΄‡ 리포 arpa_h_demo_robot_side (plan_actions.py, PLANNER.md)
Downloads last month

-

Downloads are not tracked for this model. How to track
Video Preview
loading

Model tree for napalna/rby1-skill-planner-qwen3.5-4b-lora

Finetuned
Qwen/Qwen3.5-4B
Adapter
(730)
this model