Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better estimate of battery capacity when arming #1434

Open
lvale opened this issue Oct 3, 2014 · 39 comments
Open

Better estimate of battery capacity when arming #1434

lvale opened this issue Oct 3, 2014 · 39 comments

Comments

@lvale
Copy link
Member

lvale commented Oct 3, 2014

Considering that a large percentage of users use LiPo batteries and that this parameter

sys_status->battery_remaining = mavlink_msg_sys_status_get_battery_remaining(msg);

is reset to 100% when the vehicle is armed, and that the energy consumption when armed might be considered negligible, I would like to propose that a simple method to estimate battery capacity when the vehicle is armed, to be used.
Although not perfect but way better than simple setting the value to 100%, this method would get the voltage as it does now, and when armed estimates a number of cells

NrCells (returning the smallest integer larger than or equal to (Voltage divided by 4.2v->LiPo nominal fully charged tension-<-))
and the voltage per cell (Voltage/NrCells)

With the value of Voltage per cell an empirical rule like below can be used to report the much more real value of capacity remaining with very low (->zero) current consumption.

4.2v--100%
4.00--84%
3.96---77%
3.93---70%
3.90---63%
3.86---56%
3.83---48%
3.80---43%
3.76---35%
3.73---27%
3.70---21%
3.67---14%

This will only change the initial value for battery remaining, and the current method to calculate it's value while in use remains.

I don't believe it requires a large change of the code and has no dependencies.

@lvale
Copy link
Member Author

lvale commented Oct 3, 2014

If needed the threshold value of 4.2v per cell and the table for Lipo could be selected via a new, to be created parameter, to be added that would be Battery_Type=LiPo, NiMh, Pb with different threshold values and tables.

@haygood
Copy link

haygood commented Oct 4, 2014

The battery type field you mention is what I was coming here to suggest. For now it could just. Say LiPo or "other", and be expanded over time with other types. A note could be included to mention that selecting "other" will revert to resetting to 100% when armed (I.e. the current behavior).

@lvale
Copy link
Member Author

lvale commented Oct 4, 2014

@haygood
Thanks, Really a good idea to keep the "status quo" :)

I believe it's a so simple addition, that I'm sure Randy will "fit it" to 3.2 :)

@rmackay9
Copy link
Contributor

rmackay9 commented Oct 4, 2014

I wish I could fit it in but I'm afraid AC3.2 is already 4 months overdue. Nothing is going into it except critical fixes.

@lvale
Copy link
Member Author

lvale commented Oct 4, 2014

One can always try :)

I'm sure the Iris/Iris+ crowds would benefit from this :)

btw: where in the code is the 100% capacity defined to be reported ? couldn't find it.

@lcasassa
Copy link

lcasassa commented Nov 6, 2014

If I am not wrong, mission planner is the one that calculates the percentage. The firmware only has a current integrator (mAh) that reports to the mission planner and mission planner calculates the percentage because it knows the total capacity of the battery (the board does not know this).

On re-arming or reset the board, the mAh is reset to 0 what leads to 100% of battery in mission planner.

A simple solution is to save the old mAh value on disarm and use it on arm (even after a reset) and a button somewhere to set it to 0 (I change my battery button). Or the other way around, a button to use the old value(I am using the same battery).

But I think the board should know the percentage of remaining battery so we can trigger a fail-safe not only with low voltage.

@lvale
Copy link
Member Author

lvale commented Nov 6, 2014

The percentage is a parameter reported by the Flight Controller via a Mavlink msg

sys_status->battery_remaining = mavlink_msg_sys_status_get_battery_remaining(msg);

@lcasassa
Copy link

lcasassa commented Nov 8, 2014

Interesting.

@jschall
Copy link
Contributor

jschall commented Nov 8, 2014

Just being able to identify which battery we have would be good. What about
cheap NFC tags and an NFC reader attached to pixhawk? Use some combination
of the battery's ID, voltage, and maybe time since last use and determine
if the battery has been charged. If charged, reset, if not, don't. Save the
battery state inside the NFC tag so that the battery can be moved between
copters without losing it.

On Sat, Nov 8, 2014 at 4:25 AM, Linus Casassa notifications@github.com
wrote:

Interesting.


Reply to this email directly or view it on GitHub
#1434 (comment)
.

