diff --git a/.gitattributes b/.gitattributes index 0b249cb6..aa8c254a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1 @@ *.step binary -2ARM_ROBOT/**/*.sh text eol=lf diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 7e5f695a..00000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,13 +0,0 @@ -version: 2 -updates: - - package-ecosystem: github-actions - directory: "/" - schedule: - interval: weekly - open-pull-requests-limit: 5 - - - package-ecosystem: pip - directory: "/" - schedule: - interval: weekly - open-pull-requests-limit: 5 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index f366297e..00000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,36 +0,0 @@ -## 변경 목적 - - - -## 변경 범위 - - - -## 협업 추적 - -- 작업 환경: -- Branch writer: -- Base SHA: -- Remote head SHA: -- Local verified SHA: - -## 검증 - - - -- [ ] 관련 단위 테스트를 실행했다. -- [ ] SIM, MOCK, HW 결과를 구분했다. -- [ ] 비밀값, 장치 ID, 개인 calibration, 원시 dataset을 포함하지 않았다. -- [ ] 검증한 SHA와 PR의 remote head SHA가 같다. -- [ ] 로컬·원격 실행에서 무승인 serial open, torque/EEPROM 변경, motor jog, - 실물 ROS actuator 명령을 실행하지 않았다. - -### 실물 하드웨어 접근 - -- 접근 여부: -- 사람이 승인한 정확한 명령과 안전 조건: -- 실제 결과: - -## 위험 및 되돌리기 - - diff --git a/.github/scripts/check_markdown_links.py b/.github/scripts/check_markdown_links.py new file mode 100755 index 00000000..bfc18371 --- /dev/null +++ b/.github/scripts/check_markdown_links.py @@ -0,0 +1,119 @@ +"""Fail when a relative Markdown link or #anchor in this repository does not resolve. + +Usage: python3 .github/scripts/check_markdown_links.py [repo_root] + +Paths are matched with exact letter case, the way GitHub serves them. +Links inside fenced code blocks and absolute URLs are not checked. +""" +from __future__ import annotations + +import os +import re +import sys +import urllib.parse +from pathlib import Path + +FENCE = re.compile(r"^(\s*)(```|~~~)") +INLINE = re.compile( + r"(!?)\[((?:[^\[\]]|\[[^\[\]]*\])*)\]\(\s*(<[^>]*>|[^)\s]+)((?:\s+(?:\"[^\"]*\"|'[^']*'))?)\s*\)" +) +REF_DEF = re.compile(r"^ {0,3}\[[^\]]+\]:[ \t]*(<[^>]*>|\S+)") +HTML_ATTR = re.compile(r"\b(?:src|href)\s*=\s*([\"'])([^\"']*)\1", re.I) +SKIP = ("http://", "https://", "mailto:", "tel:", "data:", "ftp:", "/") + + +def prose_chunks(text: str): + in_code, marker, buf = False, "", [] + for line in text.splitlines(): + m = FENCE.match(line) + if not in_code and m: + in_code, marker = True, m.group(2) + continue + if in_code: + if line.strip().startswith(marker): + in_code = False + continue + buf.append(line) + return "\n".join(buf) + + +def slug(heading: str) -> str: + text = re.sub(r"!?\[([^\]]*)\]\([^)]*\)", r"\1", heading) + text = re.sub(r"<[^>]+>", "", text).replace("`", "").replace("*", "") + return re.sub(r"[^\w\- ]", "", text.strip().lower()).replace(" ", "-") + + +def anchors(md: Path, cache: dict) -> set[str]: + if md not in cache: + found, seen = set(), {} + prose = prose_chunks(md.read_text(encoding="utf-8", errors="ignore")) + for line in prose.splitlines(): + m = re.match(r"^\s{0,3}#{1,6}\s+(.*?)\s*#*\s*$", line) + if m: + s = slug(m.group(1)) + n = seen.get(s, 0) + found.add(s if n == 0 else f"{s}-{n}") + seen[s] = n + 1 + found |= set(re.findall(r" bool: + current = root + for part in rel.parts: + if part in ("", "."): + continue + if part == "..": + current = current.parent + continue + try: + names = os.listdir(current) + except (FileNotFoundError, NotADirectoryError): + return False + if part not in names: + return False + current = current / part + return True + + +def main() -> int: + root = Path(sys.argv[1] if len(sys.argv) > 1 else ".").resolve() + broken, cache = [], {} + for md in sorted(root.rglob("*.md")): + if ".git" in md.parts: + continue + prose = prose_chunks(md.read_text(encoding="utf-8", errors="ignore")) + targets = [m.group(3) for m in INLINE.finditer(prose)] + targets += [m.group(1) for line in prose.splitlines() if (m := REF_DEF.match(line))] + targets += [m.group(2) for m in HTML_ATTR.finditer(prose)] + here = md.relative_to(root).as_posix() + for t in targets: + t = t[1:-1] if t.startswith("<") and t.endswith(">") else t + if not t or t.lower().startswith(SKIP) or "://" in t: + continue + path, _, anchor = t.partition("#") + path = path.split("?")[0] + if not path: + if anchor and urllib.parse.unquote(anchor).lower() not in anchors(md, cache): + broken.append(f"{here}: {t} (anchor)") + continue + rel_from_root = Path(os.path.normpath(md.parent.relative_to(root) / urllib.parse.unquote(path))) + if rel_from_root.parts and rel_from_root.parts[0] == "..": + broken.append(f"{here}: {t} (outside repository)") + continue + if not exists_exact(root, rel_from_root): + broken.append(f"{here}: {t}") + continue + target = root / rel_from_root + if anchor and target.suffix.lower() == ".md": + if urllib.parse.unquote(anchor).lower() not in anchors(target, cache): + broken.append(f"{here}: {t} (anchor)") + for b in broken: + print(f"broken link: {b}") + print(f"Markdown links checked; broken: {len(broken)}") + return 1 if broken else 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.github/workflows/cowork-smoke.yml b/.github/workflows/cowork-smoke.yml deleted file mode 100644 index 6261ef82..00000000 --- a/.github/workflows/cowork-smoke.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Cowork smoke tests - -on: - pull_request: - branches: - - main - workflow_dispatch: - -permissions: - contents: read - -concurrency: - group: cowork-smoke-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - hardware-free-tests: - runs-on: ubuntu-latest - timeout-minutes: 15 - steps: - - name: Check out repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Set up Python - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 - with: - python-version: "3.12" - - - name: Install shell test tooling - run: | - sudo apt-get update - sudo apt-get install --yes --no-install-recommends bats shellcheck - - - name: Install pinned Python dependency - run: "python3 -m pip install --only-binary=:all: --require-hashes -r requirements-cowork.txt" - - - name: Run hardware-free verification - run: scripts/verify-hardware-free diff --git a/.github/workflows/hardware-free-tests.yml b/.github/workflows/hardware-free-tests.yml new file mode 100644 index 00000000..ed1a5a7c --- /dev/null +++ b/.github/workflows/hardware-free-tests.yml @@ -0,0 +1,47 @@ +name: Hardware-free tests + +on: + pull_request: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: hardware-free-tests-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + hardware-free-tests: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 + with: + python-version: "3.12" + + - name: Install NumPy for the sim-first evaluator tests + run: "python3 -m pip install --disable-pip-version-check --only-binary=:all: numpy==2.3.5" + + - name: SO-101 sim-first unit tests + working-directory: mini-projects + env: + PYTHONDONTWRITEBYTECODE: "1" + run: python3 -m unittest discover -s dapier_sim_first/test -v + + - name: CardBench unit tests + working-directory: mini-projects/casino_dealer + env: + PYTHONDONTWRITEBYTECODE: "1" + run: python3 -m unittest discover -s test -v + + - name: Markdown links + run: python3 .github/scripts/check_markdown_links.py diff --git a/.github/workflows/hardware-ready.yml b/.github/workflows/hardware-ready.yml deleted file mode 100644 index 3c17d512..00000000 --- a/.github/workflows/hardware-ready.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: Hardware-ready offline gates - -on: - push: - branches: - - "pro/**" - paths: - - "2ARM_ROBOT/research/**" - - "2ARM_ROBOT/docs/**" - - "contracts/**" - - "scripts/replay-localization-scenarios" - - "scripts/verify-architecture-boundaries" - - "scripts/verify-hardware-free" - - "so101_ros2/dapier_localization_core/**" - - "so101_ros2/dapier_so101_core/**" - - "so101_ros2/dapier_safety_core/**" - - ".github/workflows/hardware-ready.yml" - pull_request: - paths: - - "2ARM_ROBOT/research/**" - - "2ARM_ROBOT/docs/**" - - "contracts/**" - - "scripts/replay-localization-scenarios" - - "scripts/verify-architecture-boundaries" - - "scripts/verify-hardware-free" - - "so101_ros2/dapier_localization_core/**" - - "so101_ros2/dapier_so101_core/**" - - "so101_ros2/dapier_safety_core/**" - - ".github/workflows/hardware-ready.yml" - workflow_dispatch: {} - -permissions: - contents: read - -concurrency: - group: "hardware-ready-${{ github.workflow }}-${{ github.ref }}" - cancel-in-progress: true - -jobs: - hardware-free-tests: - runs-on: ubuntu-latest - timeout-minutes: 20 - steps: - - name: Check out repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Set up Python - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 - with: - python-version: "3.12" - - - name: Install shell test tooling - run: | - sudo apt-get update - sudo apt-get install --yes --no-install-recommends bats shellcheck - - - name: Install pinned Python dependency - run: "python3 -m pip install --only-binary=:all: --require-hashes -r requirements-cowork.txt" - - - name: Run hardware-free verification - run: scripts/verify-hardware-free diff --git a/.github/workflows/mujoco-sim.yml b/.github/workflows/mujoco-sim.yml deleted file mode 100644 index 5c3db593..00000000 --- a/.github/workflows/mujoco-sim.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: MuJoCo simulation tests - -on: - pull_request: - paths: - - "2ARM_ROBOT/sim/mobile_dual_so101/**" - - "2ARM_ROBOT/sim/turtlebot3_waffle_pi/**" - - "so101/integrations/lerobot_v0_6_so101_mujoco/overlay/**" - - "scripts/setup-mujoco-sim" - - "scripts/verify-mujoco-headless" - - "requirements-mujoco.in" - - "requirements-mujoco.txt" - - ".github/workflows/mujoco-sim.yml" - workflow_dispatch: - -permissions: - contents: read - -concurrency: - group: mujoco-sim-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - mujoco-headless-tests: - runs-on: ubuntu-latest - # Full copied-physics regressions exceeded 20 minutes on a hosted runner. - timeout-minutes: 45 - env: - DAPIER_SOURCE_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} - steps: - - name: Check out repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Set up Python - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 - with: - python-version: "3.12" - - - name: Install headless EGL runtime - run: | - sudo apt-get update - sudo apt-get install --yes --no-install-recommends libegl1 libgl1-mesa-dri - - - name: Install pinned MuJoCo dependencies - run: | - python3 -m pip install --only-binary=:all: --require-hashes -r requirements-mujoco.txt - # This local environment is not part of the upstream LeRobot wheel. - cp -R so101/integrations/lerobot_v0_6_so101_mujoco/overlay/src/lerobot/envs/so101_mujoco \ - "$(python3 -c 'import lerobot.envs; print(lerobot.envs.__path__[0])')/" - - - name: Fetch and verify pinned SO-101 assets - run: | - asset_path="$(scripts/setup-mujoco-sim --dest "$RUNNER_TEMP/so101-assets")" - echo "DAPIER_SO101_MJCF=$asset_path" >> "$GITHUB_ENV" - - - name: Record compiled model evidence - run: | - python3 2ARM_ROBOT/sim/mobile_dual_so101/model_reproducibility.py \ - --repo "$GITHUB_WORKSPACE" --output "$RUNNER_TEMP/mujoco-artifacts/reproducibility" - - - name: Upload compiled model evidence - if: always() - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 - with: - name: mujoco-model-evidence-${{ env.DAPIER_SOURCE_HEAD_SHA }} - path: ${{ runner.temp }}/mujoco-artifacts/reproducibility - if-no-files-found: error - retention-days: 14 - - - name: Run headless tests and canonical renders - env: - MUJOCO_GL: egl - PYOPENGL_PLATFORM: egl - run: scripts/verify-mujoco-headless --artifact-dir "$RUNNER_TEMP/mujoco-artifacts" - - - name: Upload MuJoCo review artifacts - if: always() - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 - with: - name: mobile-dual-so101-mujoco-${{ env.DAPIER_SOURCE_HEAD_SHA }} - path: ${{ runner.temp }}/mujoco-artifacts - if-no-files-found: error - retention-days: 14 diff --git a/.github/workflows/robotwin-contract.yml b/.github/workflows/robotwin-contract.yml deleted file mode 100644 index ed577ecb..00000000 --- a/.github/workflows/robotwin-contract.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: RoboTwin canonical contract - -on: - pull_request: - paths: - - "2ARM_ROBOT/robotwin/**" - - "2ARM_ROBOT/src/shoe_sorting_data/**" - - ".github/workflows/robotwin-contract.yml" - workflow_dispatch: - -permissions: - contents: read - -jobs: - robotwin-contract-tests: - runs-on: ubuntu-24.04 - timeout-minutes: 10 - env: - PYTHONDONTWRITEBYTECODE: "1" - steps: - - name: Check out review tree without persisted credentials - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - fetch-depth: 0 - persist-credentials: false - - name: Set up isolated Python - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 - with: - python-version: "3.13" - - name: Prepare temporary directories - run: | - work="$RUNNER_TEMP/robotwin-contract" - mkdir -p "$work/tmp" "$work/cache" - printf 'TMPDIR=%s\nXDG_CACHE_HOME=%s\n' "$work/tmp" "$work/cache" >> "$GITHUB_ENV" - - name: Install CPU-only fixture dependencies on hosted runner - run: >- - python -m pip install --disable-pip-version-check --no-cache-dir - --only-binary=:all: - numpy==2.3.5 h5py==3.15.1 opencv-python-headless==4.13.0.92 - - name: Run synthetic contracts and preservation regressions - run: | - python -B 2ARM_ROBOT/robotwin/test_contract.py - python -B 2ARM_ROBOT/robotwin/test_convert_episode.py - python -B -m unittest discover -s 2ARM_ROBOT/robotwin -p test_merge_regressions.py -v - - name: Check native ACT checkpoint boundary without hardware - env: - PYTHONPATH: 2ARM_ROBOT/src/shoe_sorting_data - run: >- - python -B -m unittest discover -s 2ARM_ROBOT/src/shoe_sorting_data/test - -p test_dapier_native_act.py -v diff --git a/.gitignore b/.gitignore index 57d12644..cfb0c890 100644 --- a/.gitignore +++ b/.gitignore @@ -4,26 +4,23 @@ **/*.local.env **/__pycache__/ **/.ruff_cache/ -casino_dealer/build/ -casino_dealer/*.egg-info/ **/robot.pkl **/*.part +*.oni .local-workspaces/ -2ARM_ROBOT/config/local/ -onshape/jdcobot100/assets/ -onshape/jdcobot100/jdcobot100.urdf -exam_ws/build/ -exam_ws/install/ -exam_ws/log/ -turtlebot3_ws/build/ -turtlebot3_ws/install/ -turtlebot3_ws/log/ -ros_dd_ws/build/ -ros_dd_ws/install/ -ros_dd_ws/log/ + +mini-projects/casino_dealer/build/ +mini-projects/casino_dealer/*.egg-info/ +ros2-practice/jdcobot100_sim/onshape/assets/ +ros2-practice/jdcobot100_sim/onshape/jdcobot100.urdf +ros2-practice/turtlebot3_ws/build/ +ros2-practice/turtlebot3_ws/install/ +ros2-practice/turtlebot3_ws/log/ +ros2-practice/ros_dd_ws/build/ +ros2-practice/ros_dd_ws/install/ +ros2-practice/ros_dd_ws/log/ # ROBOTIS-GIT upstream clones -- vendor code, not ours to track -turtlebot3_ws/src/DynamixelSDK/ -turtlebot3_ws/src/turtlebot3/ -turtlebot3_ws/src/turtlebot3_msgs/ -turtlebot3_ws/src/turtlebot3_simulations/ -*.oni +ros2-practice/turtlebot3_ws/src/DynamixelSDK/ +ros2-practice/turtlebot3_ws/src/turtlebot3/ +ros2-practice/turtlebot3_ws/src/turtlebot3_msgs/ +ros2-practice/turtlebot3_ws/src/turtlebot3_simulations/ diff --git a/2ARM_ROBOT/.gitignore b/2ARM_ROBOT/.gitignore deleted file mode 100644 index e5d07b9d..00000000 --- a/2ARM_ROBOT/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -build/ -install/ -log/ -/output/ -/config/99-dapier-hardware.rules -.venv/ -**/__pycache__/ -**/.pytest_cache/ -**/.ruff_cache/ diff --git a/2ARM_ROBOT/README.md b/2ARM_ROBOT/README.md index ffaf198c..ef252bda 100644 --- a/2ARM_ROBOT/README.md +++ b/2ARM_ROBOT/README.md @@ -1,106 +1,16 @@ -# SWEEPICK | 이동형 양팔 청소 로봇의 조작·학습·Sim-to-Real +# 2ARM_ROBOT → SWEEPICK 저장소로 이동 -청소 경로의 작은 물체를 인식해 옮기고, 확보된 공간을 청소하는 저비용 모바일 매니퓰레이터다. DAPIER 4인 팀 프로젝트에서 SO-101 양팔·카메라·이동 베이스와 학습 기반 제어를 연결했다. 물체 이동·청소·저전력 복귀·도킹·충전은 시뮬레이션 구현을 완료했다. 실물에서는 **2026-11-01에 이동 → 양팔 ACT 집기 → B 지점 놓기 → HOME 전체 과정을 사람 개입 없이 처음 성공**했고, 최종 발표 기준 작업공간 안 물체별 10회 집기는 3cm 큐브 7회, 휴지 8회, 원형 고무 7회였다(물체별 추가 파인튜닝 없음). 청소·충전의 실물 연결과 연속 성공률 측정은 남아 있다. +SWEEPICK(최종 팀 프로젝트)의 코드와 문서는 2026-11-03에 별도 저장소로 옮겼다. -**전형주는 SIM 데이터·도메인 랜덤화, 실물 Sim-to-Real 통합, 베이스↔로봇팔 ROS 2 연동을 주도했다. ACT는 팀원과 공동 개발·검증했고, Visual SLAM은 협업으로 참여했다.** 영역별 참여도는 [저장소 첫 화면의 역할 표](../README.md#내-역할과-참여도)에 있다. 아래 ACT 수치는 공동 실험 결과다. 외부 ACT·RoboTwin 구현과 팀 기여의 출처도 함께 남긴다. +**→ [Alpenj/sweepick](https://github.com/Alpenj/sweepick)** -[포트폴리오·실물 영상](https://julianjeonresume.netlify.app/) · [CUSTOM ACT V1 상세](docs/CUSTOM_ACT_V1_KO.md) · [그림·영상](https://julianjeonresume.netlify.app/research/custom-act-v1/) · [이전 조작 연구](docs/RESEARCH_SUMMARY_KO.md) · [실물 검토 브랜치](https://github.com/Alpenj/DAPIER/tree/pro/sweepick-review-20261015/2ARM_ROBOT/sweepick) · [이동 베이스(팀원 저장소)](https://github.com/shouttt1320/Dapier_project_visaul_slam) +| 찾던 내용 | 새 위치 | +|---|---| +| 프로젝트 개요·역할·결과 | [sweepick README](https://github.com/Alpenj/sweepick#readme) | +| CUSTOM ACT V1 기록 | [docs/CUSTOM_ACT_V1_KO.md](https://github.com/Alpenj/sweepick/blob/main/docs/CUSTOM_ACT_V1_KO.md) | +| 이전 조작 연구 | [docs/RESEARCH_SUMMARY_KO.md](https://github.com/Alpenj/sweepick/blob/main/docs/RESEARCH_SUMMARY_KO.md) | +| MuJoCo 양팔 모델 | [sim/mobile_dual_so101](https://github.com/Alpenj/sweepick/tree/main/sim/mobile_dual_so101) | -![SWEEPICK 실물 시연. 로봇 시점의 헤드 깊이와 양손목 RGB로 본 ACT 집기](../docs/media/sweepick-real-act-cams.gif) - -실물 ACT 실행 · 왼쪽부터 헤드 깊이, 왼손목 RGB, 오른손목 RGB · [실물 전체 과정 영상](https://julianjeonresume.netlify.app/assets/video/sweepick-real-full-run.mp4) - -![CUSTOM ACT V1의 대표 SIM 실행과 손목 RGB 관측](../docs/media/custom-act-sim.gif) - -고정 epoch30 · Test mid00_01 · [원본 영상](https://julianjeonresume.netlify.app/assets/custom-act-v1/sim-test-mid00_01.mp4) · [수집 외형](../docs/media/collection-appearances.png) · [학습 곡선](../docs/media/training-curves.png) · [영상·그림 출처](../docs/media/README.md) - -[핵심 결과](#custom-act-v1-데이터에서-실행까지) · [시스템 구성](#시스템-구성) · [실물 연동](#실물-데이터와-실행기-연동) · [코드 위치](#구성과-코드-위치) · [실행 안내](#실행-안내와-과거-실습) · [문서 목록](docs/README.md) - -## 시스템 구성 - -관측과 행동의 단위·순서·시각을 맞추고, 학습된 정책의 출력을 실행 기록과 대조한다. 아래는 연구와 실행의 역할 구분이다. 검토 브랜치의 실물 실행기와 공개 `main`의 연구 코드는 아래 코드 위치에서 따로 연결한다. - -![SWEEPICK 관측·학습·명령·SIM 평가와 실물 연동 구성](../docs/media/sweepick-system.png) - -[구성도 소스](../docs/media/sweepick-system.mmd) - -이동 베이스의 전면 Astra RGB-D는 Visual SLAM, 작업공간 OS30A RGB-D와 손목 RGB는 조작 관측에 사용한다. 센서의 역할과 관절·좌표 계약을 구분해 연결한다. - -## CUSTOM ACT V1: 데이터에서 실행까지 - -학습 손실만으로 로봇의 작업 완료를 판단하기 어려웠다. 시연을 어떤 기준으로 나누었는지, 정책이 어떤 관측을 받는지, 예측한 행동 청크가 실제 명령으로 어떻게 이어지는지를 함께 기록했다. - -| 단계 | 수행 내용 | 확인한 결과 | -|---|---|---| -| 데이터 수집 | 원본 113개 궤적의 동작을 유지하며 손목 RGB의 물체색·배경·테이블 텍스처·조명을 5개 외형으로 구성. 관측·상태·명령·시각 정합 확인 | 565개 뷰. 궤적 train/val/test=59/29/25, 뷰=295/145/125. 같은 궤적의 외형은 동일 분할 유지 | -| 학습 | 양손목 RGB·head PCD·관절 상태를 입력으로 사용하는 ACT 학습 | 200 epoch 완료. best validation은 epoch103, prior normalized L1=0.01553 | -| 추론 | 별도로 고정한 epoch30 체크포인트, temporal ensemble ON(k=0.01)으로 SIM 폐루프 평가 | 143개 위치 각 1회, 132/143 완료(92.3%). Test 위치는 27/31(87.1%) | -| 분석 | 예측 청크의 target 시점·가중치·관절 명령·실측값·phase·영상 연결 | 실패 11회 중 grasp/lift 7회, stow 4회. 청크 집계 결과를 저장 CSV에서 재구성해 확인 | - -폐루프 완료 기준은 **30 mm 이상 Lift → Stow → 물리 2초 Hold**다. 외형은 랜덤화하고 물체 크기(40 mm)·yaw·물성·카메라 보정은 고정했다. 위 92.3%는 이 SIM 조작 평가의 관측값이다. 200 epoch 최종 모델의 폐루프 결과, 실물 성공률이나 전체 청소 시나리오 성공률과 합산하지 않는다. - - -## 실패를 조사한 방법 - -학습 손실과 작업 완료를 따로 확인했다. 고정 epoch30 SIM 평가에서 완료하지 못한 11회는 grasp/lift 7회와 stow 4회로 나눴다. 예측값만 보고 원인을 정하지 않고 query·target 시점, 집계 가중치, 실제 전송 명령, 실측 관절과 영상을 같은 시간축에서 읽었다. - -기존 first-action-only 실행과 temporal ensemble의 차이도 저장 CSV로 재구성했다. 한 위치의 ON/OFF 비교와 저장 데이터의 정합성 검사는 전체 성능 향상이나 새 실물 성공률로 계산하지 않았다. [청크 집계와 비교 조건](docs/CUSTOM_ACT_V1_KO.md#4-분석--예측-청크가-명령이-되는-과정을-추적했다)에 검사 수치와 한계를 남겼다. - -## 실물 데이터와 실행기 연동 - -| 트랙 | 확인된 결과 | 지표의 의미 | -|---|---|---| -| Native 교사 콘솔, 10/18~26 | 120/198회 집기·이동·놓기·복귀 성공 판정 | 규칙 기반 교사 실행 기록 | -| 실물 데이터 ACT TJJ v2 | train46/val7 run, 관절 예측오차3.30°·epoch690 | 보류 데이터 예측 평가 | -| DAgger, 10/23~25 | 정체 후 교사 인계 교정 시연16개 | 정책이 방문한 상태의 교정 데이터 | -| 10/27 후속 연동 | ACT30·실물 실행기의 관측/절대행동·앙상블·STOP·feedback·파지 증거 경로 정합 확인 | 저장 기록·코드·정지 shadow 단계 | - -10/08의 첫 무인 왼팔 집기, 10/18~23의 약170회 자동 교사 실행, 10/23의 ACT 접근 뒤 교사 마무리 기록은 위 후속 실험에 이르는 개발 이력이다. 10/26까지의 TJJ 실물 기록에서 ACT 단독 집기 완료는 관찰되지 않았다. 신규 CUSTOM ACT V1 SIM 폐루프 완료와 이 실물 기록을 혼동하지 않는다. - -10/27의 새 IR2 정지 shadow는 양팔 각300 prefix를 30초 동안 확인했고 위반0·모터쓰기0이었다. 기록상 full180s=false/all_pass=false이므로 전체 검증 통과로 표시하지 않는다. 공개 가능한 조건과 후속 요약은 [CUSTOM ACT V1 실물 연동 기록](docs/CUSTOM_ACT_V1_KO.md#실물-연동과-후속-기록)에 모았다. - -10/16 이후 상체 실행기는 이동 베이스를 직접 움직이지 않고 정지 요청을 전달한다. Visual SLAM은 협업으로 참여했으며 이동·도킹 모듈과 상체 실행기의 연동 범위를 구분한다. 이동·도킹 공동 코드의 출처와 변경 이력도 유지한다. - -## 구성과 코드 위치 - -| 영역 | 다루는 내용 | 코드·문서 | -|---|---|---| -| 실물 집기 실행기 | 관측 → 손목 영상 정렬 → 하강 → 집기 → 들기 → 운반 → 놓기 → 복귀, 단일 장치 소유·정지 조건·웹 콘솔·저장 기록 검수 | [2ARM_ROBOT/sweepick (검토 브랜치, main 미병합)](https://github.com/Alpenj/DAPIER/tree/pro/sweepick-review-20261015/2ARM_ROBOT/sweepick) | -| 양팔 모델 | SO-101 양팔, 기본형·PGripper 구성, 이동형·책상형 장면 | [MuJoCo 모델](sim/mobile_dual_so101/README.md), [기준 배치](sim/mobile_dual_so101/INTEGRATION_SCENES.md) | -| 데이터 (이전 단계) | 관절 순서·단위, 영상과 시각, 에피소드 품질과 출처 | [shoe_sorting_data](src/shoe_sorting_data/) | -| 정책 (이전 단계) | 행동 묶음, CVAE 학습·체크포인트·추론을 다루는 작은 native ACT 구현 | [dapier_native_act.py](src/shoe_sorting_data/shoe_sorting_data/dapier_native_act.py) | -| 조작 검증 | 접촉·하중 지지·끝점 오차·미끄러짐을 분리한 비교 | [연구 기록과 고정 소스](docs/RESEARCH_SUMMARY_KO.md) | -| 이동·도킹 | RTAB-Map RGB-D 매핑·localization, Nav2와 듀얼 ArUco 도킹 | [팀 이동 모듈](https://github.com/shouttt1320/Dapier_project_visaul_slam) | -| 장치 실행 | 보정·명령 제한·피드백·정지 조건 | [공통 하드웨어 안전 절차](../docs/HARDWARE_SAFETY_KO.md) | - -native ACT 코드와 LeRobot을 재사용한 다른 학습 실험은 같은 구현으로 취급하지 않는다. native 구현은 공식 ACT 체크포인트 호환을 주장하지 않는다. - -### 장비 역할과 과거 설정 - -최근 조작 연구에서는 SO-101 양팔·PGripper, 작업공간용 OS30A RGB-D와 양손목 RGB를 다룬다. 이동 베이스 전면 중앙의 Astra RGB-D는 Visual SLAM용 별도 센서다. 두 RGB-D 센서의 역할을 섞지 않는다. - -`main`의 [hardware_roles.json](config/hardware_roles.json)은 `effective_date=2026-09-03`이며 작업공간 카메라를 H201로 기록한 **당시 구성**이다. 일부 모델 README와 실습 절차도 그 배치를 설명한다. 이후 OS30A·PGripper 연구의 설명을 읽었다고 이 설정 파일이나 모든 실행기가 갱신된 것으로 해석하지 않는다. 실제 실행에는 해당 장치와 소스 revision의 설정·보정이 필요하다. 이 문서 갱신에서는 설정이나 런타임을 변경하지 않았다. - -## 공개 구현과 실험 결과의 구분 - -| 근거 | 확인한 범위 | 별도로 남은 것 | -|---|---|---| -| `main`과 병합된 [PR #64](https://github.com/Alpenj/DAPIER/pull/64)·[PR #65](https://github.com/Alpenj/DAPIER/pull/65) | 접촉·중력 wrench 분석, 그리퍼 단독 파지와 솔버 민감도 진단·회귀 기록 | 기본 실행기의 전체 조작 성공과 실물 성공 | -| 2026-09-21 [후보 커밋 f6b61db](https://github.com/Alpenj/DAPIER/commit/f6b61dbc81273f6d775d96f7950461fac9113cbb), [PR #68](https://github.com/Alpenj/DAPIER/pull/68) | normal HOME→접근→CLOSE→LIFT→HOLD의 단일 연속 SIM. 18,203 step / 36.406 s, HOLD 3.000 s | 여러 초기조건의 신뢰도, ACT 정책 성공, 실물 성공. PR은 닫힘·미병합 | -| [PR #69](https://github.com/Alpenj/DAPIER/pull/69) | 센서·손목 후보와 제한된 native 실행 경로, 지연·정지 MOCK 피드백 연결 기록 | 센서 기반 실물 조작 검증. PR은 닫힘·미병합 | -| 2026-09-30 [후보 커밋 3720da1](https://github.com/Alpenj/DAPIER/commit/3720da1ee678793480035cbfa3db96a546c73671), [PR #70](https://github.com/Alpenj/DAPIER/pull/70) | 저장 관절값의 변환 비교와 팔꿈치 기준 측정 준비 | 실물 영점 채택·동적 검증. PR은 닫힘·미병합이며 독립 실행에 필요한 비공개 입력은 미포함 | -| [이동 모듈 소스](https://github.com/shouttt1320/Dapier_project_visaul_slam/tree/3942b81a0d3131aeed6d97fad83c2a5b389ec18a) | RTAB-Map localization·Nav2·정밀 도킹을 구성한 코드 | 현재 장비에서의 재현, 양팔 통합 과제와 실제 전기적 충전 성공 | - -PR 상태 확인일은 2026-10-08이다. 연구 PR을 닫은 것과 `main`에 병합한 것은 다르다. 위 후보 결과는 고정 커밋의 연구 이력으로 남기며, 저장소를 새로 받은 사람이 기본 실행만으로 같은 결과를 얻는다고 안내하지 않는다. - -## 실행 안내와 과거 실습 - -> **`main` 코드의 실물 실행 상태 (2026-09-04 안전 고지 유지):** 이 저장소 `main`의 2ARM_ROBOT 실행기에서 physical motion과 camera streaming은 local safety integration(connected-endpoint identity binding, 독립 device-side watchdog, 카메라 serial/cryptographic binding 확인)이 끝날 때까지 비활성화돼 있다. sweepick 실물 실행은 검토 브랜치의 별도 실행기(단일 owner·정지 조건·현장 승인)로 하며, 그 코드는 아직 `main`에 병합하지 않았다. - -현재 코드의 사용법은 해당 하위 모듈 README와 [안전 절차](../docs/HARDWARE_SAFETY_KO.md)를 먼저 확인한다. 개인 보정, 장치 식별자와 원시 영상·데이터는 공개하지 않는다. 문서의 예시 명령은 실물 실행 승인이 아니다. - -초기 ROS 2 환경 점검, 합성 토픽 녹화, golden episode 생성과 장치 snapshot 절차는 [갱신 전 README의 고정본](https://github.com/Alpenj/DAPIER/blob/9b1138dbc462d293cbde62961d4d3534a71b2061/2ARM_ROBOT/README.md)에 보존했다. 그 문서의 신발·H201 구성과 실행 제한은 당시 소스의 안내다. 과거 실행 절차를 최신 장비 설정으로 대신하지 않는다. - -외부 라이브러리와 자산은 각 폴더의 출처·라이선스를 따른다. Visual SLAM 협업 참여 범위와 공동 저장소의 외부·팀 기여 이력을 함께 보존한다. - -문서 구성 갱신: 2026-11-03(11/01 실물 첫 전체 성공, 최종 발표 물체별 결과, 역할 표 반영). 그 전 갱신: 2026-10-28. 실험 기록 기준: 2026-10-27. 새 CUSTOM ACT V1의 수집·학습·SIM 평가·분석을 추가하고 기존 실물·코드·후보 PR 기록은 보존했다. 런타임·물리 파라미터·보정값·테스트·의존성은 변경하지 않았다. +이 폴더의 커밋 이력은 DAPIER에 그대로 남아 있다: [변경 이력](https://github.com/Alpenj/DAPIER/commits/main/2ARM_ROBOT) · +[옮기기 직전 상태](https://github.com/Alpenj/DAPIER/tree/2399bfc531074f29e8c80905b6082b90f99cbd60/2ARM_ROBOT). +이미 공유된 링크가 끊기지 않도록 이 안내 파일을 남겨 둔다. diff --git a/2ARM_ROBOT/config/dual_so101_il.example.env b/2ARM_ROBOT/config/dual_so101_il.example.env deleted file mode 100644 index b746719c..00000000 --- a/2ARM_ROBOT/config/dual_so101_il.example.env +++ /dev/null @@ -1,22 +0,0 @@ -# Copy to dual_so101_il.local.env, then assign all four physical arm roles. -LEFT_FOLLOWER_PORT=UNASSIGNED -RIGHT_FOLLOWER_PORT=UNASSIGNED -LEFT_LEADER_PORT=UNASSIGNED -RIGHT_LEADER_PORT=UNASSIGNED - -LEFT_WRIST_CAMERA=/dev/dapier/left_wrist_rgb -RIGHT_WRIST_CAMERA=/dev/dapier/right_wrist_rgb - -CALIBRATION_DIR=${XDG_CONFIG_HOME:-$HOME/.config}/dapier/lerobot-calibration -FOLLOWER_ID=dapier_dual_follower -LEADER_ID=dapier_dual_leader -DATASET_REPO_ID=dapier-local/dual-so101-box-shoe -DATASET_ROOT=/home/dapier-jhj/dapier_datasets/dual-so101-box-shoe -TRAIN_OUTPUT=/home/dapier-jhj/dapier_training/dual-so101-box-shoe-act -TASK_DESCRIPTION="Open the shoe box with the right arm, then pick up the shoe with the left arm" -DATASET_FPS=15 -EPISODE_TIME_S=30 -RESET_TIME_S=15 -NUM_EPISODES=20 -TRAIN_STEPS=20000 -TRAIN_BATCH_SIZE=8 diff --git a/2ARM_ROBOT/config/hardware_roles.json b/2ARM_ROBOT/config/hardware_roles.json deleted file mode 100644 index c6f80be9..00000000 --- a/2ARM_ROBOT/config/hardware_roles.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "schema_version": "dapier.hardware-roles.v1", - "effective_date": "2026-09-03", - "platform": "SO101_dual_arm_on_turtlebot3_waffle_pi", - "devices": { - "left_arm": { - "model": "SO-101", - "stable_alias": "/dev/dapier/left_arm", - "identity": "controller_serial_private", - "hub_downstream_port": 3, - "motor_ids": [1, 2, 3, 4, 5, 6], - "calibration": "left_arm_individual" - }, - "right_arm": { - "model": "SO-101", - "stable_alias": "/dev/dapier/right_arm", - "identity": "controller_serial_private", - "hub_downstream_port": 4, - "motor_ids": [1, 2, 3, 4, 5, 6], - "calibration": "right_arm_individual" - }, - "left_gripper_rgb": { - "model": "USB UVC RGB", - "stable_alias": "/dev/dapier/left_wrist_rgb", - "identity": "hub_downstream_path", - "hub_downstream_port": 1, - "mission_role": "left_gripper_rgb" - }, - "right_gripper_rgb": { - "model": "USB UVC RGB", - "stable_alias": "/dev/dapier/right_wrist_rgb", - "identity": "hub_downstream_path", - "hub_downstream_port": 2, - "mission_role": "right_gripper_rgb" - }, - "workspace_rgbd": { - "model": "HP-ASC-H201", - "usb_id": "3438:0173", - "stable_alias": "/dev/dapier/workspace_rgbd", - "identity": "device_serial_private", - "mount": "tower_top_view", - "mission_role": "workspace_rgbd" - }, - "front_rgbd": { - "model": "Orbbec Astra S", - "usb_id": "2bc5:0402", - "stable_alias": "/dev/dapier/front_slam_rgbd", - "identity": "vid_pid_single_device", - "mount": "turtlebot3_front_camera_bracket", - "mission_role": "front_rgbd" - }, - "mobile_base": { - "model": "TurtleBot3 Waffle Pi", - "controller": "OpenCR", - "wheel_motor": "XM430-W210-T", - "edge_compute": "Raspberry Pi 4" - } - }, - "compute_roles": { - "raspberry_pi_4": "base control, lightweight sensor IO, health and watchdog", - "local_laptop": "Visual SLAM placement benchmark, perception, IL policy and LLM", - "llm_on_raspberry_pi": false - }, - "inactive_or_unverified": { - "lidar": "excluded from the active MuJoCo and Visual-SLAM sensor path; physical presence is not inferred", - "visual_slam_compute_host": "select after Raspberry Pi 4 and laptop latency/load measurement" - }, - "notes": [ - "Both arms are identical SO-101 arms for runtime commands; legacy LeRobot cache folder names do not define hardware roles.", - "Wrist camera left/right identity depends on fixed downstream hub ports because the cameras expose no unique serial.", - "No controller serial, password, calibration payload or personal device path is stored here." - ] -} diff --git a/2ARM_ROBOT/config/jdcobot_calibration/README.md b/2ARM_ROBOT/config/jdcobot_calibration/README.md deleted file mode 100644 index 9703e434..00000000 --- a/2ARM_ROBOT/config/jdcobot_calibration/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Legacy JDcobot calibration fixture - -이 폴더는 초기 JDcobot 테스트 fixture다. 현재 SO-101 좌우 calibration이나 실물 명령에 -사용하지 않는다. 현재 장비 역할은 상위 `hardware_roles.json`을 따른다. diff --git a/2ARM_ROBOT/config/jdcobot_calibration/right.json b/2ARM_ROBOT/config/jdcobot_calibration/right.json deleted file mode 100644 index e255481a..00000000 --- a/2ARM_ROBOT/config/jdcobot_calibration/right.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "schema_version": "dapier.jdcobot-calibration.v0.1", - "captured_at_local_date": "2026-08-24", - "arm_side": "right", - "controller_identity_sha256": "dbaaa6edc7dd65e79a3f1b100abf7892478b990628d34f942964412634535803", - "motor_order": { - "1": "base", - "2": "shoulder", - "3": "elbow", - "4": "wrist_pitch", - "5": "wrist_roll", - "6": "gripper_motor" - }, - "joint_zero_ticks": { - "1": 927, - "2": 1944, - "3": 2084, - "4": 2155, - "5": 1698 - }, - "joint_signs": { - "1": 1, - "2": 1, - "3": 1, - "4": 1, - "5": 1 - }, - "joint_sign_status": "provisional_existing_teleop_direction", - "zero_pose": "base forward; arm vertical; elbow and wrist straight", - "gripper": { - "motor_id": 6, - "open_tick": 1641, - "closed_tick": 1465, - "model_open_radians": 0.57, - "model_closed_radians": -0.57 - }, - "capture": { - "read_only": true, - "eeprom_writes_sent": false, - "all_hardware_error_status_zero": true, - "all_torque_off": true, - "maximum_arm_joint_drift_between_open_and_closed_capture_ticks": 4 - } -} diff --git a/2ARM_ROBOT/config/turtlebot3.env.example b/2ARM_ROBOT/config/turtlebot3.env.example deleted file mode 100644 index ddce2912..00000000 --- a/2ARM_ROBOT/config/turtlebot3.env.example +++ /dev/null @@ -1,8 +0,0 @@ -# Public, secret-free template. Copy to config/local/turtlebot3.env. -# Never add a password, token, private key, or serial identifier to this file. -TB3_HOST=CHANGE_ME -TB3_USER=user -TB3_SSH_PORT=22 -TB3_REMOTE_ROOT=/home/user/DAPIER -TB3_AUTH_MODE=ssh-key -TB3_SSH_KEY_PATH= diff --git a/2ARM_ROBOT/docs/2026-09-04-depth-box-pregrasp.md b/2ARM_ROBOT/docs/2026-09-04-depth-box-pregrasp.md deleted file mode 100644 index 9bba46d5..00000000 --- a/2ARM_ROBOT/docs/2026-09-04-depth-box-pregrasp.md +++ /dev/null @@ -1,142 +0,0 @@ -# H201 depth에서 오른팔 pre-grasp까지 연결한 기록 - -`record_id`: `DAPIER-2026-09-04-depth-box-pregrasp` - -## 내가 만들려는 동작 - -Manipulation 단계는 다음 순서로 고정한다. 이 앞뒤에는 Visual SLAM 이동이 들어가고, -팔을 움직일 때 모바일 베이스는 정지한다. - -1. top-view H201 depth로 박스를 본다. -2. 카메라 픽셀과 깊이를 로봇 base 기준 좌표로 바꾼다. -3. 오른팔을 박스 앞으로 보내고 오른손목 RGB로 오차를 보정한다. -4. 오른팔로 박스를 연다. -5. 왼팔을 박스 안으로 보내고 왼손목 RGB로 오차를 보정한다. -6. 왼팔로 신발을 들어 올린다. - -## 오늘 직접 연결한 범위 - -`vision_box_pregrasp.py`는 MuJoCo H201 카메라에서 640×460 metric depth를 렌더하고, -실물 calibration과 같은 형태의 intrinsics와 `base ← optical` transform으로 점군을 -복원한다. runtime 계산에는 박스 body pose와 segmentation ID를 넣지 않는다. - -뚜껑의 앞날개와 좌우 날개 때문에 점군 PCA만 쓰면 기본 장면에서도 방향이 약 16° -틀어졌다. depth 점군을 0~180°로 1°씩 돌려 최소면적 직사각형을 찾는 단순한 방법으로 -바꾸니 기본 장면의 박스 XY 중심 오차가 약 1.6 mm가 됐다. 이 오차는 검출 이후 -테스트에서만 MuJoCo 정답과 비교한 값이다. - -검출한 박스 pose와 실측 박스 치수로 로봇 쪽 `front tuck flap`의 중심선을 찾고, -그중 오른팔이 잡을 지점을 박스 중심선에서 오른쪽 90 mm로 정했다. 처음에는 옆쪽 -`dust flap`을 목표로 잘못 해석했고, MuJoCo 창에서 박스 본체 위로 팔이 가는 것을 보고 -앞날개 기준으로 수정했다. - -```text -앞날개 grasp 지점 전방 5 cm / 상단 9.5 cm -→ 전방 4 cm / 상단 7.5 cm -→ 전방 3 cm / 상단 5.5 cm -→ 전방 2 cm / 상단 4.5 cm -→ 집게를 연 채 전방 4 mm / 상단 5 mm 접촉점 -→ 집게 닫기 -``` - -각 목표마다 이전 관절 자세에서 오른팔 5축 DLS IK를 다시 풀고, 양팔·TurtleBot3 -본체·카메라 지지대·바닥뿐 아니라 박스 collision geom과의 거리도 검사한다. 마지막 -목표만 한 번 계산해 보내지 않는 이유는 이후 각 구간 사이에 오른손목 RGB 재관측을 -끼워 넣기 위해서다. 앞날개는 시각용 판이 아니라 충돌 geom으로 바꿨으며, 마지막 -접촉 구간에서만 목표 날개와의 접촉을 허용하고 나머지 박스 벽 충돌은 계속 막는다. - -## 실행 결과 - -```text -기본 박스 중심 추정: (418.5, -0.5) mm -기본 MuJoCo 정답: (420.0, 0.0) mm -기본 XY 중심 오차: 약 1.6 mm -앞날개 오른팔 grasp: (286.0, -90.5, 105.7) mm -박스 이동/yaw 시험: 27/27 (x/y ±30 mm, yaw ±5°) -오른팔 IK 구간: 4/4 수렴 -최대 IK residual: 0.474 mm -박스 포함 경로 검사: 4/4 통과, pre-grasp 요구 clearance 5 mm -앞날개 접촉 IK: 최대 residual 0.446 mm, 모든 조합 접촉 1건 이상 -손목 RGB 지연 이동: 박스 +10 mm 이동 시 초기 오차 10 px 초과 -손목 RGB 1회 보정: 보정 후 앞날개 edge 오차 3 px 미만 -실물 명령: 없음 -``` - -손목 RGB 보정은 판지와 바닥의 색 경계에서 앞날개 모서리 row를 검출한다. 현재 무늬 없는 -판지가 근접 화면을 거의 채우므로 손목 RGB 하나만으로 좌우·깊이를 모두 복원하지 않는다. -top-view H201이 3D XY를 계속 담당하고 손목 RGB는 마지막 전후 오차만 줄인다. SIM에서는 -5 mm probe로 pixel/m 민감도를 계산했지만, 실물에서는 이 probe를 그대로 실행하지 않고 -사전 실측한 camera/arm Jacobian과 최대 12 mm correction limit를 사용해야 한다. - -재현 명령은 다음과 같다. `DAPIER_SO101_MJCF`에는 로컬 SO-101 MuJoCo asset을 넣는다. - -```bash -DAPIER_SO101_MJCF=/path/to/so101_new_calib.xml \ -python -m unittest \ - 2ARM_ROBOT/sim/mobile_dual_so101/test/test_collision_guard.py \ - 2ARM_ROBOT/sim/mobile_dual_so101/test/test_compact_mobile_dual_so101.py \ - 2ARM_ROBOT/sim/mobile_dual_so101/test/test_vision_box_pregrasp.py -v - -python 2ARM_ROBOT/sim/mobile_dual_so101/vision_box_pregrasp.py \ - --model-path /path/to/so101_new_calib.xml --view - -python 2ARM_ROBOT/sim/mobile_dual_so101/vision_box_pregrasp.py \ - --model-path /path/to/so101_new_calib.xml --teleop - -python 2ARM_ROBOT/sim/mobile_dual_so101/vision_box_pregrasp.py \ - --model-path /path/to/so101_new_calib.xml --teleop \ - --record-output output/teleop_attempts/right_pregrasp_001 -``` - -teleop 키는 좌/우 화살표로 팔 선택, `1~6`으로 관절 선택, 위/아래 화살표로 이동, -`O/C`로 그리퍼 열기/닫기, `H`로 home, `Space`로 정지/재개한다. SIM 전용이며 -박스 collision geometry도 검사한다. - -기록 모드에서는 실제로 적용한 control target을 20 Hz로 모은 뒤 같은 compact 모델에 -재생하면서 전면/상단 RGB-D와 좌우 손목 RGB, 양팔 state/action을 기존 DAPIER episode -계약으로 저장한다. 종료 직전 3개 sample에서 오른팔 gripper frame이 visual target -15 mm 안에 계속 있어야만 `accepted`가 된다. 그 외 시도는 `recorded`이고 학습 입력으로 -승격하지 않는다. - -작은 SIM 검증에서는 65 frame accepted episode를 만들고 기존 DAPIER-native ACT dataset이 -state/action 12D, RGB-D 4 channel, action chunk를 읽는 것을 확인했다. CPU one-step 학습과 -checkpoint reload도 통과했다. 이는 저장→학습 배선 smoke이지 일반화된 자율 동작의 성공률 -증거는 아니다. 여러 위치·yaw·조명에서 accepted 시범을 모은 뒤 다음 기존 명령으로 학습한다. - -```bash -PYTHONPATH=2ARM_ROBOT/src/shoe_sorting_data \ -python -m shoe_sorting_data.dapier_native_act train \ - --root output/accepted_episodes \ - --checkpoint output/checkpoints/right_pregrasp_act.pt \ - --chunk-size 16 --batch-size 8 --max-steps 100 --device cuda -``` - -checkpoint 출력 경계도 SO-101 기준으로 연결했다. 오른팔 단계에서는 policy가 낸 왼쪽 -6축 값을 실행하지 않고 현재 측정값으로 고정한 뒤 기존 freshness·base 정지·joint limit· -E-stop·watchdog 검사를 통과시킨다. 오른쪽 arm radian은 LeRobot bus degree로, normalized -gripper는 0~100으로 바꾼다. fake bus E2E에서 checkpoint 추론부터 오른팔 6모터 -`Goal_Position` 1회 write까지 통과했으며, 좌측 bus write는 없다. 사용한 checkpoint는 -one-step wiring smoke이므로 실물 task 실행 승인의 근거는 아니다. - -카메라 확인은 아래 명령으로 `좌 손목 | H201 | 우 손목` 한 창에 표시한다. teleop/record에서는 -LeRobot이 이미 읽은 같은 observation을 OpenCV 3분할 창에 그려 카메라를 중복 open하지 않는다. -H201은 eYs3D SDK `zdDepthVec`의 640×460 `uint16 mm` 원본을 저장하고 화면에서만 컬러맵으로 -변환한다. - -```bash -2ARM_ROBOT/scripts/show_camera_dashboard -``` - -## 아직 확인하지 못한 부분 - -- 실제 H201 intrinsics와 유효 거리별 오차(단위는 SDK `uint16 mm`, invalid `16384→0` 확인) -- 실측 `base ← H201 optical` 외부 파라미터 -- 실물 오른손목 RGB의 앞날개 특징과 사전 실측 image Jacobian -- 프레임 age, 두 카메라 timestamp skew와 frame drop -- depth hole·가림·조명·박스 이동에 대한 실물 허용치 -- 오른팔 접촉 후 뚜껑 열림을 현재 visual 경로와 결합하는 작업 -- 왼팔 접근과 신발 인출 - -다음에는 저장된 실제 H201 depth를 박스 pose 함수에 연결하고, 현재 SIM 손목 RGB 보정을 -실측 Jacobian 기반 경계로 교체한다. 실제 calibration이 없으면 -MuJoCo transform을 실물 값처럼 복사하지 않고 실행을 막는다. diff --git a/2ARM_ROBOT/docs/ACT_BASELINE_20260907.md b/2ARM_ROBOT/docs/ACT_BASELINE_20260907.md deleted file mode 100644 index 85661ace..00000000 --- a/2ARM_ROBOT/docs/ACT_BASELINE_20260907.md +++ /dev/null @@ -1,144 +0,0 @@ -# 실제 녹화 4개로 ACT 연결 시험 - -record_id: DAPIER-2026-09-07-act-baseline - -나는 기존 LeRobot ACT 학습기를 재사용해 양손목 RGB + 현재 관절 상태에서 실제 전송 -action을 예측하는 작은 기준 모델을 학습한다. 자체 ACT 런타임이나 실물 성공을 -완성했다는 뜻은 아니다. H201 metric depth는 원본에 보존하고 geometry/IK용으로 -분리한다. depth 컬러맵을 RGB 입력으로 넣지 않는다. - -## 확인한 데이터 경계 - -- 15Hz, 12축(left 6 → right 6), 팔 degree / 그리퍼 0~100이다. -- 에피소드 0·1·2의 3,382프레임만 학습, 에피소드 3의 1,518프레임은 검증한다. -- 설치된 학습기의 episode split을 재사용하되 state/action 정규화 통계는 학습 - 프레임에서만 다시 계산해 메모리에 적용한다. 원본의 전체 통계 파일은 수정하지 않는다. -- 양손목 RGB에는 고정 ImageNet 통계를 쓴다. 저장된 leader→follower 보정을 다시 - 더하지 않는다. 로컬 학습만 허용하며 장치 연결·Hub 업로드는 하지 않는다. - -## 실제 실행한 조건 - -기존 LeRobot 환경의 Python에서 프로젝트 루트를 작업 디렉터리로 사용한다. -`DATASET`과 `OUTPUT`은 각각 기존 데이터와 **새로운** 로컬 출력 경로로 지정한다. -출력 폴더가 이미 있으면 기본적으로 거부한다. 아래 환경 변수는 사용자가 지정하는 -작업 경로이며 비밀값이나 모터 설정이 아니다. - -```bash -HF_HUB_OFFLINE=1 WANDB_MODE=disabled OMP_NUM_THREADS=4 python \ - 2ARM_ROBOT/scripts/train_act_baseline.py \ - --dataset.repo_id=dapier-local/dual-so101-handover-20260907 \ - --dataset.root="$DATASET" --dataset.eval_split=0.25 --output_dir="$OUTPUT" \ - --policy.type=act --policy.device=cuda --policy.push_to_hub=false \ - --policy.dim_model=128 --policy.n_heads=4 --policy.dim_feedforward=512 \ - --policy.n_encoder_layers=2 --policy.n_decoder_layers=1 \ - --policy.n_vae_encoder_layers=2 --policy.latent_dim=16 \ - --policy.chunk_size=16 --policy.n_action_steps=1 \ - --policy.optimizer_lr=0.0001 --policy.optimizer_lr_backbone=0.00001 \ - '--policy.input_features={"observation.state":{"type":"STATE","shape":[12]},"observation.images.left_wrist":{"type":"VISUAL","shape":[3,240,320]},"observation.images.right_wrist":{"type":"VISUAL","shape":[3,240,320]}}' \ - --num_workers=2 --batch_size=8 --steps=2000 --save_freq=500 --log_freq=25 \ - --eval_steps=500 --max_eval_samples=0 --env_eval_freq=0 \ - --seed=1000 --cudnn_deterministic=true -``` - -ResNet18의 기존 로컬 ImageNet 가중치를 재사용한다. 작은 transformer를 포함해 -학습 가능한 파라미터는 12,305,772개다. 새로운 의존성을 설치하지 않았다. - -## 행동 예측 검증 - -```bash -python 2ARM_ROBOT/scripts/train_act_baseline.py --self-test -python 2ARM_ROBOT/scripts/evaluate_act_baseline.py --self-test - -HF_HUB_OFFLINE=1 OMP_NUM_THREADS=2 python 2ARM_ROBOT/scripts/evaluate_act_baseline.py \ - --dataset "$DATASET" --checkpoint "$OUTPUT/checkpoints/002000/pretrained_model" \ - --contract "$OUTPUT/split_contract.json" --output "$OUTPUT/eval-002000" -``` - -체크포인트와 함께 저장된 전·후처리기를 다시 읽고, 정책에는 관절 상태와 양손목 -영상만 전달한다. 정답 action은 예측이 끝난 뒤 오차 계산에만 사용한다. 첫 action과 -16-step chunk를 각각 측정하며 에피소드 끝의 padding은 제외한다. 현재 자세를 그대로 -유지하는 기준과 학습 action 평균 기준도 함께 기록한다. - -같은 관절 상태에서 왼손목 영상만, 오른손목 영상만 반 에피소드만큼 이동시키고, -양쪽 영상을 검게 만든 입력도 검사한다. 이는 카메라 입력이 행동에 영향을 주는지 -확인하는 진단이지, 올바른 물체 인식이나 일반화 능력을 증명하는 검사가 아니다. - -## 2,000-step 실행 결과 - -2026-09-07 RTX 5050 Laptop에서 약 12분 26초 동안 실행하고 정상 종료했다. -설치된 LeRobot fork commit은 `30da8e687a6dfc617fcd94afc367ac7071c376ce`이다. -학습 로그의 25-step 평균 loss는 초반 11.510에서 마지막 0.108로 내려갔다. -검증 loss는 500/1,000/1,500/2,000 step에서 각각 0.5923/0.5399/0.5507/0.5494였다. -학습 loss에는 VAE KL 항이 포함되므로 검증 loss와 직접 같은 양으로 비교하지 않는다. - -최종 체크포인트를 다시 읽어 1,518개 보류 관측 전부로 검사했다. 아래 팔 오차는 -좌우 팔의 10개 관절별 MAE를 평균한 degree이며 그리퍼와 단위를 섞지 않는다. - -| 입력/기준 | 첫 action 팔 MAE (°) | 16-step chunk 팔 MAE (°) | -| --- | ---: | ---: | -| 정상 양손목 RGB + state | 12.059 | 11.999 | -| 현재 관절 상태 유지 | 0.868 | 1.966 | -| 학습 action 평균 | 22.900 | 22.935 | -| 왼손목 영상만 시간 이동 | 19.992 | 20.190 | -| 오른손목 영상만 시간 이동 | 26.721 | 26.349 | -| 양손목 영상 검게 처리 | 27.050 | 27.178 | - -정상 입력의 첫 action 그리퍼 MAE는 좌 11.999, 우 5.455 percentage point였다. -영상 변경이 예측에 영향을 주지만, 정상 입력도 현재 상태 유지 기준보다 오차가 -크다. **학습 연결은 확인했지만 쓸 수 있는 조작 정책으로 검증한 것은 아니다.** -이 결과만으로 4개 에피소드의 일반화나 성공률을 주장하지 않는다. 상태 유지 기준의 -오차가 작다는 사실도 물체를 집는 정책이 좋다는 뜻은 아니다. - -500→2,000 step 체크포인트의 186개 저장 tensor 중 105개가 달라졌고 최대 절대 -변화는 0.0635271이었다. 최종 normalizer의 state/action 통계도 원본의 학습 3개 -에피소드에서 재계산한 값과 일치했다. 원본 데이터·모터 보정·모델의 기존 SHA-256 -검사가 통과했다. 두 self-test와 전체 보류 관측 평가를 실행했으며, 평가 중 정답 -action을 정책에 전달하지 않았다. 최초 실행에서 이미지 라이브러리 DEBUG 로그가 -과도해 후속 실행의 파일 로그는 INFO 이상만 남기도록 수정했다. - -## 폐루프 평가에 남은 조건 - -녹화 관측을 넣은 오프라인 오차는 정책이 움직인 결과를 다시 관측하는 폐루프 -성공률이 아니다. 실제 목표는 매 주기 새 카메라 관측 → ACT 관절 action → 단위·한계· -접촉·시간 유효성 검사 → 실행 → 재관측이다. `n_action_steps=1`로 매 주기 관측을 -소비하도록 한다. 관절 action에 IK를 다시 적용하지 않으며 시뮬레이터 정답 물체 -좌표를 정책 입력으로 넣지 않는다. - -[책상형 재생](../sim/mobile_dual_so101/TABLETOP_REPLAY.md)의 초기 schema v1에서는 -첫 자세 테이블 관통 약 11.12mm를 확인했고 블록도 정적 참고물이었다. 현재 v2는 -4cm·약 20g 자유 강체와 접촉을 사용한다. 관절 영점·장착 방향과 카메라 보정은 -여전히 미검증이다. 이 조건을 해결하기 전에는 CPU 병렬 작업 성공률을 발표하지 않는다. -GPU 병렬 RL도 보상·리셋·물리 환경과 CPU 기준이 확인된 뒤 필요성을 판단한다. - -## 2026-09-08 · 실제 CPU 폐루프 연결 시험 - -`sim/mobile_dual_so101/parallel_tabletop_act.py`에서 DAPIER가 매 주기 새 양손목 -렌더 RGB와 측정 관절 상태를 구성해 기존 LeRobot ACT 체크포인트에 전달한다. -정책은 새 관측으로 action을 계산하고, 범위와 명령 변화율을 제한한 뒤 `mj_step()`으로 -실행한다. 저장된 action이나 미래 state, 물체 정답 좌표는 정책에 넘기지 않는다. -여기서 native ACT는 기존 체크포인트 형식을 직접 읽는다는 뜻이며 ACT 자체를 새로 -구현했다는 뜻이 아니다. DAPIER 소유 범위는 관측·제어 연결과 실행 로그다. - -```bash -HF_HUB_OFFLINE=1 MUJOCO_GL=egl OMP_NUM_THREADS=2 python \ - 2ARM_ROBOT/sim/mobile_dual_so101/parallel_tabletop_act.py \ - --dataset "$DATASET" --checkpoint "$OUTPUT/checkpoints/002000/pretrained_model" \ - --model /home/dapier-jhj/DAPIER/.local-workspaces/so101/lerobot/src/lerobot/envs/so101_mujoco/assets/so101_new_calib.xml \ - --output /home/dapier-jhj/DAPIER/2ARM_ROBOT/recording/simulation/20260908/act-closed-loop-new \ - --workers 2 --episodes 2 --steps 30 -``` - -MuJoCo 3.8.1의 현재 elliptic 장면에서 CPU 2개 프로세스가 각각 30 transition을 -완료했다. 각 worker의 정책 질의 30회, 양손목 이미지 각각 30종, 시뮬레이션 시간 -2초를 확인했다. 총 60 transition이고 시작을 포함한 wall time은 약 9.41초였다. -명령은 모든 step에서 변화율 제한을 거쳤고 시뮬레이터 경고는 없었다. -원본 dataset·체크포인트·scene 해시도 유지했다. 실제 15Hz 실시간 추론 성능을 -검증한 것은 아니며 EGL 렌더링은 GPU를 사용할 수 있다. - -`task_success_rate=null`, `task_evaluation_valid=false`, `physical_mapping_verified=false`다. -기록 첫 자세에서 시작한 관통 17.08mm도 그대로 보고한다. **60 transition은 집기 성공 -60회가 아니다.** 별도 영상+IK 집기 baseline의 통과도 ACT 점수로 합치지 않는다. -로컬 근거: `recording/simulation/20260908/act-closed-loop-elliptic/report.json`과 -각 episode의 관측·제안·제한 후 명령·다음 상태 trace다. 이 진입점은 optimizer/RL을 실행하지 않는다. - -원본 영상·사진·개인 calibration·체크포인트는 공개 저장소에 올리지 않는다. diff --git a/2ARM_ROBOT/docs/ACT_DATASET_ROUNDTRIP_SMOKE.md b/2ARM_ROBOT/docs/ACT_DATASET_ROUNDTRIP_SMOKE.md deleted file mode 100644 index 4764c511..00000000 --- a/2ARM_ROBOT/docs/ACT_DATASET_ROUNDTRIP_SMOKE.md +++ /dev/null @@ -1,110 +0,0 @@ -# Dataset v3 round-trip · ACT DataLoader smoke - -> 장비 주의 (2026-09-03): 본문의 JDcobot/Astra Pro 표기는 당시 분석 가정이다. 현행 실물 역할은 [`../config/hardware_roles.json`](../config/hardware_roles.json)을 따른다. - -확인일: 2026-08-21 -단계: 3/5 -범위: native LeRobot Dataset v3 writer 결과를 다시 열어 시간축·episode 경계·padding mask·ACT 입력 계약까지 검증 - -## 왜 이 순서로 진행하는가 - -파일이 생성됐다는 사실만으로 ACT 학습 입력이 맞다고 볼 수 없다. ACT는 현재 frame 하나가 아니라 `chunk_size` 길이의 미래 action window를 읽고, episode 끝을 넘는 위치는 `action_is_pad`로 loss에서 제외한다. 따라서 Stage 2 writer 다음에 바로 작은 round-trip을 두어 writer → reopen → temporal query → DataLoader → ACT loss 경로를 끝까지 확인한다. 이 gate를 통과한 뒤에야 Stage 4 evaluator가 같은 action chunk 계약 위에서 오차를 계산할 수 있다. - -## 연구 선정 기준과 사용한 정본 - -이번 단계의 판정 근거는 아래 자료로 제한했다. - -| 자료 | 선정 이유 | 실제 반영 | 분류 | -|---|---|---|---| -| LeRobot Dataset v3 공식 문서와 `LeRobotDataset` 구현 | writer lifecycle, reopen, delta window, episode padding의 현재 API 정본 | `create→add_frame→save_episode→finalize→reopen` 및 2×3 fixture 검사 | 즉시 반영 | -| LeRobot `datasets/factory.py`의 `resolve_delta_timestamps` | ACT action index를 dataset FPS 기반 seconds로 바꾸는 공식 경로 | 20 FPS에서 `[0.0, 0.05, 0.1]` exact assertion | 즉시 반영 | -| LeRobot `ACTConfig`와 `modeling_act.py` | `chunk_size`, `n_action_steps`, `action_is_pad`가 loss에 적용되는 현재 구현 | `chunk_size=3`, `n_action_steps=1`, one-forward finite loss 검사 | 즉시 반영 | -| LeRobot dataset/streaming 공식 tests | episode tail mask와 non-padded 값 검증 방식의 정본 | frame별 expected mask와 cross-episode no-leak 검사 | 즉시 반영 | -| Zhao et al., ACT 원 논문(2023) | action chunking·temporal ensemble의 원리 이해 | 구현 판정에는 사용하지 않고 배경 설명에만 사용 | 참고만 | -| Chen et al., Robo-DM(2025) | heterogeneous robot data 저장·decode trade-off 이해 | 현재 storage 병목·ACT padding 근거가 아니므로 코드 미반영 | 참고만/보류 | - -최신 VLA·world model 논문은 Dataset v3 reader나 ACT padding 계약을 바꾸지 않으며 이번 실패 위험을 직접 줄이지 않으므로 검토 목록에 추가하지 않았다. 상세 근거와 원문 URL은 [`research/LATEST_ACT_DATALOADER_ROUNDTRIP_RESEARCH_20260821.md`](research/LATEST_ACT_DATALOADER_ROUNDTRIP_RESEARCH_20260821.md)에 기록했다. - -## 구현 계약 - -`native-act-smoke`는 optional ML 환경에서만 LeRobot·Torch를 lazy import한다. 기본 ROS2 recorder 환경에는 이 패키지들을 추가하지 않는다. - -검사 순서는 다음과 같다. - -1. native v3 dataset을 새 `LeRobotDataset` instance로 reopen한다. -2. 정확히 2 episode×3 frame, 20 FPS, state/action 12D, RGB 3채널, depth 1채널인지 확인한다. -3. 공식 `resolve_delta_timestamps()`가 action window `[0, 1/fps, 2/fps]`를 만드는지 확인한다. -4. 두 episode에서 각각 `[F,F,F]`, `[F,F,T]`, `[F,T,T]` mask가 나오는지 확인한다. -5. episode 0의 padded action이 episode 1 첫 action을 가져오지 않는지 확인한다. -6. DataLoader batch shape가 action `(1,3,12)`, mask `(1,3)`인지 확인한다. -7. 공식 `ACTPolicy.forward()`가 `action_is_pad`를 포함한 batch로 finite loss를 계산하는지 확인한다. -8. 결과·runtime·LeRobot version/commit을 receipt에 저장하고 encoder receipt의 round-trip 상태를 PASS로 갱신한다. - -## RGB와 Depth의 경계 - -native Dataset v3에는 RGB `(3,H,W)`와 depth `(1,H,W)`를 모두 보존하고 round-trip한다. 하지만 공식 ACT ResNet 입력은 3채널 image를 전제로 하므로 이번 ACT baseline forward에는 RGB만 넣었다. 1채널 depth를 3채널로 복제하거나 8-bit RGB로 위장하지 않았다. - -Depth 활용은 다음 중 하나를 ACT RGB 기준선과 같은 split에서 ablation할 때만 추가한다. - -- depth 전용 encoder 또는 late fusion -- 물리 단위를 유지한 3채널 derived representation -- RGB-only 대비 validation sequence error, task success, safety rejection 개선 - -## 실행 - -교육용 Ubuntu PC에서는 별도 ML 가상환경에서 실행한다. ROS2 overlay를 source하는 기본 terminal에 LeRobot을 강제 설치하지 않는다. - -```bash -python -m shoe_sorting_data.cli generate \ - --root /tmp/dapier_stage3/raw \ - --count 2 \ - --samples 3 \ - --camera-payload \ - --camera-width 64 \ - --camera-height 64 - -python -m shoe_sorting_data.cli native-export \ - --root /tmp/dapier_stage3/raw \ - --output /tmp/dapier_stage3/lerobot \ - --repo-id local/dapier-shoe-smoke \ - --depth-unit mm - -python -m shoe_sorting_data.cli native-act-smoke \ - --root /tmp/dapier_stage3/lerobot \ - --repo-id local/dapier-shoe-smoke \ - --chunk-size 3 -``` - -`chunk_size=3`은 3-frame fixture에서 full/tail padding을 모두 검증하기 위한 smoke 전용 값이다. 실제 학습·rollout 추천값이 아니다. - -## 2026-08-21 실제 검증 결과 - -격리 환경: `C:\Users\hjjeon\.cache\dapier\lerobot-v3-d451fe4-cpu` -검증 artifact: `C:\Users\hjjeon\Documents\DAPIER\tmp\stage3-native-roundtrip-20260821-v4` - -| 항목 | 결과 | -|---|---| -| native writer/finalize/reopen | PASS | -| episode/frame | 2 / 6 | -| FPS·delta timestamps | 20 FPS / `[0.0, 0.05, 0.1]` | -| 양팔 state/action | float32 12D / 12D | -| RGB/depth | `(3,64,64)` / `(1,64,64)` | -| 두 episode tail masks | `[F,F,F]`, `[F,F,T]`, `[F,T,T]` 각각 반복 | -| cross-episode action leak | 없음 | -| DataLoader | action `(1,3,12)`, mask `(1,3)` | -| ACT one-forward | CPU, finite loss PASS | -| runtime | Python 3.12.6, Torch 2.7.1+cpu, CUDA 미사용 | -| LeRobot | 0.6.2, commit `d451fe4f1f1b00a812f95aa9534389b5e42ab155` | -| 기본 ROS2 환경 영향 | 없음 | - -Windows의 TorchCodec는 full-shared FFmpeg DLL이 없어 공식 PyAV fallback 경고를 냈다. 이번 dataset은 `use_videos=False` image 저장이므로 결과에 영향을 주지 않았다. 시스템 FFmpeg를 추가 설치하지 않았고, 향후 video export를 채택할 때만 Ubuntu 격리 환경에서 codec round-trip을 별도 gate로 검증한다. - -## PASS가 의미하지 않는 것 - -- ACT를 학습했다는 뜻이 아니다. -- 실제 신발 정리 성공률을 증명하지 않는다. -- `chunk_size=3`이 현장 최적값이라는 뜻이 아니다. -- depth를 ACT가 사용했다는 뜻이 아니다. -- synthetic 64×64 image가 Astra Pro 실데이터 품질을 대표하지 않는다. - -Stage 4 offline evaluator는 구현·검증을 완료했다. padded timestep 제외, horizon coverage, 양팔 관절·그리퍼별 error, split/cross-episode leakage gate와 실제 smoke 결과는 [`OFFLINE_EVALUATOR_ACTION_CHUNK.md`](OFFLINE_EVALUATOR_ACTION_CHUNK.md)에서 확인한다. diff --git a/2ARM_ROBOT/docs/ASTRA_RGBD_PAYLOAD_CONTRACT.md b/2ARM_ROBOT/docs/ASTRA_RGBD_PAYLOAD_CONTRACT.md deleted file mode 100644 index 8bb01113..00000000 --- a/2ARM_ROBOT/docs/ASTRA_RGBD_PAYLOAD_CONTRACT.md +++ /dev/null @@ -1,172 +0,0 @@ -# 전면 Astra + 작업공간 H201 RGB/Depth 픽셀 payload 저장 계약 - -> 계약 버전: `dapier.ros2-image-payload.v0.1` -> episode 버전: `dapier.shoe-episode.v0.3` -> 상태: 4카메라 역할 확정, MuJoCo 4카메라 lossless episode 완료, 실물 multi-camera recorder 미완료 - -## 왜 이 단계가 먼저인가 - -LeRobot Dataset이나 ACT가 실행되더라도 원본 RGB/Depth의 encoding, byte layout, timestamp, 단위가 유실되면 결과를 재검증할 수 없다. 그래서 모델 의존성을 설치하기 전에 ROS 2 `sensor_msgs/Image` payload를 손실 없이 보존하는 계약을 먼저 고정한다. - -## 저장 원칙 - -1. ROS image row bytes를 변환하지 않고 `.raw`로 저장한다. -2. RGB와 depth는 서로 다른 encoding 계약으로 검사한다. -3. raw 파일은 절대 overwrite하지 않는다. -4. 파생 LeRobot artifact는 별도 output에 만들고 raw SHA-256을 참조한다. -5. `accepted + finalized + integrity_verified` episode만 exporter가 읽는다. - -카메라 역할은 다음처럼 고정한다. - -| 역할 | 장치 | 소비자 | -|---|---|---| -| `front_rgbd` | Orbbec Astra S | TurtleBot3 Visual SLAM·map localization | -| `workspace_rgbd` | HP-ASC-H201 / eYs3D R77 | 박스·신발 top-view 인식과 재관측 | -| `left_gripper_rgb` | 왼쪽 SO-101 RGB | 왼손 접근·파지 | -| `right_gripper_rgb` | 오른쪽 SO-101 RGB | 오른손 뚜껑 접근·접촉 | - -기존 `shoe_sorting_data` Phase 0 실물 writer 예시는 호환성을 위해 `workspace_rgb/depth` 두 stream을 -유지한다. MuJoCo `sim_episode.py`는 네 카메라 역할을 같은 episode clock으로 저장한다. 네 실물 -카메라를 같은 clock으로 기록하는 adapter는 아직 완료되지 않았다. - -```text -episode_000001/ -├── episode_manifest.json -├── samples.jsonl -└── raw/ - ├── front_rgb/frame_000000.raw - ├── front_depth/frame_000000.raw - ├── workspace_rgb/frame_000000.raw - ├── workspace_depth/frame_000000.raw - ├── left_gripper_rgb/frame_000000.raw - └── right_gripper_rgb/frame_000000.raw -``` - -## frame payload metadata - -```json -{ - "contract_version": "dapier.ros2-image-payload.v0.1", - "storage": "ros2_raw_rows", - "stream": "workspace_depth", - "path": "raw/workspace_depth/frame_000000.raw", - "width": 640, - "height": 480, - "encoding": "16UC1", - "is_bigendian": 0, - "step": 1280, - "byte_count": 614400, - "sha256": "..." -} -``` - -## 허용 encoding - -| stream | 허용 encoding | 현재 의미 | -|---|---|---| -| `front_rgb`, `left_gripper_rgb`, `right_gripper_rgb` | `rgb8`, `bgr8`, `rgba8`, `bgra8`, `mono8`, `8UC1` | 전면·손목 색상 payload | -| `front_depth` | `mono16`, `16UC1`, `16SC1`, `32FC1` | 전면 SLAM depth payload | -| `workspace_rgb` | `rgb8`, `bgr8`, `rgba8`, `bgra8`, `mono8`, `8UC1` | 색상·채널 순서를 metadata로 보존 | -| `workspace_depth` | `mono16`, `16UC1`, `16SC1`, `32FC1` | 원본 정수/실수 depth 값을 변환 없이 보존 | - -`16UC1=mm`라고 코드에서 가정하지 않는다. 실제 depth unit은 각 카메라 driver 설정과 거리 실측 후 calibration/provenance snapshot에서 확정한다. - -## timing 계약 - -required payload sample은 다음을 함께 저장한다. - -- control tick 기준 `anchor_timestamp_ns` -- 현재 12개 stream(관절/명령/베이스 6 + 카메라 6)의 header timestamp -- 현재 12개 stream의 receive monotonic timestamp -- 한 sample에서 `max(header)-min(header)`인 `sync_delta_ns` -- manifest의 sync tolerance - -arrival time은 header timestamp를 대체하지 않고 지연·drop 진단에만 사용한다. - -## lifecycle 계약 - -v0.3 manifest는 다음이 아니면 유효하지 않다. - -```json -{ - "lifecycle": { - "state": "finalized", - "integrity_verified": true - } -} -``` - -기록 중이거나 crash로 끝난 디렉터리는 학습 입력으로 승격하지 않는다. - -## quality gate - -- payload 필수 모드에서 누락 차단 -- stream별 잘못된 encoding 차단 -- `step >= width * bytes_per_pixel` -- `byte_count == step * height` -- raw file SHA-256 일치 -- episode 밖으로 나가는 상대 경로 차단 -- 기존 raw frame overwrite 차단 -- sync delta 재계산 결과와 metadata 일치 -- header/receive timestamp map 완전성 - -## 실행 방법 - -```bash -ros2 run shoe_sorting_data shoe_episode generate \ - --root output/rgbd_fixture \ - --count 5 \ - --samples 4 \ - --camera-payload - -ros2 run shoe_sorting_data shoe_episode validate \ - --manifest output/rgbd_fixture/episode_000001/episode_manifest.json -``` - -## 최신 연구 보강 결과 - -상세 근거는 [`research/LATEST_RGBD_DATA_CONTRACT_RESEARCH_20260821.md`](research/LATEST_RGBD_DATA_CONTRACT_RESEARCH_20260821.md)에 있다. - -### 즉시 반영 - -- raw와 derived artifact 분리 -- frame별 header/receive timestamp와 sync Δt -- 파일별 SHA-256와 finalized gate -- scene/object/operator/session provenance 기반 split 누수 방지 유지 - -### 실물 연결 시 반영 - -- CameraInfo K/D/R/P finite·dimension 검사 -- color/depth intrinsics와 base↔camera, depth↔color transform snapshot -- camera serial, firmware, driver, ROS distro, launch parameter, git SHA -- depth unit, invalid value, invalid ratio 실측 - -### baseline 뒤 실험 후보 - -- native Dataset v3 depth video -- RGB-only와 RGB+Depth ACT ablation -- proprioception/action 보조 표현학습 - -### 보류 - -- depth를 8-bit RGB MP4로 저장 -- 실측 bandwidth 없이 4카메라를 최고 해상도·FPS로 고정 -- ACT manipulation 입력에 SLAM map을 즉시 결합 -- embodiment가 다른 대규모 외부 데이터로 곧바로 pretrain - -## 현재 완료/미완료 경계 - -완료: - -- 합성 RGB8/16UC1 raw round-trip -- 누락·변조·경로 탈출·unit/geometry·overwrite 차단 -- Windows/Ubuntu pure Python 검증 -- MuJoCo 전면/workspace RGB-D와 좌우 wrist RGB의 synchronized raw episode 저장 - -미완료: - -- Astra S와 H201 각각의 실제 encoding/resolution/depth unit 확인 -- 실제 `CameraInfo`와 calibration snapshot -- 장시간 기록의 p50/p95 sync delta와 dropped topic count -- `front_rgbd`, `workspace_rgbd`, 좌우 `gripper_rgb`를 같은 monotonic clock으로 저장하는 실물 adapter -- native LeRobot Dataset v3 변환 diff --git a/2ARM_ROBOT/docs/CAMERA_ALIGNMENT_PREPARATION.md b/2ARM_ROBOT/docs/CAMERA_ALIGNMENT_PREPARATION.md deleted file mode 100644 index b30fc9c8..00000000 --- a/2ARM_ROBOT/docs/CAMERA_ALIGNMENT_PREPARATION.md +++ /dev/null @@ -1,116 +0,0 @@ -# 손목 카메라·양팔 좌표 보정 준비 - -record_id: DAPIER-2026-09-08-camera-alignment-preparation - -나는 기존 조작 녹화와 모터 보정을 보존하고, 실물 영상과 MuJoCo의 좌표를 맞추는 -단계를 준비한다. 이 문서와 보정판 생성은 **보정 완료나 실물 실행 승인**이 아니다. -저장된 데이터의 재생·학습에는 실물 전원이 필요하지 않다. - -## 준비한 보정판 - -기존 OpenCV 4.13의 ChArUco 패턴을 사용한다. A4 가로, 10×7칸, 한 칸 25mm, -marker 18mm, DICT_4X4_50, legacy pattern OFF다. 35개 marker ID와 54개 내부 -corner ID를 검출하는 self-test를 실행한다. PDF와 PNG는 254 DPI(10px/mm), -실제 패턴 250×175mm이며 100mm 확인선을 포함한다. - -```bash -python 2ARM_ROBOT/scripts/prepare_camera_board.py --self-test -python 2ARM_ROBOT/scripts/prepare_camera_board.py --output /path/to/new-board-folder -``` - -기존 MuJoCo/LeRobot Python 환경을 사용한다. 파일을 덮어쓰지 않으며 카메라·모터를 -열지 않는다. board.json에 패턴 정의와 출력 파일 해시를 기록한다. - -출력 시 **실제 크기/100%**로 지정하고 페이지에 맞추기를 끈다. 인쇄 후 확인선이 -실제 100mm인지 자로 확인하고 평평한 판에 고정한다. 화면에 표시된 크기나 PDF의 -페이지 크기만으로 실제 인쇄 치수가 맞았다고 판단하지 않는다. - -## 다음 측정 순서 - -1. 양손목 RGB의 실제 녹화 해상도·영상 회전·초점 설정을 유지한다. 로봇과 카메라는 - 고정하고 보정판을 다양한 위치·거리·기울기로 보여 내부 파라미터와 왜곡을 구한다. - 같은 정면 사진만 반복해서 얻은 작은 오차를 충분한 보정으로 해석하지 않는다. -2. 손목 camera→gripper와 상부 depth camera→좌·우 base 변환을 각각 확인한다. - 손목 외부 보정에는 고정된 기준판과 여러 팔 자세의 동기화된 joint state가 필요하다. - 이미 저장된 leader→follower offset을 모델에 다시 더하지 않는다. -3. 보정판 기준으로 책상 면과 4cm 블록의 초기 pose를 측정해 동일한 좌표계로 옮긴다. - 관절 영점·방향은 알려진 자세에서 확인한다. 파지 성공 수치에 맞춰 보정을 조정하지 않는다. -4. 관절/영상 정합과 접촉을 확인한 뒤 실제 ACT의 새 카메라 관측 → 행동 → 물리 변화 → - 재관측 폐루프를 CPU 병렬 환경에서 평가한다. 그 결과로 GPU 병렬 학습 필요성을 판단한다. - -H201은 RGB용 보정판 결과를 depth intrinsics에 대입하지 않는다. 640×460 metric -depth 모드에 맞는 공장 보정과 거리 단위를 먼저 확인하고, rectified 영상에 왜곡 보정을 -중복 적용하지 않는다. 실제 장치를 여는 작업은 정확한 장치·명령·조건을 제시하고 -현장 승인을 받은 별도 단계에서만 수행한다. 전원 공급은 그 승인 자체가 아니다. - -sim-to-real은 카메라·단위·관절 범위·제어 주기 계약, 관측 지연·분실 시 정지, -충돌·속도·명령 제한과 shadow inference를 확인한 뒤 승인된 저속 실물 검증으로 넘긴다. -보정·원본 영상·serial 같은 개인 장치 자료는 공개 저장소에 올리지 않는다. - -## 사진으로 내부 보정 후보 계산하기 — 장치 접근 없음 - -나는 먼저 내부 보정으로 픽셀과 카메라 시선의 관계를 구한다. `K`의 초점거리 -`fx, fy`와 영상 중심 `cx, cy`, 렌즈 왜곡을 모르면 영상의 점을 올바른 방향으로 -해석하기 어렵다. 이것은 **카메라가 로봇 어디에 달렸는지** 구하는 외부 보정과 다르다. -보정판 생성기의 정의를 공유하고 OpenCV의 corner 검출 → ID별 3D/2D 대응 → -`calibrateCameraExtended`를 사용한다. 새로운 라이브러리는 추가하지 않았다. - -촬영은 별도 현장 승인 후 진행한다. 팔과 카메라를 고정한 상태에서 보정판만 움직여 -카메라마다 20장 이상을 목표로 한다. 화면 중앙·네 모서리, 여러 거리와 기울기를 -포함하고 흐림·반사·판의 휘어짐을 피한다. 모든 사진에서 판 전체가 보일 필요는 없지만 -이 도구는 최소 12개의 일직선이 아닌 corner를 요구한다. 좌우 사진은 폴더를 나누고 -같은 카메라의 해상도·초점·crop·회전·raw/rectified 설정을 유지한다. 현재 녹화의 -320×240 모드도 실제 촬영 설정과 대조해야 하며 도구는 자동 resize하지 않는다. -보정 계산에 쓰지 않을 추가 사진도 따로 남겨 후속 검증에 사용한다. - -다음은 경로와 촬영 메모를 실제 값으로 바꾼 뒤 실행할 **오프라인 예시**다. -기존 OpenCV 4.13 환경의 Python을 사용한다. - -```bash -python 2ARM_ROBOT/scripts/calibrate_camera_images.py --self-test -python 2ARM_ROBOT/scripts/calibrate_camera_images.py \ - --images /path/to/left-wrist-images --camera left_wrist \ - --measured-line-mm 95 \ - --capture-notes '실제 장치 식별·해상도·초점·raw/rectified·crop·회전 설정을 기록' \ - --output /path/to/new-left-intrinsics-candidate -``` - -오른쪽은 별도 사진 폴더와 새 출력 폴더, `--camera right_wrist`를 사용한다. -도구는 PNG/JPEG 10~300장을 받아 중복 픽셀·부족한 corner를 제외한 최소 10개 -view를 요구하고, 혼합 해상도와 16-bit depth 입력은 거부한다. 입력 해시·view별 오차· -`K`·왜곡을 `intrinsics-candidate.json`에 기록한다. 기존 출력 폴더는 덮어쓰지 않는다. - -직접 합성 사진 20장으로 실행한 결과 재투영 RMS는 **0.358px**이고, 알려진 -`fx=600, fy=610`에 대해 약 `597.3, 607.4`를 얻었다. 중복·사진 부족·혼합 해상도· -depth 입력 거부와 원본 해시 보존, 기존 출력 거부도 self-test에서 확인했다. -이는 **합성 입력으로 계산 흐름을 검증한 결과**이며 실제 카메라 정확도가 아니다. - -RMS 1px 초과, 첫 view 대비 법선 변화 15도 미만, corner 분포의 폭/높이 50% 미만, -초점거리 상대 표준편차 10% 초과 또는 영상 밖 중심점이면 경고와 종료 코드 2를 낸다. -이 기준은 촬영 품질의 휴리스틱이고, 종료 코드 0도 실물 검증 통과를 뜻하지 않는다. -출력은 항상 `candidate_only=true`다. view별 pose는 **board→camera**이며 -camera→gripper/base가 아니다. 인쇄 실측·장치 identity·미사용 사진 검증·손목 외부 -보정·H201 depth 보정은 아직 남아 있고, 런타임 보정 파일에는 자동 적용하지 않는다. - -## 이번 출력물은 확인선 95mm로 계산한다 - -인쇄 후 확인선을 재보니 100mm가 아니라 **95mm**였다. 재인쇄하지 않고 이 측정값으로 -진행한다. 위 명령의 `--measured-line-mm 95`는 이번 출력물에 대한 값이며, 다른 -출력물에는 그 출력물에서 잰 길이를 넣는다. 이 옵션은 필수이고 기본값을 추측하지 않는다. - -가로·세로가 균일하게 축소됐다는 가정에서 배율은 `95/100 = 0.95`다. 한 칸은 -23.75mm, marker는 17.10mm, 전체 **격자**는 237.5×166.25mm로 계산한다. -종이 전체 크기가 아니다. 확인선은 한 방향의 측정이므로 세로 격자가 약 166.25mm인지와 -판의 평평함을 촬영 전에 확인해야 한다. 두 축의 배율이 다르거나 판이 휘면 이 가정을 쓰지 않는다. - -PDF와 명목 보정판 정의는 유지하고, ID에 대응시킨 3D 점에만 0.95를 한 번 곱한다. -사진 resize나 카메라 `K`에 0.95를 곱하는 처리가 아니다. 동일 영상에 대해 물체 좌표와 -이동 벡터를 함께 균일 축소하면 투영 비율이 같다는 식을 self-test로 확인했다: -100mm/95mm 계산의 `K`·왜곡은 수치 허용오차 내 같고, board→camera 이동 벡터는 0.95배다. -0·음수·NaN·무한대와 이 A4 보정판에 맞지 않는 950mm 입력도 거부했다. - -출력 schema 2는 `nominal_board_definition`과 `print_measurement`를 나눠 저장한다. -`uniform_xy_scale_verified=false`와 `candidate_only=true`는 유지하며, 실제 카메라 -사진으로 보정한 결과는 아직 없다. 측정값과 가정만 로컬에 남기고 개인 보정 파일은 공개하지 않는다. - -참고: [OpenCV 4.13 ChArUco calibration](https://docs.opencv.org/4.13.0/da/d13/tutorial_aruco_calibration.html). diff --git a/2ARM_ROBOT/docs/CPP_SAFETY_MOCK_BRIDGE.md b/2ARM_ROBOT/docs/CPP_SAFETY_MOCK_BRIDGE.md deleted file mode 100644 index bd415076..00000000 --- a/2ARM_ROBOT/docs/CPP_SAFETY_MOCK_BRIDGE.md +++ /dev/null @@ -1,80 +0,0 @@ -# C++ safety controller and mock command sink - -## 목적 - -Python 정책·태스크 계획과 Visual SLAM이 만든 제안을 실제 ROS 또는 모터 계층에 -전달하기 전에, 하드웨어와 무관한 C++에서 최종 실행 조건을 검사한다. 이 단계는 -실물 명령을 보내지 않으며 `RecordingMockCommandSink`에 승인·hold·safe-stop 결과만 -기록한다. - -## 입력과 출력 - -```text -ResearchControlIntent -+ receiver-local monotonic time -+ measured joint/base state -+ localization motion decision -+ task phase/interlock facts - ↓ -SafetyController - ↓ -SafeCommand - ↓ -RecordingMockCommandSink -``` - -## 현재 강제되는 조건 - -- versioned schema, monotonic sequence, receiver-local TTL -- 명시적 operator enable -- E-stop health 확인 -- measured-state freshness -- localization `Proceed` -- collision-clear interlock -- 관절 hard position limit -- 관절 maximum requested velocity -- command horizon 동안 허용되는 최대 위치 변화량 -- arm motion 중 base settled 상태 -- base transport 중 carry envelope와 verified grasp -- base linear/angular speed limit -- command watchdog와 measured-state watchdog -- safe stop latch와 disabled 상태에서의 명시적 reset - -## safe stop 의미 - -현재 core는 물리 장치를 열지 않는다. safe stop 결과는 현재 측정 자세를 포함한 -명시적 stop/hold 요청이며, 실제 hardware sink는 다음을 추가로 구현해야 한다. - -- controller별 hold 또는 zero-velocity 명령 -- 통신 오류 시 torque-off 또는 장치 제조사가 보장하는 정지 동작 -- E-stop 회로 상태 read-back -- 실제 명령 적용 여부와 measured-state read-back -- 장치 identity, 전압, 온도와 fault 상태 검사 - -따라서 mock test 통과를 실제 비상 정지 검증으로 기록하지 않는다. - -## 장비 없이 검증 - -```bash -cd ~/DAPIER -scripts/verify-hardware-free -``` - -ROS 2 Jazzy 설치 환경에서는 다음 package build/test도 수행한다. - -```bash -source /opt/ros/jazzy/setup.bash -colcon build --symlink-install --packages-up-to dapier_safety_core -colcon test --packages-select \ - dapier_localization_core dapier_so101_core dapier_safety_core -colcon test-result --verbose -``` - -## 실물 연결 전에 남아 있는 값 - -- 양팔 실제 joint limit와 verified calibration -- SO-101 controller의 hold/torque-off semantics -- TurtleBot3 velocity controller의 watchdog/zero-command semantics -- E-stop 회로와 복구 절차 -- 실제 measured-state latency와 packet-loss 한계 -- 저속·무부하 commissioning 속도 diff --git a/2ARM_ROBOT/docs/CUSTOM_ACT_V1_KO.md b/2ARM_ROBOT/docs/CUSTOM_ACT_V1_KO.md index 6d67d42c..6c04f5ed 100644 --- a/2ARM_ROBOT/docs/CUSTOM_ACT_V1_KO.md +++ b/2ARM_ROBOT/docs/CUSTOM_ACT_V1_KO.md @@ -1,89 +1,7 @@ -# CUSTOM ACT V1 | 관측과 행동이 이어지는지 확인한 기록 +# CUSTOM ACT V1 → SWEEPICK 저장소로 이동 -SWEEPICK에서 학습 정책을 적용할 때, 낮은 예측 손실만으로는 물체를 끝까지 옮기는지 알 수 없었다. 시연의 외형을 늘리는 단계부터 분할, 모델 학습, 폐루프 실행과 실패 분석까지 같은 기록으로 연결했다. +이 문서는 2026-11-03에 SWEEPICK 저장소로 옮겼다. -**ACT는 팀원과 공동 개발·검증했다. 전형주 주 담당은 모방학습·시뮬레이션·Sim-to-Real·데이터 검증이며, Visual SLAM은 협업으로 참여했다.** 아래 ACT 수치는 공동 실험 결과다. 개인 담당 경험과 연결해 설명하되, 외부 ACT·RoboTwin 구현과 팀 기여의 출처도 함께 남긴다. +**→ [Alpenj/sweepick · docs/CUSTOM_ACT_V1_KO.md](https://github.com/Alpenj/sweepick/blob/main/docs/CUSTOM_ACT_V1_KO.md)** -[프로젝트](../README.md) · [그림·영상으로 보기](https://julianjeonresume.netlify.app/research/custom-act-v1/) · [공개 후속 요약](#실물-연동과-후속-기록)(접근 권한 필요) · [이전 접촉·계획 연구](RESEARCH_SUMMARY_KO.md) - -## 1. 데이터 수집 — 동작을 보존하고 외형을 바꿨다 - -원본113개 궤적에 각4개 외형을 더해 총565개 뷰를 구성했다. 같은 상태·관절 동작에서 손목 RGB의 물체색, 테이블·벽 텍스처와 조명을 다시 렌더링했다. 565개는 서로 다른 물리 시연565회가 아니라 113개 동작의 외형5종이다. - -| 분할 | 원본 궤적 | 외형 뷰 | -|---|---:|---:| -| Train | 59 | 295 | -| Validation | 29 | 145 | -| Test | 25 | 125 | -| 합계 | 113 | 565 | - -관절 상태·명령·시각·물체 pose와 head depth/PCD가 같은지 검사했다. 한 궤적의 외형 변형은 같은 분할에 묶어, 같은 동작이 학습과 평가에 나뉘어 들어가지 않게 했다. 정책 입력은 관절q·head PCD·양손목 RGB이며, 정답 행동·phase·물체 정답pose를 추론 입력에서 제외했다. - -![동일 궤적의 다섯 외형](../../docs/media/collection-appearances.png) - -## 2. 학습 — 학습 곡선과 작업 완료를 따로 확인했다 - -shared wrist ResNet18+1×1 projection, hidden256, chunk20, state/action12, KL10, prior z=0 구성으로200 epoch 학습을 완료했다. epoch1~3은 batch4, 이후는 batch32였으며 epoch63 이후 재개한 기록을 포함한다. - -| 지표 | 기록 | -|---|---:| -| Best validation prior normalized L1 | 0.01553423676 (epoch103) | -| Epoch200 validation prior normalized L1 | 0.01951522965 | - -최저 검증 손실은 epoch103에서 나왔다. 이200 epoch 실행의 최종·best 모델은 원본 묶음에서 폐루프 평가되지 않았다. 다음 절의 작업 완료율은 별도로 고정한 epoch30 체크포인트의 결과다. - -![학습·검증 손실과 재개 구간](../../docs/media/training-curves.png) - -## 3. 추론 — 고정한 모델로143개 위치를 실행했다 - -epoch30 체크포인트와 temporal ensemble ON(k=0.01), prior z=0을 고정해143개 위치를 각1회 평가했다. 완료 기준은 **Lift≥30mm → measured Stow(오차0.025rad 이내) → 상승·접촉 상태를 물리2초 유지**다. - -| 위치 구분 | 완료/시도 | 관측 완료율 | -|---|---:|---:| -| Train 영역 | 76/80 | 95.0% | -| Validation 영역 | 29/32 | 90.6% | -| Test 영역 | 27/31 | 87.1% | -| 전체 | 132/143 | 92.3% | - -블록색·테이블/벽 텍스처·조명을 랜덤화했다. 물체 크기40mm·yaw·물성·카메라 보정은 고정했다. Test 결과로 평가 체크포인트를 바꾸거나 선택하지 않았고 Test expert HDF5는 학습에 사용하지 않았다. Train영역80개는 학습에 쓴59개와 그 외21개를 포함하며, 그 결과는 각각59/59와17/21이다. - -143개 위치 각1회의 SIM 조작 평가이므로 이 값을 실물 성공률·반복 신뢰도·양팔 동시 조작 성능이나 전체 이동·청소·충전 과제의 완료율로 확대하지 않는다. - -- [143개 위치별 공개 결과 CSV](evidence/custom_act_v1_20261027_positions.csv) -- [조건·집계 공개 요약 JSON](evidence/custom_act_v1_20261027_summary.json) -- [작업공간 분포](https://julianjeonresume.netlify.app/assets/custom-act-v1/evaluation-workspace.svg) -- [Test mid00_01 대표 SIM 영상](https://julianjeonresume.netlify.app/assets/custom-act-v1/sim-test-mid00_01.mp4) - -## 4. 분석 — 예측 청크가 명령이 되는 과정을 추적했다 - -실패11회는 grasp/lift7회와 stow4회로 나눴다. query 시점, 예측 horizon, 실제 target 시점, 집계 가중치, 전송 명령, 실측 관절, phase와 영상을 함께 읽었다. 저장된17,280개 예측 행·15,000개 후보 행·864개 실행 행을 재계산했을 때 target정렬 오류는0, 가중치식 최대오차는1.85×10⁻⁸, 출력 재구성 최대오차는1.84×10⁻⁷rad였다. 저장 데이터의 일관성 검사이며 새 실행 결과는 아니다. - -Val mid00_00·seed701000000의 단일 비교에서는 같은 epoch30의 ON이 Lift→Stow→Hold를 마쳤고, 기존 first-action-only OFF는8.4초 관찰 후 중단됐다. 이 OFF는 원본 ACT의 청크 순차 실행과 다른 방식이다. 원본 sequential OFF는 실행하지 않았으므로, 중단을 완료 실패로 세거나 앙상블의 일반적인 성능 향상으로 단정하지 않는다. - -![명령을 생성하는 실행 방식 비교](https://julianjeonresume.netlify.app/assets/custom-act-v1/control-schedules.png) - -## 실물 연동과 후속 기록 - -SWEEPICK의 물체 이동·청소·저전력 복귀·도킹·충전 흐름은 시뮬레이션에서 구현했고 실물 통합을 진행하고 있다. 이 전체 흐름의 구현 여부와 본문의 ACT 조작 완료율은 별도 근거다. - -기존 실물 트랙은 Native 교사120/198회(10/18~26), ACT TJJ v2 train46/val7 run·관절 예측오차3.30°(epoch690), DAgger 교정 시연16개(10/23~25)를 유지한다. 각각 교사 작업 판정·오프라인 예측·교정 데이터로 의미가 다르다. - -10/27 후속 노트는 ACT30과 실물 실행기의 관측·12축 절대행동 계약, 앙상블·STOP·feedback·파지 증거 경로를 정리한다. 저장 앙상블 비교와 코드 검사는 새 실물 파지 결과가 아니다. 새IR2는30초 정지 shadow, 양팔 각300 prefix, 위반0·모터쓰기0이며 full180s=false/all_pass=false다. 이전180초 G4 UNKNOWN 기록도 그대로 보존한다. - -공개 후속 요약은 위 기록과 [프로젝트의 실물 트랙](../README.md#실물-데이터와-실행기-연동)에서 읽을 수 있다. 비공개 원본 노트의 주소·원시 로그·개인 장치 정보는 공개하지 않는다. - -## 출처와 재검증 범위 - -사용자가 제공한 `CUSTOM_ACT_V1_standalone_20261027.html`을 직접 읽었다. 내장 ZIP의 보고서·원본 시도 CSV143행·에피소드 JSON143개·학습 곡선과 로그·청크 비교 CSV를 대조했다. 원본 HTML SHA256은 다음과 같다. - -`b0d3b524f57b6ae4e2f01c2533bab8c0ca90a532c99112a13bdfdf9092fa599f` - -평가 epoch30 체크포인트 SHA256: - -`032aab674bdbe83a3e75ebf793fc98026ea0c382f994f40ef05f5c4730ab0cf7` - -공개 CSV·JSON은 평가 조건과 결과에 필요한 필드만 골랐다. 원시 데이터·학습 tensor·개인 장치 경로는 포함하지 않는다. 공식 ACT/RoboTwin 등 외부 구현의 출처·라이선스는 기존 코드와 원자료에 따른다. 이 문서는 담당 경험과 프로젝트 실험을 연결한 연구 요약이며 공동 데이터·외부 라이브러리 전체의 단독 저작을 주장하지 않는다. - -갱신2026-10-27. 저장 기록을 재계산·대조했으며, 이 문서 작업 중 시뮬레이터나 실물 로봇을 새로 실행하지 않았다. - -문서 연결 정리: 2026-10-28. 위 실험 기록의 기준일과 결과는 유지했다. +옮기기 직전의 원문: [DAPIER@2399bfc](https://github.com/Alpenj/DAPIER/blob/2399bfc531074f29e8c80905b6082b90f99cbd60/2ARM_ROBOT/docs/CUSTOM_ACT_V1_KO.md) diff --git a/2ARM_ROBOT/docs/DAPIER_NATIVE_ACT_RUNTIME.md b/2ARM_ROBOT/docs/DAPIER_NATIVE_ACT_RUNTIME.md deleted file mode 100644 index df95ec44..00000000 --- a/2ARM_ROBOT/docs/DAPIER_NATIVE_ACT_RUNTIME.md +++ /dev/null @@ -1,128 +0,0 @@ -# Stage 6 · LeRobot 완전 독립 DAPIER-native ACT runtime - -> 현행 장비 역할은 [`../config/hardware_roles.json`](../config/hardware_roles.json)을 정본으로 한다: SO-101 양팔, 전면 Astra S, top-view HP-ASC-H201, 좌·우 wrist RGB. - -> 2026-09-07 범위 확인: 여기서 독립적으로 구현한 것은 학습·추론 코어다. 현재 모든 실제 -> 카메라와 양팔 실행까지 독립 구현·검증됐다는 의미는 아니다. 이후 개발은 -> [공통 개발 기준](DEVELOPMENT_CONTRACT.md)에 따라 ROS2 실행기 의존성을 분리한다. - -## 결과 - -`shoe_sorting_data.dapier_native_act`가 finalized DAPIER episode를 직접 읽고 다음 경로를 소유한다. - -```text -raw episode + RGB/Depth payload - → episode 내부 H-step action window + padding mask - → train-only state/action normalization - → RGB-D CNN + CVAE + Transformer action decoder - → masked L1 + KL 학습 - → DAPIER-native checkpoint - → action chunk inference + stale queue reset - → control_authorized=false proposal -``` - -이 경로는 `lerobot` package를 import하지 않는다. 기존 `lerobot_v3_encoder.py`와 -`native_act_smoke.py`는 공식 구현과 비교하거나 Dataset v3를 교환할 때만 쓰는 optional -reference backend다. - -## 왜 이 순서로 구현했나 - -1. **dataset window를 먼저 직접 소유**했다. 모델보다 먼저 episode 경계, 12차원 관절 순서, - `[FFF, FFT, FTT]` tail mask를 고정해야 미래 action이 다음 episode로 새지 않는다. -2. **RGB-D decoding과 normalization을 dataset에 붙였다.** Astra raw byte를 검증한 후 RGB 3채널과 - normalized depth 1채널을 만들며, state/action 통계는 train split에서만 계산한다. -3. **작은 ACT core를 직접 구현**했다. 현재 state와 RGB-D를 조건으로 CVAE latent와 learned action - query가 H-step action을 만들고 padded target은 loss에서 제외한다. -4. **checkpoint round-trip을 학습 gate로 만들었다.** 저장 직전과 다시 load한 모델의 deterministic - inference tensor가 정확히 같지 않으면 학습 명령이 실패한다. -5. **inference를 제어와 분리**했다. CLI 출력은 `control_authorized=false`이며 action queue reset은 남은 - chunk를 버린다. 실물 명령은 기존 독립 safety supervisor 승인 뒤에만 별도 adapter가 전달한다. - -이 순서는 “LeRobot 코드를 복사해 떼는 것”이 아니라 DAPIER의 데이터·하드웨어 계약에 맞는 작은 -runtime을 직접 이해하고 소유하기 위한 것이다. - -## 구현 범위 - -| 영역 | 현재 구현 | 검증 | -|---|---|---| -| dependency | NumPy·PyTorch만 optional, LeRobot import 없음 | AST import 검사 | -| dataset | accepted train episode 직접 탐색, state/action 12D flatten, episode-tail padding | 2 episode × 3 frame mask | -| vision | raw `rgb8` 계열 + `16UC1/32FC1` depth를 4채널 tensor로 변환 | payload checksum/shape gate 재사용 | -| model | 작은 CNN, CVAE posterior, Transformer decoder, H-step action head | CPU one optimizer step·finite inference | -| loss | valid scalar만 평균한 L1 + KL | padded timestep mask 사용 | -| checkpoint | config, normalization buffer, model/optimizer state, step | save→load prediction exact equality | -| inference | 파일 checkpoint + raw dataset item → physical-unit action chunk JSON | shape·finite·`control_authorized=false` | -| queue | 앞 `n_action_steps`만 실행 후보로 보관, reset 시 모두 폐기 | stale pop 실패 test | - -## 기존 Ubuntu ROS2 교육 PC 실행 경로 — 선택적 연동 - -기존 ROS2 workspace를 먼저 build한다. - -```bash -cd ~/DAPIER/2ARM_ROBOT -colcon build --symlink-install --packages-select shoe_sorting_data -set +u -source install/setup.bash -set -u -``` - -ML 환경에는 NumPy와 CUDA 버전에 맞는 PyTorch만 있으면 된다. LeRobot, Datasets, PyArrow는 native -경로에 필요 없다. 기존 교육 PC 환경을 먼저 확인한다. - -```bash -ros2 run shoe_sorting_data shoe_dapier_act status -``` - -작은 독립 smoke는 synthetic RGB-D 2 episode를 만들고 one-step 학습, checkpoint reload, -inference, queue reset까지 실행한다. - -```bash -ros2 run shoe_sorting_data shoe_dapier_act smoke \ - --output /tmp/dapier_native_act_smoke -``` - -실제 accepted train episode로 checkpoint를 만든다. 처음에는 메모리 여유를 위해 작은 batch/chunk로 -시작하고 RTX 5050에서 측정 후 늘린다. - -```bash -ros2 run shoe_sorting_data shoe_dapier_act train \ - --root output/accepted_episodes \ - --checkpoint output/checkpoints/native_act_step100.pt \ - --chunk-size 16 \ - --batch-size 8 \ - --max-steps 100 \ - --device cuda -``` - -RGB-only 대조군은 같은 명령에 `--rgb-only`를 붙인다. checkpoint에 depth 포함 여부와 -`depth_scale`·`max_depth_m`가 함께 저장되므로 inference가 다른 전처리를 조용히 섞지 않는다. - -한 frame에서 action chunk를 생성하되 로봇에는 보내지 않는다. - -```bash -ros2 run shoe_sorting_data shoe_dapier_act infer \ - --root output/accepted_episodes \ - --checkpoint output/checkpoints/native_act_step100.pt \ - --item 0 \ - --device cuda -``` - -## 주장 경계와 다음 현장 gate - -- 이것은 ACT의 action chunk·CVAE·masked loss를 직접 구현한 **DAPIER-native 최소 재구현**이다. -- original ALOHA/LeRobot 모델과 topology, preprocessing, weight key가 같지 않으므로 그 checkpoint와 - 호환 또는 동등하다고 주장하지 않는다. -- synthetic smoke는 task success 증거가 아니다. 실제 신발 데이터의 held-out offline 평가와 - supervisor-guarded closed-loop 성공률이 별도로 필요하다. -- depth scale 기본값 `1000`은 mm→m 변환 knob다. 전면 Astra S와 top-view HP-ASC-H201 각각의 - 실측 depth unit, CameraInfo, registration을 확인한 뒤 장치별로 고정한다. -- 첫 실물 rollout은 `n_action_steps=1`, base stationary, E-stop·joint limit·watchdog 승인으로 제한한다. -- Stage 7에서 inference proposal과 Stage 5 supervisor trace 연결을 완료했다. 실제 checkpoint SHA, - reset generation, source observation identity를 검증하며 hardware publish는 계속 차단한다. -- 통합 smoke와 다음 현장 gate는 [`NATIVE_ACT_SUPERVISOR_INTEGRATION.md`](NATIVE_ACT_SUPERVISOR_INTEGRATION.md)에서 확인한다. - -## 연구 근거 - -무관한 최신 VLA/world-model을 넣지 않고, 이 구현 결정에 직접 필요한 ACT 원 논문·공식 ACT 코드와 -현재 LeRobot ACT 구현만 비교했다. 채택/실험/보류 판정과 원문 링크는 -[`research/LATEST_DAPIER_NATIVE_ACT_RESEARCH_20260824.md`](research/LATEST_DAPIER_NATIVE_ACT_RESEARCH_20260824.md)에 기록했다. diff --git a/2ARM_ROBOT/docs/DEVELOPMENT_CONTRACT.md b/2ARM_ROBOT/docs/DEVELOPMENT_CONTRACT.md deleted file mode 100644 index 05710129..00000000 --- a/2ARM_ROBOT/docs/DEVELOPMENT_CONTRACT.md +++ /dev/null @@ -1,124 +0,0 @@ -# Dual SO-101 공통 개발 기준 - -record_id: DAPIER-2026-09-07-native-hybrid-development-contract - -나는 양팔 로봇의 데이터·학습·실행 흐름을 DAPIER 코드로 이해하고 관리하는 방향으로 -개발한다. 이 문서는 양팔 프로젝트의 **설계 기준**이며, 아래 기능이 -이미 구현되었거나 실물에서 검증됐다는 뜻은 아니다. - -## 1. 개인화의 범위 - -- DAPIER가 observation/action 계약, episode 저장, 학습 연결, 제어권 전환과 실행을 - 소유한다. RoboTwin·LeRobot 실행기를 호출하는 래퍼를 완성된 자체 런타임이라 부르지 않는다. -- RoboTwin은 demonstration 생성·randomization·planner의 비교 기준으로 사용하고, - 관련 overlay와 변환 코드는 `2ARM_ROBOT/robotwin/`에 분리한다. upstream 의존성을 - 제거하기 전에는 독립 구현이라고 기록하지 않는다. -- ROS2는 외부 SLAM·이동 모듈과 연결할 때 선택 가능한 어댑터다. 핵심 데이터·학습·제어 - 계약이 ROS 메시지나 ROS 실행 환경을 필수로 요구하지 않도록 한다. -- MuJoCo/SAPIEN, PyTorch, OpenCV, 수치 라이브러리와 공식 장치 SDK는 필요한 범위에서 - 재사용한다. 개인화는 모든 라이브러리를 다시 만드는 일이 아니다. 버전·라이선스·출처를 남긴다. - -## 2. IL + IK + rule: 단계에 따라 제어권을 나눈다 - -| 단계 | 기본 역할 | 다음 단계로 넘어갈 근거 | -| --- | --- | --- | -| 이동 후 정지 | 이동 모듈 + 상태 규칙 | base 정지·관측 freshness 확인 | -| 물체 재관측 | RGB-D 인식 + 양팔 좌표 변환 | pose·보정값·불확실성 확인 | -| pre-grasp와 비접촉 접근 | IK + 충돌 검사 + 손목 visual servo | 도달 가능·가시성·오차 확인 | -| 날개 잡기·열기·물체 조작 | IL 후보 동작 + visual servo + 접촉 규칙 | 실제 접촉·들림·과부하 확인 | -| 운반·내려놓기 | 상황에 따라 IK/IL + 상태 규칙 | 지지·놓임·그리퍼 해제 확인 | - -IL과 IK를 고정 비율로 섞지 않는다. 각 단계에 입력, 출력, 제어권 소유자, 전환 조건, -timeout과 실패 로그를 명시한다. IL이 학습되기 전의 scripted expert는 IL 정책이 아니다. - -- IL 출력이 Cartesian 목표이면 IK로 관절 목표를 구한다. **관절 action chunk이면 IK를 - 다시 적용하지 않고** 단위·순서·한계·충돌·시간 유효성을 검증한다. -- 각 SO-101은 팔 5자유도와 그리퍼다. 위치 3 + 접근축 2 등 가능한 task 제약을 사용하고, - 임의의 6D 자세를 모두 정확히 만족한다고 가정하지 않는다. -- 좌우 팔 모두 변환·경로·충돌 검사 대상이다. 오른팔이 박스를 열고 왼팔이 물체를 꺼내는 - 역할 차이가 왼팔 실행 생략의 근거가 되지 않는다. -- 목표 위치에 도착했다는 이유만으로 grasp 성공으로 처리하지 않는다. 접촉·물체 들림· - 미끄러짐과 유지 여부를 확인한다. 센서가 없는 항목은 미검증으로 남긴다. -- LLM은 고수준 작업 지시만 담당한다. IL·IK·LLM 어느 것도 실행 안전 검사를 우회하지 않는다. - -## 3. 언어별 책임 - -| 계층 | 우선 언어 | 범위 | -| --- | --- | --- | -| 연구·데이터·학습 | Python | sim 구성, 전처리, demonstration, ACT, 평가, 인식·IK 실험 | -| 인식·지역화 런타임 | C++ | 실측상 필요한 영상 처리·Visual SLAM·지역화; servo loop와 분리 | -| 장치 실행·안전 | C++17 | 장치 I/O, 보정 변환, 제한·watchdog, bounded command 실행 | -| MCU·C ABI 경계 | C, 필요한 경우만 | 펌웨어·기존 SDK 호출; 별도 C 프레임워크를 만들지 않음 | - -프로파일링 없이 Python 전체를 C++로 다시 쓰지 않는다. Python에서 호출하는 native -라이브러리는 이미 native 연산을 수행할 수 있다. C++라는 이유만으로 hard real-time이나 -동적 할당 없음이 보장되지 않는다. 지연·할당·주기·실패 동작을 실제 측정한다. - -ROS2가 맡던 역할도 필요한 것만 대체한다: 같은 프로세스는 함수·명시적 자료형, -별도 프로세스는 bounded IPC 계약, TF는 시간·단위가 있는 보정 변환, QoS는 sequence· -capture/receive timestamp·TTL·backpressure, launch는 설정과 실행 진입점, 기록은 -episode와 구조화 로그로 관리한다. ROS 전체의 복제품을 새로 만들지는 않는다. - -## 4. 양팔·카메라·sim-to-real 계약 - -- 공통 순서는 left 6축 → right 6축이다. 내부 단위는 팔 rad, 그리퍼 0..1로 명시하고, - degree·tick·gripper 0..100 자료는 경계에서 이름 있는 변환으로 처리한다. -- 측정 joint state, 원래 요청 action, 안전 제한 후 실제 전송 action을 구분한다. - `action[t] = state[t+1]` 자료를 실기 command 자료와 설명 없이 섞지 않는다. -- top H201은 manipulation용 metric depth, 좌우 손목은 RGB visual servo다. - Astra는 전면 SLAM 역할이며, 연결되어 있지 않으면 실제 스트림으로 표시하지 않는다. - depth 컬러맵은 표시용이며 학습·3D 계산용 원본 거리값이 아니다. -- 실제 목표는 visual 관측 → camera frame → base 및 **좌·우 arm frame**으로 변환한다. - intrinsics, extrinsics, 단위, 보정 revision과 시간 유효성을 함께 확인한다. - 시뮬레이터 정답은 expert/평가용으로 구분하고 정책 관측이나 실물 pose로 섞지 않는다. -- 장치 역할은 검증된 identity와 calibration에 결합한다. 포트 번호를 바꿨다고 매번 - 재보정하지 않는다. 고유 serial 없는 동일 카메라를 포트 변경 후 자동 구별한다고 보장하지 않는다. -- Raspberry Pi 4에서 ARM SDK 지원, USB 대역폭, 동시 FPS, CPU·메모리·온도·지연을 - 실측한다. 학습·무거운 추론은 노트북 우선이며 Pi에는 필요한 I/O·실행만 둔다. - 양 손목 설정은 동일하게 유지하고 해상도는 관측 품질과 자원 측정을 함께 보고 결정한다. -- frame drop·시간 불일치·tracking loss·네트워크 지연·모터 이상·접촉 실패를 시험한다. - 단순 품질 경고와 실제 실행 중단 조건을 구분하며 기존 실물 승인·정지 경계는 유지한다. - -## 5. 현재 코드와 작업 완료 기준 - -### 2026-09-10 수동 녹화와 현장 안전 조건 - -- 현장 수동 teleop·에피소드 수집에는 기존 `scripts/dual_so101_il record`와 - `scripts/lerobot_cv_dashboard.py`를 재사용한다. 별도 녹화기를 임의로 만들지 않는다. -- 독립 자동 정지 장치 설치나 통신 단절 시 자동 정지 검증을 수동 녹화의 필수 - 선행조건으로 다시 추가하지 않는다. 동일한 조건을 새 이름의 문서·승인 절차· - 실행 차단 코드로 재도입하지 않는다. 새로운 필수조건이 필요하다고 판단하면 - 근거와 작업 영향을 사용자에게 설명하고 먼저 합의한다. -- 사람의 현장 입회·명시적 실행 승인·즉시 사용 가능한 전원 차단·장치 및 보정 - 확인·저속과 동작 범위 및 시간 제한은 유지한다. 수동 전원 차단이나 호스트 - 보조 정지 코드를 독립 자동 정지 검증으로 주장하지 않는다. -- 이 결정은 무인·자율 정책 rollout의 자동 정지 검증을 면제하지 않는다. - 기존 도구의 다른 차단 조건을 묵시적으로 해제하거나 실물 성공으로 기록하지 않는다. - -### 현재 구현 상태 - -### 2026-09-11 현장 감독 시험에 대한 사용자 결정 - -사람이 모든 동작을 현장에서 감독하고 즉시 전원을 차단할 수 있는 bounded 실물 -정책/궤적 시험에는 독립 자동 정지 검증을 필수 선행조건으로 두지 않는다. -장치 identity·보정 확인, 저속·동작 범위·시간 제한, 기존 오류 중단, 실행 직전의 -명시적 승인은 유지한다. 독립 자동 정지가 검증됐다고 기록하지 않으며 무인 실행은 -이 예외의 대상이 아니다. 준비된 SIM 궤적이 있다는 이유만으로 큰 시작 자세 이동을 -승인된 것으로 간주하지 않는다. - -### 현재 구현 상태 (계속) - -현재 `scripts/dual_so101_il`은 LeRobot 실행 래퍼다. 자체 Python ACT 모듈 -`src/shoe_sorting_data/shoe_sorting_data/dapier_native_act.py`와 C++ -`so101_ros2/dapier_so101_core`의 보정·관절 수학은 재사용 후보지만, 이 존재만으로 -3카메라·양팔 학습 정책·실물 closed-loop가 완성된 것은 아니다. C++ 코어의 현재 -CMake도 ament 의존성이 있으므로 ROS2-free 배포 완료라고 기록하지 않는다. - -다음 구현은 기존 계약·데이터 호환 확인 → 자체 양팔 teleop/record의 mock 검증 → -visual IK/IL 연결 → sim closed-loop → 실측 profile을 사용한 shadow → 승인된 저속 -실물 검증 순서로 진행한다. 이번 지침 추가는 기존 실행기나 하드웨어 동작을 변경하지 않는다. - -작업 시작 때 해당 단계와 모듈을 밝히고, 완료 때 변경 파일·명령·결과·artifact·남은 -불확실성을 기록한다. SIM/MOCK/HW를 구분하고, 학습·replay 성공을 실물 성공으로 -바꾸어 쓰지 않는다. 공개 GitHub에는 검증 가능한 코드·자체 설명만, 비공개 Notion에는 -학습 원문과 개인 탐구를 남긴다. 원문과 과거 설계가 최신 사용자 결정과 다르면 차이를 기록한다. diff --git a/2ARM_ROBOT/docs/DUAL_SO101_LEADER_IL_QUICKSTART_KO.md b/2ARM_ROBOT/docs/DUAL_SO101_LEADER_IL_QUICKSTART_KO.md deleted file mode 100644 index 5fad4aa8..00000000 --- a/2ARM_ROBOT/docs/DUAL_SO101_LEADER_IL_QUICKSTART_KO.md +++ /dev/null @@ -1,142 +0,0 @@ -# Dual SO-101 leader imitation learning 실행 순서 - -실행 파일은 [`../scripts/dual_so101_il`](../scripts/dual_so101_il) 하나다. LeRobot 0.6의 -`bi_so_leader`, `bi_so_follower`, `lerobot-record`, `lerobot-train --policy.type=act`를 -그대로 호출하는 기존 수집·비교 경로다. 완성된 DAPIER 자체 실행기는 아니다. - -> 2026-09-07 개발 방향: [공통 개발 기준](DEVELOPMENT_CONTRACT.md)에 따라 DAPIER가 -> 데이터·학습·실행 흐름을 소유한다. 아래 LeRobot 경로는 기존 자료를 보존하고 비교하기 위한 -> 선택적 경로로 남기며, 이를 실행했다는 이유로 자체 양팔 IL+IK가 완성됐다고 기록하지 않는다. - -## 장치 정본 - -- left/right follower: 로컬 프로필의 `/dev/serial/by-id/...` -- left/right leader: `identify-leaders`로 물리 좌·우를 확인한 뒤 로컬 프로필에만 저장 -- left wrist RGB: `/dev/dapier/left_wrist_rgb`, 320×240 YUYV 30 fps -- top H201 depth: eYs3D 공식 SDK `zdDepthVec`, 640×460 `uint16 mm`, 15 fps -- right wrist RGB: `/dev/dapier/right_wrist_rgb`, 320×240 YUYV 30 fps - -H201은 OpenCV의 `/dev/video*` RGB/IR 프레임을 depth라고 부르지 않는다. DAPIER SDK helper가 -eYs3D 공식 `zdDepthVec`를 직접 읽어 invalid sentinel `16384`를 `0`으로 바꾸고, 원본 거리값을 -`observation.images.top_h201_depth`에 무손실 `uint16 mm`로 저장한다. 화면에서만 컬러맵으로 -보인다. ACT는 양 손목 RGB와 12축 상태·행동을 학습하고, 이 raw depth는 박스 3D pose와 IK -목표 계산에 사용한다. - -## 1. 로컬 프로필 생성 - -```bash -cd /home/dapier-jhj/DAPIER-vision-relative-manipulation-02 -2ARM_ROBOT/scripts/dual_so101_il setup -``` - -생성 파일: - -```text -2ARM_ROBOT/config/dual_so101_il.local.env -``` - -이 파일은 Git에서 제외된다. -네 팔의 현재 `/dev/serial/by-id/...` 경로를 follower/leader 항목에 넣는다. -물리 장비에도 같은 역할과 serial 끝 4자리를 `LF-xxxx`, `RF-xxxx`, `LL-xxxx`, -`RL-xxxx` 형식으로 표시한다. USB hub 포트나 `ttyACM*` 번호는 장치 정본으로 쓰지 않는다. - -## 2. leader 좌·우 식별 - -```bash -2ARM_ROBOT/scripts/dual_so101_il identify-leaders -``` - -확인 문자열을 입력한 직후 5초 동안 **왼쪽 leader만** 여러 관절로 움직인다. 출력에서 -`max_joint_span`이 큰 시리얼이 왼쪽 leader다. 그 결과를 로컬 프로필의 -`LEFT_LEADER_PORT`, `RIGHT_LEADER_PORT`에 `/dev/serial/by-id/...` 전체 경로로 넣는다. - -## 3. 장치와 calibration 상태 확인 - -대시보드는 `q`로 먼저 닫아 카메라를 해제한 뒤 실행한다. - -```bash -2ARM_ROBOT/scripts/dual_so101_il status -``` - -네 serial과 세 camera가 `OK`여야 한다. 기존 단일팔 -`so101_follower_main.json`/`so101_leader_main.json`은 새 양팔 ID로 간주하지 않는다. -연결할 때마다 calibration을 다시 하지 않는다. `teleop`과 `record`는 네 side-specific -calibration 중 하나라도 없으면 LeRobot의 자동 calibration을 시작하지 않고 즉시 중단한다. -저장 파일을 복구할 수 없거나 기구 조립·모터 교체·영점 변경이 있었을 때만 명시적으로 -`calibrate-leaders` 또는 `calibrate-followers`를 실행한다. - -calibration 정본은 지워질 수 있는 `~/.cache`가 아니라 로컬 프로필의 -`CALIBRATION_DIR`(기본값 `~/.config/dapier/lerobot-calibration`)에 보관한다. 2026-09-04에는 -두 follower EEPROM에 이미 저장된 offset·관절 범위를 LeRobot 공식 decoder로 읽어 -side-specific JSON을 복구했고, 양쪽 모두 JSON과 EEPROM의 완전 일치를 확인했다. 이 복구는 -토크·목표 위치·EEPROM을 쓰거나 팔을 다시 움직여 범위를 측정하지 않았다. - -## 4. 양쪽 leader calibration - -```bash -2ARM_ROBOT/scripts/dual_so101_il calibrate-leaders -``` - -화면 지시에 따라 각 leader의 중앙 자세와 전 관절 가동 범위를 기록한다. - -## 5. 양쪽 follower calibration - -양팔 ID의 calibration이 `MISSING`일 때만 실행한다. - -```bash -2ARM_ROBOT/scripts/dual_so101_il calibrate-followers -``` - -## 6. 녹화 전 teleop 시험 - - follower 두 팔 주위의 사람과 물체를 치우고 전원 차단 수단을 잡은 상태에서 실행한다. - -```bash -2ARM_ROBOT/scripts/dual_so101_il teleop -``` - -leader를 아주 작게 움직여 물리 좌·우, 관절 방향, gripper 방향을 확인한다. 화면은 Rerun 없이 -동일 observation을 `LEFT WRIST | TOP H201 DEPTH | RIGHT WRIST` 순서로 표시한다. wrapper는 -한 control tick의 follower 변화량을 5도로 제한한다. 어느 축이 반대로 움직이거나 다른 -팔이 반응하면 즉시 종료하고 calibration/leader mapping을 수정한다. - -## 7. demonstration 녹화 - -```bash -2ARM_ROBOT/scripts/dual_so101_il record -``` - -기본 episode는 30초, reset 15초, 20회, 15 Hz다. 화면과 저장은 같은 observation을 사용하므로 -카메라를 두 번 열지 않는다. 성공 시연만 남긴다. 동작 순서는 -오른팔로 박스 날개 접근·열기 → 왼팔로 박스 내부 접근·신발 들어올리기다. base 이동은 -이 dataset의 action에 포함하지 않는다. - -녹화를 실행하면 별도 저장 버튼 없이 `30초 시연 → 15초 환경 복원 → episode 자동 저장 → -다음 episode 시작`을 20회 반복한다. 환경 복원 구간에는 박스·물체·leader/follower를 다음 -시연의 시작 상태로 되돌린다. 카메라 창의 디스켓 아이콘은 dataset 저장이 아니라 화면 캡처다. - -| 카메라 창 키 | 동작 | -| --- | --- | -| `→` | 현재 시연을 30초 전에 끝내고 복원·저장 단계로 이동 | -| `←` | 현재 시연을 버리고 복원 후 같은 episode 재녹화 | -| `Esc` 또는 `q` | 현재 시연을 저장하고 전체 녹화 정상 종료 | - -창을 강제로 닫거나 터미널을 종료하지 않는다. 하단 `STATUS`가 `RECORDING EPISODE N`, -`RESETTING ENVIRONMENT`, `FINALIZING / SAVING` 중 무엇인지 확인한다. - -## 8. ACT 학습 - -```bash -2ARM_ROBOT/scripts/dual_so101_il train -``` - -기본값은 CUDA, batch 8, 20,000 step이다. 결과는 -`/home/dapier-jhj/dapier_training/dual-so101-box-shoe-act`에 저장된다. 학습 완료는 실물 -성공 판정이 아니다. held-out loss 확인 후 MuJoCo replay, IK/충돌 gate, 저속 실물 rollout을 -별도로 통과해야 한다. - -2026-09-04 실물 smoke에서 150 frame/15 Hz episode를 다시 로드해 state/action 12D, 좌·우 -RGB `(3,240,320)`, H201 depth `(1,460,640)`와 `depth_unit=mm`를 확인했다. 기본 ResNet18 -ACT는 1채널 depth를 직접 받지 못하므로 launcher가 raw depth를 보존하면서 ACT 입력을 양 손목 -RGB+state로 제한한다. 같은 dataset으로 CUDA ACT 1-step forward/backward/checkpoint 저장까지 -통과했다. diff --git "a/2ARM_ROBOT/docs/DYNA_SLIM_\354\240\201\354\232\251_\354\241\260\354\202\254_\354\260\270\352\263\240\354\236\220\353\243\214.md" "b/2ARM_ROBOT/docs/DYNA_SLIM_\354\240\201\354\232\251_\354\241\260\354\202\254_\354\260\270\352\263\240\354\236\220\353\243\214.md" deleted file mode 100644 index c6ef889f..00000000 --- "a/2ARM_ROBOT/docs/DYNA_SLIM_\354\240\201\354\232\251_\354\241\260\354\202\254_\354\260\270\352\263\240\354\236\220\353\243\214.md" +++ /dev/null @@ -1,290 +0,0 @@ -# DAPIER 관점: DYNA Dyna-2 인프라와 SLIM-0.5B의 이중팔 로봇 적용 조사 - -> 확인일: 2026-08-20 (KST) -> 장비 주의 (2026-09-03): 본문의 JDcobot/Astra Pro 표기는 당시 분석 가정이다. 현행 실물 역할은 [`../config/hardware_roles.json`](../config/hardware_roles.json)을 따른다. -> 조사 대상: [DYNA — *Training Dyna-2 at million-hour scale, repeatably*](https://www.dyna.co/research/dyna-2-infrastructure), [SLIM 프로젝트 페이지](https://kzz1031.github.io/slim-project-page/) 및 해당 페이지가 직접 연결한 저자 원문·공개 구현 -> 작성 원칙: **[사실]**은 링크한 1차 출처의 명시 내용, **[추론/권고]**는 그 사실을 이중팔 프로젝트에 적용해 도출한 판단이다. DYNA의 성능·규모 수치는 회사의 자체 보고이며 독립 재현 결과가 아니다. - -## 1. 결론부터 - -두 페이지는 경쟁하는 정책 두 개가 아니다. - -| 판단 | 결론 | -|---|---| -| DYNA 인프라 | **대규모 로봇 데이터를 반복 가능하게 만드는 데이터·학습 운영 설계**다. MCAP, 시간 동기화, 버전 스탬프, manifest, local cache, 재시작은 이중팔 데이터 수집에도 직접 유용하다. [사실: DYNA](https://www.dyna.co/research/dyna-2-infrastructure) | -| SLIM | **행동-조건부 예측 latent를 먼저 만들고 flow matching으로 action chunk를 내는 0.47B 정책**이다. 현재 공개 recipe는 두 RGB view, 7D action, 7D state, action horizon 8의 LIBERO 기준이다. [사실: 논문 Appendix A.3–A.4](https://arxiv.org/html/2608.09771), [공개 config](https://github.com/kzz1031/SLIM/blob/main/configs/libero/stage2_policy_h8_40ep.yaml) | -| 이중팔에 대한 최선의 적용 | **DYNA의 data contract/curation 원칙을 먼저 도입하고, 그 위에서 SLIM을 14D 이상 행동·상태 schema로 확장해 작은 closed-loop baseline부터 검증**하는 순서가 타당하다. 이는 두 출처를 결합한 **추론/권고**다. | -| 바로 복제하면 안 되는 부분 | DYNA의 million-hour·PB·multi-cluster 구성은 현재 공개된 재현 artifact가 아니며, SLIM의 논문·공개 코드는 dual-arm 실험을 명시하지 않는다. 따라서 “DYNA 데이터로 SLIM을 그대로 학습” 또는 “7D checkpoint를 이중팔에 재사용”은 근거가 없다. [사실: DYNA 페이지의 공개 링크 목록](https://www.dyna.co/research/dyna-2-infrastructure), [SLIM 논문](https://arxiv.org/html/2608.09771), [SLIM README](https://github.com/kzz1031/SLIM/blob/main/README.md) | - -우선순위는 다음과 같다. - -1. **P0 — 수집 계약:** 좌·우 action/state, 모든 camera, time base, frame/calibration, gripper/contact, episode·schema·robot-software 버전을 한 episode에 함께 남긴다. 이는 DYNA가 설명한 multi-modal episode/버전 스탬프 원칙을 축소 적용한 **권고**다. [근거: DYNA](https://www.dyna.co/research/dyna-2-infrastructure) -2. **P0 — 안전한 baseline:** action chunk를 짧게 실행하고 매 chunk 재관측하는 SLIM식 closed-loop policy를 만들되, 충돌·joint limit·workspace·gripper interlock은 별도 deterministic guard가 승인해야 한다. 후반부 guard는 **권고**이며, 공개 SLIM 자료에는 그런 안전 계층이 기술되어 있지 않다. [근거: SLIM 방법](https://arxiv.org/html/2608.09771), [공개 구현](https://github.com/kzz1031/SLIM) -3. **P1 — 모델 확장:** `action_dim`, `state_dim`, action normalization, dataset adapter, evaluator를 이중팔 schema로 함께 바꾼다. config의 차원 필드는 가변이지만, canonical data/evaluation은 7D를 전제한다. [사실: config helper](https://github.com/kzz1031/SLIM/blob/main/slim/model/config.py), [data 문서](https://github.com/kzz1031/SLIM/blob/main/docs/data.md) -4. **P2 — scale-up:** 데이터가 병목이 된 뒤에만 MCAP topic-group, DAG, warehouse manifest, NVMe cache를 단계적으로 채택한다. PB/multi-cluster를 먼저 만드는 것은 과잉이다. 이는 DYNA가 보고한 병목 이동 순서를 프로젝트 규모에 맞춰 해석한 **권고**다. [근거: DYNA](https://www.dyna.co/research/dyna-2-infrastructure) - ---- - -## 2. 조사 범위와 페이지·매체 인벤토리 - -### 2-1. DYNA 인프라 페이지 - -| 항목 | 확인 결과 | -|---|---| -| 페이지 메타데이터 | 제목은 *Training Dyna-2 at million-hour scale, repeatably*, 작성자는 Dyna Robotics, 표기일은 August 2026, 본문 목차는 Introduction / Scaling challenges / Looking forward / References다. [사실: 페이지](https://www.dyna.co/research/dyna-2-infrastructure) | -| 본문 구조 | `collection → landing bucket → ingestion → training-ready MCAP episodes → curation manifest → GPU training`의 4단계 data lifecycle을 설명하고 storage, ingestion, curation, delivery, optimizer, resilience를 세부 절로 둔다. [사실: Figure 1 및 본문](https://www.dyna.co/research/dyna-2-infrastructure#scaling-challenges) | -| 도표 | Figure 1–11이 있다: lifecycle, topic-group chunking, storage/read benchmark, ingestion DAG/throughput, manifest build/load, cache orchestration/read time, optimizer sharding/cost. 각 caption과 본문 수치를 읽었다. [사실: 페이지](https://www.dyna.co/research/dyna-2-infrastructure) | -| 동적 매체 | HTML은 desktop 및 mobile MP4를 `autoplay`, `loop`, `muted`, `playsinline`, `preload=none`으로 포함한다. 직접 stream metadata 확인 결과 desktop은 H.264 1280×720/30fps/29.70s, mobile은 H.264 750×422/24fps/29.75s이며 오디오 stream은 확인되지 않았다. desktop 영상을 2초 간격 15 frame contact sheet로 추가 확인했으며, collection→ingestion→curation→training lifecycle을 반복 애니메이션으로 보여주고 별도 자막·추가 수치는 보이지 않았다. [사실: desktop asset](https://www.dyna.co/assets/videos/dyna-2-infrastructure.mp4), [mobile asset](https://www.dyna.co/assets/videos/dyna-2-infrastructure-mobile.mp4) | -| 연결된 1차 기술 문서 | MCAP, Airflow, Kubernetes, Alluxio, Muon, FSDP, Slurm, Ansible 등의 공식 문서를 References로 직접 링크한다. DYNA의 구현 code/data/model weight는 이 페이지에서 직접 링크되지 않는다. [사실: References](https://www.dyna.co/research/dyna-2-infrastructure#references) | -| 관련 first-party 페이지 | nav의 [Dyna-2 research page](https://www.dyna.co/dyna-2)는 Dyna-2를 video와 action을 함께/따로 denoise하는 WAM이라고 설명하고, 39개 task의 두 stationary bimanual YAM platform을 held-out robot 평가로 사용했다고 적는다. 이는 인프라 페이지의 모델 문맥을 보완하지만 역시 회사 자체 보고다. [사실: Dyna-2](https://www.dyna.co/dyna-2) | - -**매체 해석 한계.** 페이지의 DOM 텍스트·figure caption·수치·MP4 컨테이너/stream metadata는 확인했다. 다만 제공되는 인프라 영상에는 별도 transcript/caption track이 없고, 각 SVG/animation의 모든 프레임을 사람처럼 pixel 단위로 전수 판독했다고 주장하지 않는다. 이 노트의 figure 해석은 caption·접근 가능한 도식 텍스트와 수치에 한정한다. - -### 2-2. SLIM 프로젝트 페이지와 직접 연결 artifact - -| 항목 | 확인 결과 | -|---|---| -| 페이지 메타데이터 | description은 “compact latent interaction policy for language-guided robot manipulation”이고, 저자는 Jingkai Wang 등 10명으로 표기된다. [사실: 프로젝트 페이지](https://kzz1031.github.io/slim-project-page/) | -| 정적 그림 asset | `teaser_overview`, `mot_architecture`, `idm_fdm_objectives`, `overview_real_evaluation`, `realworld_average`, `ablation_lines`, `attention_heatmap` 및 logo SVG가 있다. 페이지 자체에는 `