21.1.1. PX4 소프트웨어 스택 내 사용자 정의 코드의 계층적 위치

21.1.1. PX4 소프트웨어 스택 내 사용자 정의 코드의 계층적 위치

PX4-Autopilot의 아키텍처 다이어그램(Architecture Diagram)을 펼쳐보면, 매우 견고하게 층이 나누어진 거대한 소프트웨어 스택(Software Stack) 구조를 발견할 수 있다. 하드웨어와 맞닿아 있는 운영체제(NuttX RTOS) 계층부터, 온갖 비행 스크립트와 로직이 돌아가는 최상위 응용 계층(Application Layer)까지 그 높이는 에베레스트산과 같다.

가장 먼저 맞닥뜨리는 근본적인 질문은 이것이다. “내가 작성한 C++ 사용자 모듈은 도대체 이 거대한 탑의 몇 층(Layer)에 입주하게 되는가?”

1. 코드의 ’주소(Address)’가 결정하는 권력과 제약

PX4 소프트웨어 스택은 크게 세 가지 주요 계층으로 구분되며, 여러분의 코드가 어느 폴더(계층)에 위치하느냐에 따라 그 코드가 누릴 수 있는 특권(Privileges)과 감내해야 할 철저한 제약사항(Restrictions)이 극명하게 달라진다.

  1. 드라이버 계층 (Hardware Abstraction Layer / Drivers):
  • 위치: src/drivers/
  • 이곳은 하드웨어와 직접 핀(Pin) 단위로 대화하는 0계층이다. I2C, SPI, UART 버스를 통해 센서로부터 원시 데이터(Raw Data)를 긁어오고 uORB로 퍼블리싱하는 역할을 한다. 사용자 정의 앱이라도 만약 새로운 센서나 페이로드 하드웨어를 제어해야 한다면, 이 계층(Driver)에서 코드를 작성해야 한다. 무자비한 하드 리얼타임(Hard Real-time)이 요구되며, 여기서 랙(Lag)이 걸리면 기체 전체의 I/O가 마비된다.
  1. 미들웨어 및 비행 제어 코어 계층 (Middleware & Flight Control Core):
  • 위치: src/modules/ (자세 제어, 위치 제어, EKF 등) 및 src/lib/
  • PX4의 척수이자 두뇌이다. GPS, IMU 등의 원시 데이터를 uORB로 받아 상태를 추정(Estimator)하고, 모터 PWM 값을 역산해내는(Controller) 수학적 심장부이다. 내가 만든 코드가 만약 기존의 PID 제어 로직을 갈아엎고 새로운 딥러닝 기반의 자세 제어(Neural Net Attitude Control) 알고리즘을 얹는 것이라면, 바로 이 층(Core Module)에 입주하게 된다. 성능을 위해 철저히 Work Queue 스케줄러의 협력형 루프에 종속된다.
  1. 응용 계층 (Application Layer / System Modules):
  • 위치: src/modules/ (사용자 커스텀 앱, 시스템 커맨더 등) 또는 src/examples/
  • 단순한 비행 제어를 넘어 “산불 지점에서 페이로드 투하 투표하기”, “LTE 통신망 로그 백업하기” 같은 상위 수준의 비즈니스 로직(Business Logic)이 수행되는 펜트하우스(Penthouse)이다. 대부분의 ’사용자 정의 앱’은 바로 이 계층에 둥지를 튼다. 코어 제어 루프만큼 빡빡한 주파수(Hz)를 강요받지 않으며, 상대적으로 C++ 코드를 자유롭고 여유롭게 짤 수 있는 구역이다.

2. 계층의 경계를 넘나드는 모듈 설계의 오판

초보 개발자들은 자신의 코드가 수행할 임무의 타이밍(Timing) 민감도를 고려하지 않고, 인터넷에 떠도는 예제 소스코드(Application Layer)를 무작정 복사해다가 1000Hz 주기의 자세 제어 수식(Core Layer)에 쑤셔 넣는 우를 범하곤 한다. 이렇게 계층적 위치를 잘못 파악한 코드는 CPU의 자원을 불법 점거하여, 결국 기체의 추락이라는 처참한 데드락(Deadlock)의 나비효과를 일으키고 만다.

따라서 커스텀 모듈을 설계하기 전 첫 번째 펜을 드는 순간, 내 C++ 클래스가 응용 계층의 평화로운 주민인지, 아니면 미들웨어 계층의 혹독한 배터리 노동자인지를 규정하는 것에서부터 PX4 아키텍처 해킹은 시작된다. 다음 단원에서는 이 응용 계층과 미들웨어 계층이 실제로 RTOS 상에서 어떻게 경계를 나누고 메모리를 공유하는지 내부 구조를 해부한다.