21비전 그리핑 · 음성 · 에이전트
YOLO + 깊이 카메라 그리핑, reSpeaker 음성 제어, 자연어 명령 에이전트 구조를 살펴봅니다.
보고, 듣고, 알아듣는 로봇팔#
지금까지 reBot 은 우리가 숫자로 알려 준 곳으로만 움직였습니다. "x 0.35 m, y 0, z 0.20 m 로 가라" 같은 식이었지요. 실제 세상에서 쓸모 있는 로봇이 되려면 세 가지가 더 필요합니다.
| 능력 | 사람으로 치면 | reBot 공식 예제 | 이 장의 절 |
|---|---|---|---|
| 보기 — 물체가 어디 있는지 스스로 찾기 | 눈 | YOLO + 깊이 카메라 그리핑 데모 | 비전 그리핑 |
| 듣기 — 소리가 난 방향과 말의 뜻 알기 | 귀 | reSpeaker Flex 음성 제어 | 음성 제어 |
| 알아듣기 — "빨간 블록 집어 줘"를 동작 순서로 바꾸기 | 머리 | 임바디드 에이전트(wrc_demo) | 에이전트 |
이렇게 몸을 가지고 실제 세상에서 보고·듣고·움직이는 AI 를 임바디드 AI(Embodied AI, 체화된 AI) 라고 부릅니다. reBot-DevArm 저장소가 첫 줄에 내세우는 목표도 "임바디드 AI 를 배우는 문턱을 낮추는 것"입니다.
이 장은 세 데모를 구조 중심으로 살펴봅니다. 설치 명령은 자주 바뀌므로 핵심만 보이고, 정확한 최신 절차는 각 위키 링크를 따라가세요. 대신 가장 중요한 수학인 "픽셀 + 깊이 → 카메라 좌표 → 로봇 좌표" 변환은 Playground 에서 직접 계산해 봅니다.
- 비전 그리핑: DM 위키 · RS 위키 · 코드 Seeed-Projects/reBot-DevArm-Grasp
- 음성: reSpeaker Flex 로 reBot 제어하기 · 코드 xr686/reBot-Arm-reSpeaker-Flex
- 에이전트: wrc_demo 튜토리얼 · 코드 TheMoonAstronaut/wrc
비전 그리핑 — 네 단계 파이프라인#
공식 그리핑 데모는 손목에 깊이 카메라(RGB-D) 를 달고, 화면에서 물체를 찾아 집습니다. 지원 카메라는 Orbbec Gemini 2, Intel RealSense D435i, D405 이고 USB 3.0 으로 연결합니다. 손목 거치대 STEP 파일은 저장소의 hardware/camera-mounts/ 에 있고, RS·DM 이 같은 거치대를 씁니다(14장).
- 인식 — YOLO(You Only Look Once, 한 번에 화면 전체에서 물체를 찾는 신경망)가 물체를 찾아 상자를 그립니다. 공식 데모의 기본 모델은
yoloe-26l-seg.pt로, 열린 어휘(open vocabulary) 모델이라"yellow banana","water bottle"처럼 찾을 물체 이름을 글로 적어 주면 따로 학습하지 않아도 찾습니다. 가벼운yoloe-26s-seg.pt, 정해진 종류만 찾는yolov8n-seg.pt도 고를 수 있습니다. - 깊이 읽기 — 깊이 카메라는 픽셀마다 "카메라에서 거기까지 몇 m" 를 알려 줍니다. 상자 안 깊이 값들의 분위수(예: 가운데 값,
depth_quantile기본 0.5)로 집을 높이를 정합니다. 한 픽셀만 읽으면 잡음에 약하기 때문입니다. - 좌표 변환 — 픽셀 (u, v) 와 깊이 d 로 카메라 기준 3D 좌표를 구하고, 이것을 로봇 베이스 기준 좌표로 바꿉니다(다음 절).
- 집기 — IK 로 닿을 수 있는지 확인한 뒤, 물체 위 접근점(
pregrasp_offset_m0.08 m)으로 가서 → 내려가서(insertion_depth_m만큼 더 깊이) → 그리퍼를 닫고 → 들어 올려 준비 자세로 돌아옵니다.
그리퍼를 어느 방향으로 벌릴지는 물체를 감싼 최소 넓이 직사각형(OBB, 회전된 상자) 의 짧은 축 으로 정합니다. 바나나처럼 긴 물체는 짧은 쪽을 잡아야 손가락 사이에 들어가니까요.
RS 위키 기준으로 정리하면 이렇습니다. 정확한 명령과 옵션은 위키의 최신판을 따르세요.
git clone https://github.com/Seeed-Projects/reBot-DevArm-Grasp.git rebot_grasp
cd rebot_grasp
conda env create -f environment.yml -n rebotarm
conda activate rebotarm
# sdk/reBotArm_control_py 에서 pip install -e . (SDK 설치)
# RS 는 CAN 을 먼저 올립니다
sudo ip link set can0 type can bitrate 1000000
sudo ip link set can0 up
python scripts/object_detection.py # ① 카메라·인식만 확인
python scripts/ordinary_grasp_pipeline.py # ② 팔 없이 그립 추정만 확인
python scripts/collect_handeye_eih.py # ③ 핸드-아이 보정
python scripts/main.py --dry-run # ④ 팔을 움직이지 않고 전체 흐름 확인
python scripts/main.py # ⑤ 실제 집기 (G 키로 집기)--dry-run 처럼 "팔은 빼고 먼저 돌려 보는" 단계가 있다는 점을 눈여겨보세요. 카메라 → 인식 → 계산 → 팔, 이렇게 한 층씩 확인하는 습관이 사고를 막습니다.
픽셀 + 깊이 → 카메라 좌표 → 로봇 좌표#
1단계 — 핀홀 공식: 픽셀을 미터로#
카메라는 바늘구멍 사진기처럼 동작한다고 볼 수 있습니다(핀홀 모델). 영상 중심 (cx, cy) 에서 (u − cx) 픽셀 떨어져 보이는 점은, 거리 d 에서 실제로 이만큼 떨어져 있습니다.
xc = (u − cx) · d / fx ← 카메라 오른쪽 방향 (m)
yc = (v − cy) · d / fy ← 카메라 아래쪽 방향 (m)
zc = d ← 카메라가 바라보는 방향 (m)닮은 삼각형 원리입니다. 같은 10 픽셀이라도 멀리 있을수록(d 가 클수록) 실제 거리는 더 큽니다. fx, fy(초점 거리, 픽셀 단위)와 cx, cy(영상 중심)는 카메라마다 정해진 내부 파라미터(intrinsics) 입니다. 공식 데모는 이 값을 config/calibration/<카메라>/intrinsics.npz 에 저장합니다.
2단계 — 외부 파라미터: 카메라 좌표를 로봇 좌표로#
카메라 좌표는 "카메라가 본 세상"입니다. 로봇은 베이스 좌표(앞 +X, 왼쪽 +Y, 위 +Z)로 생각하므로 한 번 더 바꿔야 합니다. 카메라가 로봇 어디에, 어느 방향으로 붙어 있는지를 나타내는 회전 R 과 위치 t 를 외부 파라미터(extrinsics) 라고 합니다.
p_base = R · p_cam + t10장의 변환 행렬과 똑같은 계산입니다. 문제는 R 과 t 를 자로 재서는 정확히 알 수 없다는 것입니다. 그래서 핸드-아이 보정(hand-eye calibration) 을 합니다.
| 방식 | 카메라 위치 | 변환 | reBot 예 |
|---|---|---|---|
| eye-in-hand | 손목(팔과 함께 움직임) | base_T_cam = base_T_ee(q) · ee_T_cam — 지금 관절각으로 FK 를 계산해 곱함 | 공식 그리핑 데모 (eye_in_hand 모드) |
| eye-to-hand | 스탠드·천장에 고정 | base_T_cam 하나가 고정값 | 에이전트 데모의 위쪽(overhead) 카메라 |
핸드-아이 보정은 어떻게 하나#
공식 데모는 ArUco 마커(검은 테두리의 QR 비슷한 정사각형 표식)를 책상에 두고, 팔을 여러 자세로 옮기며 "그때의 손목 자세(FK)"와 "카메라가 본 마커 자세"를 짝지어 모읍니다. 이 짝들로부터 손목-카메라 사이 변환을 푸는 고전 알고리즘이 TSAI 방법 입니다.
- 자동 모드:
python scripts/collect_handeye_eih.py— 미리 정한 50개 자세를 돌며 마커가 안정적으로 보일 때 기록 - 수동 모드:
--manual— 중력 보상 상태에서 손으로 팔을 옮기고 Enter 로 기록 - 최소 5개, 15개 이상 권장. 결과는
hand_eye.npz로 저장 - 마커 한 변 길이(
calibration.aruco.marker_length_m, 예시 0.1 m)를 실제 인쇄한 크기와 똑같이 적어야 합니다. 이 값이 틀리면 모든 거리가 같은 비율로 틀어집니다. - 그래도 몇 mm 씩 어긋나면
calibration.hand_eye_compensation_m에 X·Y·Z 보정값(m)을 넣어 미세 조정합니다.
대부분 보정 문제입니다. 마커 길이 설정, 샘플 수(15개 이상), 샘플 자세가 충분히 다양한지(회전까지 섞였는지) 확인하세요. 깊이가 흔들리면 depth_quantile, 카메라 높이, 반짝이는 표면(깊이 카메라가 약함)을 의심합니다. 이 내용은 22장 트러블슈팅 표에도 있습니다.
실습 — 가상 카메라로 큐브 집기#
실물 카메라가 없어도 변환 수학은 그대로 연습할 수 있습니다. 시뮬레이터의 큐브 위치(arm.cube())를 가짜 카메라가 픽셀로 찍었다고 치고, 우리가 그 픽셀을 다시 로봇 좌표로 되돌려 arm.move_to 로 집으러 갑니다.
3D 시뮬레이터 Python 탭을 열고, 보기 옵션에서 큐브 가 켜져 있는지 확인한 뒤 아래 코드를 붙여 넣어 실행하세요.
import math
# 가상 카메라: 책상 위 0.60 m 에서 똑바로 내려다보는 고정 카메라 (eye-to-hand)
# 영상 위쪽 = 로봇 앞(+X) 이 되도록 달았다고 가정합니다.
FX, FY, CX, CY = 600.0, 600.0, 320.0, 240.0 # 내부 파라미터 (가상의 값)
CAM = (0.30, 0.0, 0.60) # 로봇 좌표계에서 카메라 위치 (m)
HALF = 0.02 # 깊이는 큐브 '윗면'까지: 윗면 = 중심 + HALF
def robot_to_pixel(p):
"""가짜 카메라: 로봇 좌표 → (u, v, 깊이). 실제로는 YOLO + 깊이 카메라가 주는 값"""
xc = -(p[1] - CAM[1])
yc = -(p[0] - CAM[0])
d = CAM[2] - (p[2] + HALF)
return round(CX + FX * xc / d), round(CY + FY * yc / d), round(d, 3)
def pixel_to_robot(u, v, d):
"""우리가 만들 함수: (u, v, 깊이) → 로봇 좌표 (큐브 중심)"""
xc = (u - CX) * d / FX # ① 핀홀 공식
yc = (v - CY) * d / FY
zc = d
x = CAM[0] - yc # ② 외부 파라미터: R = [[0,-1,0],[-1,0,0],[0,0,-1]], t = CAM
y = CAM[1] - xc
z = CAM[2] - zc
return (x, y, z - HALF) # ③ 윗면 → 중심
cube = arm.cube()
if cube is None:
print("보기 옵션에서 큐브를 켜 주세요.")
else:
u, v, d = robot_to_pixel(cube)
print(f"카메라가 본 것 : 픽셀 ({u}, {v}), 깊이 {d} m")
x, y, z = pixel_to_robot(u, v, d)
err = math.dist((x, y, z), cube) * 1000
print(f"계산한 로봇 좌표: ({x:.3f}, {y:.3f}, {z:.3f}) m (실제와 {err:.1f} mm 차이)")
arm.home()
arm.open()
arm.move_to(x, y, z + 0.08, t=1.5, down=True) # 접근점: 8 cm 위
arm.move_line(x, y, z, t=1.0) # 곧게 내려가기
arm.close()
arm.wait(0.5)
arm.move_line(x, y, z + 0.08, t=1.0) # 들어 올리기확인할 것
- 출력의 "실제와 차이" 가 1 mm 안팎인가요? 픽셀을 정수로 반올림했기 때문에 생기는 오차입니다. 깊이 0.56 m 에서 1 픽셀은 약
0.56 / 600 ≈ 0.9 mm입니다. pixel_to_robot안의d에+ 0.01을 더해(깊이 카메라 오차 1 cm) 다시 돌려 보세요. 어느 축이 가장 많이 틀어지나요?CAM을(0.31, 0.0, 0.60)으로 바꿔 외부 파라미터가 1 cm 틀린 상황을 만들어 보세요. 단,robot_to_pixel은 진짜 카메라이니 그대로 두고pixel_to_robot만 틀린 값을 쓰게 해야 합니다. 그리퍼가 늘 한쪽으로 빗나가는 것 — 이것이 핸드-아이 보정이 틀렸을 때의 모습입니다.
move_line 이 경고를 내면 그 줄을 arm.move_to(x, y, z, t=1.0, down=True) 로 바꿔 보세요.
시뮬레이터 큐브는 "그리퍼가 충분히 닫히고 TCP 가 가까우면 붙는" 단순 규칙이라 높이가 조금 틀려도 잡힙니다. 실물에서는 TCP(그리퍼 몸체 원점)와 손가락 끝 사이 거리, 물체 높이, 책상과의 충돌을 모두 따져야 해서 공식 데모에 insertion_depth_m, min_base_z_m(이보다 낮게는 내려가지 않음) 같은 값이 있습니다.
음성 제어 — reSpeaker Flex 와 소리 방향(DoA)#
두 번째 데모는 마이크 배열 reSpeaker Flex XVF3800(마이크 4개가 원형으로 배치, XIAO ESP32S3 와 짝을 이룬 제품)로 reBot B601-DM 을 움직입니다. 두 가지 모드가 있습니다.
DoA 모드 — 소리가 난 쪽으로 돌아보기#
DoA(Direction of Arrival, 도래 방향) 는 소리가 어느 방향에서 왔는지를 각도로 알려 주는 기능입니다. 마이크 4개에 소리가 도착하는 시간 차이로 계산하며, XVF3800 칩이 0~360° 값을 USB 로 컴퓨터에 보내 줍니다. 위키에 소개된 처리 방식은 이렇습니다.
- 최근 4개 각도를 모아 가중 평균 하고, 튀는 값을 걸러냅니다(잡음·선풍기 소리 대책).
- 각도가 15° 이상(
--threshold) 바뀌면 팔이 그쪽으로 돌아 고개를 끄덕이고, 3초(--cooldown) 동안 쉽니다. - 소리가 없으면 "숨 쉬는" 듯한 대기 동작을 합니다.
음성 모드 — 말을 명령으로#
Enter 를 누르고 있는 동안 녹음하고, 놓으면 Whisper(음성→글자)로 받아 적은 뒤 LLM(Llama-3.3-70B, Groq API 경유)이 문장을 다음과 같은 구조화된 JSON 으로 바꿉니다. 답변은 Edge-TTS 로 말해 줍니다.
{"action": "turn_left", "params": {}, "reply": "왼쪽으로 돌게요"}지원 동작은 왼쪽/오른쪽 돌기, 인사(끄덕이기), 손 흔들기, 영점 복귀, 정지 정도로 단순합니다. 중요한 것은 구조입니다 — LLM 은 팔을 직접 움직이지 않고, 정해진 동작 목록 중 하나를 고를 뿐 입니다.
# 실물용 — 위키 기준. 설치는 위키의 conda/uv 절차를 먼저 따르세요.
python sound_tracking_arm.py --mode doa # 소리 방향 추적
python sound_tracking_arm.py --mode voice # 음성 명령위키 예제는 Groq API 키를 파이썬 파일에 붙여 넣는 방식을 안내하지만, 그 파일을 깃허브에 올리면 키가 그대로 공개됩니다. --groq-key 옵션이나 환경 변수로 넘기는 습관을 들이세요. 음성 모드는 인터넷 연결이 필요하고, 무료 계정은 요청 수 제한이 있습니다.
DoA 각도(0~360°)를 베이스 회전(joint1) 명령으로 바꾸는 함수를 만들어 봅니다. Python 탭에서 실행하세요.
def doa_to_joint1(doa_deg, mic_front=0):
"""마이크 각도 → joint1 각도(도). mic_front = 로봇 정면에 해당하는 마이크 각도"""
a = (doa_deg - mic_front + 180) % 360 - 180 # -180 ~ +180 로 접기
if arm.model == 'RS':
a = -a # RS 의 joint1 축은 (0,0,-1): 양(+)의 각도가 시계 방향
lo, hi = arm.limits()['joint1']
return max(lo, min(hi, a)) # 관절 한계로 자르기
arm.home()
for doa in [40, 90, 200, 330]:
j1 = doa_to_joint1(doa)
print(f"소리 {doa:3d}° → joint1 {j1:6.1f}°")
arm.move_joint(1, j1, t=1.0)
arm.move_joint(5, 20, t=0.3) # 끄덕
arm.move_joint(5, 0, t=0.3)
arm.wait(0.5)- 소리 90°(왼쪽) 일 때 팔이 화면에서 로봇의 왼쪽(+Y) 으로 도는지 확인하세요. 모델을 RS ↔ DM 으로 바꿔도 같은 쪽으로 돌아야 합니다.
- 200° 는 거의 뒤쪽입니다. 접은 값 −160° 가 joint1 한계(약 ±160.4°) 안에 들어가는지 출력으로 확인하세요.
임바디드 에이전트 — 말로 시키면 스스로 계획#
세 번째는 wrc_demo 입니다. "빨간 블록 집어 줘" 같은 자연어 명령을 받아 스스로 물체를 찾고, 집는 방법을 계획하고, 안전 검사를 통과한 동작만 실행합니다. B601-RS 와 위쪽에 고정한 RGB-D 카메라를 씁니다.
| 층 | 구성 요소(코드 이름) | 하는 일 |
|---|---|---|
| 결정 | 오케스트레이터 — 반사 → 습관 → LLM 3단 | 자주 쓰는 명령은 정규식(agent/reflex.py)으로 즉시, 해 본 적 있는 일은 경험 기억으로, 처음 보는 일만 LLM 에게 |
| 모델 | 기본 로컬 Qwen3-VL-2B-Instruct-AWQ-4bit(vLLM), 클라우드 프로필 anthropic·openai·minimax, 시험용 mock | 문장과 영상을 이해해 도구 호출을 만듦 |
| 비전 | WorldWatcher → BeliefStore | 약 3 Hz 로 YOLOE 검출, "세상에 무엇이 어디 있는지" 메모 |
| 스킬 | SkillRuntime — pick_and_place, grasp_object, place_at, teach_record/replay, task_done 등 | 물체 위치 조회 → 그립 후보 → IK 로 걸러내기 |
| 안전 | SafetyHarness.approve() | 50 Hz 경유점 하나하나 검사, 어기면 즉시 중단(fail-closed) |
| 실행 | SafeArm → motorbridge → SocketCAN | 실제 모터 구동 |
| 기록 | trace.jsonl + 키프레임 JPEG | 무엇을 왜 했는지 나중에 확인 |
왜 "반사 → 습관 → LLM" 순서일까#
LLM 은 똑똑하지만 느리고, 가끔 틀리고, 돈(또는 GPU)이 듭니다. "홈으로 가" 같은 명령은 정규식 한 줄로 충분하니 LLM 을 부를 이유가 없습니다. 사람도 뜨거운 것에 닿으면 생각하기 전에 손을 떼고(반사), 매일 하는 일은 습관대로 하고, 처음 겪는 일에서만 곰곰이 생각하지요.
왜 안전 하네스가 따로 있을까#
LLM 이 만든 계획을 그대로 믿지 않기 위해서입니다. 관절 한계에 너무 가까운지, 너무 낮게 내려가지 않는지 같은 규칙은 LLM 이 아니라 평범한 코드가 검사합니다. 위키는 IK 의 한계 여유(limit_margin=0.025)가 하네스의 여유(joint_margin=0.02)보다 반드시 커야 하고 둘을 함께 바꿔야 한다고 강조합니다. IK 가 만든 자세가 하네스에서 걸리지 않도록 한 겹 더 안쪽에서 계획하는 것입니다.
wrc_demo 위키는 실제 로봇 동작 일부를 "검증되지 않음(Not verified)"으로 표시하고, 현장에서 그리퍼와 핸드-아이 보정을 다시 확인하라고 안내합니다. 실물에 돌릴 때는 비상정지를 손 닿는 곳에 두고, 처음엔 빈 그리퍼·느린 속도로 시작하세요.
에이전트의 뼈대를 직접 만들어 보기#
LLM 을 쓰지 않아도 구조는 흉내 낼 수 있습니다. 핵심은 세 가지입니다 — ① 명령을 JSON 같은 구조화된 값으로 바꾸기, ② 허용된 스킬 목록에서만 고르기, ③ 실행 전에 안전 검사.
import json
# LLM 이 돌려줬다고 치는 답 (음성 데모의 action/params/reply 형식을 흉내)
llm_answer = '{"action": "pick", "params": {"target": "cube"}, "reply": "큐브를 집을게요"}'
Z_MIN = 0.01 # 이보다 낮게는 내려가지 않음 (하네스 규칙)
def safe_target(x, y, z):
if z < Z_MIN:
raise ValueError(f"너무 낮습니다: z={z:.3f}")
if arm.ik(x, y, z, down=True) is None:
raise ValueError("IK 해가 없습니다 (닿지 않는 곳)")
return x, y, z
def skill_pick(target):
if target != "cube" or arm.cube() is None:
return "그 물체가 보이지 않아요"
x, y, z = safe_target(*arm.cube())
arm.open()
arm.move_to(x, y, z + 0.08, t=1.5, down=True)
arm.move_to(x, y, z, t=1.0, down=True)
arm.close()
arm.move_to(x, y, z + 0.08, t=1.0, down=True)
return "집었어요"
def skill_home():
arm.home()
return "홈으로 왔어요"
SKILLS = {"pick": skill_pick, "home": skill_home} # 허용 목록
cmd = json.loads(llm_answer)
print("로봇:", cmd["reply"])
skill = SKILLS.get(cmd["action"])
if skill is None:
print("모르는 동작이라 실행하지 않습니다:", cmd["action"])
else:
try:
print("결과:", skill(**cmd["params"]))
except ValueError as e:
print("안전 검사에서 막힘 →", e)llm_answer 의 "pick" 을 "dance" 로 바꾸거나, Z_MIN 을 0.5 로 올려 보세요. 모르는 명령은 실행하지 않고, 위험한 목표는 막는 것을 확인할 수 있습니다. 진짜 에이전트는 이 llm_answer 자리에 LLM 호출이 들어가고, SKILLS 와 safe_target 이 훨씬 촘촘해질 뿐입니다.
정리#
- 비전 그리핑은 ① YOLO 인식 → ② 깊이 읽기 → ③ 카메라 좌표 → 로봇 좌표 → ④ IK 로 접근·하강·집기·들기 순서입니다. 공식 데모는 열린 어휘 모델
yoloe-26l-seg.pt와 OBB 짧은 축(그리퍼 방향), 깊이 분위수(높이)를 씁니다. - 핀홀 공식
xc = (u − cx)·d / fx가 픽셀을 미터로 바꾸고,p_base = R·p_cam + t가 카메라 좌표를 로봇 좌표로 바꿉니다.fx, fy, cx, cy는 내부 파라미터,R, t는 외부 파라미터입니다. - 외부 파라미터는 핸드-아이 보정으로 구합니다. 손목 카메라(eye-in-hand)는 현재 FK 를 한 번 더 곱합니다. 공식 데모는 ArUco 마커 + TSAI, 15개 이상 샘플을 권장합니다.
- reSpeaker Flex 는 마이크 4개의 도착 시간 차로 소리 방향(DoA)을 구하고, 음성 모드는 Whisper → LLM → JSON 명령 구조입니다.
- wrc_demo 에이전트는 반사 → 습관 → LLM 의 결정 층, 스킬 런타임, 그리고 모든 경유점을 검사하는 안전 하네스로 이루어집니다. LLM 은 "무엇을" 고르고, "어떻게 안전하게" 는 평범한 코드가 책임집니다.
확인 문제#
- 깊이 카메라가 영상 중심에서 오른쪽으로 60 픽셀 떨어진 점의 깊이를 0.5 m 로 읽었습니다. fx = 600 일 때 그 점은 카메라 중심선에서 옆으로 몇 cm 떨어져 있나요?
- 손목 카메라(eye-in-hand)로 본 물체를 로봇 좌표로 바꿀 때, 고정 카메라(eye-to-hand)보다 추가로 필요한 정보는 무엇인가요?
- 그리퍼가 물체마다 항상 같은 방향으로 약 1.5 cm 빗나갑니다. YOLO 문제와 핸드-아이 보정 문제 중 어느 쪽을 먼저 의심해야 할까요? 그 이유는?
- 에이전트 구조에서 LLM 이 팔 관절에 직접 각도를 보내지 않고
pick_and_place같은 정해진 스킬만 호출하게 하는 이유를 두 가지 말해 보세요.
xc = 60 × 0.5 / 600 = 0.05 m, 즉 5 cm 입니다.- 물체를 본 순간의 손목 자세, 즉 그때의 관절각으로 계산한 FK(
base_T_ee(q))입니다. 손목-카메라 변환(ee_T_cam)은 보정으로 한 번 구해 두고, 매번 FK 와 곱합니다. - 핸드-아이 보정입니다. YOLO 상자가 틀리면 물체·장면마다 오차가 들쭉날쭉하지만, 외부 파라미터가 틀리면 모든 점이 같은 변환으로 밀려서 항상 같은 방향·같은 크기로 빗나갑니다. 마커 길이 설정과 샘플 수를 먼저 확인하세요.
- ① 스킬 안에서 IK·관절 한계·최저 높이 같은 안전 검사를 평범한 코드로 확실히 할 수 있습니다. ② LLM 이 엉뚱한 답을 내도 허용 목록 밖의 동작은 실행되지 않습니다. (그 밖에: 스킬은 검증된 동작이라 재사용·기록이 쉽고, LLM 은 느려서 50 Hz 제어 루프를 맡을 수 없습니다.)