В последнее время я начал замечать, что мой смартфон стал разряжаться быстрее. Поиски программного «пожирателя» энергии плодов не принесли, поэтому стал задумываться, не пришло ли время заменить АКБ. Но абсолютной уверенности в том, что причина в батарее не было. Поэтому прежде чем заказывать новый аккумулятор решил попробовать измерить реальную емкость старого. Для этого было решено собрать простой измеритель емкости АКБ, тем более что идея эта вынашивалась уже давно – уж очень много батареек и аккумуляторов окружает нас в повседневной жизни, и было бы неплохо иметь возможность время от времени тестировать их.



Сама идея, лежащая в основе работы устройства, крайне проста: есть заряженный аккумулятор и нагрузка в виде резистора, нужно лишь измерять ток, напряжение и время в ходе разряда АКБ, и по полученным данным рассчитать его емкость. В принципе, можно обойтись вольтметром и амперметром, но сидеть за приборами несколько часов удовольствие сомнительное, поэтому намного проще и точнее можно сделать это используя регистратор данных. Я в качестве такого регистратора использовал платформу Arduino Uno.

1. Схема

С измерением напряжения и времени в Arduino проблем нет – есть АЦП, но чтобы измерить ток нужен шунт. У меня появилась идея использовать сам нагрузочный резистор в качестве шунта. То есть, зная на нем напряжение и предварительно измерив сопротивление, мы всегда можем рассчитать ток. Поэтому простейший вариант схемы будет состоять лишь из нагрузки и АКБ, с подключением к аналоговому входу Arduino. Но было бы неплохо предусмотреть отключение нагрузки по достижению порогового напряжение на батарее (для Li-Ion это обычно 2,5-3В). Поэтому я предусмотрел в схеме реле, управляемое цифровым пином 7 через транзистор. Конечный вариант схемы на рисунке ниже.



Все элементы схемы я разместил на кусочке макетной платы, которая устанавливается прямо на Uno. В качестве нагрузки использовал спираль из нихромовой проволоки толщиной 0,5мм, имеющей сопротивление около 3 Ом. Это дает расчетное значение тока разряда 0,9-1,2А.



2. Измерение тока

Как было сказано выше ток рассчитывается исходя из напряжения на спирали и её сопротивления. Но стоит учесть, что спираль нагревается, а сопротивление нихрома довольно сильно зависит от температуры. Чтобы компенсировать ошибку я просто снял вольт-амперную характеристику спирали, используя лабораторный блок питания и давая ей прогреться перед каждым измерением. Далее вывел в Excel уравнение линии тренда (график ниже), которое дает довольно точную зависимость i(u) с учетом нагрева. Видно, что линия не прямая.



3. Измерение напряжения

Поскольку точность данного тестера напрямую зависит от точности измерения напряжения, я решил уделить этому особое внимание. В других статьях уже неоднократно упоминали метод, позволяющих наиболее точно измерять напряжение контроллерами Atmega. Повторю лишь вкратце – суть состоит в определении внутреннего опорного напряжения средствами самого контроллера. Я пользовался материалами данной статьи.

4. Программа

Код не представляет из себя ничего сложного:

Текст программы
#define A_PIN 1
#define NUM_READS 100
#define pinRelay 7

const float typVbg = 1.095; // 1.0 -- 1.2
float Voff = 2.5; // напряжение выключения
float I;
float cap = 0;
float V;
float Vcc;
float Wh = 0;
unsigned long prevMillis;
unsigned long testStart;

void setup() {
  Serial.begin(9600);
  pinMode(pinRelay, OUTPUT);
  Serial.println("Press any key to start the test...");
  while (Serial.available() == 0) {
  }
  Serial.println("Test is launched...");
  Serial.print("s");
  Serial.print(" ");
  Serial.print("V");
  Serial.print(" ");
  Serial.print("mA");
  Serial.print(" ");
  Serial.print("mAh");
  Serial.print(" ");
  Serial.print("Wh");
  Serial.print(" ");
  Serial.println("Vcc");
  digitalWrite(pinRelay, HIGH);
  testStart = millis();
  prevMillis = millis();
}

void loop() {
  Vcc = readVcc(); //считывание опорного напряжения
  V = (readAnalog(A_PIN) * Vcc) / 1023.000; //считывание напряжения АКБ
  if (V > 0.01) I = -13.1 * V * V + 344.3 * V + 23.2; //расчет тока по ВАХ спирали
  else I=0;
  cap += (I * (millis() - prevMillis) / 3600000); //расчет емкости АКБ в мАч
  Wh += I * V * (millis() - prevMillis) / 3600000000; //расчет емкости АКБ в ВтЧ
  prevMillis = millis();
  sendData(); // отправка данных в последовательный порт
  if (V < Voff) { //выключение нагрузки при достижении порогового напряжения
    digitalWrite(pinRelay, LOW);
    Serial.println("Test is done");
    while (2 > 1) {
    }
  }
}

