Resting and actual battery voltage

Post Reply
Batperson
Posts: 18
Joined: Mon May 11, 2015 2:52 pm

Resting and actual battery voltage

Post by Batperson »

Hi,

I am finding that my battery voltage sags around 1 volt at half throttle, although performance is OK and I get runtimes of at least 10 minutes. As a result, the battery alarm is not very useful for me. It seems to me that if the internal resistance of the battery pack is known and I am measuring current as well as voltage, then it should be possible to calculate some sort of "sag factor" to apply to the measured voltage, based on the current flow. You could use this to derive the resting voltage of the battery, which would be more useful for voltage alarms than the actual voltage under load. The MSP_ANALOG message could be extended to provide the at-rest and actual voltage for OSDs and ground stations. Can anyone let me know if my assumptions are reasonable? If so I may try to implement this.

Batperson
Posts: 18
Joined: Mon May 11, 2015 2:52 pm

Re: Resting and actual battery voltage

Post by Batperson »

Well, I have implemented some code to compare the voltage at idle with the voltage after throttling up, and divide the difference by the number of amps. I am still testing it, but so far it i able to derive the resting battery voltage accurately to within around .2 of a volt. I will try to improve on that and post the code here when finished.
I am thinking that if this works, then it would be useful to extend the MSP_ANALOG message with this value which I'm calling "vbatnominal". I intend to mod my MW OSD to use this value to display the battery status icon which would then hopefully give a much better indication of the battery charge status. I would still want to see the actual battery voltage displayed in the OSD however.
Also, the "sag factor" should theoretically reflect the battery's internal resistance. I don't expect this would be terribly accurate, but if it is consistent then it would be useful to display this after each flight. Over the battery's lifetime this value should increase and give an indication of how much life is left in the battery.

User avatar
ezio
Posts: 827
Joined: Sun Apr 01, 2012 11:03 pm
Location: Paris
Contact:

Post by ezio »

What for do you need resting voltage? The correct way to measure battery voltage is under load.

User avatar
Hamburger
Posts: 2578
Joined: Tue Mar 01, 2011 2:14 pm
Location: air
Contact:

Re: Resting and actual battery voltage

Post by Hamburger »

The following would be true for original MWii ( for 32bit derivatives I do not know):
MWii does know current voltage and allows to define 3 alarm levels already. (also the nominal voltage aka resting voltage when charged can be defined)
I do not understand what it is you had to implement

Batperson
Posts: 18
Joined: Mon May 11, 2015 2:52 pm

Re: Resting and actual battery voltage

Post by Batperson »

Hi,

Currently, under normal load if my VBAT reads 10.7 during flying then it will be 11.1 when at rest. I understand this is normal, but I would like to know the "at rest" voltage while flying as well as the actual voltage, and have the option of using it for alarms, etc.

Hamburger, when you say:
also the nominal voltage aka resting voltage when charged can be defined

How can I define this and what effect will it have?

User avatar
Hamburger
Posts: 2578
Joined: Tue Mar 01, 2011 2:14 pm
Location: air
Contact:

Re: Resting and actual battery voltage

Post by Hamburger »

about nominal voltage
can be set in config.h.
is used as reference point for bar graphs with lcd.telemetry.
is used for voltage drop compensation under load (if enabled in config.h)

Batperson
Posts: 18
Joined: Mon May 11, 2015 2:52 pm

Re: Resting and actual battery voltage

Post by Batperson »

Hi Hamburger,

In LCD telemetry, VBATNOMINAL is only used to show the battery charge as a proportion of a full charge so it will still fluctuate as the motor load varies.
I see it is also used in Output.cpp if VOLTAGEDROP_COMPENSATION is defined. I couldn't find any documentation or discussion about this define, but it appears to increase the motor output to compensate for voltage sag, correct me if I'm wrong. In that case it's different to what I'm trying to implement.

FWIW, here is the code I have written. I'm using the word "nominal" in the sense of what the voltmeter would show under no load which is different from VBATNOMINAL in the multiwii code, possibly there is a better term I should have used. Anyway, here it is.

Multiwii.cpp, line 554:

