HoverPilot - RealFlight Link로 상태 정보 읽어오기

SOAP 요청을 통해 XML 응답에서 비행 상태를 추출해보자

By 전경원

이전 글에서는 RealFlight Link 포트를 확인하고 macOS에서 Windows의 RealFlight와 TCP 연결에 성공했습니다. 이제 시뮬레이터의 상태를 실제로 읽어 보겠습니다.

단순 TCP 연결만으로는 상태를 읽을 수 없다

RealFlight Link는 스트리밍 소켓이 아닙니다. 단순히 연결한다고 상태 데이터를 보내지 않습니다. TCP로 연결한 뒤 SOAP 형식의 XML 요청을 보내야 응답으로 상태 정보를 받을 수 있습니다. 상태를 읽는 과정은 네 단계입니다.

  1. TCP 연결
  2. 요청 전송 (SOAP)
  3. 응답 수신 (HTTP + XML)
  4. 데이터 파싱

코드 구성

상태 정보를 읽는 코드는 다음처럼 나눠 구성했습니다.

src/hoverpilot/
├── main.py
├── config.py
└── rflink/
    ├── client.py
    ├── protocol.py
    └── models.py

각 스크립트가 맡은 역할은 이렇습니다.

  • client.py → TCP 연결을 관리하고 요청/응답 흐름을 제어합니다.
  • protocol.py → SOAP 요청을 생성하고 HTTP/XML 응답을 파싱합니다.
  • models.py → 파싱된 상태 데이터를 구조화하는 dataclass를 정의합니다.

상태를 읽어오는 코드

실제로 상태를 읽는 코드는 생각보다 단순합니다.

state = client.request_state()
print(state.summary())

request_state() 메서드는 다음 단계를 수행합니다.

  1. TCP 연결
  2. 컨트롤러 인터페이스 초기화
  3. ExchangeData 요청 전송
  4. HTTP 응답 수신
  5. XML 파싱 → 상태 객체 생성

이후 action을 보내는 구조도 이와 거의 동일하게 이루어진다.

컨트롤러 인터페이스 초기화

RealFlight Link는 컨트롤러 인터페이스가 활성화된 이후에만 정상적인 상태 값을 반환합니다. 이를 위해 다음 두 요청을 먼저 보낸다.

  • RestoreOriginalControllerDevice: 컨트롤러 상태를 초기 상태로 되돌린다.
  • InjectUAVControllerInterface: 컨트롤러 인터페이스를 RealFlight에 연결합니다.

이 단계는 이후 ExchangeData로 상태와 입력을 주고받기 위한 초기화 과정입니다.

상태 요청 방식

RealFlight Link는 우리가 요청을 보내야만 응답을 주는 구조다. 그 요청은 일반적인 JSON이 아니라 XML 형식으로 작성해야 합니다.

<ExchangeData>
  <pControlInputs>
    <m-selectedChannels>4095</m-selectedChannels>
    ...
  </pControlInputs>
</ExchangeData>

이 XML을 HTTP POST 요청에 담아서 RealFlight로 보내면, 그에 대한 응답으로 현재 비행 상태가 XML 형태로 돌아옵니다.

SOAP 프로토콜이라고 해서 특별히 더 알아야 할 건 없습니다. XML을 HTTP 요청에 담아 보내고 응답을 파싱하는 방식으로 이해해도 충분합니다.

응답 처리 방식

RealFlight Link의 응답은 HTTP 형태로 전달되며 그 안에 XML 형태의 상태 데이터가 들어 있습니다. 처리 흐름은 이렇습니다.

  1. HTTP 응답을 받는다
  2. 응답 본문(XML)을 추출한다
  3. XML을 파싱해서 상태 값을 읽는다

이제 이 값들이 실제로 무엇을 의미하는지 해석해야 합니다.

상태 데이터 구조화

응답 XML에는 수십 개의 값이 포함되어 있습니다. 속도, 고도, 자세, 각속도, 위치, quaternion, 배터리 상태 등 다양한 비행 상태가 함께 들어옵니다. XML에서 값을 직접 꺼내 쓸 수도 있지만 필드가 많고 구조가 일정하지 않아 코드가 금방 복잡해집니다.

그래서 파싱된 값을 FlightAxisState라는 dataclass로 묶어서 관리하도록 합니다. 이렇게 구조화해두면 몇 가지가 편해진다.

  • 필요한 값에 이름으로 바로 접근할 수 있어 디버깅이 쉽습니다.
  • 각 값의 의미가 코드에 그대로 드러나 가독성이 좋습니다.
  • 이후 RL observation 벡터로 변환하기도 수월합니다.

사람이 읽을 수 있는 형태로 출력

값이 제대로 들어오는지 확인하는 가장 직관적인 방법은 값이 실제로 변하는지 확인하는 것입니다. XML 응답에는 너무 많은 값이 포함되어 있어서 모든 값을 다 출력하면 오히려 확인하기 어려우므로 앞서 본 state.summary()가 다음 값만 골라서 보여주도록 했습니다.

확인 포인트는 네 가지다.

  • 위치
  • 고도
  • 속도
  • 자세

이 상태에서 RealFlight의 Airplane Hover Trainer를 실행하고 기체를 움직이면 출력되는 값이 실제로 변하는 것을 확인할 수 있습니다. 정밀한 분석은 아니지만 값이 0에서 벗어나서 변화하는 것을 보면 일단 상태를 제대로 읽어오고 있는 것을 알 수 있습니다.

초기 상태가 0인 이유

처음 상태를 받아보면 모든 값이 0으로 채워진 것처럼 보이는 경우가 있습니다. 이 상황은 보통 다음 두 가지 이유에서 발생합니다.

  1. 컨트롤러 인터페이스가 아직 초기화되지 않은 경우
  2. 시뮬레이터가 아직 상태를 정상적으로 생성하지 못한 경우

즉, 응답은 오지만 실제 데이터가 채워지지 않은 상태다. 그래서 두 가지 처리를 추가했습니다.

  • zero-state 감지
  • 최초 응답 디버그 출력

이렇게 해두면 문제 상황을 빠르게 구분할 수 있습니다.

“응답이 없다”와 “응답은 있지만 값이 비어 있다”는 완전히 다른 문제다.

여기까지 한 일

이 단계까지 오면서 RealFlight와 통신할 수 있는 기본 구조를 구축했습니다. TCP 연결 위에 SOAP 요청을 얹고 HTTP 응답을 받아 XML을 파싱한 뒤, 상태 데이터를 코드에서 다룰 수 있는 형태로 정리했습니다.

다음 단계

이제 단순히 상태를 읽는 단계에서 나아가 시뮬레이터에 직접 영향을 주는 단계로 넘어갑니다. 다음 글에서는 조종 입력을 보내고 상태와 연결된 피드백 루프를 구성하는 과정을 다룹니다.

함께 보기

Share: LinkedIn