-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
### Summary <!-- Quick summary of changes, optional --> Track state-of-charge using coulomb counting on BMS. No current limiting implemented, solely statistical tracking. SOC is saved to EEPROM on 1s intervals, utilizing a basic wear-leveling algorithm - Address of up-to-date soc stored in page 0 of EEPROM, this is only written once per power cycle, no need to wear-level - SOC value stored in page 1-127, written once per second. Active SOC page written to will increment once per power cycle. - SOC value read is checked for corruption: - When SOC value saved to EEPROM, 4 copies saved to page - When SOC value read, 4 values are checked to see if they match and are within range - If not, read SOC from Ocv to SOC lookup table (inaccurate but better than corrupted value) SOC can be reset in one of two ways, both controlled by debug CAN messages - Update from Ocv-Soc LUT - Update with manually specified SOC value ### Changelist <!-- Give a list of the changes covered in this PR. This will help both you and the reviewer keep this PR within scope. --> - Added SOC tracking logic - Added EEPROM reading/writing - Added simple test for SOC ### Testing Done <!-- Outline the testing that was done to demonstrate the changes are solid. This could be unit tests, integration tests, testing on the car, etc. Include relevant code snippets, screenshots, etc as needed. --> - Test checking for accuracy added to TestStateMachine - LUT lookup tested in TestStateMachine (will be moved during refactor) - Tested on car: - SOC can be reset using both custom value and via OCV lookup from CAN debug message - Test value saved and recovered between power cycles from EEPROM - Checked that SOC tracks linearly with current draw (tested with full charge cycle) ### Resolved Issues <!-- Link any issues that this PR resolved like so: `Resolves #1, #2, and #5` (Note: Using this format, Github will automatically close the issue(s) when this PR is merged in). --> ### Checklist *Please change `[ ]` to `[x]` when you are ready.* - [x] I have read and followed the code conventions detailed in [README.md](../README.md) (*This will save time for both you and the reviewer!*). - [x] If this pull request is longer then **500** lines, I have provided *explicit* justification in the summary above explaining why I *cannot* break this up into multiple pull requests (*Small PR's are faster and less painful for everyone involved!*). --------- Co-authored-by: will-chaba <[email protected]>
- Loading branch information
1 parent
b377f77
commit aa40d1f
Showing
19 changed files
with
632 additions
and
295 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,110 @@ | ||
#pragma once | ||
|
||
#include "App_SharedExitCode.h" | ||
#include "App_Accumulator.h" | ||
#include "App_Timer.h" | ||
#include "App_Eeprom.h" | ||
|
||
#define STATE_OF_HEALTH (0.94f) | ||
#define SERIES_ELEMENT_FULL_CHARGE_C (5.9f * 3600.0f * 3.0f * STATE_OF_HEALTH) | ||
|
||
struct SocStats | ||
{ | ||
// Address in EEPROM where SOC is being stored (address changes for wear levelling reasons, address always stored in | ||
// address 0 of EEPROM) | ||
uint16_t soc_address; | ||
|
||
// charge in cell in coulombs | ||
double charge_c; | ||
|
||
// Charge loss at time t-1 | ||
float prev_current_A; | ||
|
||
// Indicates if SOC from EEPROM was corrupt at startup | ||
bool is_corrupt; | ||
|
||
TimerChannel soc_timer; | ||
}; | ||
|
||
/** | ||
* Function to retrieve soc closest to given open circuit voltage | ||
* @param voltage open circuit voltage | ||
* @return soc related to open circuit voltage | ||
*/ | ||
float App_Soc_GetSocFromOcv(float voltage); | ||
|
||
/** | ||
* Function to retrieve open circuit voltage closest to given soc | ||
* @param soc_percent state of charge in percent | ||
* @return open circuit voltage closest to given state of charge | ||
*/ | ||
float App_Soc_GetOcvFromSoc(float soc_percent); | ||
|
||
/** | ||
* Create the SocStats object | ||
* @param eeprom Eeprom to read stored SOC value from | ||
* @param accumulator Accumulator to get min cell voltage from | ||
* @return struct SocStats pointer to object | ||
*/ | ||
struct SocStats *App_SocStats_Create(struct Eeprom *eeprom, struct Accumulator *accumulator); | ||
|
||
/** | ||
* Destroy the SocStats object | ||
*/ | ||
void App_SocStats_Destroy(struct SocStats *soc_stats); | ||
|
||
/** | ||
* Return the active address on the EEPROM where SOC values are being stored | ||
* @param soc_stats soc_stats object to retrieve address from | ||
* @return page being used to store SOC values | ||
*/ | ||
uint16_t App_SocStats_GetSocAddress(struct SocStats *soc_stats); | ||
|
||
/** | ||
* Return if the SOC value was corrupt on startup | ||
* @param soc_stats soc_stats object to retrieve corrupt status from | ||
* @return corrupt status | ||
*/ | ||
bool App_SocStats_GetCorrupt(struct SocStats *soc_stats); | ||
|
||
/** | ||
* Update the state of charge of all series elements using coulomb counting. | ||
* @param soc_stats The charge stats of the pack | ||
* @param current The current from the cell to be updated (- is out of the cell, + is into the cell) | ||
*/ | ||
void App_SocStats_UpdateSocStats(struct SocStats *soc_stats, float current); | ||
|
||
/** | ||
* return Coulombs of the SE with the lowest SOC | ||
* @param soc_stats The charge stats of the pack | ||
* @return cloulombs of lowest SOC SE | ||
*/ | ||
float App_SocStats_GetMinSocCoulombs(struct SocStats *soc_stats); | ||
|
||
/** | ||
* Given three state-of-charge (SoCs), check whether the absolute difference | ||
* between any two SoCs is less than or equal to the specified maximum absolute | ||
* difference. If any two SoCs pass the check, return the average value of those | ||
* two SoCs. | ||
* @param max_abs_difference The maximum allowable absolute difference between | ||
* any two given SoCs, must be between 0 and 100 | ||
* inclusive | ||
* @param soc_1 The first SoC, must be between 0 and 100 inclusive | ||
* @param soc_2 The second SoC, must be between 0 and 100 inclusive | ||
* @param soc_3 The third SoC, must be between 0 and 100 inclusive | ||
* @param result This will be set to the average value of the two SoCs whose | ||
* difference is less than or equal to max_difference. Multiple | ||
* SoC pairs may have their difference less than or equal to | ||
* max_difference. In this scenario, we prioritize as follows: | ||
* | ||
* Highest Priority: soc_1 / soc_2 | ||
* soc_2 / soc_3 | ||
* Lowest Priority: soc_1 / soc_3 | ||
* | ||
* If no two suitable SoCs can be found, this will be set to NaN. | ||
* @note An example of a tiebreaker: | ||
* | ||
* max_difference = 5 | ||
* soc_1 = 0 | ||
* soc_2 = 50 | ||
* soc_3 = 100 | ||
* | ||
* In this scenario, the difference between soc_1 and soc_2 is the same as | ||
* the difference between soc_2 and soc_3, but we prioritize soc_1 / soc_2 | ||
* over soc_2 / soc_3. Thus, result is set to (soc_1 + soc_2) / 2 = 25. | ||
* @return EXIT_CODE_INVALID_ARGS if max_difference, soc_1, soc_2, or soc_3 is | ||
* not between 0 and 100 inclusive | ||
*/ | ||
ExitCode App_Soc_Vote(float max_abs_difference, float soc_1, float soc_2, float soc_3, float *result); | ||
* return percent SOC of the SE with the lowest SOC | ||
* @param soc_stats The charge stats of the pack | ||
* @return soc % of lowest SOC SE | ||
*/ | ||
float App_SocStats_GetMinSocPercent(struct SocStats *soc_stats); | ||
|
||
/** | ||
* Get the minimum series element open circuit voltage approximation given current pack SOC status | ||
* @param soc_stats current SOC of each series element in pack | ||
* @return float minimum series element open circuit voltage approximation | ||
*/ | ||
float App_Soc_GetMinOcvFromSoc(struct SocStats *soc_stats); | ||
|
||
/** | ||
* Reset SOC value based on current minimum cell voltage | ||
* @param soc_stats object to write SOC stats to | ||
* @param accumulator accumulator cell information | ||
*/ | ||
void App_Soc_ResetSocFromVoltage(struct SocStats *soc_stats, struct Accumulator *accumulator); | ||
|
||
/** | ||
* Reset SOC value with custom input | ||
* @param soc_stats object to write SOC stats to | ||
* @param soc_percent desired SOC in percent | ||
*/ | ||
void App_Soc_ResetSocCustomValue(struct SocStats *soc_stats, float soc_percent); |
Oops, something went wrong.