@jschall
Copy link
Contributor

jschall commented Nov 8, 2014

Only problem is that this is a half-measure compared to having a real BMS
on each pack.

On Sat, Nov 8, 2014 at 1:58 PM, Jonathan Challinger <mr.challinger@gmail.com

wrote:

Just being able to identify which battery we have would be good. What
about cheap NFC tags and an NFC reader attached to pixhawk? Use some
combination of the battery's ID, voltage, and maybe time since last use and
determine if the battery has been charged. If charged, reset, if not,
don't. Save the battery state inside the NFC tag so that the battery can be
moved between copters without losing it.

On Sat, Nov 8, 2014 at 4:25 AM, Linus Casassa notifications@github.com
wrote:

Interesting.


Reply to this email directly or view it on GitHub
#1434 (comment)
.

@lvale
Copy link
Member Author

lvale commented Nov 8, 2014

@jschall
I also had that idea and glued nfc tags to all my batteries and kept records of every state change (mA charged, mA discharged, number of cycles) My Samsung phone has the NFC so it was quite easy, but after a while we do forget and we lag updating more and more.....

Having a chip on each battery (or each cell as laptop batteries do) would mean a complete change of all the accessories we and most anyone has.
DJI can get away with it on a completely closed system (Phantom2)

We could only get away with it as an optional extra that would require changing everything, and also adding additional circuitry around the current PixHawk.

Perhaps 3DR could get away with it on a Iris 3....

@pepevalbe
Copy link
Contributor

I was thinking on this enhancement for the Ardupilot code when I found this old open Issue.
I find it very useful and I really like it for my plane so I would like to make some contribution.

As we can see in @lvale data (first post) the relation voltage/capacity_percentage in LiPo batteries is linear within the range 3.6v to 4v. From his data I calculated the empirical formula:

capacity% = 212.53*voltage - 765.29

This formula is valid for the linear region (from 3.6v to 4v). Outside this range we can assume:

  • Voltage higher than 4v: Battery is full (100%). This is the current behaviour of Ardupilot, no matter the voltage.
  • Voltage lower than 3.65v: Battery is empty (0%): This is the low voltage region for a LiPo so you really shoudn't start a flight under this level of voltage.

@magicrub
Copy link
Contributor

I disagree with your 3.65V to 4.0V range.

On Tue, Jun 23, 2015 at 4:10 AM, pepevalbe notifications@github.com wrote:

I was thinking on this enhancement for the Ardupilot code when I found
this old open Issue.
I find it very useful and I really like it for my plane so I would like to
make some contribution.

As we can see in @lvale https://github.com/lvale data (first post) the
relation voltage/capacity_percentage in LiPo batteries is linear within the
range 3.6v to 4v. From his data I calculated the empirical formula:

_capacity% = 212.53_voltage - 765.29*

This formula is valid for the linear region (from 3.6v to 4v). Outside
this range we can assume:

  • Voltage higher than 4v: Battery is full (100%). This is the current
    behaviour of Ardupilot, no matter the voltage.
  • Voltage lower than 3.65v: Battery is empty (0%): This is the low
    voltage region for a LiPo so you really shoudn't start a flight under this
    level of voltage.


Reply to this email directly or view it on GitHub
#1434 (comment)
.

@pepevalbe
Copy link
Contributor

@magicrub Why do you disagree?

Bellow I post two graphs with voltage/capacity data points and its simple linear regression:

lineal

