21.4.2.2.3. status(): PX4_INFO 매크로를 이용해 NSH에 현재 실행 주기, 지연 시간(Latency) 등 통계 데이터 출력 포맷팅
모듈이 백그라운드에서 아무 에러 없이 잘 돌아가고 있다고 해서 개발자가 안심할 수는 없다. 특히 제어 알고리즘이나 센서 필터 모듈이라면, 내가 의도한 주기(예: 250Hz)대로 런루프가 오차 없이 돌고 있는지, 큐(Queue)에 데이터가 쌓여 처리 지연(Latency)이 발생하고 있지는 않은지 터미널 창에서 수시로 모니터링해야 한다.
이 역할을 위해 ModuleBase는 자식 클래스에게 status() 라는 메서드를 오버라이드할 기회를 제공한다. 사용자가 터미널에 custom_app status 라고 입력하면, 시스템은 즉시 이 함수를 호출해 모듈의 건강 검진 내역을 화면에 뿌려준다.
1. PX4_INFO: 단순한 printf가 아니다
status() 함수 내부에서는 흔히 모듈의 내부 멤버 변수(예: _loop_update_rate, _last_timestamp)를 읽어와 터미널에 출력하게 된다. 초심자들은 이 대목에서 C 언어의 향수를 못 잊고 printf나 std::cout을 들이대곤 한다.
하지만 PX4 아키텍처에서 일반적인 표준 출력 함수(printf)의 사용은 절대 금기 사항이다.
그 이유는 픽스호크가 단순한 화면 출력기(Screen)를 넘어, 무선 텔레메트리(Telemetry) 라디오 통신과 uLog 로깅 파일 시스템이라는 거대한 I/O 그물망을 가지고 있기 때문이다.
이 복잡한 출력을 하나로 묶어주는 통일된 로깅 매크로가 바로 **PX4_INFO 족(Family)**이다.
int CustomApp::status()
{
// 나쁜 예: NSH 콘솔에만 찍히고, 비행 중 QGC나 로그 파일에는 남지 않는다.
// printf("Current update rate: %d Hz\n", _update_rate);
// 좋은 예: PX4의 표준 로깅 시스템(uLog, Console, Mavlink)을 모두 통과한다.
PX4_INFO("Running");
PX4_INFO("Update rate: %d Hz", _update_rate);
PX4_INFO("Max latency: %llu us", _max_latency_us);
return 0; // 정상 출력 완료
}
PX4_INFO_RAW, PX4_WARN, PX4_ERR 등 다양한 매크로 집합은 컴파일 타임에 모듈의 이름(예: [custom_app])과 파일명, 심지어 코드가 위치한 라인(Line) 넘버까지 자동으로 태깅(Tagging)하여 출력해 준다.
2. 상태 모니터링의 포맷팅(Formatting) 모범 사례
훌륭한 status() 출력은 너무 길어도 안 되고, 비행 중 급박한 순간에 직관적으로 핵심을 파악할 수 있어야 한다. PX4 코어 개발팀은 다음과 같은 통계 데이터 출력을 권장한다.
- Running State: 최상단에
Running문자열을 찍어 스레드 상태를 알린다. (ModuleBase가 기본적으로 해주기도 한다.) - Scheduling Performance (스케줄링 성능): 예약(Scheduling) 주기와 실제 루프가 도는 주기(Actual Rate)의 차이. 이를 통해 CPU 병목이나 I/O 블로킹을 진단한다.
PX4_INFO("Rate target: 100 Hz, actual: 98 Hz");
- Data Processing Latency (처리 지연): uORB 메시지가 센서에서 생성된 시간(
timestamp)과 내 모듈이 그 메시지를 읽어 들여 연산을 마친 시간 사이의 차이값(Latency). 이 값이 수백 밀리초 이상으로 튀면 제어기가 미쳐버릴 확률이 높다.
PX4_INFO("Latency: avg 1200 us, max 5000 us");
- Error Counts (에러 누적 카운트): 센서 읽기 실패 횟수나 통신 드롭(Drop) 횟수를 출력한다.
PX4_INFO("I2C error count: %d", _error_count);
status() 함수를 기깔나게 뽑아냈다면, 이제 NSH 콘솔과 모듈 사이에 마지막으로 남은 다리一つ(하나)를 놓아야 한다.
사용자가 무식하게 custom_app start나 status만 치는 것이 아니라, custom_app reset_sensor 라든가 custom_app set_mode 1 처럼 모듈 내부를 조종(Steering)할 수 있는 **명령어 라우터(Router)**를 다음 단원(21.4.3)에서 창조해 보자.