unitree - go2 - sim2real

전찬욱·2025년 12월 8일

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 threadMulti-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()

핵심

  • 콜백함수를 위해서 run() 메서드를 control_loop()와 run()으로 분리

✅ RecurrentThread 기반으로 완전히 재작성했습니다.

🔄 주요 변경사항:

  1. 구조 변경

    이전: Single-threaded with sleep

    while True:

    # control logic
    time.sleep(...)

    현재: Multi-threaded with RecurrentThread (Unitree 방식)

    control_thread = RecurrentThread(
    interval=0.02, # 50Hz
    target=self.control_loop,
    name="policy_control"
    )

  2. 새로운 메서드

  • control_loop(): RecurrentThread에서 호출되는 핵심 제어 함수
  • run(): 스레드 시작 및 모니터링
  1. Unitree 공식 예제와 동일한 패턴
  • Main thread: 초기화 + 모니터링

  • Control thread: 정확한 타이밍으로 명령 전송

  • Clean shutdown 처리

    🚀 사용 방법 (동일):

    cd /home/user/unitree_sdk2_python/example/go2/sim2real/imu

    실행 (기본 50Hz)

    python3 robot_policy_runner_imu.py eth0 ./exported/policy.onnx -d 5

    ⚡ 장점:

  1. 더 정확한 타이밍: OS scheduler에 덜 의존

  2. Unitree 공식 방법: go2_stand_example.py와 동일한 구조

  3. 더 높은 주파수 가능: 필요시 100Hz, 200Hz도 가능

  4. 비동기 모니터링: 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였으므로:

  • 50Hz (20ms): 충분한 여유 ✅
  • 100Hz (10ms): 가능 ✅
  • 200Hz (5ms): 약간 빠듯
  • 500Hz (2ms): inference 최적화 필요

  1. 실시간 주파수가 중요: 50.2-50.9 Hz는 정확히 50Hz 목표 달성

● 그렇다면 병목은 명확합니다: CRC 계산 + 네트워크 전송이 약 5-6ms를 소비하고 있습니다.

Dry run (50Hz): 관측 + 정책 + 처리 = ~17-18ms
Real robot (43Hz): 관측 + 정책 + 처리 + CRC + 전송 = ~23ms
→ CRC + 전송 = ~5-6ms

피할 수 없는 오버헤드:

  1. CRC 계산 (self.crc.Crc(self._cmd)): ~1-2ms
    • 안전성을 위해 필수
    • 이미 C library 사용 중
    • 최적화 여지 거의 없음
  2. DDS 네트워크 전송 (self.cmd_pub.Write(self._cmd)): ~3-4ms
    • LowCmd는 큰 메시지 (20개 모터 + 메타데이터)
    • DDS QoS 설정에 따라 latency 발생
    • Blocking call일 가능성

0개의 댓글