콘텐츠로 이동

시스템 구조

여기서는 XM10 안에서 내가 작성한 코드가 어떻게 동작하는지 큰 그림을 잡습니다. 세부 구현이 아니라, 처음 접하는 사람이 "내 코드는 어디에 들어가고, 그 외 일들은 누가 처리하나" 정도를 이해하는 것이 목표입니다.


큰 그림

KIT H10 외골격 로봇은 사람의 동작 제어 시스템을 본떠 만든 분산 제어 구조입니다. 부위마다 역할이 나뉘어 있어서 한 곳이 느려져도 전체가 멈추지 않습니다.

Angel Robotics Module Architecture

KIT H10 자체의 분산 제어 구조입니다. 모터 드라이버가 각자 독립적으로 동작하면서, 중앙 제어 모듈 (CM) 이 전체를 조율합니다.

KIT H10 Distributed Control

여기에 XM10 이 들어오면 통합 시스템이 됩니다. XM10 은 외골격에 직접 명령을 내리고, 센서 데이터를 받아 새로운 알고리즘을 시험할 수 있는 "두뇌 확장" 보드입니다.

KIT H10 + XM10 System Architecture


XM10 펌웨어 — 내 코드는 어디에?

XM10 보드 내부는 여러 층으로 구성되어 있습니다. 사용자가 손대는 곳은 맨 위 한 곳 뿐입니다.

xm10-system-architecture

층 누가 만드나 사용자 작업
사용자 알고리즘 (Control_Task/control_task.c) 사용자 ✅ 여기만 작성
XM API — 단순 호출 창구 (XM_* 함수들) XM 라이브러리 호출만
내부 시스템 — 센서 통신, USB, 외골격 프로토콜 등 XM 라이브러리 안 건드림
ST 표준 코드 — 운영체제, USB 드라이버, 보드 페리페럴 ST + 오픈소스 안 건드림

자동차에 비유하면 사용자는 운전자입니다. 엑셀 (XM_SetAssistTorqueRH), 브레이크 (XM_SetControlMode), 핸들 (XM_GetButtonEvent) 만 조작하면 됩니다. 엔진/변속기/ECU 같은 내부는 모두 XM 라이브러리가 알아서 돌아갑니다.

왜 이렇게 나누었나? CAN 통신, USB 송신, 모터 명령 파싱 같은 일을 사용자가 매번 직접 짜면 — 버그가 생기기 쉽고, 한 사용자가 만든 코드를 다른 사용자가 못 씁니다. 공통 부분을 라이브러리로 빼고 알고리즘만 직접 작성하도록 분리해 두면, 시스템 안정성을 보장하면서도 사용자는 본질 (제어 알고리즘) 에만 집중할 수 있습니다.


내 코드는 언제 실행되나 — 1 ms 제어 루프

XM10 의 핵심은 1 ms (1 kHz) 주기로 반복되는 제어 루프입니다. 매 1 ms 마다 다음 세 가지가 순서대로 일어납니다.

  ┌────────────────────────────────────────────────────────────┐
  │            매 1 ms 마다 반복 (1 kHz 제어 루프)              │
  │                                                            │
  │   [입력]            [내 코드]            [출력]             │
  │     ↓                  ↓                   ↓               │
  │  CAN 으로          Control_Loop()         XM 함수가          │
  │  받은 센서 →       에서 내가      →    KIT H10 에게       │
  │  데이터가          작성한 알고리즘     CAN 명령 전송       │
  │  자동 갱신                                                 │
  │                                                            │
  │  XM.status.h10.    XM_TSM_Run         XM_SetAssistTorque  │
  │   leftHipAngle      → on_loop()       XM_SendUsbDataWithId │
  │   rightHipAngle                                            │
  │   isLeftFootContact                                        │
  └────────────────────────────────────────────────────────────┘
                              ↓ 1 ms 후 반복
  • 입력 (자동): CAN-FD 로 받은 KIT H10 의 센서·상태가 XM.status.h10.* 전역 변수에 매 cycle 자동으로 채워집니다.
  • 처리 (내가 작성): Control_Loop() 안에서 그 데이터를 읽고 알고리즘을 돌립니다.
  • 출력 (자동): XM_SetAssistTorqueRH(...) 같은 함수를 호출하면, 내부에서 CAN 메시지로 변환해 KIT H10 에 자동으로 보냅니다.

USB 통신도 함수 호출 한 번이면 끝입니다. 내가 신경 쓸 건 알고리즘 본질뿐입니다.