void sendData() {
  Serial.print((millis() - testStart) / 1000);
  Serial.print(" ");
  Serial.print(V, 3);
  Serial.print(" ");
  Serial.print(I, 1);
  Serial.print(" ");
  Serial.print(cap, 0);
  Serial.print(" ");
  Serial.print(Wh, 2);
  Serial.print(" ");
  Serial.println(Vcc, 3);
}


float readAnalog(int pin) {
  // read multiple values and sort them to take the mode
  int sortedValues[NUM_READS];
  for (int i = 0; i < NUM_READS; i++) {
    delay(25);
    int value = analogRead(pin);
    int j;
    if (value < sortedValues[0] || i == 0) {
      j = 0; //insert at first position
    }
    else {
      for (j = 1; j < i; j++) {
        if (sortedValues[j - 1] <= value && sortedValues[j] >= value) {
          // j is insert position
          break;
        }
      }
    }
    for (int k = i; k > j; k--) {
      // move all values higher than current reading up one position
      sortedValues[k] = sortedValues[k - 1];
    }
    sortedValues[j] = value; //insert current reading
  }
  //return scaled mode of 10 values
  float returnval = 0;
  for (int i = NUM_READS / 2 - 5; i < (NUM_READS / 2 + 5); i++) {
    returnval += sortedValues[i];
  }
  return returnval / 10;
}


float readVcc() {
  // read multiple values and sort them to take the mode
  float sortedValues[NUM_READS];
  for (int i = 0; i < NUM_READS; i++) {
    float tmp = 0.0;
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
    ADCSRA |= _BV(ADSC); // Start conversion
    delay(25);
    while (bit_is_set(ADCSRA, ADSC)); // measuring
    uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
    uint8_t high = ADCH; // unlocks both
    tmp = (high << 8) | low;
    float value = (typVbg * 1023.0) / tmp;
    int j;
    if (value < sortedValues[0] || i == 0) {
      j = 0; //insert at first position
    }
    else {
      for (j = 1; j < i; j++) {
        if (sortedValues[j - 1] <= value && sortedValues[j] >= value) {
          // j is insert position
          break;
        }
      }
    }
    for (int k = i; k > j; k--) {
      // move all values higher than current reading up one position
      sortedValues[k] = sortedValues[k - 1];
    }
    sortedValues[j] = value; //insert current reading
  }
  //return scaled mode of 10 values
  float returnval = 0;
  for (int i = NUM_READS / 2 - 5; i < (NUM_READS / 2 + 5); i++) {
    returnval += sortedValues[i];
  }
  return returnval / 10;
}




Каждые 5 секунд данные о времени, напряжении батареи, токе разряда, текущей емкости в мАч и ВтЧ, а также напряжении питания передаются в последовательный порт. Ток рассчитывается по полученной в п. 2 функции. По достижении порогового напряжения Voff тест прекращается.
Единственным, на мой взгляд, интересным моментом в коде я бы выделил использование цифрового фильтра. Дело в том, что при считывании напряжения значения неизбежно «пляшут» вверх-вниз. Сначала я пытался уменьшить этот эффект просто сделав 100 измерений за 5 секунд и взяв среднее. Но результат по-прежнему меня не удовлетворил. В ходе поисков я наткнулся на такой программный фильтр. Работает он похожим образом, но вместо усреднения он сортирует все 100 значений измерений по возрастанию, выбирает центральные 10 и высчитывает среднее из них. Результат меня впечатлил – флуктуации измерений полностью прекратились. Я решил использовать его и для измерения внутреннего опорного напряжения (функция readVcc в коде).

5. Результаты

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



Далее легко построить график разряда АКБ:



В случае с моим Nexus 5 заявленная ёмкость аккумулятора BL-T9 – 2300 мАч. Измеренная мной – 2040 мАч при разряде до 2,5 В. В реальности контроллер вряд ли позволяет сесть батарее до такого низкого напряжения, скорее всего пороговое значение 3В. Ёмкость в этом случае 1960 мАч. Полтора года службы телефона привели к просадке емкости примерно на 15%. С покупкой новой АКБ было решено повременить.
С помощью данного тестера было разряжено уже несколько других Li-Ion аккумуляторов. Результаты выглядят очень реалистично. Измеренная емкость новых АКБ совпадает с заявленной с отклонением менее 2%.
Данный тестер подойдет и для металл-гидридных пальчиковых аккумуляторов. Ток разряда в этом случае составит около 400 мА.

