실행 계획과 준비 조건은 다른 정보다
Launch는 여러 실행물과 설정을 한 실행 계획으로 기술하는 도구다. 패키지·실행 파일·노드 이름·네임스페이스·파라미터·remap 등의 정보를 모아 반복 가능한 구성을 만든다. Python/XML/YAML 형식이 사용될 수 있지만 Launch 파일의 형식과 노드의 파라미터 YAML 파일을 같은 것으로 보지는 않는다. 전자는 무엇을 어떻게 시작할지, 후자는 해당 노드에 어떤 설정값을 줄지를 기술한다. 파일을 묶었다고 모든 노드의 초기화 완료나 데이터 준비 순서가 자동으로 보장되지는 않는다.
예를 들어 센서 프로세스의 시작 사건은 실행이 시작되었다는 증거다. 센서 데이터가 유효하고 프레임 변환이 준비되었다는 증거는 아니다. 일정 시간 기다리는 sleep을 넣으면 이 차이를 숨길 수 있지만 느린 기동이나 실패가 생겼을 때 준비를 판정하지 못한다. 필요한 조건을 ‘구성 전이 성공’, ‘유효한 첫 관측 수신’, ‘요청한 시각의 변환 존재’처럼 관찰 가능하게 적고 시간초과와 실패 처리를 정의한다. 프로세스 종료를 감시하거나 재시작하는 정책도 무조건 반복하면 원인과 상태를 잃을 수 있으므로 재시도 횟수와 중단 조건을 남긴다.
가상 Launch가 namespace=/robot_a, name=checker인 노드를 만들면 최종 노드 이름은 /robot_a/checker다. 다음 파라미터 파일의 대상이 /checker라면 그 이름과 다르다. 이 예에서는 와일드카드도 없고 다른 설정 덮어쓰기도 없다고 가정하므로 원하는 노드에 이 항목이 적용되었다고 볼 수 없다.
/checker:
ros__parameters:
stale_limit_ms: 100
대상을 /robot_a/checker로 맞추는 것이 이번 예의 수정이다. 단, 파일 수정만으로 선언·검증·적용 성공을 모두 입증한 것은 아니다. 노드의 실제 현재값과 적용 응답, 사용하는 파일의 버전을 확인한다. 같은 경로의 옛 파일이나 이후의 덮어쓰기가 있으면 화면에 보이는 파일과 실제 설정이 달라질 수 있다. 공식 Launch 구성 자료는 노드 이름·설정 범위·와일드카드를 함께 설명한다.
Lifecycle은 준비 절차를 드러내는 상태기계다
관리 노드(LifecycleNode)는 구성과 활성화 등의 상태 전이를 명시하는 인터페이스를 제공한다. 대표 주요 상태는 unconfigured(미구성), inactive(구성했으나 비활성), active(활성), finalized(종결)다. configuring이나 activating 같은 전이 상태에서 콜백이 실행되고 그 결과에 따라 다음 주요 상태를 정한다. 일반 노드가 모두 이 구조를 자동으로 갖는 것은 아니다.
그림 M05.04-1. Lifecycle의 주요 상태/절차를 간추린 개념도다. 가능한 모든 전이를 그린 상태전이도나 장비 안전 인증이 아니다. 활성 상태만으로 센서 최신성·실제 동작을 보장하지 않는다. 직접 제작.
단순 성공 경로는 unconfigured→configure→inactive→activate→active다. configure가 실패를 반환하는 경로는 성공한 inactive와 구별하며 기본 상태기계에서는 unconfigured로 돌아갈 수 있다. error 처리와 failure 반환은 구별해야 한다. deactivate는 active에서 inactive로, cleanup은 inactive에서 unconfigured로 돌아가 자원을 정리하는 경로다. finalized는 종결 상태이며 그 객체를 다시 activate해서 재사용하는 상태가 아니다. 전이 요청 수락·전이 콜백 결과·현재 상태를 각각 기록하면 어디까지 준비되었는지 알 수 있다.
가상 동일 시계에서 프로세스 시작은 0 ms, configure 시작은 10 ms, 성공 종료는 50 ms, activate 시작은 60 ms, 성공 종료는 70 ms라고 하자. 구성 처리 40 ms, 두 전이 사이 대기 10 ms, 활성화 처리 10 ms다. 시작부터 active까지 70 ms지만 그 안의 구성과 활성화 처리 합계는 50 ms다. 두 수치를 같은 지연이라고 부르지 않는다. configure가 실패했다면 뒤의 active 도달을 예정표만으로 채우지 않는다.
중요한 한계도 있다. Lifecycle 상태 이름만으로 모든 사용자 타이머·구독·일반 발행이 자동 차단되지는 않는다. Jazzy C++의 LifecyclePublisher는 활성 상태를 검사하여 발행하지만, 사용자가 만든 별도 작업과 외부 스레드에는 적절한 상태 검사가 필요하다. 공식 lifecycle 예제의 발행 제어를 모든 응용 코드의 보편적 차단이나 실물 안전 기능으로 과장하지 않는다. active는 필요한 구성 절차를 통과한 상태이지 센서 정확도·전원·실행기 안전의 최종 보증이 아니다.
로그가 말하는 것과 말하지 않는 것
ROS 로그의 심각도는 DEBUG, INFO, WARN, ERROR, FATAL 순서다. 설정한 수준보다 낮은 메시지는 출력 대상에 전달되지 않을 수 있다. ERROR 기준에서는 INFO의 ‘구성 완료’ 기록이 보이지 않는 것이 정상일 수 있다. 로그는 콘솔·파일·/rosout 등의 대상으로 갈 수 있고 대상별 비활성화나 필터도 가능하다. 따라서 ‘화면에 오류가 없다’는 관찰은 오류가 없었다는 결론과 다르다. 메시지를 생성하는 코드 경로가 실행됐는지, 로거와 출력 대상의 설정이 무엇인지 확인한다.
진단 메시지는 구성요소의 상태와 설명·관련 값을 정해진 구조로 전달하는 데 쓰인다. DiagnosticStatus의 OK/WARN/ERROR/STALE은 로그의 DEBUG~FATAL 체계와 별도다. STALE은 오래되거나 갱신되지 않은 상태를 표현하는 데 쓰일 수 있으며, 숫자 수준만 보고 전원 고장 같은 원인을 확정하지 않는다. 진단을 만든 노드의 판단 규칙과 마지막 갱신 시각이 필요하다. 가상 마지막 갱신 200 ms, 현재 350 ms면 나이는 150 ms다. 이 응용이 한계를 100 ms로 정의하고 나이>한계일 때 stale로 분류한다면 조건을 넘는다. 다른 노드에도 이 규칙이 자동 적용되는 것은 아니다.
복구 계획은 기록을 지우고 다시 시작하는 것으로 끝나지 않는다. 설정 버전과 실패 상태를 보존하고, 무엇이 유효해졌을 때 다시 구성/활성화할지 적는다. 기존 1장의 관측과 제어 예제에도 같은 관점을 적용하여 프로세스가 살아 있음·통신 상태·관측 유효성·명령 허용을 따로 본다.