아키텍처: 커널과 데이터 흐름
CRSF 스트림이 날갯짓과 꼬리가 되는 법: 모듈 경계, 두 믹서 커널, 링크가 끊겨도 익룡을 살리는 상태 머신.
신호 경로
PteronautOS는 ExpressLRS 4.x 포크로, 수신기의 "PWM 패스스루" 꼬리를 전용 비행 제어 계층으로 교체했습니다. 데이터 경로는 의도적으로 짧습니다:
CRSF radio link
│ 2.4 GHz SX1280
▼
rx_main / CRSF decoder → ChannelData[] (raw 172…1811 µs-window values)
▼
Ornithopter::update() → _readChannels() → voice* fields
▼ kernel select: activeProfile ≥ GEARBOX_2VTAIL_1RUD ?
_computeServoMixer() OR _computeGearboxMixer()
▼
_f[] (indexed by ServoFunc tag, µs)
▼
PWM output stage → funcMap channels → wing / rudder / motor servos
네 가지 구조적 아이디어가 기체를 안정시키고 코드를 작게 유지합니다:
- 채널은 일찍 정규화됩니다. 172–1811 CRSF 창의 모든 원시 값은 −1…+1(
_crsfToNorm) 또는 백분율로 한 번 매핑되며, 그 단위만 믹서를 흐릅니다 — 반복되는 매직 상수는 없습니다. -
출력은 고정이 아니라 태그됩니다. 믹서는
_f[ServoFunc]에 기록합니다. 어떤 태그가 어느 물리 PWM 출력을 구동할지는 나중에 프로필의funcMap이 결정합니다 — 참조: 믹서 레퍼런스 .모델의 핀을 다시 매핑해도 믹서 수학은 건드리지 않습니다. - 커널은 컴파일 타임 패밀리이자 런타임 선택입니다. 파형 플래퍼와 기어박스 컨트롤러 모두 바이너리에 있으며, 활성 프로파일이 매 업데이트 틱마다 커널을 고릅니다.
- 페일세이프는 사건이 아니라 상태입니다. 링크 다운, 벤치 모드, 스틱 오버라이드는
update()의 명시적 게이트라서, 무선이 조용한 동안 서보가 절대 날뛰지 않습니다.
두 커널: 파형과 기어박스
커널은 정규화된 채널을 서보-함수 µs로 바꾸는 블록입니다. PteronautOS에는 믹서 프로파일 번호로 구분되는 두 패밀리가 있습니다:
| 패밀리 | 프로파일 | 액추에이터 | 모델 원형 |
|---|---|---|---|
| 파형(서보) 커널 | 0 SERVO_2WING · 1 SERVO_2WING_1RUD · 2 SERVO_4WING | 플래핑 날개 서보(2–4), 크레스트 러더 | 직결 구동 서보 오르니톱터 — 고전적인 PteronautOS |
| 기어박스 커널 | 3 GEARBOX_2VTAIL_1RUD · 4 GEARBOX_1MOT_2VTAIL · 5 GEARBOX_1MOT_2VTAIL_1RUD · 6 GEARBOX_1ELE_1RUD · 7 GEARBOX_1MOT_1ELE_1RUD | 모터 ESC, V-테일 또는 엘리베이터 + 러더 | 기어식/모터식 플래퍼 + 일반 꼬리면 |
선택은 update()에서 단일 검사 PROFILE_IS_GEARBOX(activeProfile ≥ 3)로 이뤄집니다. 프로파일 자체는 런타임 값입니다: 빌드 플래그 MIXER_PROFILE에서 시작하지만(레거시 ORNITHOPTER_GEARBOX=1은 여전히 프로파일 5로 매핑) WebUI에서 setOrnithopterProfile()로 실시간 전환할 수 있습니다 — 재플래시 없이 새 기체로 다시 조준합니다. 총 8개 프로파일이 있으며, 정확한 funcMap 배치는 믹서 레퍼런스에 표로 정리돼 있습니다.
파형 커널 (서보 날갯짓)
파형 커널은 대부분이 "PteronautOS"라고 부르는 것입니다: 두 날개 서보가 매 사이클 형태를 조각하는 위상 연속 진동을 수행합니다. 그 구조:
- 히스테리시스가 있는 플랩 게이트. 플래핑에 진입하려면 스로틀이 플랩 임계값(기본 원시 ≈303, PWM 등가 ≈1.08 ms)을 넘어야 하며, 임계값 − 50 히스테리시스 아래에서만 빠져나와 경계 스로틀이 날개를 떨리게 하지 않습니다.
- 주파수. FlappingOscillator는 실제 경과 시간(µs 클록, dt 최대 100 ms)으로 전진합니다. 케이던스 목표는 프로파일의
throttleFrequencyMix를 통해 CH6 플랩 주파수 채널과 스로틀을 0.5 Hz…flapBaseFreq/10 내에서 혼합합니다. - 진폭. 서보 속도 상한의 스로틀 백분율
ampMax = degPerSec / (2·f)(degPerSec = 60°/servoSpeed_ms), 승수 전 55° 하드 캡. 따라서 진폭은 고주파에서 자연히 무너집니다 — 날개는 서보가 낼 수 있는 것 이상을 요구하지 않습니다. - 격렬도. 다운스트로크(파워)와 업스트로크(회복) 격렬도는 스트로크마다 형성되며, 엘리베이터로 비대칭 변조되고(당기면 다운스트로크가, 밀면 업스트로크가 강해짐) 스로틀 커플링으로 변조된 뒤, 형태 함수 전에 0…8로 제한됩니다.
- 요 차동. 러더는 좌우 반대 격렬도(
rudderFerocityRange)와 좌우 반대 진폭(rudderAmplitudeDifferential)을 더하는 한편, 스트로크 반전 위상은 양 날개 사이에 공유됩니다 — 선회 권위는 비동기 날개가 아니라 비대칭 드웰에서 나옵니다. - 기하와 출력. 형성된 −1…+1 파는 도(degree)로 스케일되고, 에일러론/엘리베이터 조향(±60° 권위 × 스케일)에 대해 혼합되며, 글라이드/플랩 중심 트림으로 오프셋되고, 0…180°로 클램프된 뒤, 구성 가능한
servoMinUs…servoMaxUs엔벨로프(기본 988…2012)를 통해 함수별 트림과 함께 µs로 변환됩니다. - 글라이드 분기. 플랩 임계값 아래에서 오실레이터가 감쇠하고 날개는 정적 글라이드 각도를 유지합니다 — 벤치 및 발진에 이상적인 상태입니다.
기어박스 커널 (모터+꼬리)
기어박스 커널은 순수 날갯짓을 모터 구동계 + 일반 꼬리면과 맞바꿉니다. 모터는 연속 회전하므로 이 커널은 틱마다 무상태이며 — 오실레이터 없음 — 대신:
- 스로틀(아밍)을 1000…2000 µs ESC 명령(
SF_MOTOR)으로 매핑하고, 디스암 시 아이들; - 테일을 혼합합니다: V-테일 프로파일은 엘레본 블렌드(에일러론+엘리베이터 / 에일러론−엘리베이터)를, 고전 프로파일은 별도 엘리베이터 면을 사용합니다;
- Zephyrus(활성 시)가 롤 및 피치 PID 보정을 테일 서보에 ±250 µs(
ZEPHYR_GEARBOX_CLAMP_US)로 제한해 주입하도록 하여, 센서 글리치가 서보를 절대 멈추게 하지 못합니다; - 파형 커널과 동일한 러더 믹서와 µs/트림/클램프 엔벨로프를 유지합니다.
하나의 오르니톱터 장비가 두 성격을 모두 지닐 수도 있습니다: 파형 커널로 플래핑 발진, 기어박스로 순항 — 런타임 프로파일 전환은 update()의 그 한 줄입니다.
상태: 링크·벤치·페일세이프
update()는 WiFi/서보 루프에서 고정 ~10 ms 케이던스로 호출되며, 믹서 실행 전 세 플래그를 검사합니다:
| 상태 | 조건 | 동작 |
|---|---|---|
| 정상 비행 | linkUp + 아밍 채널 활성 | 전체 커널 출력이 스틱을 따릅니다 |
| 벤치/패널 모드 | benchMode(WebUI 패널, RC 링크 없음) |
중립 스틱이 주입되고, 스로틀은 플랩 임계값 아래에 정차 — 글라이드 + 트림 편집이 실시간으로 보입니다 |
| 스틱 오버라이드 | stickOverride(가상 채널) |
펌웨어 주입 채널 값이 CRSF를 완전히 대체합니다(서보 스윕, 테스트) |
| 페일세이프 | 링크 손실(onLinkDown / enterFailsafe) |
모든 함수가 1500 µs로 중앙화되고, 오실레이터 리셋, 모터 → 최소; CRSF 패킷 손실도 RX 수준에서 중앙화됩니다 |
손실 시 중앙화 동작은 의도적으로 두 계층에 중복됩니다: CRSF 스택이 채널 데이터를 페일세이프하고, Ornithopter 계층이 기하(진동 상태 포함)를 페일세이프합니다. 따라서 죽은 무선은 글라이딩 가능하고 제어 가능한 기체를 남깁니다 — 결코 발버둥치는 기체가 아닙니다.
소스 지도
| 모듈 | 경로 | 책임 |
|---|---|---|
| Ornithopter 계층 |
src/lib/Ornithopter/
|
커널 선택, 믹서 수학, 파형 오실레이터, 프로파일, 트림 |
| 비행 프로파일 및 상수 |
src/lib/Ornithopter/OrnithopterConfig.h
|
MixerProfile 열거형, ServoFunc 태그, funcMap, 모든 수치 기본값 |
| 오실레이터 / 형태 |
src/lib/Ornithopter/OrnithopterWaveform.h
|
위상 적분, 격렬도 형성, 반전 로직 |
| Zephyrus(선택) |
src/lib/Zephyrus/
|
MPU6050 + Mahony AHRS, 듀얼 PID, 크레스트/테일 보정 |
| PWM 출력 스테이지 |
src/lib/ServoOutput/devServoOutput.cpp
|
CRSF 틱에 동기화된 µs를 기록합니다 |
| WebUI 설정 |
src/lib/WIFI/devWIFI.cpp
|
런타임 프로파일 + 튜닝 영속성(/pteronautos) |
바로 아래 계층 — 숫자가 정확히 어떤 파형을 만들고, 각 프로파일이 함수를 PWM 출력에 어떻게 배선하는지 — 는 믹서 레퍼런스 에 있고, 서보 타이밍 엔벨로프는 서보 커널 문서에 있습니다.