Перейти к содержанию

Ветер в EKF

EKF3 оценивает ветер сам — это state-переменные 22 и 23 (NE-компоненты). Никаких ручек «задать ветер» в EKF нет, только косвенная настройка через EK3_WIND_P_NSE.

Жизненный цикл

┌─ На земле ────┬─ Взлёт ──────┬─ Полёт c aiding ─┬─ Coast ─────────┬─ После CMD 43003 ─┐
│ inhibitWind   │ инициализация │ active learning  │ frozen (truth)  │ relearn (быстро)  │
│ States=true   │ из airspeed   │ из TAS-fusion    │                 │                   │
└──────────────┴──────────────┴─────────────────┴─────────────────┴───────────────────┘

Фаза 1 — на земле

inhibitWindStates = true (default после init). State 22/23 не интегрируется, ковариация нулевая. EKF на земле ветер не учит.

Триггер активации (AP_NavEKF3_Control.cpp:62-70):

canEstimateWind = ((finalInflightYawInit && dragFusion) || assume_zero_sideslip()) &&
                  !onGround &&                     // ← блокировка
                  PV_AidingMode != AID_NONE;

// Активация только при:
inhibitWindStates && canEstimateWind && 
(sq(vx) + sq(vy) > 25.0 || dragFusion)            // ← groundspeed > 5 m/s

Фаза 2 — момент включения

В момент когда groundspeed превышает 5 m/s, EKF делает один snapshot инициализации (Control.cpp:84-86):

const ftype windSpeed = sqrtF(sq(vel.x) + sq(vel.y)) - tasDataDelayed.tas;
stateStruct.wind_vel.x = windSpeed * cosF(yaw);
stateStruct.wind_vel.y = windSpeed * sinF(yaw);

Формула опирается на допущение «launched into wind» — если ты взлетаешь против ветра, то groundspeed < tas (ветер тебя тормозит), и windSpeed получается отрицательным, направленным против heading. Это и есть истинный вектор ветра.

Если взлетаешь поперёк/по ветру — инициализация будет с ошибкой, но EKF исправит её в фазе 3 за 5–10 секунд активного обучения.

Фаза 3 — крейс с aiding

TAS-fusion каждый цикл подкармливает state 22/23 через Kalman update. Скорость дрейфа задаётся параметром EK3_WIND_P_NSE (process noise):

Значение Поведение
0.1 (default upstream) Ветер «гибче», быстро меняется за TAS-измерением
0.05 (наш baseline) Медленнее, устойчивее при шумном TAS
0.02 Очень медленно — ветер почти заморожен, для очень стабильных условий

Фаза 4 — coast (long gap без коррекций)

Когда dead_reckoning=true (PV_AidingMode != AID_NONE && нет vel-источника):

// AP_NavEKF3_core.cpp:1106-1111
if (!inhibitWindStates) {
    isDragFusionDeadReckoning = filterStatus.flags.dead_reckoning && !dragTimeout;
    if (isDragFusionDeadReckoning || !windStateIsObservable) {
        treatWindStatesAsTruth = true;
        P[23][23] = P[22][22] = 0.0f;     // ← ковариация = 0
    }
}

Ковариация в 0 = Kalman gain в 0 = state не двигается из новых наблюдений. Это и есть «заморозка».

Зачем это нужно: при отсутствии velocity-источника EKF превращает airspeed в pure NE-velocity-обзёрвацию через замороженный ветер. Если бы ветер не замораживался — TAS-fusion бы списывал накопленную IMU-drift на «изменение ветра» и оценка позиции разъехалась бы быстрее.

Фаза 5 — возврат коррекций

// AP_NavEKF3_core.cpp:1113-1118
} else {
    if (treatWindStatesAsTruth) {
        treatWindStatesAsTruth = false;
        if (windStateIsObservable) {
            P[23][23] = P[22][22] = sq(WIND_VEL_VARIANCE_MAX);   // ← резко вверх
        }
    }
    ...
}

