Хотел написать регулятор для руфтопа. Пришлось решать дифференциальные уравнения

от автора

В прошлый раз я писал про голосового ИИ на Asterisk и Яндексе. Фурора не случилось, но статью открыли семьсот с лишним человек, в ленте её увидели тысячи, а в комментариях нашлись те, кто делает ровно то же — с той же болью и теми же вопросами про задержки и оценку качества ответов. Значит, тема живая, и про свои проекты писать имеет смысл. Сегодня — про другое, с виду куда более скучное: регулятор для руфтопа. Руфтоп — это крышный кондиционер, большой ящик на крыше торгового центра. Логика — по сути термостат с амбициями: держи температуру, включай компрессоры по запросу, не дай им зачастить. Я закладывал пару недель.

Кончилось это системой дифференциальных уравнений с собственным движком, которая пять раз убедила меня, что всё работает, а потом оказывалось — неправильно. А главное — кончилось не регулятором вовсе. Регулятор оказался тривиальным. Настоящая работа была в другом: научить систему честно говорить, что с железом не так, — и не соврать при этом ни одной цифрой.

Голый тезис на весь текст: термостат — это просто, пока не спросишь, что физически происходит внутри. После этого вопроса начинается наука. А приборы, которые показывают «всё в норме», врут убедительнее всего — и заставить их не врать труднее, чем написать сам регулятор.

Сначала было пять прошивок

Регулятор должен был жить на железе. Первые версии — под Wirenboard, логику целил в нашу платформу. Попутно разбирался, как подцепиться к чужим контроллерам по их протоколу, иначе железо молчало.

Версий вышло пять. Причина дурацкая: конфигурация объекта не стояла на месте. То датчик в середине контура, то на входе испарителя. То механический ТРВ, то электронный. Под конец мы какое-то время не были уверены даже в хладагенте — в обсуждениях всплывал то R407C, то R-410A, а это разные рабочие давления и разные пороги защит. Когда плывёт даже рабочее тело, спорить о таймерах пусковой логики — как спорить о цвете занавесок в доме без фундамента. Логика была прибита гвоздями к конкретному железу и рассыпалась от переезда датчика на полметра. Тогда я не сформулировал, в чём беда, и сел писать шестую версию.

А потом мы начали спорить о физике

Сначала спорили нормально: где гистерезис, как чередовать компрессоры, что делать на пуске. А потом сползли в физику процессов, откуда возврата нет. Успевает ли набраться перегрев на пуске? Всплывёт ли давление на переходе настолько, что сработает защита? На эти вопросы нельзя ответить мнением. На живом железе проверять — либо опасно (гонять компрессор в аварию), либо невоспроизводимо. Мы спорили о поведении системы, которую никто из нас не видел изнутри.

Я много моделировал в MATLAB и к «давайте посчитаем, а не поспорим» был предрасположен. Поэтому пошёл не спорить громче, а за решателем дифуров. Слово «честно» — посчитать честно — я тогда обронил случайно. Оно преследовало меня весь проект.

Кроличья нора началась с конвертера

Коротко, что за зверь холодильный контур. Хладагент бегает по кругу и таскает тепло: кипит в испарителе (забирает тепло из помещения), сжимается компрессором, отдаёт тепло в конденсаторе наружу и сжижается, дозатор роняет давление — и по новой. У нас двухконтурный руфтоп на R407C, два компрессора разной мощности (SANYO на 29.9 и 14.5 кВт, соотношение два к одному — зачем такая асимметрия, будет ясно).

Утонул я на ерунде: нужна была функция «по температуре дай давление насыщения и обратно». Но R407C — зеотропная смесь, кипит не при одной температуре, а в диапазоне: между линией пузырька и линией росы несколько градусов, глайд. У нас 6.1 K на испарителе и 4.7 на конденсаторе. То есть на вопрос «какая температура у хладагента при таком давлении» честный ответ — «смотря какая фаза, разброс шесть градусов». Написал отдельную библиотеку T↔P под R407C с разнесёнными линиями и сверил с CoolProp: максимальная погрешность вышла меньше 0.000001 % при пороге теста 0.5 %. Маленький кирпич, но на нём «термостат за пару недель» закончился.

