🏋️ PRD: 무게 모드 체계 v2.0
SPOEX 2026 체험용 · v2.0 — 2026-03-20
작성: 황호랑 🐯 · 확인: 황성우님
이전: v1.0 (2026-03-15)
1. 개요
1.1 한 줄 요약
6종 무게 모드 전체를 SPOEX 체험용으로 완성 — 리모컨 모달에서 안전한 범위 내 즉석 전환 + 실기 검증.
1.2 배경
- SPOEX 2026 (03.26~29) 부스에서 방문객에게 "같은 머신인데 무게 느낌이 다르다" 체험 제공
- FW 제어 로직(case 3/4/5)은 구현 완료 (3/15)
- 앱 리모컨 모달은 모드 0~2만 표시 중 → 확장 필요
- HIL 검증 완료, 실기 테스트 미수행 → 플래싱 + 안정성 검증 필수
1.3 목표
| # | 목표 | 완료 기준 |
| 1 | 리모컨에서 6모드 전환 | 모드 탭 → FW MODE_CHANGE → ACK |
| 2 | 모드별 파라미터 조절 | 프리셋 선택 or 슬라이더 → 0xF5 05 → 체감 변화 |
| 3 | 체험 안전 보장 | 위험 파라미터 앱 단에서 원천 차단 |
| 4 | 실기 안정성 검증 | 모드 전환 + 부하 변화 시 모터 이상 없음 |
2. 무게 모드 전체 체계
2.1 6모드 맵
➖
Constant
위치·속도 무관 일정 저항
FW ✅ 앱 ✅
↕️
Negative
이완(하강) 시 추가 저항
FW ✅ 앱 ✅
📈
Band
위치 비례 — 탄성밴드
FW ✅ 앱 ✅
⚡
Isokinetic
속도 제한 — 등속성
FW ✅ 앱 ❌
💧
Hydraulic
속도 비례 저항 — 유체
FW ✅ 앱 ❌
〰️
Vibration
주기적 부하 진동
FW ✅ 앱 ❌
2.2 모드별 물리 모델
Constant (case 0)
F = TargetWeight × OnOffScale
Negative (case 1)
수축(상승): F = TargetWeight × OnOffScale
이완(하강): F = (TargetWeight + EccWeight) × OnOffScale
Band (case 2)
F = TargetWeight × (position / rangeHigh) × OnOffScale
바닥: 가벼움, 끝점: 설정무게 100%
Isokinetic (case 3) ⚡
|v| ≤ v_target: F = TargetWeight × OnOffScale
|v| > v_target: F = TargetWeight × (1 + K × (|v| - vt) / vt) × OnOffScale
- 양방향 (수축+이완 모두 속도 제한)
v_target 이하에서는 Constant와 동일 체감
- 초과 시 K_gain 배수로 급증 → "벽을 만나는" 느낌
Hydraulic (case 4) 💧
spdRatio = (|v| / v_ref)^n [clamp: 0 ~ (v_max/v_ref)^n]
F = TargetWeight × (minRatio + (1 - minRatio) × spdRatio) × OnOffScale
- 정지 시에도
minRatio만큼 기본 텐션 유지
- n=2.0: 공기(고속만), n=1.0: 선형(오일), n=0.7: 꿀(저속도 무거움)
Vibration (case 5) 〰️
F = TargetWeight × (1 + amplitude × sin(2πft)) × OnOffScale
- 케이블 머신 특성상 1~3Hz가 "무게 변화 체감" 핵심
- 5Hz+ → 팔 관성으로 핸들 떨림에 가까움
2.3 3중 안전 체인
F 계산
→
Slew Rate
50 kg/s
→
MaxWeight
Flash or 50kg
→
MaxCurrent
30A
| 안전장치 | 역할 | 값 | 적용 범위 |
| Slew Rate | 토크 변화 속도 제한 | 50 kg/s (ISR당 0.01kg) | case 3/4/5 |
| MaxWeight | 절대 kg 상한 | Flash값 or fallback 50kg | 모든 모드 |
| MaxCurrent | 하드웨어 전류 보호 | 30A | 모든 모드 |
3. 모드별 파라미터 & 체험 안전 범위
3.1 Isokinetic ⚡
| 파라미터 | 변수명 | 단위 | 기본값 | 체험 범위 | BLE paramId |
| 목표 속도 | Iso_Vtarget | mm/s | 200 | 100~600 | 0x10 |
| 제동 게인 | Iso_Kgain | — | 5.0 | 2.0~8.0 | 0x11 |
체험 프리셋:
| 프리셋 | v_target | K_gain | 체감 |
| 🐌 Slow | 150 mm/s | 5.0 | 천천히만 — 재활 느낌 |
| 🚶 Normal | 300 mm/s | 5.0 | 적당한 속도 제한 (기본) |
| 🏃 Fast | 500 mm/s | 3.0 | 빨라도 OK, 살짝 브레이크 |
3.2 Hydraulic 💧
| 파라미터 | 변수명 | 단위 | 기본값 | 체험 범위 | BLE paramId |
| 기준속도 | Hydro_Vref | mm/s | 300 | 100~600 | 0x01 |
| 최대속도 cap | Hydro_Vmax | mm/s | 600 | 300~1000 | 0x02 |
| 속도 지수 | Hydro_n | — | 1.0 | 0.5~2.0 | 0x03 |
| 최소 텐션 | Hydro_minRatio | — | 0.10 | 0.05~0.30 | 0x04 |
체험 프리셋:
| 프리셋 | n | minRatio | v_ref | 체감 |
| 🌬️ Air | 2.0 | 0.05 | 300 | 저속 가볍고 고속에서 확 무거움 |
| 💧 Water | 1.5 | 0.10 | 300 | 물속 운동 (기본) |
| 🫗 Oil | 1.0 | 0.15 | 300 | 선형, 직관적 |
| 🍯 Honey | 0.7 | 0.20 | 300 | 느려도 무거움 |
3.3 Vibration 〰️
| 파라미터 | 변수명 | 단위 | 기본값 | 체험 범위 | BLE paramId |
| 주파수 | Vib_Freq | Hz | 2.0 | 1.0~3.0 | 0x30 |
| 진폭 | Vib_Amplitude | — | 0.30 | 0.10~0.30 | 0x31 |
체험 프리셋:
| 프리셋 | Freq | Amplitude | 체감 |
| 🌊 Gentle | 1.0 Hz | 0.15 | 느긋한 파도 |
| 💪 Rehab | 2.0 Hz | 0.25 | 리듬감 있는 진동 (기본) |
| ⚡ Intense | 3.0 Hz | 0.30 | 빠른 진동 |
3.4 체험 안전 제한 (앱 단 강제)
| 제한 | 값 | 사유 |
| Vibration 주파수 상한 | 3.0 Hz | 5Hz+ → 떨림 체감, 모터 부담 |
| Vibration 진폭 상한 | 0.30 (±30%) | 40%+ → 무게 급변 불쾌 |
| Isokinetic K_gain 상한 | 8.0 | 10+ → 급정지 느낌, 안전 우려 |
| Isokinetic v_target 하한 | 100 mm/s | 그 이하 → 거의 움직이지 못함 |
| Hydraulic n 상한 | 2.0 | 3+ → 고속 시 폭증 |
| 공통 Slew Rate | 50 kg/s 고정 | FW 하드코딩, 변경 불가 |
| 공통 MaxWeight | Flash값 | FW 하드코딩 |
4. BLE 프로토콜
4.1 MODE_CHANGE (0x68)
앱 → FW: [FF FF] [04] [68] [side] [mode] [CHK]
side: 0=Left, 1=Right, 2=Both
mode: 0~5
FW → 앱: ACK [FF FF] [04] [68] [side] [result] [CHK]
result: 1=OK, 0=Fail
FW 8b9c555에서 mode ≤ 5 허용 완료
4.2 파라미터 변경 (0xF5 05)
앱 → FW: [FF FF] [psize] [F5] [05] [paramId] [value_H] [value_L] [CHK]
FW → 앱: ACK [FF FF] [02] [F5] [05]
| paramId | 파라미터 | 인코딩 |
| 0x01 | Hydro_Vref | uint16, mm/s |
| 0x02 | Hydro_Vmax | uint16, mm/s |
| 0x03 | Hydro_n | uint16, ×100 |
| 0x04 | Hydro_minRatio | uint16, ×100 |
| 0x10 | Iso_Vtarget | uint16, mm/s |
| 0x11 | Iso_Kgain | uint16, ×100 |
| 0x20 | SlewRate | uint16, kg/s (변경 불가) |
| 0x30 | Vib_Freq | uint16, ×100 |
| 0x31 | Vib_Amplitude | uint16, ×100 |
4.3 앱→FW 시퀀스
MODE_CHANGE(mode) → ACK 대기 (2초)
- 프리셋 선택 시 → 해당 모드 파라미터
0xF5 05 연속 전송 (각 paramId 별개 패킷, 간격 50ms)
- Weight ON 상태에서도 모드+파라미터 변경 가능 (단, SET_RANGE는 Weight OFF 필요)
5. 앱 설계 — 리모컨 모달 업그레이드
5.1 모드 셀렉터: 3개 → 6개
2행 3열 그리드 — 신규 모드는 accent 테두리로 구분:
5.2 모드별 파라미터 패널
Isokinetic (mode 3) 선택 시
┌─ ⚡ Isokinetic 설정 ──────────────────┐
│ 프리셋: [🐌 Slow] [🚶 Normal] [🏃 Fast] │
│ 목표 속도 ──●──────── 300 mm/s │
│ 제동 게인 ──●──────── 5.0× │
└──────────────────────────────────────┘
Hydraulic (mode 4) 선택 시
┌─ 💧 Hydraulic 설정 ──────────────────┐
│ 점성: [🌬️ Air] [💧 Water] [🫗 Oil] [🍯 Honey] │
│ 최소 텐션 ──●──── 10% │
└──────────────────────────────────────┘
Vibration (mode 5) 선택 시
┌─ 〰️ Vibration 설정 ──────────────────┐
│ 프리셋: [🌊 Gentle] [💪 Rehab] [⚡ Intense] │
│ 주파수 ──●──── 2.0 Hz │
│ 진폭 ──●──── 25% │
└──────────────────────────────────────┘
5.3 BLE 전송 타이밍
- 모드 선택 (탭): 즉시
MODE_CHANGE 전송
- 프리셋 선택 (탭): 즉시 해당 파라미터 일괄 전송 (50ms 간격)
- 슬라이더 조절: 300ms 디바운스 →
0xF5 05 전송
5.4 상태 인디케이터
현재: ['Const', 'Neg', 'Band'] → 변경: ['Const', 'Neg', 'Band', 'Iso', 'Hydro', 'Vib']
6. FW 현황 — 이미 구현된 커밋
| 커밋 | 내용 |
8b9c555 | MODE_CHANGE 상한 2→5 허용 |
c14abdc | Isokinetic(case 3) + Hydraulic minRatio + Slew Rate + 0xF5 05 + 50ms_1 슬롯 |
6059401 | Vibration(case 5) |
bc5ced2 | MaxWeight 공통 cap (모든 모드) |
✅ FW 추가 작업: 없음. 플래싱만 하면 됨.
⚠️ 실기 테스트 결과에 따라 파라미터 기본값 튜닝은 필요할 수 있음.
7. 구현 순서
| # | 작업 | 예상 시간 | 비고 |
| 1 | ✅ 완료 PRD v2.0 작성 | — | 본 문서 |
| 2 | 앱: 모드 셀렉터 3→6 확장 | 30분 | 2행 3열 그리드 |
| 3 | 앱: Isokinetic 파라미터 패널 | 30분 | 프리셋 + 슬라이더 |
| 4 | 앱: Hydraulic 파라미터 패널 | 30분 | 점성 프리셋 + 슬라이더 |
| 5 | 앱: Vibration 파라미터 패널 | 20분 | 프리셋 + 슬라이더 |
| 6 | 앱: BLE 전송 연결 | 20분 | 0xF5 05 + 디바운스 |
| 7 | 앱: 상태 인디케이터 6모드 | 5분 | modeNames 확장 |
| 8 | FW: Windows 빌드 + 플래싱 | 30분 | STM32 빌드 → J-Link |
| 9 | 실기 테스트: 모드 전환 | 1시간 | 6모드 순환 전환 안정성 |
| 10 | 실기 테스트: 파라미터 변경 | 1시간 | 프리셋별 체감 + 안전 확인 |
| 11 | 실기 테스트: 부하 안정성 | 1시간 | 급속 당김/놓기 + 모드 전환 |
총 예상: 앱 ~2.5시간 + FW 플래싱 30분 + 실기 테스트 3시간
8. 실기 테스트 체크리스트
8.1 기본 동작
| # | 테스트 | 기대 | Pass |
| T-1 | 6모드 순환 전환 (Weight OFF) | 모두 ACK OK | ☐ |
| T-2 | 6모드 순환 전환 (Weight ON) | 모두 ACK OK, 부하 전환 부드러움 | ☐ |
| T-3 | Constant 10kg → Isokinetic 전환 | 저속: 동일, 고속: 무거워짐 | ☐ |
| T-4 | Constant 10kg → Hydraulic 전환 | 정지: 1kg 텐션, 빠르게: 10kg+ | ☐ |
| T-5 | Constant 10kg → Vibration 전환 | 리듬감 있는 무게 변화 | ☐ |
8.2 Isokinetic 세부
| # | 테스트 | 기대 | Pass |
| I-1 | Slow(150mm/s) → 빠르게 당김 | 즉시 무거워짐 ("벽") | ☐ |
| I-2 | Fast(500mm/s) → 빠르게 당김 | 여유 있음, 살짝 브레이크 | ☐ |
| I-3 | K_gain 2→8 변경 → 속도 초과 | 벽 강도 차이 체감 | ☐ |
8.3 Hydraulic 세부
| # | 테스트 | 기대 | Pass |
| H-1 | Water → 천천히 당김 | 가벼움 + 약간의 텐션 | ☐ |
| H-2 | Water → 빠르게 당김 | 확실히 무거움 | ☐ |
| H-3 | Honey → 천천히 당김 | 저속에서도 무거움 | ☐ |
| H-4 | Air → 천천히 당김 | 거의 안 느껴짐 | ☐ |
8.4 Vibration 세부
| # | 테스트 | 기대 | Pass |
| V-1 | Gentle(1Hz) 프리셋 | 느긋한 무게 변화 | ☐ |
| V-2 | Rehab(2Hz) 프리셋 | 리듬감 있는 진동 | ☐ |
| V-3 | Intense(3Hz) 프리셋 | 빠른 무게 변화 | ☐ |
| V-4 | 정지 상태에서 진동 | 무게 흔들림 체감 | ☐ |
8.5 안전성
| # | 테스트 | 기대 | Pass |
| S-1 | 최대 무게(50kg) + Isokinetic 급속 | Slew Rate로 부드럽게 증가 | ☐ |
| S-2 | Vibration 3Hz + 30% 진폭 | 모터 소음/진동 이상 없음 | ☐ |
| S-3 | 모드 전환 중 급속 놓기 | 안전하게 언로딩 | ☐ |
| S-4 | 연속 모드 전환 (빠르게 탭탭탭) | 에러 없음, 마지막 모드 적용 | ☐ |
9. 미결 사항
- ☐ Isokinetic K_gain 5.0 기본값 — 실기 체감 후 조정
- ☐ Hydraulic v_ref 300mm/s — 실측 속도 기반 조정
- ☐ Vibration 고주파(3Hz) 실기 모터 발열/소음 확인
- ☐ 체험 시 스태프 안내 매뉴얼 (어떤 순서로 모드 전환할지)
- ☐ LED 색상 모드별 차별화 (선택)
변경 이력
2026-03-14 v0.1 — Hydraulic 초안
2026-03-15 v1.0 — Isokinetic+Vibration 추가, 3중 안전 체인, BLE 프로토콜
2026-03-20 v2.0 — SPOEX 체험용 전면 재작성 — 6모드 통합, 체험 안전 범위, 리모컨 UI 설계, 실기 테스트 체크리스트