As you can see the first graph equation, where I removed the end points, is very much precise for the linear region. The drawback of using this formula is that values outside the range [3.601v, 4.072] will result in capacity higher than 100% or lower than 0% (which can't be possible).

In my opinion it is ok to just cap the formula to 100% or 0%. We could use different formulas for different regions but I don't think there would be much diffence.
Anyway when the voltage is higher than 4.072v it probably means that the battery is just charged and if voltage is lower than 3.601v, which is less than 14% of capacity, no one should begin any flight.

@R-Lefebvre
Copy link
Contributor

A problem is that this curve is only realistic for some batteries, but not all. I have a number of high-energy-density low-C LiPos, and they have completely different voltage characteristics. They have high internal resistance so they need to be flown at quite a bit lower voltage. Under load, they are "full" at about 3.8V/cell, and you need to fly them down to at least 3.3V/cell to get everything out of them.

This concept would need to use some parameters so these values could be tailored to individual setups.

@WickedShell
Copy link
Contributor

I routinely fly batteries similar to what rob has described. I can find a
voltage graph of my cells if its relevant but it varies drastically by
battery and the only way to know is to benchmark each battery. (Not to
mention the massive difference of load vs no load)
On Jun 24, 2015 4:31 AM, "Robert Lefebvre" notifications@github.com wrote:

A problem is that this curve is only realistic for some batteries, but not
all. I have a number of high-energy-density low-C LiPos, and they have
completely different voltage characteristics. They have high internal
resistance so they need to be flown at quite a bit lower voltage. Under
load, they are "full" at about 3.8V/cell, and you need to fly them down to
at least 3.3V/cell to get everything out of them.


Reply to this email directly or view it on GitHub
#1434 (comment)
.

@pepevalbe
Copy link
Contributor

Load vs no load makes no difference here since we are measuring voltage at startup where the battery is supposed to be in a rest state (under no load). After this initial rough estimation, battery voltage no matters for the remaining capacity since we are measuring current.

Data from first post is the same as in http://www.rcgroups.com/forums/showpost.php?p=6660057&postcount=7. This data is supposed to be the result of measurements with different brand lipo packs. This is realistic for all my batteries and think it is for most of them. However we could have two parameters (gain and offset) to represent the voltage/capacity curve for individuals setup.

I'm working on the code modifications. Hopefully I will be able to do some tests soon. This will be my first contribution!

@lvale
Copy link
Member Author

lvale commented Jun 24, 2015

Hi
The numbers for the samples of batteries I used were found on the web (don't recall exactly where), and I've also seen those on RCGroups, and were very closely related to my own observations (yes for some time I kept NFC tags on all my batteries and recorded every aspect of usage of each battery on a spreadsheet, and I even included battery temperature and ambient temperature :) )

The purpose of these values were to have an easy way to "estimate" battery capacity on first connection of the battery to the system, so no load applied, which renders the considerations about internal resistance outside the scope.

I didn't want to use any first order polynomial to get an estimate for battery capacity (I went as far as using 3rd degree polynomials) because we're using discrete non continuos values and the sample size was only in the hundreds of measurements and we really only need an estimate, not a accurate value for capacity, and the 11 intervals were way much better than that completely wrong and misleading 100% we get when the battery is connected. So I see no need to do linear regression. The intervals approach is "good enough" - also, your car gas tank, does it report how many liters/gallons it has or only an approximation ? :) Mine also estimates mileage until empty :)

Adding to that the issue, with the current power modules we use (the 3DR one) that only have one calibration point, which gives us also very erroneous info for the battery state while using. - Note: On this issue I decided to calibrate my power module multiplier only at the cutoff voltage.

While we won't have "Smart batteries" widely available (not every one of us has Solo's or even Phantom's or Inspire's) this small change could prevent problems.

I do use this stepped battery capacity approximation already on my Taranis, being calculated by a script, and it works great, but YMMV.

Regarding Rob's example of the 3.3V cells, note that I don't establish a 0% capacity because the voltages below that 3.67V threshold are all over the place, so if below 3.67V the estimate is 14%. If Rob plugs a battery that has a Voltage without load below 3.67V the system would tell him that the "estimate" was 14%. It's his batteries so if he wants to fly with that estimate......I tend to "care" for my batteries

@pepevalbe
Copy link
Contributor

Hi @lvale
I agree that the intervals approach is good enough. The thing is that this linear regression is actually easier to implement. With just one formula there is no need to handle a table with those intervals:

float calculate_initial_capacity_percentage(float voltage, uint8_t num_cells)
{   
  float capacity = 212.53*voltage/num_cells-765.29;

  if (capacity > 100) {
      capacity = 100;
  }
  else if (capacity < 0) {
      capacity = 0;
  }

  return capacity;
}

@lvale
Copy link
Member Author

lvale commented Jun 25, 2015

Yes, but like that, the capacity is always changing (up and down) while the interval solution is more "stable" :)

