21.8.4.1. COMMAND_LONG 메시지 패키징 규칙
앞 장에서 우리는 vehicle_command_s 구조체를 채워 넣는 코드를 보았다. 이 구조체는 사실 픽스호크 내부에서만 쓰이는 독자적인 규격이 아니라, 드론계의 만국 공통어인 MAVLink (Micro Air Vehicle Link) 프로토콜의 COMMAND_LONG (#76) 메시지를 그대로 1:1로 복사해 놓은 도화지다.
따라서 이 도화지(vcmd.param1 ~ vcmd.param7)에 아무 숫자나 마구잡이로 적어 넣으면, 메인 커맨더(commander 모듈)나 액추에이터는 이 명령서를 즉시 쓰레기통(Rejected) 에 쳐박아 버릴 것이다. MAVLink의 엄격한 패키징(Packaging) 규칙을 정확히 이해하고 준수해야만 드론의 팔다리(Actuators)를 내 마음대로 움직일 수 있다.
1. MAVLink 커맨드 마이크로매니지먼트
MAVLink 홈페이지(mavlink.io)의 메시지 레퍼런스를 뒤져보면 세상 온갖 종류의 명령어들이 수백 개 정의되어 있다.
카메라 셔터 누르기(MAV_CMD_IMAGE_START_CAPTURE), 낙하산 펴기(MAV_CMD_DO_PARACHUTE), 심지어 자폭하기(MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN) 같은 명령어들이다.
이 수많은 명령어 중 어떠한 것을 고르더라도, 봉투(vehicle_command_s)에 담아야 하는 요소는 정확히 3가지 파트로 나뉜다.
- 배송지 (Target System/Component): 이 봉투를 누가 뜯어보아야 하는가?
- 명령어 번호 (Command ID): 구체적으로 무슨 행동을 원하는가?
- 옵션 인자 (Param 1 ~ 7): 행동의 구체적인 수치값.
2. NAN(Not a Number)의 미학
가장 주의해야 할 부분은 7개의 파라미터 묶음이다.
MAVLink의 COMMAND_LONG 메시지는 효율성을 위해 “모든 명령어는 무조건 7개의 플로트(Float) 파라미터 칸을 갖춘 버스에 태운다“고 규칙을 정해 버렸다.
명령어 방 번호에 따라 1번과 2번 파라미터만 쓰고 나머지는 안 쓰는 경우가 허다하다.
이때 C++ 개발자의 본능대로 쓰지 않는 파라미터인 param3 ~ param7 에 무심코 0.0f을 채워 넣었다간 알 수 없는 버그의 늪에 빠지게 된다.
어떤 명령어에서는 0.0이 “기본값(Default)“을 의미하거나, “0초 딜레이“라는 아주 명확한 값(Value) 으로 해석되어 버리기 때문이다!
따라서, MAVLink 표준에서는 **“내가 의도하지 않은(사용하지 않는) 빈칸에는 무조건 NAN(Not a Number)을 채워 넣는다”**는 신성한 규칙을 강제하고 있다.
// [안 좋은 예시: C++ 초보자의 0.0 병]
vcmd.param1 = 1.0f; // 명령 값
vcmd.param2 = 0.0f; // 안 쓴다고 0.0을 넣으면, 커널이 '0.0'이라는 구체적인 명령으로 오해함!
vcmd.param3 = 0.0f;
// [올바른 예시: MAVLink 장인의 방식]
#include <math.h> // NAN 매크로 사용
vcmd.param1 = 1.0f;
vcmd.param2 = NAN; // "이 칸은 비어있으니 절대 읽지 마시오" 라는 확실한 표시
vcmd.param3 = NAN;
vcmd.param4 = NAN;
vcmd.param5 = NAN;
vcmd.param6 = NAN;
vcmd.param7 = NAN;
빈칸에 NAN을 채워 넣어야 메인 커맨더가 명령어 무결성 검사(Sanity Check)를 통과시켜 준다.
봉투 껍데기의 규칙은 이해했다. 그렇다면 우리가 만들어갈 ’페이로드 자동 투하’를 위해서는 과연 저 거대한 MAVLink 명령어 사전 안에서 어떤 명령어(Command ID)를 꺼내어 파라미터를 조립해야 할까?
서보 모터의 관절을 꺾어버리는 구체적인 파라미터 포맷팅 실전 코딩을 다음 21.8.4.1.1장에서 이어가 보자.