RSS блога
Подписка
Простая электронная нагрузка на Arduino.
Однажды захотелось провести ревизию своих аккумуляторов разной степени убитости.
А ещё чесались руки покодить на Arduino, и вот что из этого вышло.
Всем привет.
Предлагаю вашему вниманию простую схему для тестирования ёмкости аккумуляторов током до 5 А с рабочим напряжением до 20 В.
На муське уже был DIY обзор вполне годной самодельной электронной нагрузки.
В комментариях обсуждали плюсы и минусы данной схемы, а также мысли о программном поддержании тока разряда самой Ардуино.
За основу взял схему товарища Ksiman
Операционных усилителей в наличии не было, пришлось обходиться без них:
Как видите, в железе она реально простая.
На схеме видим аккумулятор в трёхпроводной схеме подключения, который будет разряжаться через датчик тока R2.
Нога currentSense мониторит ток. Для большей точности АЦП опорное напряжение выбрано внутреннее (1.1 вольт для atmega328). Теоретическая точность поддержания тока – 10ма.
Схема также мониторит напряжение на аккумуляторе, подавая его через делитель 1/5 или 1/20 на ногу VSense.
Делитель работает в двух режимах – если напряжение аккумулятора выше 5В, нога «1/5-1/20» замыкается на землю, получаем делитель 1 к 20.
Разрядность измерения напряжения в таком случае около 20 мВ (1100мВ опорного ардуино/1024 шага) * коэффициент деления 20
Если напряжение аккумулятора меньше 5В, ардуино подвешивает ногу «1/5-1/20» в высокоимпедансное состояние, резистор R5 оказывается подвешенным в воздухе и в делении не участвует – получается делитель 1 к 5 и большая точность измерения напряжения. Спасибо товарищу Ksiman за идею.
Принцип работы весьма прост: через «интерфейс» (монитор com порта) задаём ток разряда в миллиамперах, а также напряжение отсечки в милливольтах, и нажимаем запуск.
В этот момент запускается PD регулятор, который в цикле несколько десятков раз в секунду отслеживает ток на датчике тока R2, сравнивает его с целевым значением и подаёт ШИМ 5В (нога PWM) на интегратор R1C1, увеличивая или уменьшая ток нагрузки.
Напряжение на затворе меняется очень плавно (постоянная времени R1C1 около 0.5с), за счёт этого обеспечивается хорошая точность поддержания тока несмотря на весьма посредственную линейность полевика в линейном режиме.
Частота ШИМ 2кГц, разрядность 10 бит.
Измерения напряжения и тока пятикратное, максимальное и минимальное значения выкидываются, оставшиеся 3 усредняются.
Настраиваем (раздел по настройке чуть ниже).
Идём в монитор com-порта:
Я немного заигрался, и получилась вот такая большая менюха.
Здесь отображаются текущие показания вольтметра и амперметра, целевой ток и напряжение, а также меню запуска и настройки.
Нагрузка показывает 4159 мВ, реальное напряжение на батарее при этом 4.16 В
Выбираем нужный режим, напряжение отсечки, ток и запускаем процесс.
Получившиеся данные с помощью технологии CopyPaste копируем в Excel и строим графики.
Относительно свеженькая VTC5, куплена около года назад, использовалась раз 30.
А вот этим двум товарищам явно пора на покой, в процессе разряда нагрелись до 42 и 37 градусов, отдав смешные 1300 и 1670 мАч. Это и не удивительно, я 3 года практически ежедневно разряжал их «досуха» в мощной электронной сигарете.
А вот 18650 элемент с интригующим названием SkywolEye на токе 1 А выдал потрясающие 69мАч.
Уже в процессе написания данной статьи, хохмы ради прикрутил режим разряда постоянным сопротивлением и постоянной мощностью:
Как видим, на токах до 200 мА ловить нечего – практически постоянная ошибка в 25-30 мА.
Ток при этом стабильный, но неправильный. Можно было откалибровать программно, но это не спортивно, у нас всё-таки простая электронная нагрузка.
Начиная от 2,5 Ампер ошибка вызвана разогревом шунта, при обдуве его вентилятором схема выходит на заданный ток.
Последняя строка, выделенная синим — максимальный ток, который удалось получить с 5 вольт управляющего напряжения. Здесь использовал Arduino UNO — у неё нет диода по входу USB, и транзистор открывается лучше.
Погрешность по току стала меньше, т.к. здесь использовал маленький чит в виде обдува шунта. Будем считать, что я сделал шунт помощнее :-)
С напряжением дела обстоят получше, погрешность небольшая, растёт с током, вызвана, очевидно, сопротивлениями силовых контактов.
Что касается измерения энергии:
Протестированная мной VTC5 по данным прибора имеет заряд в 2449 мАч и энергию в 8618 мВтч.
Из случайных источников взял энергию данного элемента и сравнил с тем, что у меня получилось.
Источник 1
Источник 2
Видим погрешность измерения заряда («ёмкости») и энергии 2% и 4%, соответственно.
Следует также учесть тот факт, что элемент у меня не новый и его запасённая энергия меньше.
Если пересчитать полученную мной энергию с учётом погрешности измерений напряжения в 33мВ (т.е. «увеличить» напряжение в расчёте на эти 33 мВ), то отданная энергия получается 8703 мВтч, что на 1 % больше посчитанного ардуиной значения.
В открытом режиме осциллографа пульсации тока не видны, показываю в закрытом режиме (это когда постоянная составляющая сигнала отфильтровывается) на пределе 5 мв/дел, т.е. 1 клетка это 50 мА тока в нагрузке.
Осциллограмма меня не порадовала, была видна какая-то широченная помеха шириной аж в 2 клетки.
В одной клетке 200нс умещаются аж 3 периода, нетрудно посчитать период сигнала в 66, (6) нс и частоту в 15 мгц, что подозрительно похоже на частоту кварца Ардуино.
Как оказалось, ардуино довольно шумная и излучает вокруг себя во все стороны радиопомеху на своей тактовой частоте. Даже с щупом, замкнутым на собственный земляной крокодильчик, эта помеха имела место. Даже с выключенной электронной нагрузкой. Даже просто на голой ардуине, к которой подведено только питание. К счастью, помеха не имеет видимого влияния на стабилизацию тока, т.к. довольно симметричная и быстрая.
Однако в процессе разряда аккумуляторов ток всё же немного «дрожит», примерно на 1/10 ячейки, или 5 мА.
Выглядит вот так.
Мультиметр на «точном» пределе в 200ма такие пульсации практически не замечает, по его показаниям ток меняется на 1-2 мА.
Изначально использовал вот такую , припаяв 3 провода для трехпроводного включения. Её вполне можно использовать на обозреваемых «детских» токах.
Потом подоспела вот такая
За свои деньги неплохой «любительский» вариант, удобна универсальностью, хотя я почти уверен, что на высоких токах будет давать сильную погрешность.
Силовой транзистор подойдёт в принципе любой, типа IRP460, IRFP260, irfp250, w20nk50z и им подобные. Не стоит гнаться за большими токами и напряжениями в даташитах, также как и за маленьким сопротивлением Rds(on) – всё равно транзистор работает в линейном режиме, т.е. он своим сопротивлением регулирует ток в нагрузке.
В первом приближении размер корпуса транзистора намного важнее тока и напряжения. Больше корпус – больше сможет рассеять. Одну банку 18650 на токе в 4 ампера можно и на irf840 разрядить. Я на всякий случай прицепил транзистор на винтовой клеммник для оперативной его замены в случае отстрела :-)
Я также экспериментировал с безымянными IGBT в TO-247 из плазменного телевизора, они классные, но за счёт падения напряжения эмиттер-коллектор одиночные ni-mh не потестировать.
Токовый шунт необходимо выбирать с запасом по мощности, чтобы не нагревался, либо искать резисторы, выполненные из материалов с низким температурным коэффициентом сопротивления. У меня валялись выдранные откуда-то пятиваттные MPR-5W, собрал 3 шт последовательно, на токе 5А греются до 50 градусов. Судя по показаниям старенького MAS830, на 3 амперах ток разряда в работе уплывает на 20 мА из-за нагрева резисторов, т.е. сборочку резисторов надо бы делать помощнее.
Минусовой провод от батареи до общей на схеме точки делаем максимально толстым и коротким – на нём просаживается часть напряжения, что несколько ухудшает точность замера напряжения на АКБ по сравнению с классической 4-х проводной схемой. Я не заморачивался и взял чёрный провод от БП ATX.
Мой вариант выполнен в классическом раздолбайско-макетном стиле
схема простая, работает, но не без косяков.
Плюсы данного девайса:
• простота сборки, повторяемость: сможет собрать и настроить любой, у кого есть Ардуино, мультиметр и горстка доступных радиокомпонентов.
• Приемлемая точность измерения ёмкости (заряда) в диапазоне от 500 мА.
• Несколько режимов измерения.
За простоту приходится расплачиваться:
• Минимальный ток разряда 150мА
• Завышает ток на 10-20 мА на низких токах разряда (до 500 мА).
• сложность масштабирования (необходимо городить дополнительные PD контроллеры под каждый дополнительный мосфет)
• плохая точность расчёта запасённой энергии.
Надеюсь, данная заготовка пригодится тем, кто хотел потестировать свои аккумуляторы в лёгком режиме, но по каким-то причинам стеснялся. Точности в 2% для измерения ёмкости должно хватить.
В процессе макетирования родилось много идей и задумок, но я сегодня решил всё же не отходить от темы электронной нагрузки из минимума деталей.
Например, сюда можно приделать схему заряда, датчик температуры батарей и выводить её в график, или аварийно останавливать разряд по температуре.
Чтобы увеличить точность, всё же не обойтись без ОУ: необходимо усиливать сигнал с шунта с помощью ОУ и, соответственно, повышать опорное, например, брать его с той же TL431.
Конструктивные комментарии по проведённой работе приветствуются.
Надеюсь, было интересно. Спасибо за внимание.
А ещё чесались руки покодить на Arduino, и вот что из этого вышло.
Всем привет.
Предлагаю вашему вниманию простую схему для тестирования ёмкости аккумуляторов током до 5 А с рабочим напряжением до 20 В.
На муське уже был DIY обзор вполне годной самодельной электронной нагрузки.
В комментариях обсуждали плюсы и минусы данной схемы, а также мысли о программном поддержании тока разряда самой Ардуино.
За основу взял схему товарища Ksiman
Операционных усилителей в наличии не было, пришлось обходиться без них:
Как видите, в железе она реально простая.
На схеме видим аккумулятор в трёхпроводной схеме подключения, который будет разряжаться через датчик тока R2.
Нога currentSense мониторит ток. Для большей точности АЦП опорное напряжение выбрано внутреннее (1.1 вольт для atmega328). Теоретическая точность поддержания тока – 10ма.
Схема также мониторит напряжение на аккумуляторе, подавая его через делитель 1/5 или 1/20 на ногу VSense.
Делитель работает в двух режимах – если напряжение аккумулятора выше 5В, нога «1/5-1/20» замыкается на землю, получаем делитель 1 к 20.
Разрядность измерения напряжения в таком случае около 20 мВ (1100мВ опорного ардуино/1024 шага) * коэффициент деления 20
Если напряжение аккумулятора меньше 5В, ардуино подвешивает ногу «1/5-1/20» в высокоимпедансное состояние, резистор R5 оказывается подвешенным в воздухе и в делении не участвует – получается делитель 1 к 5 и большая точность измерения напряжения. Спасибо товарищу Ksiman за идею.
Принцип работы весьма прост: через «интерфейс» (монитор com порта) задаём ток разряда в миллиамперах, а также напряжение отсечки в милливольтах, и нажимаем запуск.
В этот момент запускается PD регулятор, который в цикле несколько десятков раз в секунду отслеживает ток на датчике тока R2, сравнивает его с целевым значением и подаёт ШИМ 5В (нога PWM) на интегратор R1C1, увеличивая или уменьшая ток нагрузки.
Напряжение на затворе меняется очень плавно (постоянная времени R1C1 около 0.5с), за счёт этого обеспечивается хорошая точность поддержания тока несмотря на весьма посредственную линейность полевика в линейном режиме.
Частота ШИМ 2кГц, разрядность 10 бит.
Измерения напряжения и тока пятикратное, максимальное и минимальное значения выкидываются, оставшиеся 3 усредняются.
Использование
Заливаем скетч в ардуино (я использовал nano).Настраиваем (раздел по настройке чуть ниже).
Идём в монитор com-порта:
Я немного заигрался, и получилась вот такая большая менюха.
Здесь отображаются текущие показания вольтметра и амперметра, целевой ток и напряжение, а также меню запуска и настройки.
Нагрузка показывает 4159 мВ, реальное напряжение на батарее при этом 4.16 В
Выбираем нужный режим, напряжение отсечки, ток и запускаем процесс.
Управление подробно
Нажимаем 3, вводим значение напряжение отсечки:
Я выбрал 3000 мВ. Новые показания отобразились на панели управления.
Устанавливаем ток разряда.
Нажимаем 2, вводим ток в миллиамперах, например 3500
Нажимаем 1, процесс запускается.
Перед запуском программа проверяет наличие аккумулятора – если напряжение ниже 500мВ, то процесс не запустится, высветится сообщение.
До нуля при, этом, разрядить аккумулятор можно, выставив соответствующий Cut-off через меню.
В любой момент в процессе разрядки, как и в любом пункте меню, можно прерваться, отправив 0
Как видим, ток не достиг целевого значения, напряжения на затворе не хватило, чтобы транзистор нормально открылся. Тут всему винойнапряжение USB конструктивная особенность платы nano: у неё по входу питания от USB стоит диод, после которого в питание приходит 4.6 вольта. Расчёт всё равно получится корректным, т.к. для расчёта используются реальные показания тока. Но мы для красоты выставим ток 3А :-)
При показаниях 3.910 мВ реальное напряжение на клеммах батареи 3.93В, а относительно нуля ардуины – всё те же 3.91В.
Разница падает на сопротивлении клеммника и минусового отрезка провода от держалки батареи до общей точки — плата за трёхпроводную схему измерения. Да и сама держалка имеет контакты слишком мелкого диаметра, аккумулятор то и дело норовит коснуться лишь части поверхности электрода держалки, что тоже увеличивает сопротивление.
По достижении заданного напряжения или по нажатию 0 разряд прекращается.
Я выбрал 3000 мВ. Новые показания отобразились на панели управления.
Устанавливаем ток разряда.
Нажимаем 2, вводим ток в миллиамперах, например 3500
Нажимаем 1, процесс запускается.
Перед запуском программа проверяет наличие аккумулятора – если напряжение ниже 500мВ, то процесс не запустится, высветится сообщение.
До нуля при, этом, разрядить аккумулятор можно, выставив соответствующий Cut-off через меню.
В любой момент в процессе разрядки, как и в любом пункте меню, можно прерваться, отправив 0
Как видим, ток не достиг целевого значения, напряжения на затворе не хватило, чтобы транзистор нормально открылся. Тут всему виной
При показаниях 3.910 мВ реальное напряжение на клеммах батареи 3.93В, а относительно нуля ардуины – всё те же 3.91В.
Разница падает на сопротивлении клеммника и минусового отрезка провода от держалки батареи до общей точки — плата за трёхпроводную схему измерения. Да и сама держалка имеет контакты слишком мелкого диаметра, аккумулятор то и дело норовит коснуться лишь части поверхности электрода держалки, что тоже увеличивает сопротивление.
По достижении заданного напряжения или по нажатию 0 разряд прекращается.
остальные пункты меню
Пункт 4 меню для дебага – просто показывает показания АЦП. Нажать 0 для выхода в главное меню.
Пункт 5 слегка приоткрывает транзистор, не используя PD-регулятор, чтобы можно было померить ток амперметром. Был нужен для дебага, пригодится для калибровки. Нажать 0 для выхода в главное меню.
Пункт 6 позволяет переключать стиль отображения информации CSV или «нормальный»
CSV режим – удобен для отправки данных в эксель.
«Нормальный» режим:
Power – это на самом деле не мощность, а текущее значение ШИМ (1023 максимум, т.е. 5 вольт)
Calcs per interval говорит нам о том, сколько раз PD регулятор поработал за время между отправкой данных в com-порт (2 секунды по умолчанию).
Пока писал обзор, дописал и протестировал пункт 7 — переключение между режимами разряда постоянным током, сопротивлением или мощностью.
Пункт 5 слегка приоткрывает транзистор, не используя PD-регулятор, чтобы можно было померить ток амперметром. Был нужен для дебага, пригодится для калибровки. Нажать 0 для выхода в главное меню.
Пункт 6 позволяет переключать стиль отображения информации CSV или «нормальный»
CSV режим – удобен для отправки данных в эксель.
«Нормальный» режим:
Power – это на самом деле не мощность, а текущее значение ШИМ (1023 максимум, т.е. 5 вольт)
Calcs per interval говорит нам о том, сколько раз PD регулятор поработал за время между отправкой данных в com-порт (2 секунды по умолчанию).
Пока писал обзор, дописал и протестировал пункт 7 — переключение между режимами разряда постоянным током, сопротивлением или мощностью.
Получившиеся данные с помощью технологии CopyPaste копируем в Excel и строим графики.
Относительно свеженькая VTC5, куплена около года назад, использовалась раз 30.
А вот этим двум товарищам явно пора на покой, в процессе разряда нагрелись до 42 и 37 градусов, отдав смешные 1300 и 1670 мАч. Это и не удивительно, я 3 года практически ежедневно разряжал их «досуха» в мощной электронной сигарете.
А вот 18650 элемент с интригующим названием SkywolEye на токе 1 А выдал потрясающие 69мАч.
Уже в процессе написания данной статьи, хохмы ради прикрутил режим разряда постоянным сопротивлением и постоянной мощностью:
Поговорим о точности измерений
В процессе тестирования замерял напряжение и ток самыми простыми мультиметрами, для контроля сверяясь с осциллографом. Результаты свёл в таблицу.Как видим, на токах до 200 мА ловить нечего – практически постоянная ошибка в 25-30 мА.
Ток при этом стабильный, но неправильный. Можно было откалибровать программно, но это не спортивно, у нас всё-таки простая электронная нагрузка.
Начиная от 2,5 Ампер ошибка вызвана разогревом шунта, при обдуве его вентилятором схема выходит на заданный ток.
Последняя строка, выделенная синим — максимальный ток, который удалось получить с 5 вольт управляющего напряжения. Здесь использовал Arduino UNO — у неё нет диода по входу USB, и транзистор открывается лучше.
Погрешность по току стала меньше, т.к. здесь использовал маленький чит в виде обдува шунта. Будем считать, что я сделал шунт помощнее :-)
С напряжением дела обстоят получше, погрешность небольшая, растёт с током, вызвана, очевидно, сопротивлениями силовых контактов.
Что касается измерения энергии:
Протестированная мной VTC5 по данным прибора имеет заряд в 2449 мАч и энергию в 8618 мВтч.
Из случайных источников взял энергию данного элемента и сравнил с тем, что у меня получилось.
Источник 1
Источник 2
Видим погрешность измерения заряда («ёмкости») и энергии 2% и 4%, соответственно.
Следует также учесть тот факт, что элемент у меня не новый и его запасённая энергия меньше.
Если пересчитать полученную мной энергию с учётом погрешности измерений напряжения в 33мВ (т.е. «увеличить» напряжение в расчёте на эти 33 мВ), то отданная энергия получается 8703 мВтч, что на 1 % больше посчитанного ардуиной значения.
А давайте посмотрим сигналы
На затворе полевика всё ровно и гладко, давайте покажу сигнал на токовом шунте.В открытом режиме осциллографа пульсации тока не видны, показываю в закрытом режиме (это когда постоянная составляющая сигнала отфильтровывается) на пределе 5 мв/дел, т.е. 1 клетка это 50 мА тока в нагрузке.
Осциллограмма меня не порадовала, была видна какая-то широченная помеха шириной аж в 2 клетки.
В одной клетке 200нс умещаются аж 3 периода, нетрудно посчитать период сигнала в 66, (6) нс и частоту в 15 мгц, что подозрительно похоже на частоту кварца Ардуино.
Как оказалось, ардуино довольно шумная и излучает вокруг себя во все стороны радиопомеху на своей тактовой частоте. Даже с щупом, замкнутым на собственный земляной крокодильчик, эта помеха имела место. Даже с выключенной электронной нагрузкой. Даже просто на голой ардуине, к которой подведено только питание. К счастью, помеха не имеет видимого влияния на стабилизацию тока, т.к. довольно симметричная и быстрая.
Однако в процессе разряда аккумуляторов ток всё же немного «дрожит», примерно на 1/10 ячейки, или 5 мА.
Выглядит вот так.
Мультиметр на «точном» пределе в 200ма такие пульсации практически не замечает, по его показаниям ток меняется на 1-2 мА.
Выбор компонентов и рекомендации
Держалка батарейИзначально использовал вот такую , припаяв 3 провода для трехпроводного включения. Её вполне можно использовать на обозреваемых «детских» токах.
Потом подоспела вот такая
За свои деньги неплохой «любительский» вариант, удобна универсальностью, хотя я почти уверен, что на высоких токах будет давать сильную погрешность.
Силовой транзистор подойдёт в принципе любой, типа IRP460, IRFP260, irfp250, w20nk50z и им подобные. Не стоит гнаться за большими токами и напряжениями в даташитах, также как и за маленьким сопротивлением Rds(on) – всё равно транзистор работает в линейном режиме, т.е. он своим сопротивлением регулирует ток в нагрузке.
В первом приближении размер корпуса транзистора намного важнее тока и напряжения. Больше корпус – больше сможет рассеять. Одну банку 18650 на токе в 4 ампера можно и на irf840 разрядить. Я на всякий случай прицепил транзистор на винтовой клеммник для оперативной его замены в случае отстрела :-)
Я также экспериментировал с безымянными IGBT в TO-247 из плазменного телевизора, они классные, но за счёт падения напряжения эмиттер-коллектор одиночные ni-mh не потестировать.
Токовый шунт необходимо выбирать с запасом по мощности, чтобы не нагревался, либо искать резисторы, выполненные из материалов с низким температурным коэффициентом сопротивления. У меня валялись выдранные откуда-то пятиваттные MPR-5W, собрал 3 шт последовательно, на токе 5А греются до 50 градусов. Судя по показаниям старенького MAS830, на 3 амперах ток разряда в работе уплывает на 20 мА из-за нагрева резисторов, т.е. сборочку резисторов надо бы делать помощнее.
Минусовой провод от батареи до общей на схеме точки делаем максимально толстым и коротким – на нём просаживается часть напряжения, что несколько ухудшает точность замера напряжения на АКБ по сравнению с классической 4-х проводной схемой. Я не заморачивался и взял чёрный провод от БП ATX.
Мой вариант выполнен в классическом раздолбайско-макетном стиле
Настройка скетча
В начале скетча прописаны и прокомментированы все параметры, в том числе какие пины ардуино задействованы. Все настройки собраны в первых 30 строках программы.
Из важного –
необходимо перевести ардуино на внутреннее опорное напряжение (можно просто залить описываемый скетч) и измерить напряжение между ref и землёй Ардуино, подставить значение в милливольтах в переменную vref
Также необходимо настроить ваши коэффициенты под «точный» и «неточный» делители напряжения.
Ну и пару раз замерить ток и напряжение мультиметром и сравнить с показаниями ардуино, дабы в программе скорректировать сопротивление шунта и коэффициенты деления, чтобы показания нашего прибора стали верными.
Из важного –
необходимо перевести ардуино на внутреннее опорное напряжение (можно просто залить описываемый скетч) и измерить напряжение между ref и землёй Ардуино, подставить значение в милливольтах в переменную vref
Также необходимо настроить ваши коэффициенты под «точный» и «неточный» делители напряжения.
Ну и пару раз замерить ток и напряжение мультиметром и сравнить с показаниями ардуино, дабы в программе скорректировать сопротивление шунта и коэффициенты деления, чтобы показания нашего прибора стали верными.
Программа
// 2021
//Электронная нагрузка by BSP.
#include "wiring_private.h"
//================================подключение к Ардуино
#define CurrentSensePin A1
#define VoltageSensePin A2
#define MosfetDrivePin 9 //можно сменить на 10, только на эти пины настроен быстрый ШИМ
#define voltDividerPin A3
//-------------------------------------------НАСТРОЙКА---------------------------------------
#define highDivRatio 20 // 20.7; //Делитель напряжения в измерителе 1:20
#define lowDivRatio 5.025 //Делитель напряжения в измерителе 1:5
float sensorResOhm = 0.1 ;// 0.1 ом токовый шунт
word vref = 1088;//1074;//1100; Измерить данное значение у себя на Ардуино и подставить сюда ( в милливольтах).
float calibrationCurr =1;// измеренный ток будет домножаться на это значение, без необходимости лучше не трогать
int offsetCurr = 0; //25 если ток неверный всегда на одно и то же значение, подставим его сюда.
//положительное число занижает реальный ток, отрицательное - завышает
bool csvRepStyle = true; //по умолчанию отчёт в режиме CSV
String delim = ";"; //разделитель для CSV
int reportInterval = 2000;//интервал выдачи показаний на COM порт, мс
float calibrationVolt =1 ;//измеренное напряжение будет домножаться на это значение, без необходимости лучше не трогать
word dischMode =1; //1 для СС режима, 2 для constant power, 3 для constant resistance
//===============================Первоначальная настройка напряжения и тока
word targetCurrent = 150;// mA первоначальный ток разряда, который будет отображаться в меню
word CutOffVoltage = 900 ;//mV Первоначальное напряжение отсечки
word MinVoltThreshold = 500; //mV минимальное первоначальное напряжение, ниже которого разряд не запустится
word targetPower = 4000;
word constRes = 10000;
/////////////////////////////////////////////ПИД-Регулятор///////////////////////////////////////////////////////_____________________________________
float Kp = .1; //без причины лучше не трогать
int Kd = 1; //без причины лучше не трогать
////////////////////////////////////////////////////-----флаги МЕНЮ
byte set = 0;
bool set2Entered =0;
bool set3Entered =0;
bool set6Entered =0;
bool set7Entered =0;
bool showMainMenu = 1;
////////////////////////// Время
unsigned long currentTime = 0;
unsigned long lastTime = 0; //для интервала репортинга
unsigned long lastMillis = 0;// храним время для интервала подсчёта емкости.
unsigned long dischStart; // Время начала разряда
///////////////////////////////////////////////
char valChar[6];
String valString;
float currentValueNow = 0;
float voltageValueNow = 0;
float powerValueNow =0;
float mah =0;
float mwh =0;
bool div5VState = false; //отслеживающая состояние делителя 1/5 --true, 1/20-- false
int error; // ошибка для ПД регулятора
int prevErr = 0;
int dError;
int power =0; //Это значение подаётся на транзистор через analogWrite
int prevPower =0 ;
int powerCorrection = 0; //значение - результат вычисления PID, корректирующее ток до таргета.
///////////////////////////////////////////////
float voltageDividerRatio = highDivRatio;
void setup()
{
analogReference (INTERNAL);
pinMode(CurrentSensePin, INPUT);
pinMode(VoltageSensePin, INPUT);
pinMode(MosfetDrivePin, OUTPUT);
max5Volt (false);
analogWrite (MosfetDrivePin,0);
Serial.begin(9600);
//частота преобразования АЦП - делитель 32
cbi(ADCSRA, ADPS1);
// Пины D9 и D10 - 2 кГц 10bit ШИМ
TCCR1A = 0b00000011; // 10bit
TCCR1B = 0b00001010; // x8 fast pwm
}
void loop()
{
if (showMainMenu==1) {
currentValueNow = calcCurrentNew (5) ;
voltageValueNow = calcVoltageNew (10);
Serial.println ("---------MAIN MENU-------");
Serial.print ("VoltageValueNow");
Serial.print (" ");
Serial.println (voltageValueNow,0);
Serial.print ("CurrentValueNow");
Serial.print (" ");
Serial.println (currentValueNow,0);
switch (dischMode) {
case 1:
Serial.print ("Target current (mA):");
Serial.print (" ");
Serial.println (targetCurrent);
break;
case 2:
Serial.print ("Target Power (mW):");
Serial.print (" ");
Serial.println (targetPower);
break;
case 3:
Serial.print ("Constant resistance (mOhm):");
Serial.print (" ");
Serial.println (constRes);
break;
}
Serial.print ("Cut Off Voltage(mV):");
Serial.print (" ");
Serial.println (CutOffVoltage);
Serial.println (" ");
Serial.println("Press 7 to change discharge mode");
Serial.println("Press 6 to change to CSV report style");
Serial.println("Press 5 to open mosfet (4debug)"); //режим отладки (приоткрываем стразистор)
Serial.println("Press 4 to see voltage sensor readings (4debug)");
Serial.println("Press 3 to set Cut off voltage in mV");
switch (dischMode) {
case 1:
Serial.println("Press 2 to set discharge current in mA");
break;
case 2:
Serial.println("Press 2 to set discharge power in mW");
break;
case 3:
Serial.println("Press 2 to set discharge resistance mOhm");
break;
}
Serial.println("Press 1 to start");
Serial.println("Press 0 to go to main menu"); //выход из режимов
showMainMenu =0;
}
if (Serial.available() > 0&& set==0 )
{
int val=Serial.read(); //прочитать что было послано в порт
switch(val)
{
case 48: analogWrite(MosfetDrivePin, 0); set=0; showMainMenu = 1; break; //если приняли 0 остановить разряд, выбрать 0 режим и открыть менюшку
case 49: set=1; break; //если приняли 1 то запустить режим 1
case 50: set=2; break; //если приняли 2 то запустить режим 2
case 51: set=3; break; //если приняли 3 то запустить режим 3
case 52: set =4; break; ////если приняли 4 то запустить режим 3
case 53: set =5; break;
case 54: set =6; break;
case 55: set =7; break;
}
}
/////////////////////////////////////////////////////////////////////////////////////////START STOP
if (set ==1)
{
currentValueNow = calcCurrentNew (5) ;
voltageValueNow = calcVoltageNew (20);
if (voltageValueNow <= MinVoltThreshold)
{
Serial.print ("No discharge -- Voltage is lower than threshold ");
Serial.print (MinVoltThreshold);
Serial.print (" ");
Serial.println ("mV");
set = 0;
showMainMenu =1;
}
else
{
Serial.println ("Starting discharge.. ");
Serial.println (F("time;Current, mA;Voltage,mV;mAh;mwh;mW"));
int calc = 0;
mah = 0;
mwh =0;
dischStart = millis ();
if (dischMode ==1 )
{
while ( set ==1 && voltageValueNow > CutOffVoltage)
{
currentValueNow = calcCurrentNew (5) ;//5
voltageValueNow = calcVoltageNew (5);//3
if (millis() - lastTime > reportInterval ) ///show current readings every interval without stopping PID
{
if (csvRepStyle ==true)
{
mah+= currentValueNow * (millis()-lastTime)/3600000;
mwh+= (currentValueNow * (millis()-lastTime)/3600000)*voltageValueNow/1000.00;
Serial.print((millis() - dischStart) / 1000);
Serial.print (delim);
Serial.print (currentValueNow,0);
Serial.print (delim);
Serial.print (voltageValueNow,0);
Serial.print (delim);
Serial.print (mah);
Serial.print (delim);
Serial.println (mwh);
calc = 0;
lastTime = millis ();
}
else
{
Serial.print ("Сurrent:");
Serial.print (currentValueNow,0);
Serial.println (" mA");
Serial.print ("Voltage:");
Serial.print (voltageValueNow,0);
Serial.println (" mV");
mah+= currentValueNow * (millis()-lastTime)/3600000;
mwh+= (currentValueNow * (millis()-lastTime)/3600000)*voltageValueNow/1000;
Serial.print ("mah:");
Serial.println (mah);
Serial.print ("mwh:");
Serial.println (mwh);
Serial.print ("Mosfet Power:");
Serial.println (power);
Serial.print ("Calcs/interval:");
Serial.println (calc);
calc = 0;
lastTime = millis ();
}
}
error = targetCurrent - currentValueNow; // current error
dError = error - prevErr;
powerCorrection = error * Kp + dError * Kd ;
if (powerCorrection > 1023) //добавлено как защита от переполнения
{
powerCorrection = 1023;//добавлено как защита от переполнения
}
if (powerCorrection < -1023)//добавлено как защита от переполнения
{
powerCorrection = -1023;//добавлено как защита от переполнения
}
power = prevPower + powerCorrection; // Корректируем текущее значение ШИМ, в зависимости от того, выше таргета ток, или ниже.
if (power > 1023)
{
power = 1023;
}
if (power < 0)
{
power = 0;
}
if (power ==255) // мы поправили таймер и он стал 10 битным, однако в ф-ции analogWrite прописано что еслии 255 то digitalWrite 1.
//с 10-битным таймером это неверно.
{
power = 254;
}
analogWrite(MosfetDrivePin, power);
prevPower = power; // Корректируем текущее значение ШИМ, в зависимости от того, выше таргета ток, или ниже.
prevErr = error;
if (Serial.available() > 0 ) //прерываемся и выходим по нажатию 0
{
int val2=Serial.read();
if (val2 ==48) {analogWrite(MosfetDrivePin, 0); set = 0; showMainMenu =1;}
Serial.println ("Discharge interrupted by user ");
}
calc++;
}
}
//end dischMode==1
else if (dischMode ==2 )
{
while ( set ==1 && voltageValueNow > CutOffVoltage)
{
currentValueNow = calcCurrentNew (5) ;//5
voltageValueNow = calcVoltageNew (5);//3
powerValueNow = currentValueNow * voltageValueNow/1000.00;
if (millis() - lastTime > reportInterval ) ///show current readings every interval without stopping PID
{
if (csvRepStyle ==true)
{
mah+= currentValueNow * (millis()-lastTime)/3600000;
mwh+= (currentValueNow * (millis()-lastTime)/3600000)*voltageValueNow/1000.00;
Serial.print((millis() - dischStart) / 1000);
Serial.print (delim);
Serial.print (currentValueNow,0);
Serial.print (delim);
Serial.print (voltageValueNow,0);
Serial.print (delim);
Serial.print (mah);
Serial.print (delim);
Serial.print (mwh);
Serial.print (delim);
Serial.println (powerValueNow);
calc = 0;
lastTime = millis ();
}
else
{
Serial.print ("Сurrent:");
Serial.print (currentValueNow,0);
Serial.println (" mA");
Serial.print ("Voltage:");
Serial.print (voltageValueNow,0);
Serial.println (" mV");
mah+= currentValueNow * (millis()-lastTime)/3600000;
mwh+= (currentValueNow * (millis()-lastTime)/3600000)*voltageValueNow/1000;
Serial.print ("mah:");
Serial.println (mah);
Serial.print ("mwh:");
Serial.println (mwh);
Serial.print ("Power,mW:");
Serial.println (powerValueNow);
Serial.print ("Mosfet Power:");
Serial.println (power);
Serial.print ("Calcs/interval:");
Serial.println (calc);
calc = 0;
lastTime = millis ();
}
}
targetCurrent = targetPower*1000.00/voltageValueNow;
error = targetCurrent - currentValueNow; // current error
dError = error - prevErr;
powerCorrection = error * Kp + dError * Kd ;
if (powerCorrection > 1023) //добавлено как защита от переполнения
{
powerCorrection = 1023;//добавлено как защита от переполнения
}
if (powerCorrection < -1023)//добавлено как защита от переполнения
{
powerCorrection = -1023;//добавлено как защита от переполнения
}
power = prevPower + powerCorrection; // Корректируем текущее значение ШИМ, в зависимости от того, выше таргета ток, или ниже.
if (power > 1023)
{
power = 1023;
}
if (power < 0)
{
power = 0;
}
if (power ==255) // мы поправили таймер и он стал 10 битным, однако в ф-ции analogWrite прописано что еслии 255 то digitalWrite 1.
//с 10-битным таймером это неверно.
{
power = 254;
}
analogWrite(MosfetDrivePin, power);
prevPower = power; // Корректируем текущее значение ШИМ, в зависимости от того, выше таргета ток, или ниже.
prevErr = error;
if (Serial.available() > 0 ) //прерываемся и выходим по нажатию 0
{
int val2=Serial.read();
if (val2 ==48) {analogWrite(MosfetDrivePin, 0); set = 0; showMainMenu =1;}
Serial.println ("Discharge interrupted by user ");
}
calc++;
}
}//end dischMode==2
else if (dischMode ==3 )
{
while ( set ==1 && voltageValueNow > CutOffVoltage)
{
currentValueNow = calcCurrentNew (5) ;//5
voltageValueNow = calcVoltageNew (5);//3
powerValueNow = currentValueNow * voltageValueNow/1000.00;
if (millis() - lastTime > reportInterval ) ///show current readings every interval without stopping PID
{
if (csvRepStyle ==true)
{
mah+= currentValueNow * (millis()-lastTime)/3600000;
mwh+= (currentValueNow * (millis()-lastTime)/3600000)*voltageValueNow/1000.00;
Serial.print((millis() - dischStart) / 1000);
Serial.print (delim);
Serial.print (currentValueNow,0);
Serial.print (delim);
Serial.print (voltageValueNow,0);
Serial.print (delim);
Serial.print (mah);
Serial.print (delim);
Serial.print (mwh);
Serial.print (delim);
Serial.println (powerValueNow);
calc = 0;
lastTime = millis ();
}
else
{
Serial.print ("Сurrent:");
Serial.print (currentValueNow,0);
Serial.println (" mA");
Serial.print ("Voltage:");
Serial.print (voltageValueNow,0);
Serial.println (" mV");
mah+= currentValueNow * (millis()-lastTime)/3600000;
mwh+= (currentValueNow * (millis()-lastTime)/3600000)*voltageValueNow/1000;
Serial.print ("mah:");
Serial.println (mah);
Serial.print ("mwh:");
Serial.println (mwh);
Serial.print ("Mosfet Power:");
Serial.println (power);
Serial.print ("Calcs/interval:");
Serial.println (calc);
calc = 0;
lastTime = millis ();
}
}
targetCurrent = voltageValueNow*1000.00/constRes;
error = targetCurrent - currentValueNow; // current error
dError = error - prevErr;
powerCorrection = error * Kp + dError * Kd ;
if (powerCorrection > 1023) //добавлено как защита от переполнения
{
powerCorrection = 1023;//добавлено как защита от переполнения
}
if (powerCorrection < -1023)//добавлено как защита от переполнения
{
powerCorrection = -1023;//добавлено как защита от переполнения
}
power = prevPower + powerCorrection; // Корректируем текущее значение ШИМ, в зависимости от того, выше таргета ток, или ниже.
if (power > 1023)
{
power = 1023;
}
if (power < 0)
{
power = 0;
}
if (power ==255) // мы поправили таймер и он стал 10 битным, однако в ф-ции analogWrite прописано что еслии 255 то digitalWrite 1.
//с 10-битным таймером это неверно.
{
power = 254;
}
analogWrite(MosfetDrivePin, power);
prevPower = power; // Корректируем текущее значение ШИМ, в зависимости от того, выше таргета ток, или ниже.
prevErr = error;
if (Serial.available() > 0 ) //прерываемся и выходим по нажатию 0
{
int val2=Serial.read();
if (val2 ==48) {analogWrite(MosfetDrivePin, 0); set = 0; showMainMenu =1;}
Serial.println ("Discharge interrupted by user ");
}
calc++;
}
}
analogWrite(MosfetDrivePin, 0);
delay (100);
Serial.println ("Discharge completed ");
set = 0;
}
}
//////////////////////////////////////////////////////ПУНКТ2//////////////////////////////////////////////
if (set ==2) //set discharge current in mA
{
if (set2Entered ==0)
{
switch (dischMode) {
case 1:
Serial.print ("Enter discharge current, mA ");
Serial.println ("Press 0 to exit to main menu");
break;
case 2:
Serial.print ("Enter discharge power, mW ");
Serial.println ("Press 0 to exit to main menu");
break;
case 3:
Serial.print ("Enter discharge resistance,mOhm ");
Serial.println ("Press 0 to exit to main menu");
break;
}
set2Entered =1;
}
if (Serial.available() > 0 )
{
valString = Serial.readStringUntil('\n'); //прочитать всё
int val;
Serial.println(valString);
valString.toCharArray(valChar,sizeof(valChar));
val = atof(valChar);
valString = "";
if (val ==0)
{
set2Entered =0;
set = 0;
showMainMenu =1;
}
else {
switch (dischMode) {
case 1:
targetCurrent = val;
Serial.print ("Target current is set to ");
Serial.print (targetCurrent);
Serial.println ("mA");
break;
case 2:
targetPower = val;
Serial.print ("Target power is set to ");
Serial.print (targetPower);
Serial.println ("mW");
break;
case 3:
constRes = val;
Serial.print ("Constant resistance is set to ");
Serial.print (constRes);
Serial.println ("mOmh");
break; }
}
set = 0;
set2Entered =0;
showMainMenu =1;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
if (set ==3) //set Cut off voltage in mV
{
if (set3Entered ==0)
{
Serial.println ("Enter cut-off voltage. Press 0 to exit to main menu");
set3Entered =1;
}
if (Serial.available() > 0 )
{
valString = Serial.readStringUntil('\n'); //прочитать всё
int val;
Serial.println(valString);
valString.toCharArray(valChar,sizeof(valChar));
val = atof(valChar);
valString = "";
if (val ==0)
{
set = 0;
set3Entered =0;
showMainMenu =1;
}
else {CutOffVoltage = val;}
Serial.print ("Cut-off voltage is set to ");
Serial.print (CutOffVoltage);
Serial.println ("mV");
set = 0;
set3Entered =0;
showMainMenu =1;
}
}
if (set==4)
{
while (set==4)
{
currentValueNow = calcCurrentNew(3) ;
voltageValueNow = calcVoltageNew(3);
Serial.print ("Voltage:");
Serial.print (voltageValueNow);
Serial.println (" mV");
Serial.print ("Raw value from DAC ");
Serial.println (calcVoltageNewDAC (3));
delay (500);
if (Serial.available() > 0 ) //прерываемся и выходим по нажатию 0
{
int val2=Serial.read();
if (val2 ==48) {analogWrite(MosfetDrivePin, 0);set = 0; showMainMenu =1;}
}
}
}
if (set ==5)
{
while (set==5)
{
analogWrite(MosfetDrivePin, 750);//90ma at 750 @4V
currentValueNow = calcCurrentNew(5) ;
voltageValueNow = calcVoltageNew(5);
Serial.print ("Сurrent:");
Serial.print (currentValueNow);
Serial.println (" mA");
Serial.print ("Voltage:");
Serial.print (voltageValueNow);
Serial.println (" mV");
delay (500);
if (Serial.available() > 0 ) //прерываемся и выходим по нажатию 0
{
int val2=Serial.read();
if (val2 ==48) {analogWrite(MosfetDrivePin, 0); delay (100);set = 0; showMainMenu =1;}
}
}
}
if (set ==6)
{
if (set6Entered ==0)
{
Serial.println ("Set Report Style. Press 2 for csv style, 1 for normal style.");
Serial.println ("Press 0 to exit to main menu");
set6Entered =1;
}
if (Serial.available() > 0 )
{
valString = Serial.readStringUntil('\n'); //прочитать всё
int val;
Serial.println(valString);
valString.toCharArray(valChar,sizeof(valChar));
val = atof(valChar);
valString = "";
if (val ==0)
{
set = 0;
set6Entered =0;
showMainMenu =1;
}
if (val ==1)
{
csvRepStyle = false;
Serial.println ("Report style was set to normal");
set = 0;
set6Entered =0;
showMainMenu =1;
}
else if (val ==2)
{
csvRepStyle = true;
Serial.println ("Report style was set to CSV");
set = 0;
set6Entered =0;
showMainMenu =1;
}
else
{
Serial.println ("Wrong choice.");
set6Entered =0;
}
}
}
if (set ==7) //set discharge current in mA
{
if (set7Entered ==0)
{
Serial.println ("Enter discharge mode. 1 for CC ,2 for constant Power, 3 for constant Resistance");
Serial.println ("Press 0 to exit to main menu");
set7Entered =1;
}
if (Serial.available() > 0 )
{
valString = Serial.readStringUntil('\n'); //прочитать всё
int val;
Serial.println(valString);
valString.toCharArray(valChar,sizeof(valChar));
val = atof(valChar);
valString = "";
switch (val){
case 0:
set7Entered =0;
set = 0;
showMainMenu =1;
break;
case 1:
dischMode = val;
Serial.print ("Discharge mode is set to ");
Serial.println (dischMode);
set = 0;
set7Entered =0;
showMainMenu =1;
break;
case 2:
dischMode = val;
Serial.print ("Discharge mode is set to ");
Serial.println (dischMode);
set = 0;
set7Entered =0;
showMainMenu =1;
break;
case 3:
dischMode = val;
Serial.print ("Discharge mode is set to ");
Serial.println (dischMode);
set = 0;
set7Entered =0;
showMainMenu =1;
break;
default:
Serial.println ("Wrong choice.");
set7Entered =0;
}
}
}
}
int calcVoltageNew (int iternum)
{
int Max =0;
int Min = 20000;
int volt_m[iternum-1];
float volt = 0;
for (int i = 0;i<iternum;i++)
{
volt_m[i]= analogRead(VoltageSensePin);
if (volt_m[i] >Max) {Max = volt_m[i]; }
if (volt_m[i] <Min) {Min = volt_m[i]; }
delay (1);
volt+= volt_m[i];
}
volt = volt - Min;
volt = volt - Max;
//1.Если напряжение ниже (250/1023)*vref, и делитель 1:20, включаем повышенную точность (делитель 1:5) со следующей итерации.
if (div5VState == false && volt<(250 * (iternum-2)))
{
volt = volt * vref *calibrationVolt;
volt = volt/1024;
volt = volt * voltageDividerRatio ;//mV
volt = volt/(iternum-2);
max5Volt(true);
return volt;
}
//2.Если напряжение выше (1022/1023)*vref, и делитель 1:5, включаем делитель 1:20 со следующей итерации.
if (div5VState == true && volt>=(1022 * (iternum-2)))
{
volt = volt * vref *calibrationVolt;
volt = volt/1024;
volt = volt * voltageDividerRatio ;//mV
volt = volt/(iternum-2);
max5Volt(false);
return volt;
}
volt = volt * vref *calibrationVolt;
volt = volt/1024;
volt = volt * voltageDividerRatio ;//mV
volt = volt/(iternum-2);
return volt;
}
int calcCurrentNew (int iternum)
{
word Max =0;
word Min = 20000;
int volt_m[iternum-1];
float curr = 0;
for (int i = 0;i<iternum;i++)
{
volt_m[i]= analogRead(CurrentSensePin);
if (volt_m[i] >Max) {Max = volt_m[i]; }
if (volt_m[i] <Min) {Min = volt_m[i]; }
delay (1);
curr+= volt_m[i];
}
curr = curr - Min;
curr = curr - Max;
curr = curr * vref * calibrationCurr;
curr = curr / 1024;
curr = curr/ sensorResOhm ;//mA
curr = curr/(iternum-2);
curr=curr+offsetCurr;//ацп ошибается на это значение вне зависимости от тока, корректируем смещение
return curr;
}
int calcVoltageNewDAC (int iternum)
{
int Max =0;
int Min = 20000;
int volt_m[iternum-1];
float volt = 0;
for (int i = 0;i<iternum;i++)
{
volt_m[i]= analogRead(VoltageSensePin);
if (volt_m[i] >Max) {Max = volt_m[i]; }
if (volt_m[i] <Min) {Min = volt_m[i]; }
delay (1);
volt+= volt_m[i];
}
volt = volt - Min;
volt = volt - Max;
volt = volt/(iternum-2);
return volt;
}
void max5Volt (bool state)
{
if (state == true)
{
pinMode (voltDividerPin,INPUT);
voltageDividerRatio = lowDivRatio;
div5VState = true;
}
if (state ==false )
{
pinMode (voltDividerPin,OUTPUT);
digitalWrite (voltDividerPin,0);
voltageDividerRatio = highDivRatio;
div5VState = false;
}
}
Выводы и результаты
схема простая, работает, но не без косяков.
Плюсы данного девайса:
• простота сборки, повторяемость: сможет собрать и настроить любой, у кого есть Ардуино, мультиметр и горстка доступных радиокомпонентов.
• Приемлемая точность измерения ёмкости (заряда) в диапазоне от 500 мА.
• Несколько режимов измерения.
За простоту приходится расплачиваться:
• Минимальный ток разряда 150мА
• Завышает ток на 10-20 мА на низких токах разряда (до 500 мА).
• сложность масштабирования (необходимо городить дополнительные PD контроллеры под каждый дополнительный мосфет)
• плохая точность расчёта запасённой энергии.
Надеюсь, данная заготовка пригодится тем, кто хотел потестировать свои аккумуляторы в лёгком режиме, но по каким-то причинам стеснялся. Точности в 2% для измерения ёмкости должно хватить.
В процессе макетирования родилось много идей и задумок, но я сегодня решил всё же не отходить от темы электронной нагрузки из минимума деталей.
Например, сюда можно приделать схему заряда, датчик температуры батарей и выводить её в график, или аварийно останавливать разряд по температуре.
Чтобы увеличить точность, всё же не обойтись без ОУ: необходимо усиливать сигнал с шунта с помощью ОУ и, соответственно, повышать опорное, например, брать его с той же TL431.
Конструктивные комментарии по проведённой работе приветствуются.
Надеюсь, было интересно. Спасибо за внимание.
Самые обсуждаемые обзоры
+98 |
3741
228
|
+118 |
3678
94
|
+41 |
1745
61
|
+44 |
2999
73
|
Наоборот же хорошо, всё в одном месте, никуда не потеряется. Это же не гигабайтное видео.
Нагрузка у вас получилась отличная. Уверен, что будут желающие повторить её и через месяц и через год-два. Вот только на github'е исходники сохранятся, а на других ресурсах есть огромный шанс того, что они потеряются.
Было бы очень классно, если бы вы выложили исходники на github вместе со схемами и картинками.
И это я ещё молчу про потенциальные PR'ы.
Вмемориз.
ЗЫ: Прошу заметить, что вариант держателя «Изначально использовал» принципиально не подходит для защищенных АБ.
Про автозамену кавычек на сайте не знал.
Положил под тег «код», проверил, компилируется.
Спасибо, что заметили!
А вообще проводил измерения емкости 4-х аккумуляторов в 4-х приборах. Отклонение в емкости от 0,07 до 3,07% Есть готовая таблица, но здесь ее не показать.
2. вам ничего не мешает измерять напряжение не только на верхнем конце R2, но и на его нижнем конце. Частично выправится малотоковый диапазон.
3. в PD верните назад i.
4. Как и «Измерения напряжения и тока пятикратное» — совсем не достижение. Их надо измерять постоянно. Знаете, зачем делается oversampling ADC?
2. Попробую.
3. Пробовал, пока с i только хуже, очевидно, коэффициент не подобрал. На серьёзный, правильный рассчет pid коэффициентов пока морально не готов.
4. Странная формулировка про достижение. Я описываю, как работает. Оверсэмплинг увеличивает точность-разрядность измерений, но, насколько я понимаю, надо измерять сотни раз, чтобы получить бОльшую точность на моей скорости измерения. Я пока буду сэмплить, фет ток уведёт куда-нибудь.
Мы же не температуру слона измеряем. Что Вы имеете в виду под «постоянно»? Я и так их постоянно измеряю, сто раз в секунду, и фетом рулю столько же.
А можно ли использовать для улучшения детализации не встроенный ацп ардуино, а схемы типа INA219 | INA 226 — и как с ними тогда поменяется схема?
Дальше снимаете с дачика данные тока и напряжения и на их основе вычисляете сигнал для транзистора.
Автор пошёл своим путём, скорее всего для максимального упрощения, но на мой взгляд это слишком уж упрощено.
Можно было использовать схему от gandf, но со своим ПО и возможно добавить режим cv
Кроме того, с шунта напряжение измеряется вторым ОУ, с изменяемым коэфициентом усиления, вот собственно вся основная схема.
При этом
1. Нет зависимости тока и режима работы от примененного транзистора
2. Больше температурная стабильность
3. Аппаратная ОС
4. Коррекция тока по измеренному значению.
Т.е. по сути к схеме из этой статьи добавлено 2 ОУ и немного мелочевки.
Если нагрузку из этого поста я повторить ещё осилю, то нагрузка от gandf для меня уже за пределами возможностей. Тогда уж лучше купить готовый Atorch DL24.
Полностью согласен, это хороший вариант. Мне же как раз больше понравилась работа М8 от Gandf, хотя она гораздо проще функциональна, но здесь больше влияет специфика применения.
Первую собрал самостоятельно вообще неподготовленный человек.
Ацп у ардуины не точное, конечно сравнение с референсом в ардуине хорошо, но он к сожалению тоже сильно гуляет.
Спишем на придирку.
Но это, это бред к сожалению.
Встроенный кварц у ардуины вообще 0, внешний не сильно лучше, время там скачет сильнее курса рубля, и секунда может быть вполне посчитана как 0.8. А еще есть варианты упереться в точность вычисления на ардуине тк она тоже далеко не идеальна, и показатели этого тестера можно принимать как очень примерные.
Те то что там 2000mAh а не 1000 понятно, но если в комнате меняется температура а питание не сильно стабильное, то +-300мАч на такой схеме можно получать без проблем.
Если дорабатывать именно эту плату то при измерении аккумов можно вообще убрать резистивный делитель, те ацп у нас 5в ( для 5в атмеги, и мы так и так не выйдем за рамки).
Если же хотим чуть больше, то лучше нормально посчитать, ибо в версии автора там диапазон примерно до 24-25в и измеряя 12в мы теряем в точности которой у нас и так не много (ага, 0.02в* на огромный шум*на прогулку Vref На луну).
Но реально есть смысл брать сразу INA3221 ибо кроме повышенной точности там есть кучка fancy pancy фильтров, защит от уплывания ацп итд.
Но больше косяков конечно вносит подсчет времени который крив. В идеале скинуть работу на таймер и вызывать код подсчета именно по этим дискретным чисам, модулей для дуины много, убегают они 0.5-1мин в месяц, ну или 2.5сек в день, или 0.1сек в час, те раз в 50 меньше чем ардуина может в стоке.
И получить ограничение при измерении напряжения до 26 вольт, реально этого мало, разве что только для небольших сборок. Именно из-за подобного ограничения лично мне не нравятся нагрузки ZKE.
Хотя сама идея использования отдельного АЦП однозначно хорошая.
Это касается именно ардуины? Я всегда думал что обычно там пара кондеров+кварц и накосячить очень сложно. Да и тесты обычно идут не более 5 часов, нормального кварца хватает с головой.
Обычно достаточно сделать переключение диапазонов, например до 5 вольт и более, так как высокая точность нужна обычно именно при низких напряжениях.
И в итоге будет еще хуже, сегодня надо проверить одну ячейку, а завтра попался аккумулятор 8.4 вольта и все?
У автора проблема не в кварцах и АЦП, а в примитивном управлении транзистором и большой зависимости от внешних факторов. Там такой разбег будет, что уход кварца это последнее о чем бы я думал.
Это как?
По-вашему, вычисления в unsigned long в Arduino имеют бОльшую погрешность, чем вычисления в unsigned long на мощном десктопе?
Это же понятно, что данная нагрузка скорее эксперимент с ардуиной и пригодится разово потестировать аккумуляторы «для фонарика» простому обывателю. Простая конструкция, быть может, повторив которую, кому-то не придётся покупать электронную нагрузку, которая потом будет пылиться в шкафу.
все уже сделано
www.radiokot.ru/forum/viewtopic.php?f=11&t=138699
Плюнул. Будет надо — сделаю свое. С блютузом и лабвью. По крайней мере, ошибки в ней будут «мои».
Оценить качество схемотехническое части не могу, но за стиль оформления поста (нет даже схемы с картинкой, чтобы с первого взгляда оценить «а смогу ли я это хотябы попробовать сделать?») и качество ответов автора того поста твёрдая двойка.
Особенно порадовало, что в одном месте посылает «а вот пусть читают те дофига страниц по ссылке», а сразу же рядом «а это мне было лень на схеме прорисовывать» и «а тут я слепил из того, что попалось под руку и такие цифры на схеме прописал, можете ставить что угодно».
Т.е. схема явно не уровня «взял и повторил», а «вот вам, профессионалам, заготовка, там местами косяки, но вы же профессионалы, сможете в процессе реализации их сами исправить, можете даже что-то улучшить».
Так что полностью поддерживаю.
Я бы еще подключил ее между стоком полевика и аккумом, а то этот шунт является ООС и призакрывает полевик, уменьшая мах ток.
Спасибо за быстрый ответ, имел ввиду готовый модуль, вечером попробую сеобрать, а то у меня как раз нагрузка навернулась.А тут такой вариант как раз для такого чайника как я.Да ещё попробую к ней экран прикрутить 16 2, шоб без компа.
Программная ООС по току всё-же имеет право на жизнь, несмотря на множество недостатков.
(Делал, даже на stm32 выходит погано, но там это было вынужденно. Впрочем, и там пришлось таки поставить nсp1450, в ущерб пмехам.)
Почему бы при разрядке не перегонять энергию в другие аккумуляторы?
Я понимаю что 0,01кВт*ч с одного аккума это мизер, но при массовых тестах мы имеем более солидные цифры и обильное тепловыделение
Мало того, есть и обычные электронные нагрузки, в смысле не только для тестов аккумуляторов, которые имеют функцию рекуперации в сеть.
Но в первом случае можно случайно упереться в то, что аккумулятор который работает как нагрузка, уже зарядился, а тест надо продолжать, да и зачем нужен дополнительный износ аккумуляторов.
Во втором случае цена, такие устройства дороже.
Кроме того это усложняет конструкцию.
А куда потом девать заряженные аккумуяторы при массовых тестах, разряжать другой нагрузкой? :)
Но это фигня, бредит проект для моторчика под параплан со смешной мощностью и не менее смешной нагрузкой на батарейки. Плюс «нагрузка» (механическая) на этот моторчик. Поэтому прорабатывается вариант с удвоенным количеством моторов (наша разработка — мотор) по концепту мотор-генератор. Но и это мелочи. Моторчик только демашка для натурных испытаний. Одна пятая нормальной версии. Впрочем, вряд-ли будет сделано.
Так что, возврат энергии на высокую часть — вполне нормальное решение. Да и не особо сложное.
Все относительно…
Зимой вообще тепло не пропадает, обогревает помещение.
Сравнивать, действительно, не совсем верно, но нужно было хоть примерно прикинуть.
Теперь представьте, что у каждого провода есть сопротивление.
Вы прицепили делитель напряжения таким образом, что включили сопротивление провода от плюсового вывода аккумулятора до прехохрвнителя., похоронив тем самым преимещуства трёхпроводной схемы измерения. Также вы вносите некую неопределённость, куда подключать землю ардуино. На моей схеме явно указано что куда, а по Вашей версии схемы есть шанс включить ардуино таким образом, что часть сопротивление провода от шунта до ардуино будет являться шуном.
Минимальный ток тоже можно раз в 10 снизить, если сделать управляемый полевым транзистором шунт. По аналогии с R5 только для резистора токового шунта. Один низкоомный через полевик подключается, высокоомный остается включенным всегда. Но это усложнение небольшое.
В тексте Вы писали:
Какие размышления на этот счет, принципиальная схема?
Понять сразу алгоритм работы очень сложно.
" скомпилировал код в IDE версии, 1.6.9 компилит нормально. Свежие версии собирают не правильно. Что-то в компиляторе изменили…"