Skip to content

ULG → 시계열 데이터 변환 가이드

Raw

고객으로부터 PX4 ULog 파일(.ulg)과 차트 요구사항을 받아, 시계열 어노테이터용 dm_schema JSON을 만드는 전체 과정을 다룹니다.

flowchart TD
  subgraph 고객 제공물
    A["① ULG 파일들 (.ulg)"]
    B["② 차트 그룹 정의서<br/>(어떤 센서를 묶을지, 그룹 순서, 채널 이름)"]
  end

  subgraph 내부 작업
    C["③ 트랙 설정 파일 작성<br/>(track-config.yaml)<br/>⏱ 고객별 1회"]
    D["④ 변환 스크립트 실행<br/>(ulg2dm.py)<br/>⏱ 파일 수만큼 반복"]
    E["⑤ 스키마 검증<br/>(validate_dm_schema.py)<br/>⏱ 변환 후 매번"]
  end

  subgraph 결과물
    F["⑥ dm_schema JSON<br/>(어노테이터에 로드 가능)"]
  end

  B --> C
  A --> D
  C --> D
  D --> E
  E -->|✓ 통과| F
  E -->|✗ 오류| D
구분빈도설명
트랙 설정 파일 작성고객별 1회고객 요구사항을 YAML로 변환
변환 스크립트 실행ULG 파일 수만큼 반복공통 스크립트, 설정만 교체
스키마 검증변환 후 매번결과 JSON이 올바른지 확인

Python 3.8 이상이 필요합니다.

패키지용도
pyulogPX4 ULog 바이너리 파일을 파싱하여 채널 데이터를 추출
pyyaml트랙 설정 파일(YAML)을 읽기 위해 사용
numpy서로 다른 샘플레이트의 센서 데이터를 공통 타임그리드로 리샘플링(선형 보간)
python -m venv .venv && source .venv/bin/activate
pip install pyulog pyyaml numpy

실습에 필요한 파일을 다운로드합니다:

파일설명다운로드
log_55_2026-2-11-15-59-02.ulgPX4 비행 로그 샘플 (12MB)다운로드
ulg2dm.pyULG → dm_schema 변환 스크립트다운로드
track-config.yaml트랙 설정 파일 예시다운로드
validate_dm_schema.pydm_schema 검증 스크립트다운로드

  1. Raw ULG 파일 — PX4 오토파일럿이 기록한 바이너리 센서 로그
  2. 차트 그룹 정의 — 어떤 센서값들을 함께 보여줄지 (예: 엑셀, 문서, 구두)

고객 요구사항 예시:

  1. 자세 (roll, pitch, yaw 각도)
  2. GPS 위치 (위도, 경도, 고도)
  3. 로컬 위치 (NED 좌표계 x, y, z)
  4. 배터리 (전압, 전류)
  5. 모터 출력 (motor 0~3)

고객의 요구사항을 YAML 설정 파일로 변환하려면, ULG 파일에 어떤 채널이 존재하는지 먼저 확인해야 합니다:

python ulg2dm.py --input log_55_2026-2-11-15-59-02.ulg --list-topics

출력 예시:

파일: log_55_2026-2-11-15-59-02.ulg
시간: 286.6초
토픽 수: 61

  actuator_motors[0] (2866 samples, ~10.0Hz)
    - timestamp_sample
    - control[0]
    - control[1]
    - control[2]
    - control[3]
    ...

  battery_status[0] (1434 samples, ~5.0Hz)
    - voltage_v
    - voltage_filtered_v
    - current_a
    ...

  vehicle_attitude_setpoint[0] (5731 samples, ~20.0Hz)
    - roll_body
    - pitch_body
    - yaw_body
    ...

  vehicle_gps_position[0] (1433 samples, ~5.0Hz)
    - lat
    - lon
    - alt
    ...

고객의 차트 그룹 정의를 track-config.yaml 형식으로 변환합니다. 고객별 1회만 작성하면 됩니다.

# 공통 설정
sample_rate: 10          # 모든 채널을 이 Hz로 리샘플링
time_axis:
  origin: absolute       # absolute | relative | boot (아래 설명 참고)
  format: "HH:mm:ss"    # Day.js 포맷 문자열
  timezone: UTC          # 고객이 원하는 타임존 (ULG에는 타임존 정보 없음)

# 차트 그룹 정의 (순서대로 표시됨)
tracks:
  - id: attitude                    # 트랙 고유 ID (kebab-case)
    name: "자세 (Attitude)"         # 화면 표시 이름
    chart_type: line                # line | scatter | psd | fft
    channels:
      - topic: vehicle_attitude_setpoint   # ULG 채널 이름
        field: roll_body                   # 채널 내 필드 이름
        name: "Roll"                       # 화면 표시 이름
        unit: "rad"                        # 변환 후 단위
        color: "#42a5f5"                   # 채널 색상
        scale: 1.0                         # 값 변환 계수 (선택, 기본값 1.0)
