В авиации для измерения скорости движения летательного аппарата в воздушной среде используется трубка Пито и электронный вычислитель или air data computer.

Многие авиационные ADC измеряют полное и статическое давление от набегающего потока и с помощью нехитрых формул получают несколько параметров, необходимых для выполнения полетов - высота, вертикальная скорость и воздушная скорость.
Для измерения барометрической высоты в теории и в большинстве случаев и на практике - достаточно одного датчика абсолютного давления, например простой датчик Bosch bmp180 вполне для этого подходит.

Когда же появляется задача достаточной точности в 1 метр или еще лучше в 10 см то тут нужны более точные датчики и немного иные измерения.
Для измерения воздушной или приборной скорости полета, нужно уже использовать датчик дифференциального давления, чтобы из полного давления, принимаемого в трубку Пито - получить давление динамическое.
В промышленности для этого используют подобные датчики и системы, называемые дифференциальными.

Стало интересно сделать свою реализацию барокамер для двух отдельных датчиков и попробовать с их помощью получить заветные значения высоты и воздушной скорости.
Хотя технически это осуществимо, использование двух датчиков абсолютного давления с погрешностью ±1 кПа, скорее всего, приведет к большим неопределенностям на более низких скоростях полета(от 0 до 30 км/ч).

Но меня подстёгивала именно идея "попробовать" и после уже сделать выводы.
Опять таки, всем известный датчик фирмы TE модель MS4525DO

Или модель MPX12DP

Кстати, MS4525DO хоть и имеет промышленно выполненный корпус и барокамеры, содержит всего лишь 14-битный АЦП

В модуле, с которым провожу эксперимент, планирую использовать два датчика MS5611 фирмы MEAS Switzerland с 24-битным АЦП.
Приступаю к моделированию корпуса, чтобы потом напечатать его на 3д принтере.

Но моделирование оказалось не самым сложным в этом процессе, сложнее оказалось напечатать и собрать герметичные барокамеры, и это оказалось очень сложной задачей.

Проект практически был приостановлен, потому что около 2-х недель были попытки найти оптимальную технологию печати и варианты барокамер.
Первая причина проблем - использование 3Д печати, как мы все знаем, печать выполняется методом экструзии пластика и соответственно неминуемо мы имеет пустоты и поры

А если 3д принтер невысокого качества то можно получить вообще решето как ниже не фото

А вот печать на более качественном 3д принтере черным PETG пластиком

Серым ABS пластиком

Но все равно, даже на 100 % заполнении пластиков, получаемые барокамеры травили воздух и вся идея сходила на нет.
Было решено сделать барокамеры отдельным модулем и печатать их на фотополимерном принтере, в итоге сделано несколько вариантов барокамер

Вроде бы все, проблемы негерметичности решены, но и тут не обошлось без некоторых утечек в местах прохождения проводов от датчиков к микроконтроллеру.

Еще немного повозившись с герметизацией стыков и отверстий я получил 100% герметизацию барокамер.
Чем собственно я проверяю герметичность? Обычно в авиации используют специальные air data test set'ы или установки калибровки давления, в самом простом варианте это КПУ-3

Я для этих целей применил модифицированный манометр низкого давления(0-6 кПа) и высокотехнологичный медицинский шприц на 20 мл

Сам манометр немного модифицировал, наклеив шкалу в knots и получилась такая установка

Для пересчета давления в воздушную скорость пришлось прибегнуть к некоторым расчетам, не претендующим на великую точность, но очень близкими к реальности