local function batstatus()
        cell_nr = math.ceil(getValue(216) / 4.2)
        cellv=(getValue(216)/ cell_nr)
        cap_est=0


        if cellv>=4.2 then cap_est=100
            elseif cellv>=4.00 then cap_est=84
            elseif cellv>=3.96 then cap_est=77
            elseif cellv>=3.93 then cap_est=70
            elseif cellv>=3.90 then cap_est=63
            elseif cellv>=3.86 then cap_est=56
            elseif cellv>=3.83 then cap_est=48
            elseif cellv>=3.80 then cap_est=43
            elseif cellv>=3.76 then cap_est=35
            elseif cellv>=3.73 then cap_est=27
            elseif cellv>=3.70 then cap_est=21
            elseif cellv>=3.67 then cap_est=14
                cap_est=0
            end
    return cellv, cell_nr, cap_est
end

@pepevalbe
Copy link
Contributor

My idea is to estimate the initial capacity at startup (just one time). After this calculation, capacity percentage will only get lower depending on your current consumption. There is no posibilty of changing up/down this way.

@lvale
Copy link
Member Author

lvale commented Jun 25, 2015

Yes, I get that. Just check the voltage fluctuation on startup (transients) and see if and when you can get a "stable" reading to be used as initial value.

@katealhola
Copy link

Using simple battery voltage calculating remaining capacity has multiple disadvantages. It may work if there is no load but even in these cases total capacity of battery mey not be nominal value. Umder load battery voltage varies based on load current and battery inetrnal resistance. The RFID would be ideal but it needs to store real measured capacity of battery.

There is still very simple alternative, send over MAVLink and display real used mAh value on gtoundstation. That would be super simple way. I serched from history that sending consumed mAH value has been requested several times but it was never integrated. Then you could just write real capaceity to battery. Is there any good reason NOT send it over Mavlink when it ready calculated internally.

Small extra feature to ground station software to allow setting capacity estimate easilly will help even more. Something like increment or decrement capacity estimate by 100mAh by button press. When youngo flying, take battery and read your sticker about capacity estimate,if your battery gives only 2700 of nominal 3000, press minis button theree times. When you land, use toral mAh used to see domuou need write new value to sticker.

Then if you have RFID, then all this is dome automatically.

I feel current persent display nearly useless because i don' t hace any way to see what was real used capacity and no simple way to adjust it.

@rmackay9
Copy link
Contributor

There is a BATTERY_STATUS MAVLink message that has appeared recently which could be used to send along the additional capacity used information. By the way, the battery percent remaining is calculated using the total battery capacity (set by the user) and the consumed capacity (which is calculated by integrating the instantaneously measured current.

http://mavlink.org/messages/common#BATTERY_STATUS

@ilihack
Copy link

ilihack commented Oct 31, 2015

i dont now its needet i made a nice Regression and tune the parameter :)

194,1046 * x^7 - 3664,4875 * x^6 + 25812,8262 * x^5 - 70418,3286 * x^4 - 52792,5277 *x^3 + 770690,9093 * x^2 - 1695393,7152 * x + 1253614,3348

in the calculation it will be:

