27.2.2. ekf2 모듈 래퍼(Wrapper) 클래스 구조 (src/modules/ekf2)
순수한 C++ 수학 엔진인 ECL 코어(Estimation and Control Library)가 PX4 펌웨어 환경(NuttX/ROS2) 내에서 생명력을 얻으려면, 외부 세계의 uORB 메시지망과 ECL을 견고하게 이어주는 다리(Bridge)가 필요하다. 이 다리 역할을 전담하는 핵심 통신 장교 컴포넌트가 바로 ekf2 모듈 래퍼(Wrapper) 다.
본 절에서는 src/modules/ekf2/ 디렉터리에 위치한 메인 래퍼 클래스의 설계 사상과, 이 클래스가 PX4의 표준 모듈 규격을 만족하면서 24상태 필터의 입출력을 통제하는 구조적 특징을 해부한다.
1. 펌웨어와 수학의 경계: EKF2 클래스의 탄생
PX4 소스 코드 트리의 src/modules/ekf2/ 폴더를 열어보면 EKF2.cpp 와 EKF2.hpp 파일이 가장 먼저 눈에 띈다. 이 파일 안에 정의된 class EKF2 가 바로 기체가 켜질 때 백그라운드 스레드로 구동되어 지속적으로 센서 융합을 지휘하는 실질적인 PX4 프로세스 데몬(Daemon)이다.
EKF2 클래스는 내부적으로 두 개의 완전히 다른 세계를 품고 있다.
- 바깥세상 (PX4 Middleware): uORB 토픽(IMU, GPS, 기압계 등)을 구독(Subscribe)하고 내뱉는(Publish) 일련의 입출력 파이프라인.
- 안쪽 세상 (ECL Math Core): 실제 24상태 칼만 필터 수학 공식이 구현된
Ekf코어 클래스의 단일 인스턴스(_ekf).
// src/modules/ekf2/EKF2.hpp 클래스 헤더 구조 축약
class EKF2 : public ModuleBase<EKF2>, public ModuleParams, public px4::WorkItem
{
public:
// 모듈 수명 주기(Lifecycle) 함수들
static int task_spawn(int argc, char *argv[]);
static int custom_command(int argc, char *argv[]);
static int print_usage(const char *reason = nullptr);
// EKF 메인 프로세싱 루프
void Run() override;
private:
// 외부 펌웨어 의존성 (uORB 구독/발행)
uORB::SubscriptionInterval _sensor_selection_sub{ORB_ID(sensor_selection)};
uORB::Publication<vehicle_local_position_s> _vehicle_local_position_pub;
// 내부 순수 수학 코어 인스턴스 (의존성 단절점)
Ekf _ekf;
// 하드웨어 종속적인 버퍼 및 타이밍 변수들...
};
2. 모듈의 다중 상속(Multiple Inheritance) 구조 분석
C++ 클래스 선언부를 보면, EKF2는 단순히 독립적인 객체가 아니라 PX4 시스템 뼈대를 구성하는 3개의 거대한 객체 지향 기반 클래스(Base Class)를 동시에 다중 상속받고 있다.
이 상속 아키텍처는 EKF 모듈이 펌웨어 운영체제 위에서 어떻게 스케줄링되고 파라미터 제어를 받는지 명확하게 설명해 준다.
2.1 ModuleBase<EKF2> (생명 주기 관리)
PX4 펌웨어 내의 모든 백그라운드 데몬(명령줄에서 ekf2 start로 켜지는 모듈들)은 반드시 ModuleBase 템플릿을 상속해야 한다.
이 뼈대 덕분에 사용자가 콘솔 제어창(NuttShell, NSH)에서 ekf2 status를 치면 필터의 현재 내부 상태를 콘솔에 출력할 수 있고, ekf2 stop을 치면 메모리 누수 없이 안전하게 스레드를 종료시킬 수 있는 표준화된 CLI(Command Line Interface) 제어권을 획득하게 된다.
2.2 ModuleParams (파라미터 연동)
ECL 코어는 기체 종속적인 튜닝값(EKF2_GPS_DELAY, EKF2_GYR_NOISE 등)을 수십 개나 가지고 있다. ModuleParams를 상속함으로써 EKF2 클래스는 QGroundControl에서 파라미터가 변경되었을 때 uORB parameter_update 토픽을 감지하고, 이 바뀐 실수 배열 값들을 내부의 순수 C++ _ekf 수학 코어 객체로 전달하여 실행 중(On-the-fly) 필터 특성을 즉각적으로 변경하는 파라미터 라우터(Router) 역할을 수행한다.
2.3 px4::WorkItem (비동기 스케줄링)
가장 핵심이 되는 상속이다. 모던 PX4 아키텍처에서는 무거운 모듈인 EKF 루프를 별도의 무한 while(1) 스레드로 돌려 컨텍스트 스위칭(Context Switching) 비용을 낭비하지 않는다.
대신 고주파(High-rate) IMU 데이터가 큐(Queue)에 도달할 때마다 운영체제의 워크 큐(Work Queue) 스케줄러가 Run() 메서드를 비동기적으로 단발성 호출(Tick)하도록 구성된 WorkItem 패턴을 채택하고 있다. 이는 EKF의 실시간 마감 시간(Hard Real-time Deadline)을 보장하는 최적화된 콜백 메커니즘이다.
3. 요약: 데이터 문지기(Gatekeeper)로서의 역할
결론적으로 src/modules/ekf2 폴더 내의 코드들은 복잡한 수학 편미분 방정식이나 쿼터니언 자코비안 연산에는 전혀 관심이 없다. 오로지 “초당 250번 쏟아지는 uORB 센서 토픽들을 가장 빠르고 누락 없이 읽어내어, C++ 구조체로 예쁘게 포장한 뒤, 내부의 _ekf 코어에게 적절한 타이밍에 밥을 떠먹여 주는(Feed) 문지기” 역할에만 극도로 집중한다.
이러한 철저한 역할 분담(Separation of Concerns) 아키텍처가 PX4 코드를 항공 우주 산업의 상용 소프트웨어 표준(DO-178C 등)에 부합할 만큼 견고하고 테스트 용이하게(Testable) 만들어 주는 원동력이다. 다음 절에서는 이 래퍼 클래스의 다중 상속 뼈대 중에서도, 센서 동기화를 책임지는 핵심 엔진인 WorkItem과 스케줄링 설계의 본질을 더 깊이 파헤친다.