28.3.4. 출력 계층: Controller 층으로의 제어 목표(Setpoint) 하달

28.3.4. 출력 계층: Controller 층으로의 제어 목표(Setpoint) 하달

조종기의 스틱을 비틀든, GCS에서 복잡한 다각형 웨이포인트를 다운로드하든, 아니면 비상 상황에서 맹목적으로 하강을 하든, 모든 FlightTask 자식 클래스들의 땀방울은 단 하나의 최종 목적지를 향해 수렴한다.

바로 하단에 위치한 물리적 제어기(Controller, 예: 멀티로터의 mc_pos_control 또는 고정익의 fw_pos_control)들에게 “지금 당장 기체가 도달해야 할 물리적 목표 상태(Setpoint)가 무엇인지“를 알려주는 것이다.

1. 아키텍처의 완벽한 디커플링 (Decoupling)

이 출력 계층의 가장 위대한 아키텍처적 성취는 **“내가 지금 무슨 비행 모드인지 제어기(Controller)에게 철저하게 숨긴다”**는 점이다.

과거의 낙후된 FC(Flight Controller) 펌웨어들은 PID 제어 루프 안에 if(현재모드 == AUTO) 같은 끔찍한 분기문들이 수백 줄씩 얽혀 있었다. 하지만 PX4의 FMM 아키텍처에서는 하단의 위치/속도 제어기들이 현재 비행 모드가 오빗(Orbit)인지 수동(Manual)인지 알 길이 없으며, 알 필요도 없다.

제어기들은 오직 FMM이 던져주는 표준화된 수학적 지시서 컨베이어 벨트만을 뚫어지게 쳐다보고 무지성으로 추종(Tracking)할 뿐이다.

2. 궁극의 포맷: trajectory_setpoint_s 구조체

그렇다면 FMM이 제어기에게 던져주는 이 표준화된 지시서의 정체는 무엇일까? 바로 _trajectory_setpoint라는 단일 C++ 내부 구조체(Struct)이자 uORB 메시지인 trajectory_setpoint.msg다.

모든 FlightTaskupdate() 가상 함수가 끝날 무렵, 기체 동역학의 근간을 이루는 4가지 핵심 운동학적(Kinematic) 변수들을 이 구조체 안에 꽉꽉 압축하여 채워 넣는다.

  • 위치 (Position X, Y, Z): 기체가 도달해야 할 최종 좌표 (NED 기준).
  • 속도 (Velocity X, Y, Z): 그 좌표를 향해 날아갈 때의 3차원 지시 속도.
  • 가속도 (Acceleration X, Y, Z): 궤적을 꺾거나 가감속할 때 필요한 타겟 가속도 (다이렉트 추력(Thrust) 매핑의 근원).
  • 요(Yaw) 및 요 각속도(Yawspeed): 기수의 방향과 회전 속도.

이때 재미있는 점은, 비행 모드의 특성에 따라 모든 변수가 다 채워지지 않는다는 것이다. 예를 들어 고도 유지(Altitude Hold) 수동 모드에서는 조종사가 스틱으로 직접 속도를 뽑아내므로 X, Y ‘위치’ 값은 무의미하다 (이 경우 위치 셋포인트 필드에 NAN 상수값을 채운다). 그러면 하단 제어기들은 이 NAN이 찍힌 필드는 쿨하게 무시하고 나머지 유효한 숫자들만 가지고 수학 연산을 수행한다.

3. FMM의 마침표: send_vehicle_local_position_setpoint()

FlightTask 내부에서 구조체 패킹(Packing)이 끝나면, FMM 루프의 마지막 5번째 단계에서 드디어 이 구조체를 uORB 퍼블리셔(Publisher)를 통해 발행(Publish)한다.

이 메시지가 uORB 메시지 버스에 뿌려지는 순간, 뒤에서 조용히 대기표를 뽑고 기다리던 수많은 제어기(Position, Attitude Controller) 프로세스들이 일제히 깨어나 폴링(Poll)을 시작하며, 드론의 프로펠러가 FMM의 의지대로 포효하기 시작하는 것이다.

이 표준화된 출력 터미널 구조 덕분에, 우리는 기존 커널이나 제어기를 단 한 줄도 손대지 않고도 세상에 존재하지 않던 완전히 새로운 비행 궤적 알고리즘을 새로운 FlightTask 클래스로 짜 넣을 수 있게 된 것이다.