Конструктивно и аппаратно все было готово к испытаниям, оставалось написать небольшой код для вычисления высот и скоростей полета
код для вычисления скоростей
// Структура для хранения IAS и TAS
typedef struct {
float ias_m_s; // Indicated Airspeed in m/s
float ias_kmh; // Indicated Airspeed in km/h
float ias_knots; // Indicated Airspeed in knots
float tas_m_s; // True Airspeed in m/s
float tas_kmh; // True Airspeed in km/h
float tas_knots; // True Airspeed in knots
} Speeds;
// Функция для вычисления давления по высоте
static inline double calcPressure(double alti) {
return (1013.25 pow((1.0 - (6.5 alti / 288150.0)), 5.255));
}
// Methods for Athmosphere Model used in Aviation
// With density of water from:
http://www.csgnetwork.com/waterinformation.html
// @ 22.8 degree: 0.997585
// earth gravity: 9.0807 m/s^2
// and standard ICAO air density with 1.225 kg/m3 there is:
// V(km/h) = sqrt(2*( <mmH2O> 0.997585 9.807 )/1.225) * 3.6
// Функция для расчета IAS и TAS на основе давления, высоты и температуры
static inline Speeds calculateSpeeds(float pascal, float altitude, float temp) {
// Проверяем, если динамическое давление меньше или равно нулю
if (pascal <= 0) {
return (Speeds){0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; // Устанавливаем IAS и TAS в 0
}
// Вычисляем IAS из давления в Паскалях (м/с)
float ias_m_s = sqrt(2 * pascal / 1.225);
// Преобразуем IAS в км/ч и узлы
float ias_kmh = ias_m_s * 3.6;
float ias_knots = ias_m_s * 1.94384;
// float ias_knots = ias_kmh / 1.852;
// Вычисляем TAS на основе IAS (в м/с)
//
https://en.wikipedia.org/wiki/True_airspeed#Calculating_true_airspeed
double pressureRatio = calcPressure(altitude) / 1013.25;
float tas_m_s = ias_m_s / sqrt(288.15 / (temp + 273.15) * pressureRatio);
// Преобразуем TAS в км/ч и узлы
float tas_kmh = tas_m_s * 3.6;
float tas_knots = tas_m_s * 1.94384;
// Возвращаем значения в структуре
Speeds speeds = {ias_m_s, ias_kmh, ias_knots, tas_m_s, tas_kmh, tas_knots};
return speeds;
}
код для вычисления высот
float Variometer::calcAltitude(long pressure)
{
return 44330 * (1.0 - pow((float) pressure / (float) this->qnh, 0.1903));
}
В основном тут простая математика, основанная на данных давлений, получаемых от двух датчиков абсолютного давления, установленных в раздельных барокамерах.
Высота вычислялась с помощью вычисления атмосферного давления одним датчиком, я назвал его STATIC.
Скорость воздушная вычисляется путем вычитания из полного давления PRESSURE давления STATIC, тем самым получаем давление динамическое, которое уже подставляется в формулу, тем самым рассчитывается скорость полета IAS.
Для получения скорости полета истинной - TAS, что для малой авиации в основном не так применимо, требуется коррекция скорости в зависимости от высоты полета и температуры воздуха, т.к. все мы знаем, что с высотой воздух становиться более разряженным, а самолет летит "опираясь" крылом на этот самый воздух и очень важно знать скорость TAS.
Есть еще скорость GS, но ее можно вычислить только по GPS или с помощью инерциального блока.
До проверки в железе, весь код был успешно оттестирован на основе симуляции давлений в авиасимуляторе FlightGear

Данные полученные посредством формул были близки с получаемыми в полете.
Компьютер воздушных данных выдает данные воздушной скорости, высоты полета и вертикальной скорости в шину CAN и соединен с указателем

P.S. Самым сложным моментом при создании блока измерения воздушных данных была не только герметизация барокамер, но и написание кода для вычисления вертикальной скорости или скорости изменения высоты полета.
В механических вариометрах для этого используется система анероидной коробки и капилляра, а как это реализовать в коде ... пришлось некоторое время порыться в интернете в поисках кода и решений, которое в итоге привело к написанию рабочего класса на основе решения для параглайдеров
// alghoritm like here
//
https://www.instructables.com/DIY-Arduino-Variometer-for-Paragliding/
void Variometer::calcVario(long now)
{
float N1 = 0;
float N2 = 0;
float N3 = 0;
float D1 = 0;
float D2 = 0;
// move the array one position to the left
for(int i = 1; i <= MAX_SAMPLES; i++)
{
this->pressureArray[(i - 1)] = this->pressureArray[i];
this->timeArray[(i - 1)] = this->timeArray[i];
};
// after moving, add the new value to the end of the array
this->pressureArray[MAX_SAMPLES] = this->lastPressure;
this->timeArray[MAX_SAMPLES] = now;
float elapsedTime = this->timeArray[MAX_SAMPLES - SAMPLES];
for(int i = (MAX_SAMPLES - SAMPLES); i < MAX_SAMPLES; i++)
{
float altitude = calcAltitude(this->pressureArray[i]);
N1 += (this->timeArray[i] - elapsedTime) * altitude;
N2 += (this->timeArray[i] - elapsedTime);
N3 += (altitude);
D1 += (this->timeArray[i] - elapsedTime) * (this->timeArray[i] - elapsedTime);
D2 += (this->timeArray[i] - elapsedTime);
};
// this->vario = 1000 ((SAMPLES N1) - N2 N3) / (SAMPLES D1 - D2 * D2);
this->vario = intervalVario ((SAMPLES N1) - N2 N3) / (SAMPLES D1 - D2 * D2);
}