К численному интегрированию у меня личное: ещё в институте решал цепь «генератор — асинхронный двигатель» методом Рунге-Кутты. Тогда и зацепило: если знаешь, как величина меняется в каждый момент, можно по шагам проследить её эволюцию, не имея формулы ответа. Апериодическим звеном 1/(Tp+1) тепловую инерцию калорифера или помещения описать можно — а холодильный контур нельзя: он нелинеен и гуляет по всему диапазону. Эйлер слишком груб и на резких переходах разваливается. Рунге-Кутта точнее, но и её не хватило: система жёсткая — зона меняется за минуты, давления за секунды, и явные методы вынуждены идти шагом самого быстрого процесса. Лечится неявным жёстким решателем. В итоге уравнения и производные собираю через CasADi, жёсткую систему решаю через SUNDIALS IDAS, свойства беру из CoolProp. Студент с Рунге-Куттой дорос ровно настолько, чтобы понять, почему Рунге-Кутты мало.

И тут второй уровень боли: захотел собирать модель из кирпичей и соединять как на схеме — и понял, что переизобретаю акаузальное моделирование. В физике у трубы нет «входа»: соединяешь порты — выполняются законы сохранения, давление в узле общее, что втекло, то вытекло. Это законы Кирхгофа для труб. Движок должен сам собрать уравнения из схемы, а не считать по захардкоженной формуле. Ровно это десятилетиями делает Modelica; я этого не знал и пошёл изобретать сам. Получилась система дифференциально-алгебраических уравнений, у которой есть коварное свойство — «индекс»: соберёшь наивно — решатель расходится. Приходится причёсывать структуру (понижение индекса). Для класса задач вроде руфтопа это делается один раз.

Как модель обманывала меня — и почему это важнее регулятора

Я строил вертикальными срезами, у каждого «гейт» — набор проверок. Зелёный гейт — едем дальше. Проблема в том, что зелёный гейт несколько раз означал не «верно», а «я ещё не нашёл, где соврал».

Первый обман: энергобаланс сходился. Сколько тепла забрал испаритель плюс работа компрессора должно равняться теплу конденсатора — у меня сходилось, я был доволен. А на следующем срезе всё развалилось: форма уравнений теплообменников была с ошибкой в знаках, создавала ложное равновесие, а баланс сходился, потому что я считал невязку из тех же уравнений. Тавтология: уравнения подтверждали сами себя. Лечение — независимый свидетель: невязку считаю не из динамики, а из независимо измеренных потоков (воздушная сторона против стороны хладагента). Если форма верна — две оценки сходятся; если сходятся только по построению — видно по расхождению. В установившемся режиме осталось порядка сотых-десятых долей процента от теплосъёма (на тривиальном тесте с аналитическим ответом — машинная точность; на реальном контуре это шум физики, не ошибка).

Второй обман: совпадение с каталогом. Стационарная точка совпала с паспортом — я обрадовался. Зря: у меня была горсть свободных ручек (КПД, объём, коэффициенты теплопередачи) на две-три цели, попасть в каталог тривиально. Это была калибровка, а не валидация. Честный шаг — реальная полиномиальная карта по паспорту: взял каталог SANYO, построил карту EN 12900, десять коэффициентов, регрессия по пятидесяти точкам, сошлось с погрешностью 0.07 % среднеквадратичной. Теперь совпадение что-то значит, потому что коэффициенты из паспорта, а не из рук.

Третий обман короче: численный параметр сглаживания (чтобы решатель не спотыкался на резком переходе) двигал установившуюся точку. «Ручка громкости» двигала мелодию — костыль протёк в физику. Лечится формой сглаживания, у которой вне узкой окрестности влияние строго ноль; проверяется прогоном при двух значениях — ответ обязан совпасть.

Четвёртый — уже не баг, а разворот: хотел, чтобы реверсивный клапан получался сам, без if режим, чтобы роль теплообменника возникала из направления потока. Не выходило. Оказалось, в формализме «давление общее, расход в ноль» смену роли выразить нельзя: кто владеет энтальпией в точке, зависит от направления потока. Modelica ровно для этого ввела stream-переменные. То есть я не «не смог» — уткнулся в известную границу формализма. Расширил движок stream-коннекторами, и роль стала возникать сама.

Регулятор: подушки безопасности — это не педаль газа