Большая ковариация = большой gain = быстрое перетюнивание из накопленных TAS-наблюдений. Это умышленно: после долгого coast'а EKF должен быстро восстановить актуальную оценку ветра.

Почему AHRS_WND_* больше не работает

Параметры AHRS_WND_ENBL/SPD/DIR (коммит 8702e23) пишут в _wind поле DCM, не в EKF state:

// libraries/AP_AHRS/AP_AHRS_DCM.cpp:735+
if (ahrs.get_windEnableParam() == 1) {
    _wind = wind;   // ← перезапись DCM-оценки ветра
}

_wind — это поле AP_AHRS_DCM, не NavEKF3_core::stateStruct.wind_vel. EKF их не читает. Поиск setWind / setWindEstimate / set_external_wind_estimate в AP_NavEKF3/ — ноль вхождений.

Когда AHRS_WND_* фактически работает

Только когда AHRS отдаёт ответы через DCM-backend:

  • AHRS_EKF_TYPE = 0 (DCM forced) — у нас не так.
  • EKF "unhealthy" → fallback на DCM. Этот fallback отключается битом 0 AHRS_OPTIONS (DisableDCMFallbackFW).

С нашим baseline (AHRS_EKF_TYPE = 3 + AHRS_OPTIONS = 32) DCM никогда не активируется → _wind никем не читается → AHRS_WND_* это dead code.

⇒ Из параметров можно убрать (вернуть в дефолт 0):

AHRS_WND_ENBL = 0
AHRS_WND_SPD  = 0
AHRS_WND_DIR  = 0

Можно ли pre-seed'нуть ветер вручную?

Нет, на текущем коде это невозможно:

  • Внешнего API в EKF3 нет (поиск setWind показывает 0).
  • Lua-биндинг ahrs:get_wind() существует (read), ahrs:set_wind() — не существует.
  • Даже если бы существовал, он бы писал в DCM _wind, не в EKF state.

Реализовать можно через C++ патч: добавить NavEKF3::setWindNE(wn, we) который пишет в stateStruct.wind_vel и поднимает ковариацию. Это новый код в EKF core. На текущем этапе не делаем.

Дисциплина «взлёт против ветра»

Из-за того что EKF инициализирует ветер из формулы wind = groundspeed − tas в heading самолёта, и эта формула опирается на «launched into wind», взлетать нужно против ветра. Иначе:

  • Первоначальная оценка ветра будет некорректной.
  • EKF исправит её за 5–10 секунд активного полёта.
  • В эти 5–10 секунд позиция может слегка дрейфовать (на величину wind × time).

Для критичных миссий с длинным дед-рекнингом это важно. Для обычных миссий — некритично.

Альтернатива: хорошо откалиброванный pitot (ARSPD_RATIO ≈ 2.0) сокращает время сходимости. См. также Tridge на форуме ArduPilot: разница 0.5 m/s vs 1.0 m/s drift по airspeed калибровке.

EK3_WIND_P_NSE — что подкручивать

Единственная ручка управления ветром в EKF:

Сценарий Рекомендация
Default (ArduPilot upstream) 0.1
Valkyrie baseline 0.05 — устойчивее при шумном TAS
Стабильный ветер на полёт 0.02
Сильно меняющиеся условия 0.1 (default)

Менять следует только после стабильных лётных тестов и наблюдения за wind_vel в логах.

Что pilot должен знать о ветре

  • ✅ EKF учит ветер сам — никаких ручных действий не требуется.
  • Взлетать против ветра — операционная дисциплина.
  • ✅ В полёте ветер виден в Mission Planner: Statuswind_dir, wind_vel (это EKF-оценка).
  • ❌ Не пытаться «подкормить» ветер через AHRS_WND_* — не сработает.
  • ❌ Не верить EKF wind estimate первые 5–10 секунд после взлёта — может быть с ошибкой.