float calculate_initial_capacity_percentage(float voltage, uint8_t num_cells)
{
float capacity = 0;

if (voltage > 4,2 {
capacity = 100 ;
}
else if (voltage < 3,2) {
capacity = 0 ;
}
else {
capacity = 194,1046 * voltage/num_cells^7 - 3664,4875 * voltage/num_cells^6 + 25812,8262 * voltage/num_cells^5 - 70418,3286 * voltage/num_cells^4 - 52792,5277 *voltage/num_cells^3 + 770690,9093 * voltage/num_cells^2 - 1695393,7152 * voltage/num_cells + 1253614,3348;
}
return capacity;
}

if the above to cpu Havy than this is the lite version:

float calculate_initial_capacity_percentage(float voltage, uint8_t num_cells)
{
float capacity = 0;

if (voltage > 4.2 {
capacity = 100;
}
else if (voltage < 3.2 {
capacity = 0;
}
else if (voltage > 4 {
capacity = 80 voltage/num_cells - 236;
}
else if (voltage < 3,67) {
capacity = 29.787234 * voltage/num_cells - 95.319149 ;
}
else if (voltage > 3,67 and voltage < 4 ) {
capacity = 212.53*voltage/num_cells-765.29;
}
return capacity;
}

this code its will work easy and have only 3 Liner Regressions Between 0% - 4% - 84% - 100 %
i hope this help...


@pepevalbe
Copy link
Contributor

Thank you for posting @ilihack
I don't think we need such a high order polynomial regression. Remember that obtaining battery capacity from voltage is just a rough estimation.

Your lite version is almost the same as what I proposed in #2496. I used a single region so it is easier to parametrize. Region 0-4% is not really needed (nobody should fly) but region 84%-100% could be improved

@lvale
Copy link
Member Author

lvale commented Jan 17, 2016

@rmackay9 We should look at how PX4 is doing this.

Example: Same battery (partially discharged to unknown level) connected to the latest PX4 from master reports as 60% capacity and connected to latest APM Copter reports 100%.

Measured voltage at battery terminals reports 3.85V per cell, so PX4 estimate is way more accurate.

@lvale
Copy link
Member Author

lvale commented Jan 2, 2017

@rmackay9 still relevant, but do we close ?

@rmackay9
Copy link
Contributor

rmackay9 commented Jan 3, 2017

I don't mind if we leave it open or closed really. I'm not against the change, it's just not a development task that I can take on personally right now. If someone else wants to take it on, I'm happy to review the PR of course..

@jaxxzer
Copy link
Member

jaxxzer commented Feb 22, 2017

I have implemented a crude estimate of capacity remaining at boot instead of assuming a fully charged battery. It is not perfect, but it is better than assuming a fully charged battery. The lookup table was taken from the bebop monitor code, and could be improved for general use.

jaxxzer@38aa4df

@jaxxzer jaxxzer added the Sub label Feb 22, 2017
@amilcarlucas
Copy link
Contributor

There is #7047 . How about improving that one ?

@lvale lvale closed this as completed Aug 19, 2018
@renno-bih
Copy link

@jaxxzer
This estimation is definitely better than having no checks at boot. I'd like to see this feature in new versions of arducopter. Having battery_percentage parameter resetting every time I power cycle aircraft is very annoying and can also be dangerous if operator only relies on percentage and not voltage.

@Me-WM
Copy link

Me-WM commented Oct 14, 2019

Considering that a large percentage of users use LiPo batteries and that this parameter

sys_status->battery_remaining = mavlink_msg_sys_status_get_battery_remaining(msg);

is reset to 100% when the vehicle is armed, and that the energy consumption when armed might be considered negligible, I would like to propose that a simple method to estimate battery capacity when the vehicle is armed, to be used.
Although not perfect but way better than simple setting the value to 100%, this method would get the voltage as it does now, and when armed estimates a number of cells

NrCells (returning the smallest integer larger than or equal to (Voltage divided by 4.2v->LiPo nominal fully charged tension-<-))
and the voltage per cell (Voltage/NrCells)

With the value of Voltage per cell an empirical rule like below can be used to report the much more real value of capacity remaining with very low (->zero) current consumption.

4.2v--100%
4.00--84%
3.96---77%
3.93---70%
3.90---63%
3.86---56%
3.83---48%
3.80---43%
3.76---35%
3.73---27%
3.70---21%
3.67---14%

This will only change the initial value for battery remaining, and the current method to calculate it's value while in use remains.

I don't believe it requires a large change of the code and has no dependencies.

This equation could give a better results
capacity% = 27214.63 - 21876.46*cell_voltage + 5809.991*cell_voltage^2 - 509.1462*cell_voltage^3

@jaxxzer
Copy link
Member

jaxxzer commented Oct 14, 2019

This issue still stands. At least on Sub.

@jaxxzer jaxxzer reopened this Oct 14, 2019
@roque-canales
Copy link

Dont forget liion batteries 4.2 to 2.5v per cell range

@IamPete1
Copy link
Member

IamPete1 commented Aug 8, 2021

You can achieve this with the reset remaining command from scripting.

@roque-canales
Copy link

roque-canales commented Aug 8, 2021

Can you share with us lua for reset remaining at the level we want?

@IamPete1
Copy link
Member

IamPete1 commented Aug 8, 2021

@roque-canales We don't have a example, but it would be something like this:

function update()

local voltage = battery:voltage(0)
local percent = 0
if voltage > 10 then
    percent = 100
else if voltage > 5 then 
   percent = 50
end

battery:reset_remaining(0, percent)

return update, 1000

end

return update, 15000

Not tested in at all, but hopefully you get the idea.

@roque-canales
Copy link

Perfect! Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests