BMS_SoC_estimator.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843
  1. /*
  2. * BMS_SoC_estimator.c
  3. *
  4. * Created on: Jul 18, 2016
  5. * Author: le8041
  6. */
  7. /**
  8. * @file BMS_SoC_estimator.c
  9. * @brief SoC Estimator as implemented on embedded System
  10. */
  11. #include "BMS_Master.h"
  12. static float const_Ri_charge[BMS_SOC_NR_OF_RI_POINTS]={2.0,2.700};
  13. static int8_t const_Ri_charge_temp[BMS_SOC_NR_OF_RI_POINTS]={10,20};
  14. static float const_Ri_discharge[BMS_SOC_NR_OF_RI_POINTS]= {2.692E-3,2.700E-3};
  15. static int8_t const_Ri_discharge_temp[BMS_SOC_NR_OF_RI_POINTS] = {10,20};
  16. /*
  17. * @brief setting initial SoC soly dependend from the Battery voltage
  18. * @param voltage Cell Voltage
  19. */
  20. float calc_start_SoC(uint16_t voltage) {
  21. // set initial Look up Table
  22. uint8_t LutSize=6;
  23. uint8_t i=0;
  24. float offset;
  25. MASTER_SOC_ESTIMATOR_INIT_SOC_LUT_t init_SoC_Lut[6];
  26. init_SoC_Lut[0].SoC=0;
  27. init_SoC_Lut[0].OCV=2900;
  28. init_SoC_Lut[1].SoC=0.0665;
  29. init_SoC_Lut[1].OCV=3166;
  30. init_SoC_Lut[2].SoC=0.285;
  31. init_SoC_Lut[2].OCV=3281;
  32. init_SoC_Lut[3].SoC=0.6621;
  33. init_SoC_Lut[3].OCV=3320;
  34. init_SoC_Lut[4].SoC=0.94;
  35. init_SoC_Lut[4].OCV=3379;
  36. init_SoC_Lut[5].SoC=1.0;
  37. init_SoC_Lut[5].OCV=3450;
  38. // calc SoC from Current
  39. if( voltage <= init_SoC_Lut[0].OCV) {
  40. // SoC is 0
  41. return init_SoC_Lut[0].SoC;
  42. }
  43. if( voltage >= init_SoC_Lut[5].OCV) {
  44. // SoC is 1
  45. return init_SoC_Lut[5].SoC;
  46. }
  47. for(i=0;i< LutSize-1;i++) {
  48. // SoC is between 0 and 1
  49. // find area in SoC LUT
  50. if(init_SoC_Lut[i].OCV <= voltage && init_SoC_Lut[i+1].OCV >= voltage) {
  51. // SoC area between i and i+1
  52. // linear interpolation of SoC
  53. offset=( init_SoC_Lut[i+1].SoC - init_SoC_Lut[i].SoC) / (init_SoC_Lut[i+1].OCV - init_SoC_Lut[i].OCV ) * (voltage - init_SoC_Lut[i].OCV );
  54. return (offset + init_SoC_Lut[i].SoC);
  55. }
  56. }
  57. return -1;
  58. }
  59. /*
  60. * @brief set initial Constants for SoC estimation Algorithm
  61. * @param cell_SoC pointer to SoC estimation struct
  62. * @param voltage cell voltage for dertermination of initial SoC after System power up
  63. */
  64. int32_t bms_SoC_init_estimator(MASTER_SOC_ESTIMATOR_t* cell_SoC, uint16_t voltage) {
  65. uint8_t i;
  66. cell_SoC->capacity=26.0; // initial capacity 25.0 Ah
  67. cell_SoC->delta_t = 0.25; // intergration constant in seconds
  68. cell_SoC->delta_t = cell_SoC->delta_t /(60*60) ; // integration constant in hours
  69. // set initial SoC
  70. cell_SoC->SoC_percentage=calc_start_SoC(voltage) ;
  71. cell_SoC->SoC_percentage_smooth= cell_SoC->SoC_percentage;
  72. cell_SoC->SoC_Ah= cell_SoC->SoC_percentage * cell_SoC->capacity ;
  73. // set charge thresholds
  74. cell_SoC->ChargeThreshold.Ri_charge= 2.0 ; // Ri in mOhm
  75. cell_SoC->ChargeThreshold.U_bat_max=3450;
  76. cell_SoC->ChargeThreshold.U_ocv_correct=3379;
  77. cell_SoC->ChargeThreshold.U_ocv_SoC=0.94 ;
  78. // set discharge thresholgs
  79. cell_SoC->DischargeThreshold.Ri_discharge =2.7; // Ri in mOhm
  80. cell_SoC->DischargeThreshold.U_bat_min=2900;
  81. cell_SoC->DischargeThreshold.U_ocv_correct=3166;
  82. cell_SoC->DischargeThreshold.U_ocv_SoC=0.0665;
  83. // set initail Derating
  84. cell_SoC->MaxBatteryChargeCurrent=bms_calc_charge_derating(cell_SoC->SoC_percentage);
  85. cell_SoC->MaxBatteryDischargeCurrent=bms_calc_discharge_derating(cell_SoC->SoC_percentage);
  86. cell_SoC->state=BMS_SOC_IDLE;
  87. cell_SoC->flags=0;
  88. cell_SoC->ChargeRelaxationWaitCnt=0;
  89. cell_SoC->DischargeRelaxationWaitCnt=0;
  90. cell_SoC->ChargeSmoothFlags=0;
  91. cell_SoC->Ocv_calc=voltage;
  92. return 0;
  93. }
  94. /*
  95. * @brief set initial Constants for SoC estimation Algorithm
  96. * @param cell_SoC pointer to SoC estimation struct
  97. * @param voltage cell voltage for dertermination of initial SoC after System power up
  98. */
  99. int32_t bms_SoC_init_estimator_FRAM(MASTER_SOC_ESTIMATOR_t* cell_SoC, uint16_t voltage) {
  100. float SoC=50.0;
  101. cell_SoC->capacity=26.0; // initial capacity 25.0 Ah
  102. cell_SoC->delta_t = 0.25; // intergration constant in seconds
  103. //read_fram_float(&SoC,10,BMS_STARTUP_SOC);
  104. SoC= read_fram_get_SoC();
  105. cell_SoC->delta_t = cell_SoC->delta_t /(60*60) ; // integration constant in hours
  106. // set initial SoC
  107. cell_SoC->SoC_percentage=SoC ;
  108. cell_SoC->SoC_percentage_smooth= cell_SoC->SoC_percentage;
  109. cell_SoC->SoC_Ah= cell_SoC->SoC_percentage * cell_SoC->capacity ;
  110. // set charge thresholds
  111. cell_SoC->ChargeThreshold.Ri_charge= 2.0 ; // Ri in mOhm
  112. cell_SoC->ChargeThreshold.U_bat_max=3450;
  113. cell_SoC->ChargeThreshold.U_ocv_correct=3379;
  114. cell_SoC->ChargeThreshold.U_ocv_SoC=0.94 ;
  115. // set discharge thresholgs
  116. cell_SoC->DischargeThreshold.Ri_discharge =2.7; // Ri in mOhm
  117. cell_SoC->DischargeThreshold.U_bat_min=2900;
  118. cell_SoC->DischargeThreshold.U_ocv_correct=3166;
  119. cell_SoC->DischargeThreshold.U_ocv_SoC=0.0665;
  120. // set initail Derating
  121. cell_SoC->MaxBatteryChargeCurrent=bms_calc_charge_derating(cell_SoC->SoC_percentage);
  122. cell_SoC->MaxBatteryDischargeCurrent=bms_calc_discharge_derating(cell_SoC->SoC_percentage);
  123. cell_SoC->state=BMS_SOC_IDLE;
  124. cell_SoC->flags=0;
  125. cell_SoC->ChargeRelaxationWaitCnt=0;
  126. cell_SoC->DischargeRelaxationWaitCnt=0;
  127. cell_SoC->ChargeSmoothFlags=0;
  128. cell_SoC->Ocv_calc=voltage;
  129. return 0;
  130. }
  131. /*
  132. * @brief finite State machien for the estimation of the State of charge
  133. * @param est pointer to estimator struct
  134. * @param I_batt current in mA
  135. * @param U_batt_max maximum Cell Voltage in System in mV
  136. * @param U_batt_min minimum Cell Voltage in System in mV
  137. * @param temp_batt temperature of battery
  138. */
  139. uint32_t bms_SoC_running_fsm(MASTER_SOC_ESTIMATOR_t* est, int16_t I_batt, uint16_t U_batt_max,uint16_t U_batt_min, int8_t temp_min, int8_t temp_max) {
  140. uint16_t OCV; // Open Clamp Voltage according to battery Model
  141. float R_i=0;
  142. switch(est->state) {
  143. case BMS_SOC_IDLE:
  144. // idle state
  145. // check if battery is charged, discharged or resting
  146. if(I_batt < -1*BMS_SOC_MIN_CURRENT_MA) {
  147. //I_bat < 0 => Battery is charged
  148. est->state=BMS_SOC_CHARGE;
  149. return 0;
  150. }
  151. else if(I_batt > BMS_SOC_MIN_CURRENT_MA) {
  152. //I_bat > 0 => Battery is discharged
  153. est->state=BMS_SOC_DISCHARGE;
  154. return 0;
  155. }
  156. else {
  157. // Battery is at rest
  158. est->state=BMS_SOC_REST;
  159. return 0;
  160. }
  161. return -1;
  162. break;
  163. case BMS_SOC_CHARGE:
  164. // Battery is charged
  165. // if Battery has been discharged to 0% SoC remove Discharge Flag
  166. if(est->flags==BMS_SOC_FLAG_SOC_0 && est->SoC_percentage > est->DischargeThreshold.U_ocv_SoC) {
  167. est->flags=0;
  168. }
  169. if(est->flags==BMS_SOC_FLAG_SOC_LOW && est->SoC_percentage > est->DischargeThreshold.U_ocv_SoC) {
  170. // clear BMS_SOC_LOW flage
  171. est->flags=0;
  172. }
  173. if(est->ChargeSmoothFlags== BMS_SOC_DISCHARGE_SMOOTH_01 && est->SoC_percentage >0.01) {
  174. // clear BMS_SOC_DISCHARGE_SMOOTH_01 flage
  175. est->ChargeSmoothFlags=0;
  176. }
  177. // calculate OCV
  178. // ToDo: add temperature dependency of Ri
  179. R_i=est->ChargeThreshold.Ri_charge;
  180. OCV= U_batt_max - R_i * (-1)*(float)I_batt/1000; // I_batt is negative ;
  181. est->Ocv_calc = OCV;
  182. // Check in Which Area SoC is
  183. if(est->flags==BMS_SOC_FLAG_SOC_100) {
  184. // SoC = 100% has been reached keep SoC at 100% until discharging starts;
  185. // => do nothing
  186. est->state=BMS_SOC_CHARGE_SMOOTHING;
  187. return 0;
  188. }
  189. else if(U_batt_max > est->ChargeThreshold.U_bat_max) {
  190. // if Cell voltage is higer than a threshold battery is considered fully charged
  191. // comparison with cell voltage is necessary because battery model might not be accurate enought
  192. est->state=BMS_SOC_CHARGE_AREA_FULL;
  193. return 0;
  194. }
  195. if(est->ChargeSmoothFlags== BMS_SOC_CHARGE_SMOOTH_99) {
  196. // SoC => 99% wait till U_bat max is reached
  197. est->state=BMS_SOC_CHARGE_SMOOTHING;
  198. return 0;
  199. }
  200. else if(OCV > est->ChargeThreshold.U_ocv_correct) {
  201. // open clamp Voltage is higer than threshold
  202. // => Battery is nearly fully charged => SoC from columb counting has to be updated
  203. // Wait for 20 Seconds for update of SoC
  204. est->state=BMS_SOC_CHARGE_WAIT_FOR_RELAXATION;
  205. return 0;
  206. }
  207. else {
  208. // no special Area to take care of
  209. // proceed with columb counting
  210. est->state=BMS_SOC_CHARGE_C_CNT;
  211. // no more need to wait for relaxation
  212. est->ChargeRelaxationWaitCnt=0;
  213. return 0;
  214. }
  215. return -1;
  216. break;
  217. case BMS_SOC_CHARGE_AREA_FULL:
  218. // battery is considered full
  219. // set SoC to 100%
  220. est->SoC_percentage=1.0;
  221. est->SoC_Ah=est->capacity;
  222. // set maximum charge current to 0
  223. est->MaxBatteryChargeCurrent=0;
  224. // set Flag to prevent SOC from falling in case of relaxation
  225. est->flags=BMS_SOC_FLAG_SOC_100;
  226. est->state=BMS_SOC_READY;
  227. pushSoCFiFo(est);
  228. return 0;
  229. break;
  230. case BMS_SOC_CHARGE_WAIT_FOR_RELAXATION:
  231. // wait a couple of seconds for Relaxation to prevent
  232. // unjustified update of SoC
  233. est->ChargeRelaxationWaitCnt++;
  234. if(est->ChargeRelaxationWaitCnt > 100) {
  235. est->state= BMS_SOC_CHARGE_AREA_HIGH;
  236. }
  237. else {
  238. est->state= BMS_SOC_CHARGE_C_CNT;
  239. }
  240. return 0;
  241. break;
  242. case BMS_SOC_CHARGE_AREA_HIGH:
  243. // open clamp Voltage is higer than threshold
  244. // => Battery is nearly fully charged => SoC from columb counting has to be updated
  245. // set SoC predefined Value
  246. if(est->flags!=BMS_SOC_FLAG_SOC_HIGH) {
  247. // save old SoC value for Smoothing
  248. est->SoC_Ah_smooth=est->SoC_Ah;
  249. est->SoC_percentage_smooth=est->SoC_percentage;
  250. // update SoC value
  251. est->SoC_percentage=est->ChargeThreshold.U_ocv_SoC;
  252. est->SoC_Ah=est->SoC_percentage * est->capacity;
  253. // set high flag to indicate that SoC has already been updated
  254. est->flags=BMS_SOC_FLAG_SOC_HIGH;
  255. }
  256. // continue columb cnt
  257. est->state=BMS_SOC_CHARGE_C_CNT;
  258. return 0;
  259. break;
  260. case BMS_SOC_CHARGE_C_CNT:
  261. // calculate SoC using coulumb counting
  262. est->SoC_Ah=est->SoC_Ah + est->delta_t* (-1)*(float)I_batt/1000 ;
  263. est->SoC_percentage=est->SoC_Ah/est->capacity;
  264. est->state=BMS_SOC_CHARGE_SMOOTHING;
  265. return 0;
  266. break;
  267. case BMS_SOC_CHARGE_SMOOTHING:
  268. // prevents jumps in SoC
  269. if(est->flags == BMS_SOC_FLAG_SOC_100) {
  270. est->ChargeSmoothFlags=0;
  271. est->SoC_percentage_smooth=est->SoC_percentage ;
  272. est->state=BMS_SOC_READY;
  273. pushSoCFiFo(est);
  274. return 0; ;
  275. }
  276. else if(est->SoC_percentage > 0.99 && est->flags != BMS_SOC_FLAG_SOC_100) {
  277. // keep SoC at 99,5% until U_bat Max is reached
  278. if(est->ChargeSmoothFlags != BMS_SOC_CHARGE_SMOOTH_99){
  279. est->ChargeSmoothFlags= BMS_SOC_CHARGE_SMOOTH_99;
  280. est->SoC_percentage_smooth=est->SoC_percentage;
  281. est->SoC_Ah_smooth=est->SoC_Ah;
  282. }
  283. else {
  284. // do nothing
  285. }
  286. est->state=BMS_SOC_CHARGE_DERATE;
  287. return 0;
  288. }
  289. else if(est->SoC_percentage >= est->ChargeThreshold.U_ocv_SoC && est->flags != BMS_SOC_FLAG_SOC_HIGH && est->ChargeSmoothFlags != BMS_SOC_CHARGE_SMOOTH_OVERSHOOT) {
  290. // Overshoot event has occured
  291. // this means that the SoC estimated by coulumb counting is higher than
  292. // the SoC based on the OCV => implement slowed coulumb cnt
  293. // save SoC
  294. est->SoC_Ah_smooth=est->SoC_Ah;
  295. est->SoC_percentage_smooth=est->SoC_percentage;
  296. // Set Flag to indicate that overshoot correction is enabled
  297. est->ChargeSmoothFlags=BMS_SOC_CHARGE_SMOOTH_OVERSHOOT;
  298. est->state=BMS_SOC_CHARGE_DERATE;
  299. return 0;
  300. }
  301. else if( est->ChargeSmoothFlags == BMS_SOC_CHARGE_SMOOTH_OVERSHOOT) {
  302. // Smooth out overshoot by slowed down coulumb counting
  303. est->SoC_Ah_smooth=est->SoC_Ah_smooth + est->delta_t* (-1)*(float)I_batt/1000*0.5 ;
  304. est->SoC_percentage_smooth=est->SoC_Ah_smooth/est->capacity;
  305. if( bms_SoC_get_norm(est->SoC_percentage_smooth - est->SoC_percentage) <= 0.001) {
  306. // if Error is smaller than 0.5 % stopp correction
  307. est->ChargeSmoothFlags=0;
  308. est->SoC_percentage_smooth=est->SoC_percentage;
  309. est->SoC_Ah_smooth=est->SoC_Ah;
  310. }
  311. est->state=BMS_SOC_CHARGE_DERATE;
  312. return 0;
  313. }
  314. else if( est->flags == BMS_SOC_FLAG_SOC_HIGH && est->ChargeSmoothFlags !=BMS_SOC_CHARGE_SMOOTH_UNDERSHOOT) {
  315. // Undershoot event hast occured
  316. // this means that SoC estimated by coulumb counting is lower
  317. // than SoC based on the OCV => implement faster coulumb cnt
  318. // Set Flag to indicate that undershoot correction is enabled
  319. est->ChargeSmoothFlags=BMS_SOC_CHARGE_SMOOTH_UNDERSHOOT;
  320. est->state=BMS_SOC_CHARGE_DERATE;
  321. return 0;
  322. }
  323. else if( est->flags == BMS_SOC_FLAG_SOC_HIGH && est->ChargeSmoothFlags ==BMS_SOC_CHARGE_SMOOTH_UNDERSHOOT) {
  324. // smooth out under shoot by speeding up coulumb counting
  325. est->SoC_Ah_smooth=est->SoC_Ah_smooth + est->delta_t* (-1)*(float)I_batt/1000*4 ;
  326. est->SoC_percentage_smooth=est->SoC_Ah_smooth/est->capacity;
  327. if( bms_SoC_get_norm(est->SoC_percentage_smooth - est->SoC_percentage) <= 0.001) {
  328. // if Error is smaller than 0.1 % stopp correction
  329. est->ChargeSmoothFlags=0;
  330. est->SoC_percentage_smooth=est->SoC_percentage;
  331. est->SoC_Ah_smooth=est->SoC_Ah;
  332. }
  333. est->state=BMS_SOC_CHARGE_DERATE;
  334. return 0;
  335. }
  336. else {
  337. est->SoC_Ah_smooth=est->SoC_Ah;
  338. est->SoC_percentage_smooth=est->SoC_percentage;
  339. est->state=BMS_SOC_CHARGE_DERATE;
  340. return 0;
  341. }
  342. return 0;
  343. break;
  344. case BMS_SOC_CHARGE_DERATE:
  345. // derate SoC depending on the SoC and temperature
  346. bms_set_derating(est, temp_min, temp_max,U_batt_min);
  347. est->state=BMS_SOC_READY;
  348. pushSoCFiFo(est);
  349. return 0;
  350. break;
  351. case BMS_SOC_DISCHARGE:
  352. // Battery is discharged
  353. // if Battery has been charged to 100% SoC remove charge Flag
  354. // no more need to wait for relaxation at charging
  355. est->ChargeRelaxationWaitCnt=0;
  356. if(est->flags==BMS_SOC_FLAG_SOC_100 && est->SoC_percentage < est->ChargeThreshold.U_ocv_SoC) {
  357. est->flags=0;
  358. }
  359. if(est->flags==BMS_SOC_FLAG_SOC_HIGH && est->SoC_percentage < est->ChargeThreshold.U_ocv_SoC) {
  360. // clear BMS_SOC_HIGH flag
  361. est->flags=0;
  362. }
  363. if(est->ChargeSmoothFlags== BMS_SOC_CHARGE_SMOOTH_99 && est->SoC_percentage <0.99) {
  364. // clear BMS_SOC_DISCHARGE_SMOOTH_01 flage
  365. est->ChargeSmoothFlags=0;
  366. }
  367. // calculate OCV
  368. // ToDo: add temperature dependency of Ri
  369. R_i=est->DischargeThreshold.Ri_discharge;
  370. OCV= U_batt_min + R_i *(float)I_batt/1000; // I_batt is positive ;
  371. est->Ocv_calc = OCV;
  372. if(est->flags==BMS_SOC_FLAG_SOC_0) {
  373. // SoC = 0% has been reached keep SoC at 0% until charging starts;
  374. // => do nothing
  375. est->state=BMS_SOC_DISCHARGE_SMOOTHING;
  376. return 0;
  377. }
  378. else if(U_batt_min < est->DischargeThreshold.U_bat_min ) {
  379. // if Cell voltage is lower than a threshold battery is considered fully discharged
  380. // comparison with cell voltage is necessary because battery model might not be accurate enought
  381. est->state=BMS_SOC_DISCHARGE_AREA_EMPTY;
  382. return 0;
  383. }
  384. else if(est->ChargeSmoothFlags== BMS_SOC_DISCHARGE_SMOOTH_01) {
  385. // SoC => 99% wait till U_bat max is reached
  386. est->state=BMS_SOC_CHARGE_SMOOTHING;
  387. return 0;
  388. }
  389. else if(OCV < est->DischargeThreshold.U_ocv_correct) {
  390. // open clamp Voltage is lower than threshold
  391. // => Battery is nearly fully discharged => SoC from columb counting has to be updated
  392. est->state=BMS_SOC_DISCHARGE_WAIT_FOR_RELAXATION;
  393. return 0;
  394. }
  395. else {
  396. // no special Area to take care of
  397. // proceed with columb counting
  398. est->state=BMS_SOC_DISCHARGE_C_CNT;
  399. est->DischargeRelaxationWaitCnt=0;
  400. return 0;
  401. }
  402. return 0;
  403. break;
  404. case BMS_SOC_DISCHARGE_AREA_EMPTY:
  405. // Battery is considered empty => Set SoC to 0%
  406. est->SoC_percentage=0;
  407. est->SoC_Ah=0;
  408. // set Maximum discharge Current to 0
  409. est->MaxBatteryDischargeCurrent =0;
  410. // set empty flag
  411. est->flags=BMS_SOC_FLAG_SOC_0;
  412. est->state=BMS_SOC_DISCHARGE_SMOOTHING;
  413. return 0;
  414. break;
  415. case BMS_SOC_DISCHARGE_WAIT_FOR_RELAXATION:
  416. // wait a couple of seconds for Relaxation to prevent
  417. // unjustified update of SoC
  418. est->DischargeRelaxationWaitCnt++;
  419. if(est->DischargeRelaxationWaitCnt > 100) {
  420. est->state= BMS_SOC_DISCHARGE_AREA_LOW;
  421. }
  422. else {
  423. est->state= BMS_SOC_DISCHARGE_C_CNT;
  424. }
  425. return 0;
  426. break;
  427. case BMS_SOC_DISCHARGE_AREA_LOW:
  428. // open clamp Voltage is lower than threshold
  429. // => Battery is nearly fully dicharged => SoC from columb counting has to be updated
  430. // set SoC predefined Value
  431. if(est->flags!=BMS_SOC_FLAG_SOC_LOW) {
  432. // save SoC for Smoothing
  433. est->SoC_Ah_smooth=est->SoC_Ah;
  434. est->SoC_percentage_smooth=est->SoC_percentage;
  435. est->SoC_percentage=est->DischargeThreshold.U_ocv_SoC;
  436. est->SoC_Ah=est->SoC_percentage * est->capacity;
  437. // set LOW flag to indicate that SoC has already been updated
  438. est->flags=BMS_SOC_FLAG_SOC_LOW;
  439. }
  440. // continue columb cnt
  441. est->state=BMS_SOC_DISCHARGE_C_CNT;
  442. return 0;
  443. break;
  444. case BMS_SOC_DISCHARGE_C_CNT:
  445. // calculate SoC using coulumb counting
  446. est->SoC_Ah=est->SoC_Ah - est->delta_t* (float)I_batt/1000;
  447. est->SoC_percentage= est->SoC_Ah/est->capacity;
  448. est->state=BMS_SOC_DISCHARGE_SMOOTHING;
  449. return 0;
  450. break;
  451. case BMS_SOC_DISCHARGE_SMOOTHING:
  452. // prevents jumps in SoC
  453. if(est->flags == BMS_SOC_FLAG_SOC_0) {
  454. est->ChargeSmoothFlags=0;
  455. est->SoC_percentage_smooth=est->SoC_percentage ;
  456. est->state=BMS_SOC_READY;
  457. pushSoCFiFo(est);
  458. return 0; ;
  459. }
  460. else if(est->SoC_percentage <= 0.01 && est->flags != BMS_SOC_FLAG_SOC_0) {
  461. // keep SoC at 0,5% until U_bat Max is reached
  462. if(est->ChargeSmoothFlags != BMS_SOC_DISCHARGE_SMOOTH_01){
  463. est->ChargeSmoothFlags= BMS_SOC_DISCHARGE_SMOOTH_01;
  464. est->SoC_percentage_smooth=est->SoC_percentage;
  465. est->SoC_Ah_smooth=est->SoC_Ah;
  466. }
  467. else {
  468. // do nothing
  469. }
  470. est->state=BMS_SOC_DISCHARGE_DERATE;
  471. return 0;
  472. }
  473. else if(est->SoC_percentage <= est->DischargeThreshold.U_ocv_SoC && est->flags != BMS_SOC_FLAG_SOC_LOW && est->ChargeSmoothFlags != BMS_SOC_DISCHARGE_SMOOTH_OVERSHOOT) {
  474. // Overshoot event has occured
  475. // this means that the SoC estimated by coulumb counting is lower than
  476. // the SoC based on the OCV => implement slowed coulumb cnt
  477. // save SoC
  478. est->SoC_Ah_smooth=est->SoC_Ah;
  479. est->SoC_percentage_smooth=est->SoC_percentage;
  480. // Set Flag to indicate that overshoot correction is enabled
  481. est->ChargeSmoothFlags=BMS_SOC_DISCHARGE_SMOOTH_OVERSHOOT;
  482. est->state=BMS_SOC_DISCHARGE_DERATE;
  483. return 0;
  484. }
  485. else if( est->ChargeSmoothFlags == BMS_SOC_DISCHARGE_SMOOTH_OVERSHOOT) {
  486. // Smooth out overshoot by slowed down coulumb counting
  487. est->SoC_Ah_smooth=est->SoC_Ah_smooth - est->delta_t*(float)I_batt/1000*0.5 ;
  488. est->SoC_percentage_smooth=est->SoC_Ah_smooth/est->capacity;
  489. if( bms_SoC_get_norm(est->SoC_percentage_smooth - est->SoC_percentage) <= 0.001) {
  490. // if Error is smaller than 0.1 % stopp correction
  491. est->ChargeSmoothFlags=0;
  492. est->SoC_percentage_smooth=est->SoC_percentage;
  493. est->SoC_Ah_smooth=est->SoC_Ah;
  494. }
  495. est->state=BMS_SOC_DISCHARGE_DERATE;
  496. return 0;
  497. }
  498. else if( est->flags == BMS_SOC_FLAG_SOC_LOW && est->ChargeSmoothFlags !=BMS_SOC_DISCHARGE_SMOOTH_UNDERSHOOT) {
  499. // Undershoot event has occured
  500. // this means that SoC estimated by coulumb counting is higher
  501. // than SoC based on the OCV => implement faster coulumb cnt
  502. // Set Flag to indicate that undershoot correction is enabled
  503. est->ChargeSmoothFlags=BMS_SOC_DISCHARGE_SMOOTH_UNDERSHOOT;
  504. est->state=BMS_SOC_CHARGE_DERATE;
  505. return 0;
  506. }
  507. else if( est->flags == BMS_SOC_FLAG_SOC_LOW && est->ChargeSmoothFlags ==BMS_SOC_DISCHARGE_SMOOTH_UNDERSHOOT) {
  508. // smooth out under shoot by speeding up coulumb counting
  509. est->SoC_Ah_smooth=est->SoC_Ah_smooth - est->delta_t*(float)I_batt/1000*4 ;
  510. est->SoC_percentage_smooth=est->SoC_Ah_smooth/est->capacity;
  511. if( bms_SoC_get_norm(est->SoC_percentage_smooth - est->SoC_percentage) <= 0.001) {
  512. // if Error is smaller than 0.1 % stopp correction
  513. est->ChargeSmoothFlags=0;
  514. est->SoC_percentage_smooth=est->SoC_percentage;
  515. est->SoC_Ah_smooth=est->SoC_Ah;
  516. }
  517. est->state=BMS_SOC_DISCHARGE_DERATE;
  518. return 0;
  519. }
  520. else {
  521. est->SoC_percentage_smooth=est->SoC_percentage;
  522. est->SoC_Ah_smooth=est->SoC_Ah;
  523. est->state=BMS_SOC_DISCHARGE_DERATE;
  524. return 0;
  525. }
  526. return 0;
  527. break;
  528. case BMS_SOC_DISCHARGE_DERATE:
  529. // derate SoC depending on the SoC and temperature
  530. bms_set_derating(est, temp_min, temp_max,U_batt_min);
  531. est->state=BMS_SOC_READY;
  532. pushSoCFiFo(est);
  533. return 0;
  534. break;
  535. case BMS_SOC_REST:
  536. // no charging or discharging => do nothing
  537. bms_set_derating(est, temp_min, temp_max,U_batt_min);
  538. est->state=BMS_SOC_READY;
  539. pushSoCFiFo(est);
  540. return 0;
  541. break;
  542. case BMS_SOC_READY:
  543. // SoC Estimation complete do nothing
  544. // push FIFO
  545. pushSoCFiFo(est);
  546. return 0;
  547. break;
  548. }
  549. }
  550. /*
  551. * set all SOC FIFO entries to start value;
  552. * SoC FIFO necessary to give inverter time to react to derating and Master to transmit Derating info to inverter
  553. */
  554. uint32_t initSoCFifo(MASTER_CAN0_STRUCT_t* s) {
  555. uint8_t i;
  556. for(i=0;i<SOC_SMOOTH_FIFO_SIZE;i++) {
  557. s->SoC_estimator.SoC_percentage_smooth_FiFo[i]=s->SoC_estimator.SoC_percentage_smooth ;
  558. }
  559. return TRUE;
  560. }
  561. uint32_t pushSoCFiFo(MASTER_SOC_ESTIMATOR_t* est) {
  562. uint8_t i;
  563. for(i=SOC_SMOOTH_FIFO_SIZE-1;i>0;i--) {
  564. est->SoC_percentage_smooth_FiFo[i]=est->SoC_percentage_smooth_FiFo[i-1];
  565. }
  566. est->SoC_percentage_smooth_FiFo[0]=est->SoC_percentage_smooth;
  567. return TRUE;
  568. }
  569. float bms_SoC_get_norm(float value) {
  570. if(value >=0) {
  571. return value;
  572. }
  573. else {
  574. return value * (-1);
  575. }
  576. }
  577. /*
  578. * @brief set Derating
  579. */
  580. uint32_t bms_set_derating(MASTER_SOC_ESTIMATOR_t* est, int8_t minTemp, int8_t maxTemp, uint16_t Umin) {
  581. float derating_charge_temp_high;
  582. float derating_charge_temp_low;
  583. float derating_charge_SoC;
  584. float output_derate;
  585. float derating_discharge_temp_high;
  586. float derating_discharge_temp_low;
  587. float derating_discharge_SoC;
  588. float derating_discharge_Umin1;
  589. // charging
  590. derating_charge_SoC=bms_calc_charge_derating(est->SoC_percentage_smooth);
  591. derating_charge_temp_high=bms_calc_charge_temp_derating(maxTemp);
  592. derating_charge_temp_low=bms_calc_charge_temp_derating(minTemp);
  593. // lowest current is choosen to be set as max charge current
  594. if(derating_charge_temp_low < derating_charge_temp_high) {
  595. output_derate = derating_charge_temp_low;
  596. }
  597. else{
  598. output_derate =derating_charge_temp_high;
  599. }
  600. if(output_derate > derating_charge_SoC ) {
  601. output_derate = derating_charge_SoC;
  602. }
  603. est->MaxBatteryChargeCurrent=output_derate;
  604. // discharging
  605. derating_discharge_SoC= bms_calc_discharge_derating(est->SoC_percentage_smooth);
  606. derating_discharge_temp_high = bms_calc_discharge_temp_derating(maxTemp);
  607. derating_discharge_temp_low = bms_calc_discharge_temp_derating(minTemp);
  608. derating_discharge_Umin1 = bms_calc_discharge_Umin1(Umin);
  609. // lowest current is choosen to be set as max charge current
  610. if(derating_discharge_temp_low < derating_discharge_temp_high) {
  611. output_derate = derating_discharge_temp_low;
  612. }
  613. else{
  614. output_derate =derating_discharge_temp_high;
  615. }
  616. if(output_derate > derating_discharge_SoC ) {
  617. output_derate = derating_discharge_SoC;
  618. }
  619. if(output_derate >= derating_discharge_Umin1) {
  620. output_derate =derating_discharge_Umin1;
  621. }
  622. est->MaxBatteryDischargeCurrent=output_derate;
  623. return 0;
  624. }
  625. float bms_calc_charge_derating(float SoC) {
  626. float delta_I=0;
  627. float I_C_10=2.5; // Current at C tens
  628. float delta_I_max= BMS_SLAVE_MAX_CURRENT_A - I_C_10 ;
  629. float delta_SoC = 0.15; // Linear interpolation region from 80 to 95 %
  630. if( SoC <= 0.8) {
  631. //return maximum allowed current
  632. return BMS_SLAVE_MAX_CURRENT_A ;
  633. }
  634. else if( SoC > 0.8 && SoC < 0.95) {
  635. //return linear interpolation between max aurrent and C/10
  636. delta_I= (delta_I_max * (0.95 - SoC)) / (delta_SoC);
  637. return I_C_10 + delta_I;
  638. }
  639. else if( SoC >= 0.95 && SoC < 0.999){
  640. // finish loading with C/10
  641. return I_C_10 ;
  642. }
  643. else{
  644. return 0;
  645. }
  646. return -1;
  647. }
  648. float bms_calc_discharge_Umin1(uint16_t Umin) {
  649. if(Umin < BMS_SOC_UMIN_1_DERATE) {
  650. return 0.0;
  651. }
  652. else {
  653. return 25.0;
  654. }
  655. }
  656. float bms_calc_discharge_derating(float SoC) {
  657. float delta_I=0;
  658. float I_C_10=2.5; // Current at C tens
  659. float delta_I_max= BMS_SLAVE_MAX_CURRENT_A - I_C_10 ;
  660. float delta_SoC = 0.15; // Linear interpolation region from 20 to 5 %
  661. if( SoC >= 0.2) {
  662. //return maximum allowed current
  663. return BMS_SLAVE_MAX_CURRENT_A ;
  664. }
  665. else if( SoC < 0.2 && SoC > 0.05) {
  666. //return linear interpolation between max aurrent and C/10
  667. delta_I= (delta_I_max * ( SoC - 0.05)) / (delta_SoC);
  668. return I_C_10 + delta_I;
  669. }
  670. else if(SoC <= 0.05 && SoC > 0.001) {
  671. // finish discharging with C/10
  672. return I_C_10 ;
  673. }
  674. else {
  675. //
  676. return 0;
  677. }
  678. return -1;
  679. }
  680. /*
  681. * @ brief derate maximum charge current
  682. */
  683. float bms_calc_charge_temp_derating(int8_t Temp) {
  684. float maxCurrent;
  685. float I_C1=25.0; // Current at C1 = 25A
  686. // calc derated current for min temperature
  687. if( Temp <= -5) {
  688. maxCurrent= I_C1*0.2;
  689. return maxCurrent;
  690. }
  691. else if(Temp > -5 && Temp <=0) {
  692. maxCurrent=linear_interpolate(-5,0,Temp,I_C1*0.2,I_C1*0.5);
  693. return maxCurrent;
  694. }
  695. else if(Temp >0 && Temp <=25) {
  696. maxCurrent=linear_interpolate(0,25,Temp,I_C1*0.5,I_C1);
  697. return maxCurrent;
  698. }
  699. else if(Temp> 25 && Temp <=35) {
  700. maxCurrent=I_C1;
  701. return maxCurrent;
  702. }
  703. else if(Temp> 35 && Temp <= 45) {
  704. maxCurrent=linear_interpolate(35,45,Temp,I_C1,0);
  705. return maxCurrent;
  706. }
  707. else{
  708. return 0;
  709. }
  710. }
  711. /*
  712. * @ brief derate maximum discharge current
  713. */
  714. float bms_calc_discharge_temp_derating(int8_t Temp) {
  715. float maxCurrent;
  716. float I_C1=25.0; // Current at C1 = 25A
  717. // calc derated current for min temperature
  718. if( Temp <= 0) {
  719. maxCurrent= I_C1*0.2;
  720. return maxCurrent;
  721. }
  722. else if(Temp > 0 && Temp <=25) {
  723. maxCurrent=linear_interpolate(0,25,Temp,I_C1*0.2,I_C1);
  724. return maxCurrent;
  725. }
  726. else if(Temp >25 && Temp <=40) {
  727. maxCurrent=I_C1;
  728. return maxCurrent;
  729. }
  730. else if(Temp> 40 && Temp <=45) {
  731. maxCurrent=I_C1=linear_interpolate(40,45,Temp,I_C1,0);
  732. return maxCurrent;
  733. }
  734. else{
  735. return 0;
  736. }
  737. }
  738. /*
  739. *@brief do linear interpolation
  740. */
  741. float linear_interpolate(int8_t x1,int8_t x2,int8_t x,float y1,float y2) {
  742. // solve linear eq y1= m*x1 + b
  743. // y2= m*x2 + b
  744. float m = (y2-y1)/(float)(x2-x1);
  745. float b = y1 - m*(float)(x1);
  746. //
  747. return m*(float)(x) + b;
  748. }