21.1.1.1. 응용 계층(Application Layer)과 미들웨어 계층(Middleware Layer)의 경계

21.1.1.1. 응용 계층(Application Layer)과 미들웨어 계층(Middleware Layer)의 경계

PX4의 모듈을 개발할 때 가장 빈번하게 혼동을 겪는 지점이 바로 ’응용 계층(Application Layer)’과 ’미들웨어 계층(Middleware Layer)’의 모호한 경계선이다. 두 계층 모두 src/modules/ 디렉터리 안에 사이좋게 코드가 배치될 수 있고, 두 계층 모두 uORB를 통해 메시지를 주고받는다. 겉보기엔 똑같은 C++ 클래스처럼 보이지만, 운영체제(NuttX RTOS)가 이 둘을 다루는 취급 방식은 천양지차이다.

여러분의 모듈이 이 두 계층 중 어디에 속해야 하는지를 결정짓는 것은 코드의 물리적 폴더 위치가 아니라, **‘그 모듈이 소비하는 데이터의 성격’**과 **‘시간 제약성(Latency Tightness)’**이다.

1. 미들웨어 계층 (Middleware Layer): 0.001초의 틈도 허용되지 않는 컨베이어 벨트

미들웨어 계층의 모듈들은 기체의 비행 안정성을 책임지는 ’코어(Core)’이다. 센서 데이터 필터링(EKF2), 자세 제어(Attitude Controller), 위치 제어(Position Controller) 모듈이 여기에 속한다.

  • 실행 패러다임: 데이터 구동(Data-driven) 및 동기적 동인(Synchronous Trigger)
  • 스케줄링 특성: 이들은 주로 센서로부터 새로운 데이터(예: 1kHz의 자이로 데이터)가 uORB에 방출되는 즉시, 하드웨어 인터럽트 수준의 속도로 깨어나 즉각적으로 연산을 수행해야 한다.
  • 제약 사항: 미들웨어 모듈 안에서는 sleep(), usleep(), 혹은 응답을 무한정 기다리는 네트워크 블로킹(Blocking) I/O 함수를 호출하는 것이 절대적으로 금지된다. 만약 미들웨어 모듈에서 printf나 SD 카드 파일 쓰기로 인해 10ms의 지연이 발생한다면, 그 뒤를 이어서 처리되어야 할 모터 출력 제어 루프가 밀리면서 기체는 공중에서 뒤집어질 수 있다.
  • 사용자 정의 앱의 개입 시나리오: 기존 제어기를 대체하는 완전히 새로운 제어 알고리즘(Custom Controller)을 시연하거나, 파생형 센서 퓨전 알고리즘을 펌웨어에 밀어 넣고자 할 때 이 계층에 개발 플래그를 꽂는다.

2. 응용 계층 (Application Layer): 지능적 결정을 내리는 여유로운 브레인

응용 계층의 모듈들은 기체가 ‘어떻게(How) 날 것인가’ 보다는 ’무엇을(What) 할 것인가’에 집중한다. 대표적으로 비행 모드 관리자(Commander), 임무 수행 로직(Navigator), 외부 컴퓨터(Companion Computer)와의 MAVLink 통신 데몬이 속한다.

  • 실행 패러다임: 이벤트 구동(Event-driven) 및 여유 있는 폴링(Polling)
  • 스케줄링 특성: 50Hz, 10Hz, 심지어 1Hz의 아주 느린 주기로 돌아가도 기체가 물리적으로 추락하지 않는다. Commander 모듈은 배터리 전압 상태나 MAVLink 명령을 초당 몇 번만 확인하면 충분하다.
  • 특권 (Privileges): 응용 계층 모듈은 미들웨어에 비해 파일 로깅을 하거나, 긴 연산을 하거나, 유한 상태 기계(FSM)를 돌릴 수 있는 ’시간적 여유’를 부여받는다. 어느 정도의 블로킹 함수(예: 네트워크 패킷 대기)를 사용하더라도 (올바른 스레드 분리 설계만 뒷받침된다면) 모터 제어 루프를 방해하지 않는다.
  • 사용자 정의 앱의 시나리오: 특정 GPS 좌표에 도달했을 때 릴레이를 스위칭하여 물건을 떨어뜨리거나, LTE 텔레메트리로 지상 연동 서버에 현재 고도를 보고하는 등의 ’비즈니스 로직’을 짤 때 압도적으로 많이 사용되는 계층이다.

3. 모듈 스펙 시트 작성 시 계층의 확정

따라서 사용자 정의 앱을 개발하기 전에는 반드시 다음 질문에 답해야 한다.

“이 모듈이 1초(1000ms) 동안 멈췄을 때, 기체가 즉각적으로 추락하는가?”

만약 대답이 “그렇다(Yes)“라면 여러분의 코드는 미들웨어 계층의 초고속 스레드 풀(Work Queue 등)에 배정되어야 하고 극한의 캐시 친화성(Cache Friendly) 최적화를 거쳐야 한다.
답이 “아니다, 그냥 임무만 실패할 뿐 기체는 제자리 비행(Loiter)을 유지한다“라면, 여러분은 응용 계층에 머무르면서 상대적으로 편안하고 비동기적인 C++ 로직을 즐길 수 있다.

이러한 논리적 계층 구분은 런타임 환경에서 프로세스와 메모리를 어떻게 다룰 것인지 그 물리적인 아키텍처 결정을 좌우하게 되는데, 이는 다음 장인 21.1.1.1.1 단원의 ‘NuttX 프로세스 간 메모리 공유 구조’ 파트에서 더욱 명확해질 것이다.