전원 켠 순간부터 내 코드까지

사용자가 Debug (F11) 로 펌웨어를 올리고 나면, 다음 흐름으로 보드가 시작됩니다.

  전원 ON
    ↓
  부트 코드 + 보드 초기화 (자동)
    ↓
  XM 라이브러리 초기화
    — 페리페럴 설정, USB 준비, CAN 연결, 센서 인식 등
    ↓
  내부 스케줄러 시작
    ↓
  ┌──────────────────────────────────────────────────────────────┐
  │  스케줄러가 여러 작업을 우선순위에 따라 번갈아 실행:           │
  │                                                              │
  │   • 제어 루프 task → Control_Setup() 1회 → Control_Loop() 매 1 ms  │
  │   • CAN 수신       → 외골격 센서 데이터 받아 XM.status 갱신   │
  │   • CAN 송신       → 모터 명령 전송                          │
  │   • USB 작업       → USB-CDC 시리얼 통신                     │
  │   • 디바이스 검색  → 센서 허브 자동 연결                      │
  └──────────────────────────────────────────────────────────────┘

내 코드의 진입점은 두 함수입니다.

  • Control_Setup() — 부팅 직후 1회만 실행. 초기화 코드 (LED 상태 설정, TSM 등록 등) 를 여기에.
  • Control_Loop() — 매 1 ms 마다 호출. 알고리즘 본체를 여기에.

CAN 수신 같은 다른 작업이 동시에 돌아가지만, 사용자는 신경 쓰지 않아도 됩니다. 시스템이 알아서 처리합니다.


내 파일은 어디?

Extension_Module/
├── XM_FW/
│   ├── XM_Apps/
│   │   └── Control_Task/
│   │       └── control_task.c     ← 여기가 사용자 작업 공간
│   └── (그 외 폴더는 모두 XM 라이브러리 — 건드리지 않습니다)
└── examples/                  ← 48 개 예제의 control_task.c 모음
    ├── 00_Quick_Start/quick_start.c
    ├── 14_PD_Realtime_Control/pd_realtime_control.c
    └── ...

예제를 시험하려면 해당 폴더의 .c 파일 내용을 XM_Apps/Control_Task/control_task.c 에 통째로 복사해서 빌드하면 됩니다.


통신 정리

두 종류만 알면 됩니다.

채널 누가 누구와 내가 쓰는 함수
CAN-FD XM10 ↔ KIT H10 외골격 XM.status.h10.* 읽기, XM_SetAssistTorque*() 쓰기
USB 시리얼 (CDC) XM10 → PC 터미널(텍스트) / xm10 도구(사용자 구조체) XM_SendUsbDebugMessage, XM_SendUsbDataWithId

세부 프로토콜은 라이브러리가 알아서 처리합니다. 사용자는 함수 호출만 하면 됩니다.


흔한 실수

증상 원인 해결
main() 에 코드를 추가했는데 동작 안 함 main() 은 내부 스케줄러를 시작하고 끝남. 사용자 코드 자리는 control_task.c XM_Apps/Control_Task/control_task.c 만 수정
Control_Loop 안에서 while(1) 사용 한 cycle 안에서 빠져나오지 못해 다른 작업이 멈춤 Control_Loop 는 한 번 실행하고 return — 반복은 시스템이 알아서
Control_Loop 안에서 HAL_Delay(100) 사용 100 ms 동안 다른 작업 모두 정지 XM_GetTick() 차이로 비차단 타이머 구성
XM.status.h10.leftHipAngle 이 항상 0 KIT H10 미연결 또는 ASSIST 모드 진입 전 XM_IsCmConnected() + h10Mode == XM_H10_MODE_ASSIST 확인
XM_SetAssistTorque* 호출했는데 토크 안 나옴 제어 모드 진입 누락 Active 진입 시 XM_SetControlMode(XM_CTRL_CONTROL) 1회 호출
XM 라이브러리 폴더의 코드를 수정 라이브러리는 SDK 가 제공하는 코드 — 임의 수정 시 시스템 깨짐 항상 Control_Task/ 안에서만 작업
Control_Loop 가 1 ms 안에 못 끝남 무거운 sprintf, 부동소수 누적 연산 등 Ex.18 Debug Monitor 로 실행 시간 측정 후 분산
두 예제를 동시에 빌드 시도 Control_Task/ 안에 .c 파일은 하나만 한 번에 한 예제만 복사해서 빌드

다음 단계