Комментарии (28)


  1. kuld
    15.01.2016 12:52

    А зачем реле? Оно управляется через транзисторный ключ, берем транзистор помощнее и вкл/выкл нагрузку им.


    1. Rumlin
      15.01.2016 12:57

      и лучше не использовать биполярный транзистор.


    1. unnk2004
      15.01.2016 13:16
      +1

      Первая и основная причина — не было достаточно мощного транзистора, но было реле. Вторая — придется учитывать потери в тр-ре, что скажется на точности (хотя, может и не существенно).


      1. ploop
        15.01.2016 13:20
        +1

        Сопротивление некоторых полевиков меньше сопротивления контактов реле, хотя заморачиваться не стоит, можно взять любой.


        1. unnk2004
          15.01.2016 13:44

          На тот момент у меня были только полевики на 600В из компьютерного БП. А сопротивление там ой-ой. Да и пороговое напряжение открытия высокое. Это уже потом я выпаял подходящие из материнской платы. Не вижу принципиальной разницы между реле и полевиком в данном применении.


  1. ToSHiC
    15.01.2016 13:01
    +3

    У многих типов аккумуляторов есть особенность: чем больше разрядный ток, тем меньше ёмкость. Т.к. телефон практически никогда не выедает 1 ампер из батареи, её ёмкость при использовании в телефоне больше измеренной вами. Попробуйте увеличить сопротивление в 2 раза и измерить ёмкость ещё раз.


    1. ploop
      15.01.2016 13:06

      Это обусловлено внутренним сопротивлением. Которое, кстати, тоже просто измерить, измеряя напряжение под нагрузкой и без.


      1. ToSHiC
        15.01.2016 13:09

        Подозреваю, что ещё и химия замешана.


    1. unnk2004
      15.01.2016 13:14

      Вы правы. Но для Li-Ion аккумуляторов этот эффект выражен слабо. Здесь можно увидеть графики разряда АКБ Panasonic различными токами (нижний правый график). Результирующая емкость меняется не сильно. Я использовал меньший ток для разряда — результат одинаковый, но ждать дольше.


      1. Rumlin
        15.01.2016 13:19
        +2

        График для новых. Сильно б/у аккумуляторы имеют заметное внутреннее сопротивление.


        1. Firz
          15.01.2016 15:09
          +1

          Более того, для каждого типа химии, серии и производителя свои характеристики. Которые они и указывают из-за относительно большой разницы друг между другом.


    1. Firz
      15.01.2016 15:01

      Добавлю. Более того, принято измерять емкость аккумуляторов при разряде токами 0.2С (где это возможно, конечно). В случае же замеров, у автора токи были ~0.5С.
      p.s. Собственно, японцы и свинцовые автомобильные аккумуляторы так же измеряют, по-этому то, что у нас замеряется при токах 0.05С и маркируется как 70Ah, у японцев замеряется при токах 0.2С и маркируется 50Ah.


      1. unnk2004
        15.01.2016 15:08

        Постараюсь еще раз повторить измерение током 0.2С, стало интересно на сколько велика будет разница.


        1. Firz
          15.01.2016 15:27
          +2

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


  1. ploop
    15.01.2016 13:08

    Но стоит учесть, что спираль нагревается, а сопротивление нихрома довольно сильно зависит от температуры

    Купите кантал (фехраль), найти можно в магазинах, продающих электронные сигареты и расходники к ним. ТКС этого сплава очень мал, не повлияет на измерения.


    1. REPISOT
      15.01.2016 14:23
      +1

      А еще лучше шунт и усилитель тока шунта типа INA194 или сразу готовый модуль из Китая.


  1. Alexeyslav
    15.01.2016 14:25
    +1

    А я для этой цели использовал мультиметр с возможностью записи измерений. Отключения, правда, не было но у него есть режим задания границ при выходе измеряемой величины за пределы которых прибор начинает пищать.
    Сейчас посмотрел сколько стоит этот прибор и ужаснулся. Ардуинка выйдет дешевле, даже пожалуй сотня ардуинок.
    Проблема еще кстати в том что даже сопротивление соединяющих проводов влияет на результат — напряжение на низкоомном резисторе и на клеммах аккумулятора может ощутимо отличаться. Как дальнейшее развитие можно заменить все-таки реле на полевик и для каждой точки делать два измерения — под током и ЭДС батареи, отключая кратковременно разрядный резистор.

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


    1. unnk2004
      15.01.2016 14:40

      Спасибо за совет. Да, при использовании полевика вкупе с небольшим LC-фильром можно с помощью ШИМ той же ардуинки изменять ток разряда. Я уже в матлабе реализовал рабочую модель. Теперь дело за железом.


    1. unnk2004
      15.01.2016 16:04

      По вашей наводке нашел вот такой метод фильтрации:

      output[i] = alpha*input[i] + (1-alpha)*output[i-1]

      0<=alpha<=1

      Кажется, это не совсем метод скользящего окна, но только что попробовал — работает тоже отлично. А главное реализуется в одну строку. Так что спасибо еще раз за совет.


      1. Alexeyslav
        15.01.2016 17:33
        +1

        Это простое усреднение, классика при alpha = 0.5
        Скользящее окно — это усреднение N последних результатов измерений на каждом шаге измерения. Очень удобно когда N кратно степени двойки, достаточно сумму сдвинуть на соответствующее количество бит.


  1. ssg1712
    15.01.2016 14:34
    +1

    В качестве нагрузки с постоянным током можно использовать вот такую штуку:


    1. unnk2004
      15.01.2016 14:46

      Скажу честно, целиком видео не посмотрел, но успел понять что речь идет о поддержании тока на постоянном уровне с высокой точностью? В данном случае, как мне кажется, важнее точно измерять ток, нежели поддерживать. Сейчас отлаживаю такую схему в матлабе:

      Можно задавать ток изменяя ширину импульсов. Ну, а для измерения придется шунт все же заказать.


      1. ploop
        15.01.2016 15:19

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


      1. kumbr_87
        16.01.2016 00:15
        +2

        Нет, unnk2004, вы не правы, напряжение литиевого аккумулятора в процессе разряда падает процентов на 20, соответственно и ток будет по мере разряда в вашей реализации падать на 20%, на практике же устройства обычно оснащаются стабилизатором напряжения, который делает из диапазона 4.2-3.5В например постоянное напряжение 3.3В от которого уже питается устройство. Если это устройство идеологически правильное и оснащено импульсным преобразователем напряжения с высоким КПД то по мере разряда аккумулятора (понижении напряжения), ток потребляемый преобразователем напряжения будет возрастать дабы обеспечить на выходе постоянную мощность.

        Более того, помимо указанных в посте ploop трех способов есть еще один метод, когда тестируют емкость непосредственно для определенного типа устройств и емкость выражается не в привычных А*ч, а например во времени прослушивания музыки, интернет серфинга, просмотра видео, или например количество кадров на одном заряде. По поводу первых примеров ничего не скажу, но для определения количества кадров на одном заряде есть стандарты CIPA. В стандарте описывается что то вроде — включили фотоаппарат на 30 секунд с настройками по умолчанию, сделали кадр, выключили, потом повторили тоже самое со вспышкой максимальной мощности, данные указаны для примера, но суть думаю понятна.

        P.S. не разряжайте литий до 2.5В, это сильно убивает ресурс батареи.


        1. unnk2004
          16.01.2016 12:17

          Спасибо за комментарий.

          На практике интересующее меня устройство — смартфон. Когда он просто лежит, потребление тока мизерное. Когда я играю в тяжелую игрушку на максимальной яркости, потребление тока максимальное. Поэтому говорить о равномерно возрастающем токе по мере разрада АКБ здесь не приходится.

          Понятно, что емкость можно выразить в чем угодно: мАч, Вч, минутах, кадрах, мБ и т.д. Я не ставил перед собой задачи разработки оптимального метода нормирования емкости аккумуляторов, а хотел лишь проверить соответствие остаточной емкости АКБ значению, указанному производителем. А производитель, как мы знаем, измеряет её с помощью разряда постоянным током. Действительно, в моем случае ток не был постоянным, но измерения показали, что изменение его на 20-30% не приводят к существенной ошибке. Это видно также на кривых в спецификации на АКБ.

          P.S. По поводу глубокого разряда в курсе, но это однократное измерение.


    1. HWman
      17.01.2016 14:19

      Люблю видосы Давида Джонса, может потому что он есть такой какой есть, не старается быть как-то лучше на камеру.


  1. Speccyfan
    15.01.2016 16:39
    +3

    Serial.print(«s»);
    Serial.print(" ");
    Serial.print(«V»);
    Serial.print(" ");
    Serial.print(«mA»);
    Serial.print(" ");
    Serial.print(«mAh»);
    Serial.print(" ");
    Serial.print(«Wh»);
    Serial.print(" ");
    Serial.println(«Vcc»);

    Я бы так написал:
    Serial.println(F(«s V mA mAh Wh Vcc»));


    1. unnk2004
      15.01.2016 16:42

      Гениально!) спасибо.