Регулятор рос параллельно. Асимметрия компрессоров два к одному тут и пригодилась: два одинаковых дали бы три ступени, а неодинаковые — четыре (ноль, треть, две трети, полная), заметно точнее под переменную нагрузку. Главный принцип, который я зашил: реле высокого и низкого давления — это защита, а не орган управления. Здоровый агрегат почти никогда ими не срабатывает; компрессор включается по потребности и таймерам, а давления держатся в рабочем окне. Нельзя ездить, постоянно срабатывая подушками безопасности. Отсюда обвязка, которой в наивном термостате нет: пуск только при подтверждённом протоке воздуха, минимальные времена работы и паузы (масло должно вернуться), задержка между пусками, подогрев картера на стоянке, потолок на тактование. Все числа — из паспорта компрессора и в конфиге, а не в коде: урок пяти прошивок.

Тут честно про язык. Боевой модуль мы пишем под нашу платформу — это отдельная работа, которая начнётся после прогона всех тестов; назову его новым модулем. Но сама логика — та, из-за которой было пять прошивок и споры за кофе, — на удивление компактна: влезает на копеечный STM32F103 и мигает ножками на контакторы. Поэтому фрагмент ниже привожу на C, как будто пишу на голом контроллере — так честнее по духу и приятнее читать.

Числа за именованными константами — они в конфиге. Выход ступени — в конечном счёте просто GPIO на катушках контакторов. Ничего умного. И это ключевая мысль: вся сложность проекта была не здесь.

Дашборд и пятый обман — тот, что врал на экране

Всё это было бы стопкой логов, если бы не дашборд: живая панель, где в ускоренном времени (сутки за три с половиной минуты) двигаются манометры, ползёт рабочая точка по диаграмме цикла, переключается стейт-машина. Когда споришь, всплывёт ли давление на переходе, бесполезно смотреть в столбец чисел — а тут видно.

 дашборд — схема контура, P–h цикл, манометры, тренды давлений, стейджинг, панель здоровья. Прогон на модели

дашборд — схема контура, P–h цикл, манометры, тренды давлений, стейджинг, панель здоровья. Прогон на модели

И вот на этом дашборде меня поджидал пятый обман — на голову хуже четырёх, потому что был не в уравнениях, а на экране.

На панели есть «процент работоспособности» — сводное здоровье агрегата. Я хотел показать деградацию: пусть крутишь ползунок «загрязнение конденсатора» и видишь, как машина стареет. Кручу в 80 % — здоровье послушно ползёт вниз, оси краснеют. Красота. А потом по привычке — уже наученный — глянул на физику под индикатором. И физики не было. Давление конденсации стояло как вкопанное: 17.6 бара при чистом конденсаторе, 17.6 при заросшем на 80 %. COP не шелохнулся. Грязный конденсатор, который в реальности гонит давление вверх, в модели не делал ничего — только двигал стрелочку. Причина: коэффициент теплопередачи конденсатора был вшит в уравнения как константа на этапе компиляции, а ползунок двигал другую переменную — ту, что шла только в подсчёт индикатора, не в сами уравнения. Красивая деградация была бутафорией: индикатор падал, машина под ним работала как новая.

Вот он, зелёный пульт, который врёт, — только я построил его сам и сам на нём попался. Лечилось красиво: загрязнение стал подавать как эффективную температуру воздуха на конденсаторе — грязный конденсатор это ведь то же, что конденсатор, обдуваемый более горячим воздухом. И это не «похоже», а математически тождественно реальному падению теплоотдачи: распишешь уравнение теплообмена — подстановка даёт ровно тот же член. После фикса загрязнение на 80 % погнало давление с 17.6 до 22 с лишним бар, компрессор стал жрать почти на киловатт больше, COP просел с 4.2 до 3.2 — все величины поехали согласованно и в физически правильную сторону. Деградация перестала быть картинкой и стала физикой.

Этот обман — переломный для всей истории. Потому что дальше начинается то, ради чего всё затевалось.

Ради чего всё затевалось: аналитика, которая не имеет права врать

Регулятор держит температуру — это умеет любой термостат. Ценность не в том, чтобы включать компрессор, а в том, чтобы за недели до аварии сказать: «в контуре A утечка, через три недели встанешь по низкому давлению». Зелёный пульт, показывающий «всё ОК» ровно до момента, когда компрессор умер, — бесполезен и опасен. Модуль аналитики — это попытка построить панель, которая не врёт. А пятый обман показал, как легко построить ту, что врёт.

Один принцип держит весь модуль. Ни одно число в отчёте не появляется «из головы алгоритма». У каждого статуса — правило с порогом. У каждой вероятности диагноза — приор и множители правдоподобия. У каждого процента здоровья — раскладка штрафов. Любую цифру раскладываешь перед заказчиком построчно: вот датчик, вот значение, вот правило. Панель не врёт только тогда, когда за каждой стрелкой измерение и правило, а не ощущение.

И сразу честная граница, потому что она определяет всё. Мы диагностируем температурами — набор термодатчиков на контур, без токовых клещей, без аналоговых датчиков давления, без датчика уровня масла. Значит, часть отказов видим уверенно, часть — косвенно, а часть не видим в принципе, и честно про это говорим. Термометр не заменит токовые клещи там, где нужны токовые клещи.

Стандартный рабочий набор — пять температур на контур. Нагнетание компрессора — здоровье компрессора, перегрев нагнетания и признак реверса фаз. Середина конденсатора и линия жидкости вместе дают переохлаждение, а оно рассказывает про заправку и утечку. Вход и выход испарителя — их разность даёт перегрев, а перегрев рассказывает про работу ТРВ и мокрый ход; и тот же вход испарителя, ушедший ниже нуля, ловит замерзание. Середина конденсатора против наружного воздуха — это approach: по нему видно загрязнение конденсатора, а по резкому росту — отказ вентилятора. Наружная температура — не диагностическая, а опорная: по ней строятся динамические нормы и считается approach. Этих пяти датчиков хватает примерно на восемьдесят пять процентов словаря отказов плюс термозащита компрессора — это стандартная комплектация для тиража; датчики воздуха, входа конденсатора и корпуса компрессора добавляются в расширенной.

 холодильный контур с расположением пяти датчиков — нагнетание компрессора, линия жидкости, вход и выход испарителя, середина конденсатора

холодильный контур с расположением пяти датчиков — нагнетание компрессора, линия жидкости, вход и выход испарителя, середина конденсатора

Две главные производные — переохлаждение и перегрев — считаются от линейных температур против кривых насыщения R407C, тех самых с глайдом. И честно: где нет датчика давления, точки насыщения — оценка по коилу, а не прямое измерение; это заложенное допущение, не факт.

Сырая величина сама по себе ничего не значит — её надо с чем-то сравнить, и способов четыре. Первый — статический порог: абсолютные коридоры норма/внимание/авария, для величин с абсолютным смыслом (перегрев, переохлаждение, температура нагнетания). Второй — динамическая норма: для величин, которые естественно ползут вслед за улицей (approach, переохлаждение), норма — коридор своего температурного бина, посчитанный робастно через медиану и разброс. Это принципиально: фиксированный порог на approach срабатывал бы ложно в каждый жаркий день. Третий — тренд: наклон по суточным медианам за месяц, устойчивый по знаку неделю; вот он ловит медленную деградацию до того, как она станет аварией — утечка это переохлаждение, ползущее вниз неделями, — и заодно даёт прогноз, через сколько дней будет достигнут аварийный порог. Четвёртый — осцилляции: hunting терморегулирующего вентиля виден как устойчивый период колебаний перегрева полминуты-две, этим он и отличается от шума.

И три сита, без которых любой из четырёх врёт. Детекторы работают только в установившемся режиме — когда компрессор отработал минут пять и несколько минут после смены ступени; это отсекает процентов девяносто ложных срабатываний ещё до порогов, а величина, снятая на переходе, пишется в историю, но статуса не порождает. Дальше — валидация датчика (обрыв, спайк, залипание понижают доверие к его показаниям). И выдержка с гистерезисом: статус — это состояние, пережившее задержку, кроме защитных правил, которые выдержку минуют и сразу дают аварию с остановом.

Один симптом редко указывает на одну причину — они пересекаются. Поэтому поверх статусов наивный байес: у каждой гипотезы отказа частота-приор и множители правдоподобия по свидетельствам, на выходе нормированные проценты и топ-три причины с классами «уверенный», «вероятный», «возможный». Честно про эти числа — и это тот же принцип, что весь проект: приоры и множители сейчас экспертные, стартовые, они не измерены на реальных отказах, а проставлены по опыту и после года эксплуатации замещаются частотами парка. Формулы при этом не меняются. Ценность не в том, что «модель знает», а в том, что каждый процент раскладывается на приор и список свидетельств и его можно защитить построчно. Так же устроен процент здоровья (штраф на вес, объяснимо до строки) и риск отказа за месяц.

Что из этого реально ловится. Три самых частых отказа — утечка, грязный конденсатор, забитый фильтр воздуха — закрываются уверенно: утечка видна по переохлаждению, ползущему вниз (тренд — ключ, одно значение ни о чём); грязный конденсатор — по растущему approach относительно динамической нормы; забитый фильтр — по тому, что вход испарителя уходит ниже нуля при исправном хладагенте. Разрегулировку ТРВ ловит осцилляция перегрева; отказ вентилятора конденсатора — по резкому росту approach при работающем компрессоре, и тогда одна корневая строка в отчёте подавляет следствия: «вентилятор секции A стоит», а не три отдельные тревоги. Что-то добирается только расширенным набором: некондиционируемые газы отличаются от простой грязи по датчику входа конденсатора, а мёртвый подогрев картера — по холодному корпусу компрессора на стоянке.

А теперь то, что для честного тона важнее всего, — прямо сказать, где температура пас совсем. Износ контактора компрессора не увидеть никаким расширением — нужен датчик тока. Дрейф прессостатов — аналоговые датчики давления. Плохой возврат масла — датчик уровня. Утечку меньше трёх процентов в год — электронный течеискатель и полугодовые проверки. И это не мелкий хвост: контактор тихо убивает компрессор за пятьсот-тысячу пусков, и обычные жёсткие защиты его не ловят. Мы честно закрываем то, что закрываем термометрами, и не делаем вид, что термометр видит ток.

Честный статус и что дальше

Где всё сейчас — и тут постараюсь не выдать желаемое за действительное, потому что вся статья про то, как легко это сделать. Модель делает своё: на ней гоняется регулятор по всем режимам и авариям, не трогая реальный компрессор. Она честна в границах: проектную точку воспроизводит в единицы процентов по давлениям, но полной валидации по всему диапазону пока нет, и часть параметров — коэффициенты теплопередачи — калибровочные, а не выведенные из геометрии. Испаритель, если совсем честно, не непрерывная модель с интегрируемой границей кипения, а переключаемая двухзонная — и где на панели показана «доля двухфазной зоны», это оценка по балансу, а не переменная состояния; я это прямо подписал, чтобы самому потом не обмануться.

Регулятор отлажен против модели и ждёт переноса в боевой новый модуль под нашу платформу — после полного прогона тестов. Модуль аналитики пока существует как спецификация с экспертными числами: критерии оценки настоящие и датчики настоящие, а вот вероятности диагнозов ещё не проверены на реальных отказах — их предстоит откалибровать статистикой парка. Я не называю это готовым предиктором; это честный каркас, у которого каждая цифра защитима, а не выдумана.

И принципиальное, ровно то, что показал пятый обман: программные отсечки не должны быть единственной защитой. Физические реле высокого и низкого давления обязаны стоять в цепи контактора аппаратно, чтобы рвать компрессор напрямую, даже если контроллер завис. Индикатору верить нельзя — аппаратное реле не обманешь.

Поэтому честная концовка — не «и всё заработало на крыше», а «модель и регулятор проверены в симуляции, аналитика спроектирована, следующая глава — боевой модуль, калибровка на парке и железо». Если из всего остаётся одна мысль, то вот она. Термостат можно написать за пару недель. А система, которой можно доверить дорогое железо, начинается с честной модели того, что внутри происходит, и заканчивается панелью, которая не врёт, — а честность имеет неприятное свойство ловить тебя на том, что ты выдаёшь желаемое за действительное. Пять раз подряд, как минимум. И хуже всего врёт не расчёт, а зелёный индикатор, которому хочется верить.

ссылка на оригинал статьи https://habr.com/ru/articles/1057154/