BMS_SoC_estimator.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822
  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. return 0;
  228. break;
  229. case BMS_SOC_CHARGE_WAIT_FOR_RELAXATION:
  230. // wait a couple of seconds for Relaxation to prevent
  231. // unjustified update of SoC
  232. est->ChargeRelaxationWaitCnt++;
  233. if(est->ChargeRelaxationWaitCnt > 100) {
  234. est->state= BMS_SOC_CHARGE_AREA_HIGH;
  235. }
  236. else {
  237. est->state= BMS_SOC_CHARGE_C_CNT;
  238. }
  239. return 0;
  240. break;
  241. case BMS_SOC_CHARGE_AREA_HIGH:
  242. // open clamp Voltage is higer than threshold
  243. // => Battery is nearly fully charged => SoC from columb counting has to be updated
  244. // set SoC predefined Value
  245. if(est->flags!=BMS_SOC_FLAG_SOC_HIGH) {
  246. // save old SoC value for Smoothing
  247. est->SoC_Ah_smooth=est->SoC_Ah;
  248. est->SoC_percentage_smooth=est->SoC_percentage;
  249. // update SoC value
  250. est->SoC_percentage=est->ChargeThreshold.U_ocv_SoC;
  251. est->SoC_Ah=est->SoC_percentage * est->capacity;
  252. // set high flag to indicate that SoC has already been updated
  253. est->flags=BMS_SOC_FLAG_SOC_HIGH;
  254. }
  255. // continue columb cnt
  256. est->state=BMS_SOC_CHARGE_C_CNT;
  257. return 0;
  258. break;
  259. case BMS_SOC_CHARGE_C_CNT:
  260. // calculate SoC using coulumb counting
  261. est->SoC_Ah=est->SoC_Ah + est->delta_t* (-1)*(float)I_batt/1000 ;
  262. est->SoC_percentage=est->SoC_Ah/est->capacity;
  263. est->state=BMS_SOC_CHARGE_SMOOTHING;
  264. return 0;
  265. break;
  266. case BMS_SOC_CHARGE_SMOOTHING:
  267. // prevents jumps in SoC
  268. if(est->flags == BMS_SOC_FLAG_SOC_100) {
  269. est->ChargeSmoothFlags=0;
  270. est->SoC_percentage_smooth=est->SoC_percentage ;
  271. est->state=BMS_SOC_READY;
  272. return 0; ;
  273. }
  274. else if(est->SoC_percentage > 0.99 && est->flags != BMS_SOC_FLAG_SOC_100) {
  275. // keep SoC at 99,5% until U_bat Max is reached
  276. if(est->ChargeSmoothFlags != BMS_SOC_CHARGE_SMOOTH_99){
  277. est->ChargeSmoothFlags= BMS_SOC_CHARGE_SMOOTH_99;
  278. est->SoC_percentage_smooth=est->SoC_percentage;
  279. est->SoC_Ah_smooth=est->SoC_Ah;
  280. }
  281. else {
  282. // do nothing
  283. }
  284. est->state=BMS_SOC_CHARGE_DERATE;
  285. return 0;
  286. }
  287. else if(est->SoC_percentage >= est->ChargeThreshold.U_ocv_SoC && est->flags != BMS_SOC_FLAG_SOC_HIGH && est->ChargeSmoothFlags != BMS_SOC_CHARGE_SMOOTH_OVERSHOOT) {
  288. // Overshoot event has occured
  289. // this means that the SoC estimated by coulumb counting is higher than
  290. // the SoC based on the OCV => implement slowed coulumb cnt
  291. // save SoC
  292. est->SoC_Ah_smooth=est->SoC_Ah;
  293. est->SoC_percentage_smooth=est->SoC_percentage;
  294. // Set Flag to indicate that overshoot correction is enabled
  295. est->ChargeSmoothFlags=BMS_SOC_CHARGE_SMOOTH_OVERSHOOT;
  296. est->state=BMS_SOC_CHARGE_DERATE;
  297. return 0;
  298. }
  299. else if( est->ChargeSmoothFlags == BMS_SOC_CHARGE_SMOOTH_OVERSHOOT) {
  300. // Smooth out overshoot by slowed down coulumb counting
  301. est->SoC_Ah_smooth=est->SoC_Ah_smooth + est->delta_t* (-1)*(float)I_batt/1000*0.5 ;
  302. est->SoC_percentage_smooth=est->SoC_Ah_smooth/est->capacity;
  303. if( bms_SoC_get_norm(est->SoC_percentage_smooth - est->SoC_percentage) <= 0.001) {
  304. // if Error is smaller than 0.5 % stopp correction
  305. est->ChargeSmoothFlags=0;
  306. est->SoC_percentage_smooth=est->SoC_percentage;
  307. est->SoC_Ah_smooth=est->SoC_Ah;
  308. }
  309. est->state=BMS_SOC_CHARGE_DERATE;
  310. return 0;
  311. }
  312. else if( est->flags == BMS_SOC_FLAG_SOC_HIGH && est->ChargeSmoothFlags !=BMS_SOC_CHARGE_SMOOTH_UNDERSHOOT) {
  313. // Undershoot event hast occured
  314. // this means that SoC estimated by coulumb counting is lower
  315. // than SoC based on the OCV => implement faster coulumb cnt
  316. // Set Flag to indicate that undershoot correction is enabled
  317. est->ChargeSmoothFlags=BMS_SOC_CHARGE_SMOOTH_UNDERSHOOT;
  318. est->state=BMS_SOC_CHARGE_DERATE;
  319. return 0;
  320. }
  321. else if( est->flags == BMS_SOC_FLAG_SOC_HIGH && est->ChargeSmoothFlags ==BMS_SOC_CHARGE_SMOOTH_UNDERSHOOT) {
  322. // smooth out under shoot by speeding up coulumb counting
  323. est->SoC_Ah_smooth=est->SoC_Ah_smooth + est->delta_t* (-1)*(float)I_batt/1000*4 ;
  324. est->SoC_percentage_smooth=est->SoC_Ah_smooth/est->capacity;
  325. if( bms_SoC_get_norm(est->SoC_percentage_smooth - est->SoC_percentage) <= 0.001) {
  326. // if Error is smaller than 0.1 % stopp correction
  327. est->ChargeSmoothFlags=0;
  328. est->SoC_percentage_smooth=est->SoC_percentage;
  329. est->SoC_Ah_smooth=est->SoC_Ah;
  330. }
  331. est->state=BMS_SOC_CHARGE_DERATE;
  332. return 0;
  333. }
  334. else {
  335. est->SoC_Ah_smooth=est->SoC_Ah;
  336. est->SoC_percentage_smooth=est->SoC_percentage;
  337. est->state=BMS_SOC_CHARGE_DERATE;
  338. return 0;
  339. }
  340. return 0;
  341. break;
  342. case BMS_SOC_CHARGE_DERATE:
  343. // derate SoC depending on the SoC and temperature
  344. bms_set_derating(est, temp_min, temp_max);
  345. est->state=BMS_SOC_READY;
  346. return 0;
  347. break;
  348. case BMS_SOC_DISCHARGE:
  349. // Battery is discharged
  350. // if Battery has been charged to 100% SoC remove charge Flag
  351. // no more need to wait for relaxation at charging
  352. est->ChargeRelaxationWaitCnt=0;
  353. if(est->flags==BMS_SOC_FLAG_SOC_100 && est->SoC_percentage < est->ChargeThreshold.U_ocv_SoC) {
  354. est->flags=0;
  355. }
  356. if(est->flags==BMS_SOC_FLAG_SOC_HIGH && est->SoC_percentage < est->ChargeThreshold.U_ocv_SoC) {
  357. // clear BMS_SOC_HIGH flag
  358. est->flags=0;
  359. }
  360. if(est->ChargeSmoothFlags== BMS_SOC_CHARGE_SMOOTH_99 && est->SoC_percentage <0.99) {
  361. // clear BMS_SOC_DISCHARGE_SMOOTH_01 flage
  362. est->ChargeSmoothFlags=0;
  363. }
  364. // calculate OCV
  365. // ToDo: add temperature dependency of Ri
  366. R_i=est->DischargeThreshold.Ri_discharge;
  367. OCV= U_batt_min + R_i *(float)I_batt/1000; // I_batt is positive ;
  368. est->Ocv_calc = OCV;
  369. if(est->flags==BMS_SOC_FLAG_SOC_0) {
  370. // SoC = 0% has been reached keep SoC at 0% until charging starts;
  371. // => do nothing
  372. est->state=BMS_SOC_DISCHARGE_SMOOTHING;
  373. return 0;
  374. }
  375. else if(U_batt_min < est->DischargeThreshold.U_bat_min ) {
  376. // if Cell voltage is lower than a threshold battery is considered fully discharged
  377. // comparison with cell voltage is necessary because battery model might not be accurate enought
  378. est->state=BMS_SOC_DISCHARGE_AREA_EMPTY;
  379. return 0;
  380. }
  381. else if(est->ChargeSmoothFlags== BMS_SOC_DISCHARGE_SMOOTH_01) {
  382. // SoC => 99% wait till U_bat max is reached
  383. est->state=BMS_SOC_CHARGE_SMOOTHING;
  384. return 0;
  385. }
  386. else if(OCV < est->DischargeThreshold.U_ocv_correct) {
  387. // open clamp Voltage is lower than threshold
  388. // => Battery is nearly fully discharged => SoC from columb counting has to be updated
  389. est->state=BMS_SOC_DISCHARGE_WAIT_FOR_RELAXATION;
  390. return 0;
  391. }
  392. else {
  393. // no special Area to take care of
  394. // proceed with columb counting
  395. est->state=BMS_SOC_DISCHARGE_C_CNT;
  396. est->DischargeRelaxationWaitCnt=0;
  397. return 0;
  398. }
  399. return 0;
  400. break;
  401. case BMS_SOC_DISCHARGE_AREA_EMPTY:
  402. // Battery is considered empty => Set SoC to 0%
  403. est->SoC_percentage=0;
  404. est->SoC_Ah=0;
  405. // set Maximum discharge Current to 0
  406. est->MaxBatteryDischargeCurrent =0;
  407. // set empty flag
  408. est->flags=BMS_SOC_FLAG_SOC_0;
  409. est->state=BMS_SOC_DISCHARGE_SMOOTHING;
  410. return 0;
  411. break;
  412. case BMS_SOC_DISCHARGE_WAIT_FOR_RELAXATION:
  413. // wait a couple of seconds for Relaxation to prevent
  414. // unjustified update of SoC
  415. est->DischargeRelaxationWaitCnt++;
  416. if(est->DischargeRelaxationWaitCnt > 100) {
  417. est->state= BMS_SOC_DISCHARGE_AREA_LOW;
  418. }
  419. else {
  420. est->state= BMS_SOC_DISCHARGE_C_CNT;
  421. }
  422. return 0;
  423. break;
  424. case BMS_SOC_DISCHARGE_AREA_LOW:
  425. // open clamp Voltage is lower than threshold
  426. // => Battery is nearly fully dicharged => SoC from columb counting has to be updated
  427. // set SoC predefined Value
  428. if(est->flags!=BMS_SOC_FLAG_SOC_LOW) {
  429. // save SoC for Smoothing
  430. est->SoC_Ah_smooth=est->SoC_Ah;
  431. est->SoC_percentage_smooth=est->SoC_percentage;
  432. est->SoC_percentage=est->DischargeThreshold.U_ocv_SoC;
  433. est->SoC_Ah=est->SoC_percentage * est->capacity;
  434. // set LOW flag to indicate that SoC has already been updated
  435. est->flags=BMS_SOC_FLAG_SOC_LOW;
  436. }
  437. // continue columb cnt
  438. est->state=BMS_SOC_DISCHARGE_C_CNT;
  439. return 0;
  440. break;
  441. case BMS_SOC_DISCHARGE_C_CNT:
  442. // calculate SoC using coulumb counting
  443. est->SoC_Ah=est->SoC_Ah - est->delta_t* (float)I_batt/1000;
  444. est->SoC_percentage= est->SoC_Ah/est->capacity;
  445. est->state=BMS_SOC_DISCHARGE_SMOOTHING;
  446. return 0;
  447. break;
  448. case BMS_SOC_DISCHARGE_SMOOTHING:
  449. // prevents jumps in SoC
  450. if(est->flags == BMS_SOC_FLAG_SOC_0) {
  451. est->ChargeSmoothFlags=0;
  452. est->SoC_percentage_smooth=est->SoC_percentage ;
  453. est->state=BMS_SOC_READY;
  454. return 0; ;
  455. }
  456. else if(est->SoC_percentage <= 0.01 && est->flags != BMS_SOC_FLAG_SOC_0) {
  457. // keep SoC at 0,5% until U_bat Max is reached
  458. if(est->ChargeSmoothFlags != BMS_SOC_DISCHARGE_SMOOTH_01){
  459. est->ChargeSmoothFlags= BMS_SOC_DISCHARGE_SMOOTH_01;
  460. est->SoC_percentage_smooth=est->SoC_percentage;
  461. est->SoC_Ah_smooth=est->SoC_Ah;
  462. }
  463. else {
  464. // do nothing
  465. }
  466. est->state=BMS_SOC_DISCHARGE_DERATE;
  467. return 0;
  468. }
  469. else if(est->SoC_percentage <= est->DischargeThreshold.U_ocv_SoC && est->flags != BMS_SOC_FLAG_SOC_LOW && est->ChargeSmoothFlags != BMS_SOC_DISCHARGE_SMOOTH_OVERSHOOT) {
  470. // Overshoot event has occured
  471. // this means that the SoC estimated by coulumb counting is lower than
  472. // the SoC based on the OCV => implement slowed coulumb cnt
  473. // save SoC
  474. est->SoC_Ah_smooth=est->SoC_Ah;
  475. est->SoC_percentage_smooth=est->SoC_percentage;
  476. // Set Flag to indicate that overshoot correction is enabled
  477. est->ChargeSmoothFlags=BMS_SOC_DISCHARGE_SMOOTH_OVERSHOOT;
  478. est->state=BMS_SOC_DISCHARGE_DERATE;
  479. return 0;
  480. }
  481. else if( est->ChargeSmoothFlags == BMS_SOC_DISCHARGE_SMOOTH_OVERSHOOT) {
  482. // Smooth out overshoot by slowed down coulumb counting
  483. est->SoC_Ah_smooth=est->SoC_Ah_smooth - est->delta_t*(float)I_batt/1000*0.5 ;
  484. est->SoC_percentage_smooth=est->SoC_Ah_smooth/est->capacity;
  485. if( bms_SoC_get_norm(est->SoC_percentage_smooth - est->SoC_percentage) <= 0.001) {
  486. // if Error is smaller than 0.1 % stopp correction
  487. est->ChargeSmoothFlags=0;
  488. est->SoC_percentage_smooth=est->SoC_percentage;
  489. est->SoC_Ah_smooth=est->SoC_Ah;
  490. }
  491. est->state=BMS_SOC_DISCHARGE_DERATE;
  492. return 0;
  493. }
  494. else if( est->flags == BMS_SOC_FLAG_SOC_LOW && est->ChargeSmoothFlags !=BMS_SOC_DISCHARGE_SMOOTH_UNDERSHOOT) {
  495. // Undershoot event has occured
  496. // this means that SoC estimated by coulumb counting is higher
  497. // than SoC based on the OCV => implement faster coulumb cnt
  498. // Set Flag to indicate that undershoot correction is enabled
  499. est->ChargeSmoothFlags=BMS_SOC_DISCHARGE_SMOOTH_UNDERSHOOT;
  500. est->state=BMS_SOC_CHARGE_DERATE;
  501. return 0;
  502. }
  503. else if( est->flags == BMS_SOC_FLAG_SOC_LOW && est->ChargeSmoothFlags ==BMS_SOC_DISCHARGE_SMOOTH_UNDERSHOOT) {
  504. // smooth out under shoot by speeding up coulumb counting
  505. est->SoC_Ah_smooth=est->SoC_Ah_smooth - est->delta_t*(float)I_batt/1000*4 ;
  506. est->SoC_percentage_smooth=est->SoC_Ah_smooth/est->capacity;
  507. if( bms_SoC_get_norm(est->SoC_percentage_smooth - est->SoC_percentage) <= 0.001) {
  508. // if Error is smaller than 0.1 % stopp correction
  509. est->ChargeSmoothFlags=0;
  510. est->SoC_percentage_smooth=est->SoC_percentage;
  511. est->SoC_Ah_smooth=est->SoC_Ah;
  512. }
  513. est->state=BMS_SOC_DISCHARGE_DERATE;
  514. return 0;
  515. }
  516. else {
  517. est->SoC_percentage_smooth=est->SoC_percentage;
  518. est->SoC_Ah_smooth=est->SoC_Ah;
  519. est->state=BMS_SOC_DISCHARGE_DERATE;
  520. return 0;
  521. }
  522. return 0;
  523. break;
  524. case BMS_SOC_DISCHARGE_DERATE:
  525. // derate SoC depending on the SoC and temperature
  526. bms_set_derating(est, temp_min, temp_max);
  527. est->state=BMS_SOC_READY;
  528. return 0;
  529. break;
  530. case BMS_SOC_REST:
  531. // no charging or discharging => do nothing
  532. est->state=BMS_SOC_READY;
  533. return 0;
  534. break;
  535. case BMS_SOC_READY:
  536. // SoC Estimation complete do nothing
  537. // push FIFO
  538. pushSoCFiFo(est);
  539. return 0;
  540. break;
  541. }
  542. }
  543. /*
  544. * set all SOC FIFO entries to start value;
  545. * SoC FIFO necessary to give inverter time to react to derating and Master to transmit Derating info to inverter
  546. */
  547. uint32_t initSoCFifo(MASTER_CAN0_STRUCT_t* s) {
  548. uint8_t i;
  549. for(i=0;i<SOC_SMOOTH_FIFO_SIZE;i++) {
  550. s->SoC_estimator.SoC_percentage_smooth_FiFo[i]=s->SoC_estimator.SoC_percentage_smooth ;
  551. }
  552. return TRUE;
  553. }
  554. uint32_t pushSoCFiFo(MASTER_SOC_ESTIMATOR_t* est) {
  555. uint8_t i;
  556. for(i=SOC_SMOOTH_FIFO_SIZE-1;i>0;i--) {
  557. est->SoC_percentage_smooth_FiFo[i]=est->SoC_percentage_smooth_FiFo[i-1];
  558. }
  559. est->SoC_percentage_smooth_FiFo[0]=est->SoC_percentage_smooth;
  560. return TRUE;
  561. }
  562. float bms_SoC_get_norm(float value) {
  563. if(value >=0) {
  564. return value;
  565. }
  566. else {
  567. return value * (-1);
  568. }
  569. }
  570. /*
  571. * @brief set Derating
  572. */
  573. uint32_t bms_set_derating(MASTER_SOC_ESTIMATOR_t* est, int8_t minTemp, int8_t maxTemp) {
  574. float derating_charge_temp_high;
  575. float derating_charge_temp_low;
  576. float derating_charge_SoC;
  577. float output_derate;
  578. float derating_discharge_temp_high;
  579. float derating_discharge_temp_low;
  580. float derating_discharge_SoC;
  581. // charging
  582. derating_charge_SoC=bms_calc_charge_derating(est->SoC_percentage_smooth);
  583. derating_charge_temp_high=bms_calc_charge_temp_derating(maxTemp);
  584. derating_charge_temp_low=bms_calc_charge_temp_derating(minTemp);
  585. // lowest current is choosen to be set as max charge current
  586. if(derating_charge_temp_low < derating_charge_temp_high) {
  587. output_derate = derating_charge_temp_low;
  588. }
  589. else{
  590. output_derate =derating_charge_temp_high;
  591. }
  592. if(output_derate > derating_charge_SoC ) {
  593. output_derate = derating_charge_SoC;
  594. }
  595. est->MaxBatteryChargeCurrent=output_derate;
  596. // discharging
  597. derating_discharge_SoC= bms_calc_discharge_derating(est->SoC_percentage_smooth);
  598. derating_discharge_temp_high = bms_calc_discharge_temp_derating(maxTemp);
  599. derating_discharge_temp_low = bms_calc_discharge_temp_derating(minTemp);
  600. // lowest current is choosen to be set as max charge current
  601. if(derating_discharge_temp_low < derating_discharge_temp_high) {
  602. output_derate = derating_discharge_temp_low;
  603. }
  604. else{
  605. output_derate =derating_discharge_temp_high;
  606. }
  607. if(output_derate > derating_discharge_SoC ) {
  608. output_derate = derating_discharge_SoC;
  609. }
  610. est->MaxBatteryDischargeCurrent=output_derate;
  611. return 0;
  612. }
  613. float bms_calc_charge_derating(float SoC) {
  614. float delta_I=0;
  615. float I_C_10=2.5; // Current at C tens
  616. float delta_I_max= BMS_SLAVE_MAX_CURRENT_A - I_C_10 ;
  617. float delta_SoC = 0.15; // Linear interpolation region from 80 to 95 %
  618. if( SoC <= 0.8) {
  619. //return maximum allowed current
  620. return BMS_SLAVE_MAX_CURRENT_A ;
  621. }
  622. else if( SoC > 0.8 && SoC < 0.95) {
  623. //return linear interpolation between max aurrent and C/10
  624. delta_I= (delta_I_max * (0.95 - SoC)) / (delta_SoC);
  625. return I_C_10 + delta_I;
  626. }
  627. else if( SoC >= 0.95 && SoC < 0.999){
  628. // finish loading with C/10
  629. return I_C_10 ;
  630. }
  631. else{
  632. return 0;
  633. }
  634. return -1;
  635. }
  636. float bms_calc_discharge_derating(float SoC) {
  637. float delta_I=0;
  638. float I_C_10=2.5; // Current at C tens
  639. float delta_I_max= BMS_SLAVE_MAX_CURRENT_A - I_C_10 ;
  640. float delta_SoC = 0.15; // Linear interpolation region from 20 to 5 %
  641. if( SoC >= 0.2) {
  642. //return maximum allowed current
  643. return BMS_SLAVE_MAX_CURRENT_A ;
  644. }
  645. else if( SoC < 0.2 && SoC > 0.05) {
  646. //return linear interpolation between max aurrent and C/10
  647. delta_I= (delta_I_max * ( SoC - 0.05)) / (delta_SoC);
  648. return I_C_10 + delta_I;
  649. }
  650. else if(SoC <= 0.05 && SoC > 0.001) {
  651. // finish discharging with C/10
  652. return I_C_10 ;
  653. }
  654. else {
  655. //
  656. return 0;
  657. }
  658. return -1;
  659. }
  660. /*
  661. * @ brief derate maximum charge current
  662. */
  663. float bms_calc_charge_temp_derating(int8_t Temp) {
  664. float maxCurrent;
  665. float I_C1=25.0; // Current at C1 = 25A
  666. // calc derated current for min temperature
  667. if( Temp <= -5) {
  668. maxCurrent= I_C1*0.2;
  669. return maxCurrent;
  670. }
  671. else if(Temp > -5 && Temp <=0) {
  672. maxCurrent=linear_interpolate(-5,0,Temp,I_C1*0.2,I_C1*0.5);
  673. return maxCurrent;
  674. }
  675. else if(Temp >0 && Temp <=25) {
  676. maxCurrent=linear_interpolate(0,25,Temp,I_C1*0.5,I_C1);
  677. return maxCurrent;
  678. }
  679. else if(Temp> 25 && Temp <=35) {
  680. maxCurrent=I_C1;
  681. return maxCurrent;
  682. }
  683. else if(Temp> 35 && Temp <= 45) {
  684. maxCurrent=linear_interpolate(35,45,Temp,I_C1,0);
  685. return maxCurrent;
  686. }
  687. else{
  688. return 0;
  689. }
  690. }
  691. /*
  692. * @ brief derate maximum discharge current
  693. */
  694. float bms_calc_discharge_temp_derating(int8_t Temp) {
  695. float maxCurrent;
  696. float I_C1=25.0; // Current at C1 = 25A
  697. // calc derated current for min temperature
  698. if( Temp <= 0) {
  699. maxCurrent= I_C1*0.2;
  700. return maxCurrent;
  701. }
  702. else if(Temp > 0 && Temp <=25) {
  703. maxCurrent=linear_interpolate(0,25,Temp,I_C1*0.2,I_C1);
  704. return maxCurrent;
  705. }
  706. else if(Temp >25 && Temp <=40) {
  707. maxCurrent=I_C1;
  708. return maxCurrent;
  709. }
  710. else if(Temp> 40 && Temp <=45) {
  711. maxCurrent=I_C1=linear_interpolate(40,45,Temp,I_C1,0);
  712. return maxCurrent;
  713. }
  714. else{
  715. return 0;
  716. }
  717. }
  718. /*
  719. *@brief do linear interpolation
  720. */
  721. float linear_interpolate(int8_t x1,int8_t x2,int8_t x,float y1,float y2) {
  722. // solve linear eq y1= m*x1 + b
  723. // y2= m*x2 + b
  724. float m = (y2-y1)/(float)(x2-x1);
  725. float b = y1 - m*(float)(x1);
  726. //
  727. return m*(float)(x) + b;
  728. }