Code: Select all

  #if defined(COMPENSATE_VOLTAGE_SAG)  
    switch(voltsag.state) {
      case VS_STATE_NONE: // Just switched on. Seems to take about 8 seconds before ADC works.
        analog.vbatnominal = analog.vbat;
        if(currentTime >= 7000000)
          voltsag.state = VS_STATE_INITIALIZED;
          break;
      case VS_STATE_INITIALIZED: // We should have stable nominal values now.
        analog.vbatnominal = analog.vbat;
        voltsag.maxAmps = analog.amperage;
        voltsag.maxVbat = analog.vbat;
        voltsag.minVbat = analog.vbat;
        voltsag.state = VS_STATE_AWAITARM;
        break;
      case VS_STATE_AWAITARM: // Wait for arm
        analog.vbatnominal = analog.vbat;
        if(f.ARMED == 1) {
          voltsag.state = VS_STATE_SAMPLING; // Armed, begin sampling now. Better take off within the sample time.
#if defined(COMPENSATE_VOLTAGE_SAG_SAMPLE_SECONDS)         
          voltsag.sampleCompleteTime = currentTime + (COMPENSATE_VOLTAGE_SAG_SAMPLE_SECONDS * 1000000);
#endif         
        }
        break;
      case VS_STATE_SAMPLING: // Armed, throttling up
        analog.vbatnominal = analog.vbat;
        if(analog.amperage > voltsag.maxAmps) {
          voltsag.maxAmps = analog.amperage;
          voltsag.minVbat = analog.vbat;
        }
#if not defined(COMPENSATE_VOLTAGE_SAG_SAMPLE_AMPS) and not defined(COMPENSATE_VOLTAGE_SAG_SAMPLE_SECONDS)       
#error You must define either COMPENSATE_VOLTAGE_SAG_SAMPLE_AMPS, COMPENSATE_VOLTAGE_SAG_SAMPLE_SECONDS or both if COMPENSATE_VOLTAGE_SAG is defined.
#endif
       
#if defined(COMPENSATE_VOLTAGE_SAG_SAMPLE_AMPS)       
        // If we reach more than the defined amps over nominal, assume we have a good enough sample.
        // The less time needed to reach this point the more accuracy we will have.
        // COMPENSATE_VOLTAGE_SAG_SAMPLE_AMPS should be roughly equal to current flow at takeoff.
        if(voltsag.maxAmps > (COMPENSATE_VOLTAGE_SAG_SAMPLE_AMPS * 10))
          voltsag.state = VS_STATE_CALC;
#endif

#if defined(COMPENSATE_VOLTAGE_SAG_SAMPLE_SECONDS)
        // Otherwise, continue up to the sample time. Hopefully we have observed enough voltage sag
        // for an accurate calculation.
        if(currentTime >= voltsag.sampleCompleteTime)
          voltsag.state = VS_STATE_CALC;
#endif         
        break;
      case VS_STATE_CALC: // Finished sampling, calculate sag factor
        {
          uint16_t vbatDif = voltsag.maxVbat - voltsag.minVbat;
          voltsag.resistance = uint8_t((vbatDif * 1000) / voltsag.maxAmps);
          voltsag.state = VS_STATE_FINISHED;
        }
        break;
      case VS_STATE_FINISHED: // Apply sag factor to measured voltage
        analog.vbatnominal = analog.vbat + ((analog.amperage * voltsag.resistance) / 1000);
        break;
    }

    //debug[0] = analog.vbat;
    //debug[1] = analog.amperage;
    //debug[2] = voltsag.resistance;
    //debug[3] = analog.vbatnominal;
   
  #endif


Protocol.cpp, line 662:

Code: Select all

    case MSP_ANALOG:
      #if defined(COMPENSATE_VOLTAGE_SAG) and defined(COMPENSATE_VOLTAGE_SAG_EXTEND_MSP)
        s_struct((uint8_t*)&analog,8);
      #else
        s_struct((uint8_t*)&analog,7);
      #endif
      break;


Alarms.cpp, line 103:

Code: Select all

  #if defined(VBAT)
    #if defined(COMPENSATE_VOLTAGE_SAG) and defined(COMPENSATE_VOLTAGE_SAG_FOR_ALARMS)
      if (analog.vbatnominal < conf.vbatlevel_crit) alarmArray[ALRM_FAC_VBAT] = ALRM_LVL_VBAT_CRIT;
      else if ( (analog.vbatnominal > conf.vbatlevel_warn1)  || (NO_VBAT > analog.vbatnominal)) alarmArray[ALRM_FAC_VBAT] = ALRM_LVL_OFF;
      else if (analog.vbatnominal > conf.vbatlevel_warn2) alarmArray[ALRM_FAC_VBAT] = ALRM_LVL_VBAT_INFO;
      else if (analog.vbatnominal > conf.vbatlevel_crit) alarmArray[ALRM_FAC_VBAT] = ALRM_LVL_VBAT_WARN;
    #else
      if (vbatMin < conf.vbatlevel_crit) alarmArray[ALRM_FAC_VBAT] = ALRM_LVL_VBAT_CRIT;
      else if ( (analog.vbat > conf.vbatlevel_warn1)  || (NO_VBAT > analog.vbat)) alarmArray[ALRM_FAC_VBAT] = ALRM_LVL_OFF;
      else if (analog.vbat > conf.vbatlevel_warn2) alarmArray[ALRM_FAC_VBAT] = ALRM_LVL_VBAT_INFO;
      else if (analog.vbat > conf.vbatlevel_crit) alarmArray[ALRM_FAC_VBAT] = ALRM_LVL_VBAT_WARN;
      //else alarmArray[6] = 4;
    #endif
  #endif


I also modified MWOSD to use the nominal value when displaying the battery icon:
Serial.ino, line 284

Code: Select all

#if defined(USE_VBAT_NOMINAL)
    if(dataSize >= 8)
      MwVBatNominal=read8();
#endif 


Screen.ino, line 454:

Code: Select all

  if (Settings[S_SHOWBATLEVELEVOLUTION]){
#ifdef USE_VBAT_NOMINAL
    int battev=uint16_t(MwVBatNominal)/Settings[S_BATCELLS];
#else
    int battev=voltage/Settings[S_BATCELLS];
#endif   


GlobalVariables.h, line 501:

Code: Select all

uint8_t MwVBatNominal=0;

User avatar
Hamburger
Posts: 2578
Joined: Tue Mar 01, 2011 2:14 pm
Location: air
Contact:

Re: Resting and actual battery voltage

Post by Hamburger »

I see.
In this case I concur with ezio - voltage under load is what kills batteries, so why worry about noload.equivalent?
Not convinced.

User avatar
ezio
Posts: 827
Joined: Sun Apr 01, 2012 11:03 pm
Location: Paris
Contact:

Post by ezio »

Measuring voltage under load is correct way to go. Resting voltage is useless. And still don't get it what you need it for.
And how you will measure resting voltage in flight ? You can't calculate it as you don't know internal resistance of the battery etc.

Just land, disarm and check the voltage ;)

User avatar
ezio
Posts: 827
Joined: Sun Apr 01, 2012 11:03 pm
Location: Paris
Contact:

Post by ezio »


Post Reply