고객 요구사항YAML 설정
”자세 차트에 roll, pitch, yaw”1개 track, 3개 channels
”GPS 위치 차트”1개 track, lat/lon/alt channels
차트 순서tracks 배열 순서 = 화면 표시 순서
차트 이름track.name
센서값 이름channel.name

python ulg2dm.py \
  --input log_55_2026-2-11-15-59-02.ulg \
  --config track-config.yaml \
  --output output.json

출력:

변환 완료: output.json
  시간: 286.6초, 샘플: 2867, 트랙: 5, 채널: 15
python ulg2dm.py \
  --input-dir ./ulg_files/ \
  --config track-config.yaml \
  --output-dir ./json_output/

설정 파일에 적은 채널이나 필드가 ULG 파일에 없으면 경고를 출력하고 해당 채널을 건너뜁니다:

[경고] 건너뛴 채널 (1개):
  - sensor_baro.pressure (채널 없음)

변환된 JSON이 시계열 어노테이터 스키마에 맞는지 검증합니다.

python validate_dm_schema.py output.json

성공 시:

============================================================
검증: output.json
============================================================
  ✓ 검증 통과
    시간: 286.6초
    샘플: 2867개 @ 10Hz
    트랙: 5개
    채널: 15개

검증 스크립트는 어디가 왜 잘못되었는지해결 방법을 함께 안내합니다:

============================================================
검증: broken.json
============================================================
  ✗ 3개 오류 발견:

  ✗ [channels.attitude__roll] 길이(100)가 timestamps 길이(2867)와 불일치합니다.
    → 모든 채널의 값 배열은 timestamps와 동일한 길이여야 합니다.

  ✗ [channelMeta.attitude__roll.color] 필수 필드 'color'가 없습니다.

  ✗ [meta.duration] duration(100.0s)이 startTime/endTime 차이(286.6s)와 불일치합니다.
    → duration = (endTime - startTime) / 1000 이어야 합니다.
python validate_dm_schema.py --dir ./json_output/

변환 결과는 시계열 어노테이터 스키마에 정의된 형식을 따릅니다.

{
  "meta": {
    "startTime": 1613693139911,    // epoch_ms
    "endTime": 1613693426511,
    "duration": 286.6,             // 초
    "nSamples": 2867,
    "sampleRate": 10,              // Hz
    "timeAxis": {
      "origin": "absolute",
      "format": "HH:mm:ss",
      "timezone": "UTC"
    }
  },
  "timestamps": [1613693139911, 1613693140011, ...],  // epoch_ms 배열
  "tracks": [
    {
      "id": "attitude",
      "name": "자세 (Attitude)",
      "chartType": "line",
      "channels": [
        "vehicle_attitude_setpoint__roll_body",
        "vehicle_attitude_setpoint__pitch_body",
        "vehicle_attitude_setpoint__yaw_body"
      ]
    }
    // ...
  ],
  "channels": {
    "vehicle_attitude_setpoint__roll_body": [0.01, 0.012, ...],
    // ...
  },
  "channelMeta": {
    "vehicle_attitude_setpoint__roll_body": {
      "name": "Roll",
      "unit": "rad",
      "color": "#42a5f5",
      "chartType": "line"
    }
    // ...
  }
}

채널주요 필드용도
vehicle_attitude_setpointroll_body, pitch_body, yaw_body자세 설정값
vehicle_attitudeq[0]~q[3]자세 쿼터니언
vehicle_local_positionx, y, z, vx, vy, vz로컬 위치/속도 (NED)
vehicle_global_positionlat, lon, alt글로벌 위치 (EKF 융합, lat/lon은 deg 단위)
vehicle_gps_positionlat, lon, alt, vel_n_m_sGPS 원시 데이터 (lat/lon은 degE7, alt는 mm. scale 필요)
sensor_gpslat, lon, alt, vel_n_m_sPX4 v1.14+ 에서 vehicle_gps_position 대신 사용
battery_statusvoltage_v, current_a, discharged_mah배터리
actuator_motorscontrol[0]~[3]모터 출력
actuator_outputsoutput[0]~[7]PWM 출력
sensor_combinedgyro_rad[0~2], accelerometer_m_s2[0~2]IMU 원시
vehicle_accelerationxyz[0], xyz[1], xyz[2]가속도
vehicle_angular_velocityxyz[0], xyz[1], xyz[2]각속도
cpuloadload, ram_usage시스템 리소스