sdk2 python을 이용한 접근.
example/go2_stand.py에서 원하는 제어Hz를 맞추기 위해서 Recurrent Thread를 사용한다.
Unitree 공식 예제:
RecurrentThread(interval=0.002, target=callback) # ← 전용 스레드 사용
📊 비교
| 항목 | 우리 코드 | Unitree 예제 |
|---|---|---|
| 제어 주파수 | 50Hz (20ms) | 500Hz (2ms) |
| 방법 | time.sleep() | RecurrentThread |
| 정확도 | ±1ms (OS 의존) | 더 정확 (전용 타이머) |
| 구조 | Single thread | Multi-thread |
따라서 딜레이 없이 잘 동작하는 제어기를 구현하려면 예제처럼 Recurrent Thread를 사용하는게 안전하다.
from unitree_sdk2py.utils.thread import RecurrentThread
class PolicyRunnerIMU:
def __init__(self, ...):
self.control_thread = None
def control_loop(self):
"""Called every 2ms by RecurrentThread"""
# Observation → Policy → Command
# (현재 while 루프 내용)
def run(self):
# Start control thread
self.control_thread = RecurrentThread(
interval=0.002, # 500Hz
target=self.control_loop,
name="policy_control"
)
self.control_thread.Start()
핵심
✅ RecurrentThread 기반으로 완전히 재작성했습니다.
🔄 주요 변경사항:
구조 변경
while True:
# control logic
time.sleep(...)
control_thread = RecurrentThread(
interval=0.02, # 50Hz
target=self.control_loop,
name="policy_control"
)
새로운 메서드
Main thread: 초기화 + 모니터링
Control thread: 정확한 타이밍으로 명령 전송
Clean shutdown 처리
🚀 사용 방법 (동일):
cd /home/user/unitree_sdk2_python/example/go2/sim2real/imu
python3 robot_policy_runner_imu.py eth0 ./exported/policy.onnx -d 5
⚡ 장점:
더 정확한 타이밍: OS scheduler에 덜 의존
Unitree 공식 방법: go2_stand_example.py와 동일한 구조
더 높은 주파수 가능: 필요시 100Hz, 200Hz도 가능
비동기 모니터링: Main thread가 통계 출력 담당
🎛️ 제어 주파수 변경:
더 빠르게 실행하고 싶다면 PolicyConfig 수정:
class PolicyConfig:
CONTROL_DT = 0.01 # 100Hz로 변경
# 또는
CONTROL_DT = 0.005 # 200Hz로 변경
# 또는
CONTROL_DT = 0.002 # 500Hz (Unitree 예제와 동일)
주의: Policy inference 시간을 고려해야 합니다. Dry run에서 1.2ms였으므로:
● 그렇다면 병목은 명확합니다: CRC 계산 + 네트워크 전송이 약 5-6ms를 소비하고 있습니다.
Dry run (50Hz): 관측 + 정책 + 처리 = ~17-18ms
Real robot (43Hz): 관측 + 정책 + 처리 + CRC + 전송 = ~23ms
→ CRC + 전송 = ~5-6ms
피할 수 없는 오버헤드: