Началось всё с того, что я захотел повысить мощность двигателя автомобиля установкой компрессора.
В процессе понадобилось разобраться с Arduino и написать алгоритмы автоматического управления реальными устройствами, формально - управление всего одним моторчиком, но на практике - выявилось несколько подводных камней, о которых не подозреваешь в начале.
Разработка и особенности управления "реальными" устройствами с позиции человека, который раньше не работал ни с Arduino, ни тем более не управлял какими-то устройствами - показались интересными для написания статьи.
Алгоритмы получились посложнее, чем "помигать светодиодом", но с точки зрения опытных разработчиков подобных систем, которые знают все нужные алгоритмы - наверняка покажутся наивными, тем более, в автоиндустрии всё это давно отлажено - в машинах много лет стоят контроллеры управления различными подобными устройствами.
Идея
Статья не про автомобили, а про разработку, поэтому об исходной идее расскажу коротко.
Компрессор для двигателя автомобиля - это простой способ повысить мощность атмосферного двигателя. Нагнетателей воздуха существует несколько вариантов (электро, турбины), но в данном случае мы говорим о том варианте, который описан чуть ниже.
![](https://habrastorage.org/getpro/habr/upload_files/b5b/9ae/75f/b5b9ae75f5efdca71092d3d390146724.jpg)
Компрессор - это такой вентилятор в закрытом корпусе, который за счёт передачи ему вращения от двигателя - быстро вращается и нагнетает в двигатель автомобиля больше воздуха, чем двигатель мог бы "взять" самостоятельно. В результате работы компрессора на "входе" в двигатель создаётся избыточное давление. Не ~100 кПа (1 бар), а предположим 200 кПа (2 бара). Компрессор как бы "утрамбовывает" воздух в двигатель. Больше поступление воздуха = больше потребляемого бензина = больше мощность двигателя.
Схем установки компрессоров несколько, выбрал самую "феншуйную". В такой схеме компрессор устанавливается перед двигателем. Между компрессором и двигателем установлена так называемая "дроссельная заслонка". Дроссельная заслонка по-умолчанию закрыта. Когда водитель нажимает педаль газа - заслонка открывается, воздух начинает поступать в двигатель, двигатель начинает громко жужжать.
Возьмём обычный атмосферный двигатель. Перед дроссельной заслонкой обычное атмосферное давление, когда заслонка открывается - двигатель "засасывает" в себя нужный объём воздуха. Когда заслонка закрывается - двигатель просто перестаёт "засасывать" воздух.
Когда устанавливается компрессор, всё становится немного иначе и начинаются проблемы. Представьте, что заслонка открыта, компрессор работает, создаёт большое избыточное давление и водитель резко отпускает педаль газа. Заслонка резко закрывается и избыточному воздуху становится некуда уходить - в двигатель его не пускают, а между компрессором и двигателем обычная трубка, уходить уже некуда. Компрессор в такой ситуации страдает.
Существует простое решение это проблемы - например, "блоу офф". Это такой клапан, который ставится в зоне избыточного давления и при достижении определённого избытка открывается и выпускает лишний воздух, при этом создавая лишние звуки.
![](https://habrastorage.org/getpro/habr/upload_files/d0d/60a/f04/d0d60af04b424735f38bb94c9964e88b.jpg)
Но мне хотелось "феншуя", поэтому был выбран способ, практик которого я не нашёл на авто-просторах русскоязычного интернета: установка перепускного клапана в виде дополнительной дроссельной заслонки (далее ДДЗ) с электронным управлением и автоматизация управления ей на основе контроллера на Arduino.
Упрощённая схема работы показана на картинке. Избыток воздуха попадет через открытую ДДЗ на вход компрессора и компрессор начинает гонять этот воздух по кругу, не создавая избыточного давления.
![](https://habrastorage.org/getpro/habr/upload_files/d3b/d82/c44/d3bd82c44188d666d39a313cc2b958c1.png)
Кроме отказа от блоу-офф, такая схема работы позволит ездить в полностью атмосферном режиме, включая и выключая компрессор по необходимости. Когда компрессор выключен - ДДЗ полностью открыта и воздух свободно поступает в двигатель.
Я - бэкендер. Пишу всякий разный бэкенд под всякий разный интернет. Опыта написания под Arduino не было. Знаний алгоритмов управления "реальными" устройствами нет. Стал писать всё с чистого листа, предполагая, что всё просто. Оказалось, что всё не просто.
Исходная идея (и алгоритм) казались простыми: есть датчик атмосферного давления (ДАД1) до основной дроссельной заслонки (далее ОДЗ) и есть дачик давления после ОДЗ (ДАД2). На основе этих датчиков мне и нужно было открывать ДДЗ, когда есть избыток воздуха, ориентируясь на ДАД1, и закрывать, когда избытка нет, но он нужен, ориентируясь на ДАД2.
Но сначала немного о подборе комплектующих.
Комплектующие
Подбор комплектующих для разработки под Arduino - элементарен. Все те компоненты, которые мне понадобились - хорошо описаны, под многое есть готовые библиотеки или алгоритмы.
Arduino UNO R3. Сама плата ардуино. Видимо, китайская реплика, раз продавалась на Алиэкспрессе. Выбрал среднюю между Mini и Mega, примерно посчитав требуемое количество входов. Аналоговых входов в итоге не хватило из-за добавления дополнительных компонентов.
74HC4051. Мультиплексор. Позволяет расширить количество аналоговых входов, подавая на вход разные комбинации цифровых сигналов. Цифровых входов оставалось достаточно, что позволило использовать эту схемку.
BTS7960 43A H-bridge. Драйвер управления заслонкой. Позволяет управлять вращением мотора в разных направлениях с задаваемой скоростью. Выбран с запасом по мощности.
ACS712. Амперметр. Нужен для отключения системы в случае превышения тока, потребляемого заслонкой. Позже в BTS7960 обнаружил аналогичный функционал, но этот компонент оставил как резервный.
DC-DC 12v-9v и DC-DC 12v-5v. Преобразователи напряжения. Первый служит для питания системы. Второй - для отслеживания выключения системы, чтобы привести систему в "нормальное" состояние.
Регтайм 3-12 60-600. Реле задержки выключения. Когда систему выключаем кнопкой - нужно продолжать подачу питания в систему, чтобы привести её в "нормальное" состояние.
Пищалка. Для диагностики неисправностей не подключая ноутбук.
HW-316. Реле на 4 канала для включения компрессора и компонентов системы охлаждения.
Датчик атмосферного давления со встроенным датчиком температуры. Два датчика. Датчики температуры - для управления системой охлаждения. Датчики атмосферного давления - для управления заслонкой. Датчики температуры - резисторы и их "чтение" отличается от других датчиков, используется так называемый опорный резистор.
Заслонка ВАЗ 21126. В состав заслонки кроме мотора входят два датчика положения дроссельной заслонки.
Так выглядит текущий вариант схемы.
![](https://habrastorage.org/getpro/habr/upload_files/81a/6c0/d8d/81a6c0d8d3656312197ab3bbd018b1e7.png)
После сборки схемы, используя макетную плату и комплект проводов, началось самое интересное - написание алгоритма. Итак, собственно, подводные камни.
Подводные камни
Первая особенность. Считывание напряжения с аналоговых входов Arduino
В алгоритме работы мне нужно считывать данные с датчиков. Типовой датчик состоит из двух входов и одного выхода: минус, базовое напряжение и выходной сигнал.
Базовое напряжение в большинстве случаев - 5 вольт. Выходной сигнал меняется от 0 до 5 вольт. Применительно, например, к датчику положения дроссельной заслонки: 0 вольт - заслонка закрыта, 5 вольт - заслонка открыта, промежуточные значения - промежуточные положения заслонки.
Принцип чтения значения такого датчика: выходной сигнал подключается к аналоговому входу Ардуино и в программе мы получаем возможность чтения значения в виде целого числа в интервале 0-1023. Это значение "мапится" хоть на вольты, хоть сразу на процент открытия заслонки.
Оказалось неожиданностью то, что граничные значения датчиков "плавают" в достаточно большом диапазоне в зависимости от:
температуры в помещении - показания ненамного, но отличаются;
от "прогрева" системы - сразу после включения показания немного, но отличаются от тех, что выдаются позже;
вероятно, от качества соединения проводов в монтажной плате, лишнего сопротивления из-за длины;
вероятно, от качества преобразования питания в DC-DC плате, что ведёт к "плаванию" базового напряжения.
Проблему изначально предполагал, но не думал, что погрешности будут такими большими. После того, как схема будет спаяна в постоянном виде, чтобы исключить плохой контакт - буду замерять напряжение в узловых местах, чтобы понять, кто вносит наибольшую погрешность и по возможности снизить её.
Решение проблемы - калибровка датчиков. Выставление минимального и максимального значения сигнала датчика в крайних положениях. Сейчас в коде используются подобранные константные значения.
Решение №2 - в дальнейшем, возможно, будет периодическая само-калибровка с сохранением состояния в энергонезависимую память.
Решение №3 - подбор качественных DC-DC преобразователей, качественный монтаж.
Как пример всей беды - сейчас датчик положения дроссельной заслонки откалиброван на напряжения 0,5-4,2 вольта, вместо идеальных 0-5 вольт.
Вторая особенность. Калибровка "противоположных" датчиков
Дроссельная заслонка устроена интересным образом - в её составе есть два датчика положения. Датчик 1 выдаёт напряжение тем больше, чем больше заслонка закрыта. Датчик 2 выдаёт напряжение тем меньше, чем больше заслонка закрыта.
На примере: при закрытой заслонке датчик 1 в идеале выдаёт 5 вольт, датчик 2 - 0 вольт.
Предполагая, что два датчика сделаны для отказоустойчивости - я сделал алгоритм оценки положения, основываясь на датчике 1, при этом сверяясь с датчиком 2, в случае расхождения показаний больше граничного значения - система уходит в режим ошибки.
В процессе же работы с заслонкой - получил необходимость периодически калибровать граничные значения датчиков, в итоге достаточно сильно их снизив.
Из-за этого стало закрадываться подозрение, что два датчика нужны не для отказоустойчивости, а как раз для некой самобалансировки и вычисления положения в зависимости от взаимных значений.
После сборки системы и отладки на реальном "железе" подумаю над пересмотром алгоритма использования датчиков в направлении вычисления положения, основываясь на двух значениях.
Также у драйвера управления дроссельной заслонкой есть возможность чтения используемого тока, что позволит калибровать крайние значения заслонки, предполагая увеличенное потребление тока, когда заслонка открылась в крайнее положение и пытается открыться дальше.
Третья особенность. Разные показания датчиков
Казалось бы - одинаковые датчики, но выдают разные показания. Что датчики температуры, что давления, что положения заслонки. Например, в одинаковых условиях датчики давления показывают разницу в 1,4 кПа.
Проблема предполагалась изначально и очевидное решение - калибровка.
Четвёртая особенность. Это реальный мир
Особенность управления мотором заслонки через драйвер типа H-bridge - для перемещения заслонки Arduino даёт команду на движение в нужном направлении с требуемой скоростью. Драйвер получает команду и начинает двигать заслонку. Алгоритм в это время может переходить к выполнению другой логики.
Вот здесь и началось интересное поведение заслонки.
Первый вариант алгоритма управления был очень простым: проверяем процент открытия заслонки, даём команду на движение и переходим к другим частям алгоритма, возвращаемся, проверяем процент открытия и всё по новой.
Алгоритм был написан, работал до тех пор, пока я не стал отлаживать другую его часть. В какой-то момент, я включаю дополнительную отладку и заслонка начинает двигаться рывками: то не двигается, то сразу закрывается рывком, то начинает открываться-закрываться.
Проблема оказалось неожиданной: пока другая часть кода писала отладочные данные в COM порт, а это происходит медленно, если выбрана медленная скорость передачи данных - заслонка успевала закрыться намного сильнее, чем требуется.
Решение - ориентироваться на таймер, привязывать начало действий ко времени, рассчитывать требуемое положение "вперёд" в любой нужный момент времени.
В упрощённом виде алгоритм стал таким: при начале движения заслонки фиксируется время начала движения, в любой другой момент времени, когда мы получили управление - требуемое положение заслонки вычисляется в зависимости от прошедшего времени и принимается решение, что делать дальше - останавливать, продолжать движение или начинать движение в обратном направлении.
Пятая особенность. Резонанс
Получается из предыдущей особенности. Когда мы хотим привести заслонку к нужному проценту открытия - мы даём команду на движение в нужном направлении с заданной скоростью. Когда мы в следующий раз попадаем в контролирующую часть алгоритма - заслонка может быть открыта больше, чем нам нужно. Даётся команда движения в противоложном направлении. В итоге могут возникнуть постоянные незатухающие и даже увеличивающиеся колебания заслонки.
Проблема решается просто - определяется допустимая погрешность на достижение нужного процента и вводится временной параметр, не позволяющий изменять направление движения, чаще, чем требуется.
Шестая особенность. Переполнение целых чисел
В Arduino рекомендуется работать с целочисленными значениями. Работа с вещественными числами медленнее.
В моих алгоритмах нет сложной логики, которую замедлили бы расчёты с вещественными числами и не люблю преждевременную оптимизацию (люблю разумную оптимизацию), но вот решил использовать расчёты с целыми числами.
А так как для значений выбирались подходящие типы, то после математических операций в какой-то момент оно выстрелило в ногу. Проблема понятная, легко отлаживается, но уж очень давно не работал на бэкенде с целочисленной математикой.
Седьмая особенность. Отладка
Тестирование различных бэкендов - просто и незатейливо. Можно написать тест логики, который проверит написанную функцию. Можно написать интеграционный тест, который проверит взаимодействие с другой системой.
В случае же с системой, которая работает с реальными устройствами, датчиками и которая может вести себя по разному в казалось бы одинаковых условиях - тестирование написанной логики превращается в занимательные упражнения.
Изначально хотел делать по принципу "мы не ищем лёгких путей" - написать код, поставить "железо", отлаживать на машине (конечно, не во время движения для начала).
Потом мне посоветовали сделать эмулятор для всего этого.
Сначала подумал об эмуляторах электронных устройств, когда программа устанавливалась бы на эмулятор ардуино и работала бы с полностью эмулированным железом. Такое ПО, да, существует, но под него, как понял - пришлось бы написать все эти эмуляторы всех устройств. В общем, этот путь для автокорпораций с отделами на тысячи опытных разработчиков.
Итоговый выбранный вариант: сделан физический эмулятор педали газа, написан эмулятор двигателя, компрессора, датчиков, подключён как отдельный компонент к основной программе, запускается на том же Ардуино. Тестирование проводилось на стенде, используя графики, построенные по передаваемым с устройства данным. В VSCode плагин визуализации графиков из COM порта, кстати, удобный.
Алгоритм
Архитектурно алгоритм разбит на четыре части:
Часть управления дроссельной заслонкой. Назначение - получает на вход команду приведения заслонки к требуемому положению в процентах 0-100 и далее независимо у себя "внутре" приводит заслонку к нужному положению, ориентируясь на показания датчиков положения, потребления тока, таймауты и так далее.
Часть основного контроля. Назначение - по показаниям датчиков давления понимает нужен наддув или нет, нажал ли сейчас водитель педаль газа или отпустил, или нажал и удерживает в неком среднем положении для равномерного движения. Вычисляет требуемое положение заслонки, даёт команду первому компоненту на установку нужного положения.
Часть дополнительного контроля. Назначение - включение/выключение компрессора, включения системы охлаждения (водяная помпа, вентилятор) в зависимости от датчиков температуры, сбор отладочной информации, обработка ошибок и граничных значений.
Часть эмулятора двигателя. Входит в основную программу как отдельный компонент, включается флажком в коде.
Часть управления дроссельной заслонкой
Первая часть алгоритма принимает на вход требуемое положение, запоминая его. Далее периодически рассчитывает промежуточное положение, передавая его во вторую часть алгоритма.
Расчёт промежуточного положения зависит от того, открывается заслонка (открываемся быстро, делаем "blow-off") или закрывается (закрываемся медленно, постепенно повышая наддув, плавное нарастание мощности). Как уже упоминал выше в "проблемах" - расчёт промежуточного положения зависит от времени начала движения и привязан к моменту времени, так мы в каждый момент знаем, какое положение требуется - независимо от того, прошла 1 мсек или 10 мсек с прошлого расчёта.
Вторая часть алгоритма принимает на вход промежуточное положение и обеспечивает его достижение, контролируя включение мотора.
Такое разделение можно назвать макро и микро-менджмент - оно позволяет обеспечить плавное движение в условиях постоянного изменения требуемого положения. Первая часть при этом обеспечивает соблюдение привязки времени начала движения, это важно при изменении на движение в противоположную сторону.
Пример: если заслонка в настоящее время закрывается, а новое требуемое положение означает, что нужно продолжать закрываться - изменения привязки времени не происходит, продолжая плавное закрытие заслонки.
Часть основного контроля
Первая часть алгоритма ответственна за определение того, что делает водитель - нажал на педаль газа или отпустил педаль газа. Решение делается путём оценки нескольких значений давления за константные периоды времени: постоянно растёт давление - педаль газа нажата, постоянно понижается - педаль газа отпущена.
Вторая часть алгоритма рассчитывает положение по относительно простым условиям (упрощённо):
если педаль газа нажата и давление после основной дроссельной заслонки (ОДЗ) растёт - хотим закрыть дополнительную дроссельную заслонку (ДДЗ) полностью, чтобы сделать наддув;
если педаль отпущена и давление ниже лимита (ОДЗ закрыта) - выпускаем избыток воздуха, открываем ДДЗ полностью (делаем "blow-off");
в остальных случаях поддерживаем давление до ОДЗ чуть больше давления после ОДЗ немного приоткрывая или призакрывая ДДЗ - стабильное положение, когда педаль газа немного нажата и машина просто равномерно движется примерно с одной скоростью.
Третья часть приводит рассчитанное положение к требуемому с учётом таймаутов и передаёт команду на контроль в часть управления заслонкой.
Часть дополнительного контроля
Эта часть не содержит какой-то интересной алгоритмической логики. В основном здесь используются простые условия вида: температура воздуха выросла до граничного значения - включаем помпу, ещё больше выросла - включаем вентилятор.
Также здесь есть возможность выключать компрессор в случае длительного холостого хода - долго стоим на светофоре или машина припаркована с включённым двигателем.
Часть эмулятора двигателя
У эмулятора есть физическая педаль газа в виде реостата (переменного резистора) - это такой ползунок, который может двигаться между двумя позициями, в коде мы знаем насколько он сдвинут - на 0% или на 100%. Ничем не отличается от электронной педали газа.
В эмуляторе есть значение текущих оборотов двигателя. Нажимаем немного педаль газа - обороты начинают расти. Нажимаем педаль газа сильно - обороты начинают расти, но быстрее.
Дальше есть значение эффективности работы компрессора. Компрессор на разных оборотах двигателя сжимает воздух с разной эффективностью - на малых оборотах сжатие менее эффективно. Для расчёта взят график эффективности примерно похожего компрессора и упрощён: рассчитываем эффективность по линейной формуле плюс учитывается точка перегиба - когда график сильно меняет наклон. За эту точку взял 2000 оборотов.
![](https://habrastorage.org/getpro/habr/upload_files/559/2a4/b33/5592a4b334961f7bedea143be2f20fc9.jpg)
Дальше рассчитывается чистое давление перед ОДЗ, которое мог бы выдавать компрессор если ДДЗ закрыта. Оно зависит от эффективности и косвенно от оборотов.
Далее идёт расчёт давления перед ОДЗ, но с учётом положения ДДЗ. Положение ДДЗ берётся реальное. ДДЗ закрыта - давление максимально выдаваемое рассчитанное. ДДЗ открыта - давление минимальное (атмосферное), воздух "ходит по кругу".
Последнее рассчитываемое значение - давление после ОДЗ (непосредственно на входе в двигатель) - берётся предыдущее значение и к нему по похожей формуле применяется положение ОДЗ, а оно берётся равным положению педали газа. Педаль газа полностью нажата = ОДЗ полностью открыта - давление = рассчитанному давлению перед ОДЗ.
Таким образом получается система с прямой связью от педали газа и с обратной связью от реального значения положения ДДЗ, которая сама зависит от педали газа. Но работает. Примерно похоже на реальный двигатель, хотя я понимаю, что на машине оно будет вести себя иначе.
Демонстрация
Демонстрация работы получившегося прототипа на стенде.
То же самое, но на графиках.
Пояснение по видео с графиками. После записи видео заметил, что в одном моменте видео графики противоречат моим комментариям. Когда нажимается педаль газа без включения компрессора - растёт давление наддува до основной дроссельной заслонки. Так происходит потому, что в эмуляторе не предусмотрел учёт флага включения компрессора. Тестам открытия дополнительной дроссельной заслонки это не мешало, поэтому и заметил только сейчас. Если бы флаг учитывался, то правильно было бы так, как говорю в комментариях - на графиках бы было нормальное атмосферное давление.
После установки "железной" части системы и отладки уже на настоящих датчиках, педали газа, моторе, компрессоре - продолжу описание новых особенностей в следующей статье: Часть 2. Отладка на авто (здесь будет ссылка).