├── ElectrochemicalCellModel ├── ChargingCycleSimulation.m ├── ChargingcycleSimulation(CP_CV).m ├── E1model.mat ├── E2_DYN_P25.mat ├── E2_OCV_P25 .mat ├── E2model.mat ├── E2model2RC .mat ├── E2model2RCmatlab .mat ├── OCVfromSOCtemp.m ├── P14model-ocv.mat ├── PCMsim.m ├── README.md ├── SCMsim.m ├── SOCfromOCVtemp.m ├── getParamESC.m ├── processDynamic.m ├── processOCV.m ├── pulseData.mat ├── pulseModel.mat ├── runProcessDynamic.m ├── runProcessOCV.m ├── setupDynData.m ├── setupSimVehicle.m ├── setupSimVehicleplots.m ├── simCell.m ├── simCellSimulation.m ├── simVehicle.m └── tuneModel.m ├── README.md ├── State_Of_Charge ├── CellData.mat ├── CellModel.mat ├── E2model.mat ├── EKFinAction.m ├── KFinaction.m ├── LICENSE ├── OCVfromSOCtemp.m ├── P14model.mat ├── PANPackData.mat ├── PAN_CAPSTONE_DATA.mat ├── PANdata_P25.mat ├── PANdata_P45.mat ├── PANmodel.mat ├── README.md ├── SOCfromOCVtemp.m ├── SimpleEKF.m ├── bardeltadata.m ├── dOCVfromSOCtemp.m ├── getParamESC.m ├── initEKF.m ├── initSPKF.m ├── initSPKFbd.m ├── iterEKF.m ├── iterSPKF.m ├── iterSPKFbd.m ├── simCell.m ├── tuneEKF.m ├── udds.txt ├── wrapper.m ├── wrapperEKF.m ├── wrapperSPKF.m └── wrapperSPKFbd.m └── images ├── accforce.JPG ├── actualspeed.JPG ├── battSOC.JPG ├── battcurrent.JPG ├── battpower.JPG ├── blockdiagram.JPG ├── demandtorque.JPG ├── desaccforce.JPG ├── desireedacc.JPG ├── dragf2.JPG ├── dragforces.JPG ├── eqmass.JPG ├── hysteresis.JPG ├── instantaneoushysteresis.JPG ├── modelequation.JPG ├── motorpower.JPG ├── motorspeed.JPG ├── opequation.JPG ├── overallhysteresis.JPG ├── randlesscircuit.JPG ├── range.JPG ├── resistor_current.JPG ├── statespaceformofcell.JPG ├── unexplainedpart.JPG └── unexplainedpart2.JPG /ElectrochemicalCellModel/ChargingCycleSimulation.m: -------------------------------------------------------------------------------- 1 | addpath E:\BMS\ECM\Matlabfiles\work\readonly 2 | load E:\BMS\ECM\Matlabfiles\work\readonly\E1model.mat 3 | 4 | maxtime = 10001; T = 25; % Simulation run time, temperature, 5 | q = getParamESC('QParam',T,model); 6 | rc = exp(-1./abs(getParamESC('RCParam',T,model))); 7 | r = getParamESC('RParam',T,model); 8 | m = getParamESC('MParam',T,model); 9 | g = getParamESC('GParam',T,model); 10 | r0 = getParamESC('R0Param',T,model); 11 | maxV = 4.15; % maximum cell voltage of 4.15 V 12 | storez = zeros([maxtime 1]); % create storage for SOC 13 | storev = zeros([maxtime 1]); % create storage for voltage 14 | storei = zeros([maxtime 1]); % create storage for current 15 | storep = zeros([maxtime 1]); % create storage for power 16 | z = 0.5; irc = 0; h = -1; % initialize to 50% SOC 17 | CC = 9; % constant current of 9 A in CC/CV charge 18 | % Simulate CC/CV 19 | 20 | for k = 1:maxtime 21 | v = OCVfromSOCtemp(z,T,model) + m*h - r*irc; % fixed voltage 22 | ik = (v - maxV)/r0; % compute test ik to achieve maxV 23 | ik = max(-CC,ik); % but limit current to no more than CC in mag 24 | z = z - (1/3600)*ik/q; % Update cell SOC 25 | irc = rc*irc + (1-rc)*ik; % Update resistor currents 26 | fac = exp(-abs(g.*ik)./(3600*q));% Update hysteresis voltages 27 | storez(k) = z; % Store SOC for later plotting 28 | storev(k) = v - ik*r0; 29 | storei(k) = ik; % store current for later plotting 30 | storep(k) = ik*storev(k); 31 | end 32 | 33 | time = 0:maxtime -1; 34 | subplot(2,2,1); plot(time,100*storez); 35 | title('State of charge versus time'); 36 | xlabel('Time (s)'); ylabel('SOC (%)'); ylim([49 101]); grid on 37 | subplot(2,2,2); plot(time,storev); 38 | title('Terminal voltage versus time'); 39 | xlabel('Time (s)'); ylabel('Voltage (V)'); 40 | ylim([3.94 4.16]); grid on 41 | subplot(2,2,3); plot(time,storei); 42 | title('Cell current versus time'); 43 | xlabel('Time (s)'); ylabel('Current (A)'); 44 | ylim([-10 0.3]); grid on 45 | subplot(2,2,4); plot(time,storep); 46 | title('Cell power versus time'); 47 | xlabel('Time (s)'); ylabel('Power (W)'); 48 | ylim([-40 1]); grid on -------------------------------------------------------------------------------- /ElectrochemicalCellModel/ChargingcycleSimulation(CP_CV).m: -------------------------------------------------------------------------------- 1 | addpath E:\BMS\ECM\Matlabfiles\work\readonly 2 | load E:\BMS\ECM\Matlabfiles\work\readonly\E1model.mat 3 | 4 | % Get ESC model parameters 5 | maxtime = 3001; T = 25; % Simulation run time, temperature 6 | q = getParamESC('QParam',T,model); 7 | rc = exp(-1./abs(getParamESC('RCParam',T,model))); 8 | r = (getParamESC('RParam',T,model)); 9 | m = getParamESC('MParam',T,model); 10 | g = getParamESC('GParam',T,model); 11 | r0 = getParamESC('R0Param',T,model); 12 | maxV = 4.2; % maximum cell voltage of 4.15 V 13 | 14 | storez = zeros([maxtime 1]); % create storage for SOC 15 | storev = zeros([maxtime 1]); % create storage for voltage 16 | storei = zeros([maxtime 1]); % create storage for current 17 | storep = zeros([maxtime 1]); % create storage for power 18 | z = 0.5; irc = 0; h = -1; % initialize to 50% SOC, resting 19 | 20 | % Simulate CP/CV 21 | z = 0.5; irc = 0; h = -1; % initialize to 50% SOC, resting 22 | CP = 40; % constant power limit of 35 W in CP/CV charge 23 | 24 | for k = 1:maxtime, 25 | v = OCVfromSOCtemp(z,T,model) + m*h - r*irc; % fixed voltage 26 | 27 | % try CP first 28 | ik = (v - sqrt(v^2 - 4*r0*(-CP)))/(2*r0); 29 | if v - ik*r0 > maxV, % too much! 30 | ik = (v - maxV)/r0; % do CV instead 31 | end 32 | 33 | z = z - (1/3600)*ik/q; % Update cell SOC 34 | irc = rc*irc + (1-rc)*ik; % Update resistor currents 35 | fac = exp(-abs(g.*ik)./(3600*q)); 36 | h = fac.*h + (fac-1).*sign(ik); % Update hysteresis voltages 37 | storez(k) = z; % Store SOC for later plotting 38 | storev(k) = v - ik*r0; 39 | storei(k) = ik; % store current for later plotting 40 | storep(k) = ik*storev(k); 41 | end % for k 42 | 43 | time = 0:maxtime -1; 44 | subplot(2,2,1); plot(time,100*storez); 45 | title('State of charge versus time'); 46 | xlabel('Time (s)'); ylabel('SOC (%)'); ylim([49 101]); grid on 47 | 48 | subplot(2,2,2); plot(time,storev); 49 | title('Terminal voltage versus time'); 50 | xlabel('Time (s)'); ylabel('Voltage (V)'); 51 | ylim([3.94 4.21]); grid on 52 | 53 | subplot(2,2,3); plot(time,storei); 54 | title('Cell current versus time'); 55 | xlabel('Time (s)'); ylabel('Current (A)'); 56 | ylim([-10 0.3]); grid on 57 | 58 | subplot(2,2,4); plot(time,storep); 59 | title('Cell power versus time'); 60 | xlabel('Time (s)'); ylabel('Power (W)'); 61 | ylim([-40 1]); grid on -------------------------------------------------------------------------------- /ElectrochemicalCellModel/E1model.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/ElectrochemicalCellModel/E1model.mat -------------------------------------------------------------------------------- /ElectrochemicalCellModel/E2_DYN_P25.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/ElectrochemicalCellModel/E2_DYN_P25.mat -------------------------------------------------------------------------------- /ElectrochemicalCellModel/E2_OCV_P25 .mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/ElectrochemicalCellModel/E2_OCV_P25 .mat -------------------------------------------------------------------------------- /ElectrochemicalCellModel/E2model.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/ElectrochemicalCellModel/E2model.mat -------------------------------------------------------------------------------- /ElectrochemicalCellModel/E2model2RC .mat: -------------------------------------------------------------------------------- 1 | # Created by Octave 4.2.2, Mon Feb 04 18:11:09 2019 UTC 2 | # name: model 3 | # type: scalar struct 4 | # ndims: 2 5 | 1 1 6 | # length: 20 7 | # name: OCV0 8 | # type: matrix 9 | # rows: 1 10 | # columns: 201 11 | 3.030836689472196 3.169961765077647 3.246546136722454 3.304624710881497 3.351788318260179 3.392079858375642 3.427542809259539 3.459308933086329 3.48845834377828 3.515064718849477 3.539415282503497 3.56229005089512 3.583476801397727 3.603453713617947 3.622046674144973 3.639452939947117 3.655552081295931 3.670258427471886 3.682837229269422 3.692566357962985 3.699343565043186 3.704214921933865 3.708640806682776 3.712516478167002 3.716764502430098 3.720760804422888 3.72459036631361 3.728521139648887 3.732625556640022 3.736797880316326 3.740858058131411 3.745042931709435 3.749371514208518 3.753605033454966 3.758085549384994 3.762535128126995 3.766867132242773 3.771297413543564 3.77568299945893 3.780108995521102 3.78432597237866 3.788666672457941 3.792829735902654 3.796991491757384 3.801026468862268 3.804869016714674 3.808591606520827 3.812342903266781 3.815783741469219 3.81901029060391 3.822462649680749 3.825652705413007 3.828784929088853 3.831828462512594 3.83504605065465 3.8381715299483 3.841209266373306 3.844270451045133 3.847455054615506 3.850485272275481 3.853382580865354 3.856333751427004 3.85903736950342 3.861832940786382 3.864269094730055 3.866684092699114 3.868849604642005 3.870750625794967 3.872601511668613 3.874198734930453 3.875903545797708 3.877426603098015 3.878886234941777 3.880137784822974 3.881729555551868 3.882985173800874 3.884451394677357 3.885888418745554 3.887189066206249 3.888654837074608 3.890349000186788 3.891773082963854 3.893450148828022 3.894923243074664 3.896568041582614 3.898148312739866 3.899708000886823 3.901382685028944 3.903259198690997 3.904660355664011 3.906507152263128 3.90818394205928 3.909917265956043 3.911788693146466 3.913530483504351 3.915357172902234 3.917229430872048 3.919020775372271 3.920947737641486 3.92272669049051 3.924802107277779 3.926658988611658 3.928551631244436 3.930494259311822 3.932338228640996 3.934138088074443 3.935867874069019 3.937818783018235 3.93970352375448 3.941412178355832 3.943377388464963 3.945247816104685 3.947055121965991 3.948895969319864 3.950728131179928 3.952687861497738 3.954374835456365 3.956140432170045 3.958138020027797 3.959887372762663 3.961801458191638 3.963330949225218 3.965135984654085 3.966889050192 3.968475263030942 3.970322272578731 3.971897644602017 3.973578901386172 3.975075685808416 3.976663083636288 3.978340049908935 3.979727572152326 3.981347199199541 3.982824528384285 3.984342407500325 3.98579080664983 3.987120956248151 3.988481864792482 3.990051691191573 3.991409610070805 3.992722792722426 3.994052667199696 3.99553040552875 3.99686295933407 3.998260423320517 3.999590856131321 4.001187701245878 4.002587567501942 4.004108312232977 4.005643789136642 4.007335799624784 4.00874100440808 4.010496648025219 4.012298947154799 4.014039046221988 4.015782586324894 4.017772618378078 4.019713518764206 4.021814197470903 4.024054117572805 4.026341321876815 4.028449660433382 4.030748211610907 4.032872527104055 4.035199790750515 4.037147246037291 4.039200595027868 4.041190051705816 4.043112519588017 4.044856292774197 4.046872767005808 4.048566552291055 4.050282820643486 4.051887907807582 4.053671155018266 4.055408885560165 4.057150827080005 4.058917078496733 4.060580665580823 4.062559847842178 4.064433558621604 4.06646437679668 4.068289781142516 4.070404461175014 4.072601805410285 4.074724436597569 4.076952263650583 4.079523885689959 4.08211497603818 4.084945176549406 4.087857603610774 4.091089752714136 4.094412579238985 4.098087034973292 4.10213358582538 4.10639223642507 4.111141478623015 4.116569685264754 4.122766057243014 4.130285422328519 4.144807577133176 12 | 13 | 14 | # name: OCVrel 15 | # type: matrix 16 | # rows: 1 17 | # columns: 201 18 | -0.0004203629493713032 -0.0005783391026064987 -0.0005482126331611386 -0.0005320503762555926 -0.0005138554684612915 -0.0004956873205226344 -0.0004876432509413714 -0.0004709209859506418 -0.0004619284515836195 -0.0004485749925112358 -0.0004303452917946651 -0.0004158647899307154 -0.0004035032757360672 -0.0003926085064518378 -0.0003811496995352971 -0.0003691363465909111 -0.0003481561278305072 -0.0003241845159195991 -0.0002802290929736813 -0.0002019326622334465 -0.0001107449267268941 -3.790847573765372e-05 7.348204166651048e-08 1.982493443641819e-05 1.890285310324234e-05 1.527781699724654e-05 1.349248684771598e-05 9.235424202617249e-06 -1.462455864409746e-06 -1.453113656187792e-05 -2.473660102292816e-05 -3.534300950586579e-05 -4.357615098402996e-05 -4.954436070469951e-05 -5.595694182668391e-05 -5.714052509852175e-05 -5.586284666087326e-05 -5.237102489256239e-05 -5.220249064448205e-05 -5.100662812996354e-05 -4.600700722849172e-05 -4.3921058848726e-05 -4.170873765826741e-05 -3.799653197804252e-05 -3.528833783289631e-05 -2.963585228266923e-05 -1.934872102653938e-05 -1.766547995781084e-05 -1.104816369800213e-05 -6.290333469478786e-06 -6.437296827540309e-06 -8.990823384667016e-06 -5.974764805329978e-06 -4.62198779734926e-06 -8.456268885981365e-06 -3.553105806264644e-06 1.211057797841447e-06 3.232709573943082e-06 4.659759256327717e-06 6.060475767885682e-06 1.06741421249725e-05 1.277406689781254e-05 1.583222406310861e-05 1.58226143080902e-05 1.773563965350239e-05 1.672972631453185e-05 2.204220146089341e-05 3.244555246293456e-05 4.536713109433999e-05 6.366315475195e-05 7.657838166909077e-05 9.787377790862986e-05 0.000118072210378981 0.0001450527339084541 0.0001590539640336436 0.0001786151155425804 0.0001929519447210327 0.0002037458708093398 0.0002158874721571568 0.0002173894492546367 0.0002140802592555234 0.0002173688013931463 0.0002106804886045223 0.0002167914723105073 0.0002108071416654823 0.0002068927750310655 0.0002098894489718271 0.0002064803665690604 0.0001974778323616955 0.0002018782051713917 0.0001959914615167019 0.000193299624131949 0.0001892252959003712 0.0001830157559850239 0.0001810303751376892 0.0001790580268125204 0.0001726302442335687 0.0001697395544924682 0.000168470860940041 0.0001656735838350954 0.0001587616135695696 0.0001583105482465969 0.0001607687307868683 0.0001592748913171498 0.0001636542370719233 0.0001656468546509848 0.0001710090062293322 0.0001729608720402813 0.0001740591865933255 0.0001847920083153727 0.0001854575365144241 0.0001884979284049325 0.0001935405600570864 0.0002019671216347925 0.0002086518100999233 0.0002102033105023057 0.0002213277423548313 0.0002269347985682945 0.0002290225277951665 0.0002359808380382739 0.0002373937228970263 0.0002514163775869327 0.0002556691839309394 0.0002595825076242915 0.0002717561102861297 0.0002714281413544072 0.000280338423121521 0.0002821595634867881 0.0002936983066625561 0.0002946903827346676 0.0002963684800755954 0.0003058523811542491 0.0003055477307257717 0.00031012456407565 0.0003089101412488882 0.0003109102079985934 0.0003171368794770799 0.0003199661100291788 0.0003177819330097126 0.0003197148953633416 0.0003236332110078603 0.000324670146330631 0.000321258389806546 0.0003250906359607233 0.0003257563178028704 0.0003292994874854826 0.0003230221147628428 0.000329687741218402 0.0003313350771147605 0.0003343668638104913 0.0003341903133026104 0.0003482445164765586 0.0003536199694961852 0.00037064561271708 0.000387424759796793 0.0004127126011074544 0.0004329800023132124 0.0004580816457035275 0.0004753161267108532 0.0004887109690373024 0.0004902298112859219 0.0004969970342553607 0.0004907325029984244 0.00048541302401405 0.0004700313990741216 0.0004642250387850839 0.0004535943665710161 0.0004429476897351866 0.0004323611940512278 0.0004268700284725597 0.0004125349787958534 0.0004041444068790324 0.0003995006371030998 0.0003953617896419367 0.0003919039898067644 0.0003880941987257832 0.0003844935935138284 0.0003808907193317404 0.0003819443874818669 0.0003757257480448266 0.000377689116895975 0.0003712632929778804 0.0003754017615423245 0.0003719612145040978 0.0003721404974193012 0.0003766230107390574 0.0003797130824694399 0.0003733051129779532 0.0003762710719386918 0.0003740484575761436 0.000373866132643822 0.0003684519861634443 0.0003665136328406862 0.0003614498554054373 0.0003557580015110341 0.0003518529455924448 0.0003404241929370491 0.0003238034109131972 0.000299570003101876 0.0002552971087413972 9.123802185063061e-05 19 | 20 | 21 | # name: SOC 22 | # type: matrix 23 | # rows: 1 24 | # columns: 201 25 | 0 0.005 0.01 0.015 0.02 0.025 0.03 0.035 0.04 0.045 0.05 0.055 0.06 0.065 0.07000000000000001 0.075 0.08 0.08500000000000001 0.09 0.095 0.1 0.105 0.11 0.115 0.12 0.125 0.13 0.135 0.14 0.145 0.15 0.155 0.16 0.165 0.17 0.175 0.18 0.185 0.19 0.195 0.2 0.205 0.21 0.215 0.22 0.225 0.23 0.235 0.24 0.245 0.25 0.255 0.26 0.265 0.27 0.275 0.28 0.285 0.29 0.295 0.3 0.305 0.31 0.315 0.32 0.325 0.33 0.335 0.34 0.345 0.35 0.355 0.36 0.365 0.37 0.375 0.38 0.385 0.39 0.395 0.4 0.405 0.41 0.415 0.42 0.425 0.43 0.435 0.44 0.445 0.45 0.455 0.46 0.465 0.47 0.475 0.48 0.485 0.49 0.495 0.5 0.505 0.51 0.515 0.52 0.5249999999999999 0.53 0.5349999999999999 0.54 0.5449999999999999 0.55 0.5549999999999999 0.5600000000000001 0.5649999999999999 0.5700000000000001 0.575 0.5800000000000001 0.585 0.59 0.595 0.6 0.605 0.61 0.615 0.62 0.625 0.63 0.635 0.64 0.645 0.6499999999999999 0.655 0.6599999999999999 0.665 0.6699999999999999 0.675 0.6799999999999999 0.6850000000000001 0.6899999999999999 0.6950000000000001 0.7 0.7050000000000001 0.71 0.715 0.72 0.725 0.73 0.735 0.74 0.745 0.75 0.755 0.76 0.765 0.77 0.775 0.78 0.785 0.79 0.7949999999999999 0.8 0.8049999999999999 0.8100000000000001 0.8149999999999999 0.8200000000000001 0.825 0.83 0.835 0.84 0.845 0.85 0.855 0.86 0.865 0.87 0.875 0.88 0.885 0.89 0.895 0.9 0.905 0.91 0.915 0.92 0.925 0.9299999999999999 0.9350000000000001 0.9399999999999999 0.945 0.95 0.955 0.96 0.965 0.97 0.975 0.98 0.985 0.99 0.995 1 26 | 27 | 28 | # name: OCV 29 | # type: matrix 30 | # rows: 1 31 | # columns: 178 32 | 2.49 2.5 2.51 2.52 2.53 2.54 2.55 2.56 2.57 2.58 2.59 2.6 2.61 2.62 2.63 2.64 2.65 2.66 2.67 2.68 2.69 2.7 2.71 2.72 2.73 2.74 2.75 2.76 2.77 2.78 2.79 2.8 2.81 2.82 2.83 2.84 2.85 2.86 2.87 2.88 2.89 2.9 2.91 2.92 2.93 2.94 2.95 2.96 2.97 2.98 2.99 3 3.01 3.02 3.03 3.04 3.05 3.06 3.07 3.08 3.09 3.1 3.11 3.12 3.13 3.14 3.15 3.16 3.17 3.18 3.19 3.2 3.21 3.22 3.23 3.24 3.25 3.26 3.27 3.28 3.29 3.3 3.31 3.32 3.33 3.34 3.35 3.36 3.37 3.38 3.39 3.4 3.41 3.42 3.43 3.44 3.45 3.46 3.47 3.48 3.49 3.5 3.51 3.52 3.53 3.54 3.55 3.56 3.57 3.58 3.59 3.6 3.61 3.62 3.63 3.64 3.65 3.66 3.67 3.68 3.69 3.7 3.71 3.72 3.73 3.74 3.75 3.76 3.77 3.78 3.79 3.8 3.81 3.82 3.83 3.84 3.85 3.86 3.87 3.88 3.89 3.9 3.91 3.92 3.93 3.94 3.95 3.96 3.97 3.98 3.99 4 4.01 4.02 4.029999999999999 4.04 4.05 4.06 4.069999999999999 4.08 4.09 4.1 4.109999999999999 4.12 4.13 4.14 4.149999999999999 4.16 4.17 4.18 4.19 4.2 4.21 4.22 4.23 4.24 4.25 4.26 33 | 34 | 35 | # name: SOC0 36 | # type: matrix 37 | # rows: 1 38 | # columns: 178 39 | -0.01944047225841853 -0.01908088386452936 -0.01872129547064018 -0.01836170707675101 -0.01800211868286182 -0.01764253028897265 -0.01728294189508347 -0.0169233535011943 -0.01656376510730511 -0.01620417671341595 -0.01584458831952676 -0.01548499992563759 -0.01512541153174841 -0.01476582313785924 -0.01440623474397006 -0.01404664635008089 -0.0136870579561917 -0.01332746956230254 -0.01296788116841335 -0.01260829277452418 -0.012248704380635 -0.01188911598674583 -0.01152952759285664 -0.01116993919896748 -0.01081035080507829 -0.01045076241118912 -0.01009117401729996 -0.009731585623410774 -0.009371997229521587 -0.009012408835632419 -0.008652820441743253 -0.008293232047854067 -0.007933643653964898 -0.007574055260075716 -0.007214466866186549 -0.006854878472297364 -0.006495290078408196 -0.006135701684519011 -0.005776113290629844 -0.005416524896740661 -0.005056936502851491 -0.004697348108962308 -0.004337759715073141 -0.003978171321183956 -0.00361858292729479 -0.003258994533405605 -0.002899406139516437 -0.002539817745627254 -0.002180229351738085 -0.001820640957848902 -0.001461052563959734 -0.001101464170070566 -0.0007418757761813821 -0.0003764872585540595 1.046074941870284e-05 0.0004328135729967415 0.0008934600732895066 0.001357116299967736 0.001820772526645945 0.002284428753324155 0.002748084980002384 0.003211741206680594 0.003675397433358823 0.004139053660037032 0.004602709886715261 0.00506636611339347 0.005530022340071701 0.005993678566749909 0.006457334793428139 0.006920991020106348 0.007384647246784577 0.007848303473462787 0.008311959700140995 0.008775615926819224 0.009262906854475079 0.009815480165824543 0.01047162908708113 0.0112738519708771 0.01222145868073551 0.01317169092380704 0.01412192316687858 0.01507215540995016 0.01602238765302175 0.01697261989609328 0.01792487372165873 0.01890850794336975 0.01995902824495393 0.02111017143143909 0.02239422210894344 0.02371434279076274 0.02503446347258206 0.02635458415440142 0.02768041163348973 0.029046151469329 0.03048467402962366 0.03203084798995363 0.03367258916058884 0.03531433033122405 0.0369589643684468 0.03863692473462134 0.04038520579774925 0.04224166478185761 0.04420437883082204 0.0461671385195869 0.04815710599934925 0.05021487560489978 0.05237863057290506 0.05464741243009535 0.05692486932924419 0.05925716477984761 0.06169010971229291 0.06425895475122256 0.06686197748773541 0.06953062087525679 0.07234164214153865 0.07531357918422422 0.07835386804262401 0.08158850429551849 0.08517662218430382 0.08901267518141957 0.09380596397550664 0.1005779654119796 0.1116769540023469 0.1241280588290682 0.1367289787323246 0.1489905560867572 0.1607520482748504 0.1721797846083196 0.1835427943887531 0.194977623179204 0.2066615304370599 0.2187344371154958 0.2319462869469098 0.2463092458284545 0.2619424046429776 0.2780371984419544 0.2942986528158686 0.3118447516344746 0.3332648818635442 0.3667696950667215 0.3991989575595252 0.4299217977652666 0.4594553497569496 0.4869844302726404 0.5140069453828792 0.5413059548152903 0.5687955860196923 0.5963930165939205 0.6258796670145836 0.6571110735642877 0.6910692650718372 0.7257386294821117 0.7598220968721724 0.7867449384292244 0.8088713387970139 0.8317283622010279 0.8582827691433502 0.8864570320054473 0.9126770527921998 0.9345037742730058 0.9520733561079232 0.9662579034663271 0.9776737172764877 0.986563867855158 0.9926349777613723 0.9975149517180069 1.001717157230147 1.005404997408605 1.009092837587063 1.012780677765521 1.016468517943978 1.020156358122436 1.023844198300894 1.027532038479351 1.031219878657809 1.034907718836267 1.038595559014724 1.042283399193182 40 | 41 | 42 | # name: SOCrel 43 | # type: matrix 44 | # rows: 1 45 | # columns: 178 46 | -7.132940560928478e-06 -6.71491744362734e-06 -6.296894326326006e-06 -5.87887120902472e-06 -5.460848091723533e-06 -5.042824974422199e-06 -4.624801857121012e-06 -4.206778739819677e-06 -3.788755622518539e-06 -3.370732505217254e-06 -2.952709387916018e-06 -2.534686270614782e-06 -2.116663153313497e-06 -1.69864003601226e-06 -1.280616918710926e-06 -8.625938014097391e-07 -4.445706841085275e-07 -2.654756680724231e-08 3.914755504940429e-07 8.094986677952791e-07 1.227521785096491e-06 1.645544902397727e-06 2.063568019698987e-06 2.481591137000224e-06 2.899614254301509e-06 3.317637371602745e-06 3.735660488904031e-06 4.153683606205291e-06 4.571706723506552e-06 4.989729840807739e-06 5.407752958109024e-06 5.82577607541026e-06 6.243799192711497e-06 6.661822310012757e-06 7.07984542731403e-06 7.497868544615254e-06 7.915891661916491e-06 8.333914779217739e-06 8.751937896519e-06 9.169961013820259e-06 9.587984131121471e-06 1.000600724842276e-05 1.042403036572399e-05 1.084205348302522e-05 1.12600766003265e-05 1.167809971762773e-05 1.209612283492899e-05 1.251414595223026e-05 1.293216906953149e-05 1.335019218683275e-05 1.376821530413398e-05 1.418623842143522e-05 1.460426153873648e-05 1.590782091942489e-05 1.81275445773955e-05 1.986150208797106e-05 2.026003377271534e-05 2.053817640204106e-05 2.081631903136676e-05 2.109446166069243e-05 2.137260429001815e-05 2.165074691934383e-05 2.192888954866955e-05 2.220703217799524e-05 2.248517480732098e-05 2.276331743664665e-05 2.304146006597237e-05 2.331960269529808e-05 2.359774532462375e-05 2.387588795394943e-05 2.415403058327517e-05 2.443217321260086e-05 2.471031584192657e-05 2.498845847125231e-05 2.911427325398244e-05 3.730310490038047e-05 4.596285969129562e-05 5.123751493303706e-05 5.103432449805902e-05 5.072611273455408e-05 5.041790097104896e-05 5.010968920754414e-05 4.980147744403898e-05 4.949326568053401e-05 4.958937041580965e-05 5.379330186921961e-05 6.021749324458103e-05 6.555676822393823e-05 6.654590420608866e-05 6.609224001564033e-05 6.56385758251919e-05 6.518491163474376e-05 6.587260689809388e-05 7.031356807474815e-05 7.562778474256351e-05 7.853756433146402e-05 7.785027582101508e-05 7.716298731056596e-05 7.705427211763916e-05 8.058237653003928e-05 8.54537115553455e-05 8.817992856568818e-05 8.697994786459607e-05 8.577814157148673e-05 8.822072096901578e-05 9.254905201640546e-05 9.503134056472424e-05 9.371009110786735e-05 9.370598653811814e-05 9.774106424233066e-05 0.0001015077557828193 0.0001009907452014888 0.0001011786002652428 0.0001056165975164367 0.0001087906599967322 0.0001065114514935355 0.0001097997208845706 0.0001148907320296569 0.0001095240340830672 0.0001145189511794715 0.0001120981703042416 8.278065690477629e-05 -3.808804079820698e-06 -2.160329011463492e-05 -4.439425935133295e-06 2.873860339175155e-05 5.263481851873192e-05 6.368379130190496e-05 6.138971559398396e-05 5.60387318455051e-05 5.023213895143084e-05 4.356454834993918e-05 2.411250425125493e-05 1.202248418458881e-05 1.039554500055708e-05 1.080918603605055e-06 -1.198454654117601e-05 -3.071126887584119e-05 -7.123865847990089e-05 -0.0004144631668956174 -0.0006316153752260325 -0.0006436976469621657 -0.0005681076115683608 -0.0004724183639688794 -0.0004354868357765905 -0.0004657614284039626 -0.0005321923156740058 -0.000627019627743978 -0.0007596353069329903 -0.0009184027801948472 -0.001069249817503413 -0.001125922762980567 -0.001117146098879176 -0.0009987047118791251 -0.0009843100696422594 -0.001087447006111418 -0.001200592599280961 -0.001154504533734787 -0.0009813870457390575 -0.0008006864328125932 -0.0006370717513763996 -0.0005053151071612847 -0.0003903373081291923 -0.0002817053836038477 -0.0001608353319514688 -8.427752627765292e-05 -1.685519110943366e-05 4.159328588357407e-05 0.0001000417628765755 0.0001584902398695613 0.0002169387168625564 0.0002753871938555642 0.0003338356708485594 0.0003922841478415608 0.0004507326248345529 0.0005091811018275638 0.0005676295788205558 0.0006260780558135446 47 | 48 | 49 | # name: OCVeta 50 | # type: matrix 51 | # rows: 8 52 | # columns: 1 53 | 0.9804635156935547 54 | 0.9891325692345091 55 | 0.9934222644504199 56 | 0.9930821414934505 57 | 0.9923904632847294 58 | 0.9833966792302935 59 | 0.9925573821595624 60 | 0.9863740200677645 61 | 62 | 63 | # name: OCVQ 64 | # type: matrix 65 | # rows: 8 66 | # columns: 1 67 | 5.220449548678137 68 | 5.23217514935387 69 | 5.250136267265952 70 | 5.24631002667757 71 | 5.252081880278499 72 | 5.21270581784224 73 | 5.276308980428415 74 | 5.241720359596042 75 | 76 | 77 | # name: name 78 | # type: sq_string 79 | # elements: 1 80 | # length: 2 81 | E2 82 | 83 | 84 | # name: temps 85 | # type: matrix 86 | # rows: 1 87 | # columns: 8 88 | -25 -15 -5 5 15 25 35 45 89 | 90 | 91 | # name: etaParam 92 | # type: matrix 93 | # rows: 1 94 | # columns: 8 95 | -1.379922005286989 0.3899297598664481 0.8173674261046584 0.9775759733090317 0.9836660108493028 0.9929434526110458 0.9957935999293793 0.9975747911380725 96 | 97 | 98 | # name: QParam 99 | # type: matrix 100 | # rows: 1 101 | # columns: 8 102 | 4.499916980622507 4.941967221903379 4.999331119769457 5.049340926140339 5.022948849235521 5.118371855112535 5.175047239460794 5.102487476031776 103 | 104 | 105 | # name: GParam 106 | # type: matrix 107 | # rows: 1 108 | # columns: 8 109 | 6.272728509440232e-07 0.4489124794278841 8.512001810845884e-09 0.683033956121178 1.521821922749097 0.1199240315975642 0.06838491148167437 0.1594873532742363 110 | 111 | 112 | # name: M0Param 113 | # type: matrix 114 | # rows: 1 115 | # columns: 8 116 | 0 0 0 0 0 0 0 0 117 | 118 | 119 | # name: MParam 120 | # type: matrix 121 | # rows: 1 122 | # columns: 8 123 | 0 0 0 0 0 0 0 0 124 | 125 | 126 | # name: R0Param 127 | # type: matrix 128 | # rows: 1 129 | # columns: 8 130 | 0.3300193522262772 0.1235491709671471 0.06450310940141168 0.0326092645042774 0.01871327790156384 0.01118502551276652 0.008072908548656003 0.006174274684328768 131 | 132 | 133 | # name: RCParam 134 | # type: matrix 135 | # rows: 8 136 | # columns: 2 137 | 3739.400828410135 4148.032070857807 138 | 3.332248268550854 290.5681219463365 139 | 9.687915755391629 299.8817025389862 140 | 10.65560219260603 249.5850680500804 141 | 8.603964538684258 194.8101985035424 142 | 4.712947577149376 99.55618965312047 143 | 3.85809296436533 73.07150850850255 144 | 4.629565910164249 72.06129569487307 145 | 146 | 147 | # name: RParam 148 | # type: matrix 149 | # rows: 8 150 | # columns: 2 151 | 0.1842481142706689 0.2130426979025667 152 | 0.02596576290221931 0.07865287804946818 153 | 0.01668223414775956 0.05102341540406394 154 | 0.009621723432977208 0.03526351603510083 155 | 0.005284542437171193 0.02319326691543382 156 | 0.002176129000413558 0.01383801396944309 157 | 0.001371633914358149 0.01036138152532624 158 | 0.001200562629684374 0.00883954781208021 159 | 160 | 161 | # name: dOCV0 162 | # type: matrix 163 | # rows: 1 164 | # columns: 201 165 | 27.82501512109024 21.57094472502581 13.46629458038495 10.52421815377245 8.745514749414562 7.575449099936016 6.722907471068673 6.091553451874132 5.575578576314744 5.095693872521689 4.722533204564305 4.406151889422949 4.11636627228269 3.856987274724633 3.599922632917085 3.350540715095862 3.080548752476853 2.728514797349035 2.230793049109936 1.650633577376404 1.164856397087988 0.9297241639590226 0.8301556233136775 0.8123695747321857 0.8244326255886225 0.7825863883512252 0.7760335225999171 0.8035190326411978 0.8276740667438443 0.8232501491389321 0.8245051393108938 0.8513456077106962 0.856210174553107 0.8714035176475399 0.893009467202921 0.8781582857779302 0.8762285416569426 0.8815867216156459 0.8811581977537397 0.8642972919730152 0.855767693683962 0.8503763523994401 0.832481929944251 0.8196732959614206 0.7877524957290216 0.7565137658558374 0.7473886552107345 0.7192134948391882 0.6667387337128794 0.6678908211530832 0.6642414809097108 0.6322279408103704 0.6175757099586399 0.6261121565796834 0.6343067435706118 0.616321571865619 0.6098921096833188 0.624578824219979 0.6214821230347667 0.5927526249848381 0.5848479151522756 0.5654788638065789 0.5499189359378764 0.5231725226634509 0.4851151912731222 0.4580509911950692 0.4066533095853142 0.3751907026607881 0.3448109135486099 0.3302034129094356 0.3227868167562598 0.2982689144069184 0.2711181724958767 0.2843320610090849 0.2847388977900156 0.2721839125489289 0.2903244944679528 0.2737671528891816 0.2766418329054332 0.3159933980539709 0.3118245889245586 0.310114864123312 0.3150160110810152 0.3117892754592066 0.3225069665202085 0.3139959304209405 0.3234372289078191 0.3551197804174233 0.3277670635066787 0.3247953572131035 0.352358639526873 0.3410113692915218 0.3604751087185942 0.3613217548307013 0.3568479755768017 0.3698947367697158 0.3663602470036942 0.3718306769438406 0.3705915118239034 0.3854369636292709 0.3932298121148481 0.3749523966657176 0.3835270700164095 0.3786597396560065 0.3643828762621037 0.3529645428022921 0.3680694943791529 0.3835649685461462 0.3593395337596839 0.3673864710482366 0.383563774885376 0.3677733501028513 0.3648153215178418 0.3673009213937206 0.3791892177873901 0.3646704276436541 0.3452570672306976 0.3763184571432099 0.3746940592618042 0.3663438163841182 0.3443576462555775 0.333452646244714 0.3558100966781108 0.3339278376856658 0.3433222386731938 0.3422381571074773 0.3256628807440709 0.3178041206398863 0.3084182250115752 0.3264364100519668 0.3064488516037578 0.3007149290605415 0.3096956231959158 0.2995208300784036 0.2966278265545519 0.2778548747826015 0.2691058142651581 0.293073494342222 0.2927745278323179 0.2671101530852482 0.2643057128890902 0.2807612806324133 0.2810292134374226 0.2730017791766848 0.2727896797251272 0.2927277925361516 0.2996711370620186 0.2920610987098726 0.3056221634700762 0.3227487391806783 0.309721527143747 0.3160848400435512 0.3557942746719611 0.354239819676927 0.3483639170094754 0.373357215608916 0.3930932439311974 0.4041579092825209 0.4340598808599161 0.452712440591263 0.439554286057664 0.4406889734091202 0.4422866670672931 0.4451579139608697 0.4274718933236166 0.4000804277352721 0.4042805668524885 0.3911924560148883 0.3666241068381204 0.3760247417790907 0.3710259516857661 0.341005363767799 0.3321355516527014 0.3388334374780477 0.3520977752582866 0.347967206173827 0.3508192936568122 0.3429838500818683 0.3642769345445274 0.3852893040781069 0.3904528954501885 0.3856222520911778 0.3940084378333708 0.4312024267768777 0.43199754225558 0.4350458240297783 0.4799449092389274 0.5162712387597246 0.5421290859447758 0.5742627572594472 0.6144576164729187 0.6554975628210613 0.6997282259156634 0.772100658639463 0.8305201451777933 0.9007892797635364 1.017744883968419 1.162457861999933 1.371573706376417 2.204151989016179 2.904430960931492 166 | 167 | 168 | # name: dOCVrel 169 | # type: matrix 170 | # rows: 1 171 | # columns: 201 172 | -0.03159523064703909 -0.01278496837898354 0.004628872635090609 0.003435716469984713 0.003636305573295816 0.002621221751992006 0.002476633457199263 0.00257147993577519 0.002234599343940605 0.003158315978895438 0.003271020258052042 0.002684201605859793 0.00232562834788775 0.002235357620077012 0.002347215986092676 0.003299357170478994 0.0044951830671312 0.006792703485682588 0.01222518536861526 0.01694841662467872 0.01640241864957927 0.01108184087685606 0.005773341017407191 0.001882937106157583 -0.0004547117439171647 -0.0005410366255526362 -0.0006042392794629292 -0.001495494271212573 -0.002376656076449517 -0.002327414515851842 -0.002081187294398787 -0.00188395499611018 -0.001420135119883371 -0.001238079084265394 -0.0007596164393822245 9.409516581064591e-06 0.0004769500205959363 0.0003660356016391208 0.0001364396762598845 0.0006195483415990333 0.0007085569281237538 0.0004298269570224309 0.0005924526870683481 0.0006420399825371097 0.0008360679695373296 0.001593961680635694 0.001197037232485839 0.000830055732853725 0.001137514648833205 0.0004610866870461816 -0.000270048991518823 4.625320222103316e-05 0.0004368835587317756 -0.0002481504080651387 0.0001068881991084615 0.0009667326683822811 0.0006785815380207726 0.000344870145848627 0.0002827766193942599 0.0006014382868644778 0.0006713591129926859 0.0005158081938136114 0.0003048547410277661 0.0001903415590393779 9.071120064416478e-05 0.000430656180739102 0.001571582614840271 0.002332492963344659 0.003121760228901544 0.003121125057475077 0.003421062315667986 0.004149382870989025 0.004717895599982423 0.004098175365466254 0.003356238163412629 0.003389798068738917 0.002513075526675942 0.002293552743612404 0.00136435784452969 -0.0001807212901633353 -2.064786149038309e-06 -0.0003399770651001089 -5.773290826390463e-05 1.266530609599428e-05 -0.0009898697279441752 -9.176926936551395e-05 -4.124084620051407e-05 -0.001241161661013159 -0.0004602161397668702 -0.0001486370844993635 -0.0008578581039442713 -0.0006766165616330745 -0.00102838681469251 -0.0008194920762681916 -0.0003957729172503427 -0.0008400130904120566 -0.0009318472320052286 -0.0004159383293527721 -0.0004065970657372739 -0.0009709247370471381 -0.0007363035588498515 0.0002007117217298716 9.643430705529228e-05 0.0002885506285055028 0.0006371963333834927 0.0007354769157408877 0.000731401738929651 0.0003050180363993265 0.001183113627509141 0.001139834992109866 0.0003705920089559859 0.0008083023542662323 0.001346919322985998 0.001511125004283686 0.0008236188867513189 0.001267593225490801 0.00167314880659888 0.0007694785440335179 0.000904603946997939 0.0008371195101859762 0.001543553954865882 0.001827546103391316 0.0008166130037358746 0.001608692635519026 0.001184563373011571 0.0008582312835391306 0.001073142213238091 0.00133598835410351 0.001253081924787957 0.0002670173413039272 0.001116199841958148 0.0009179250650176343 0.0004272182921400889 0.0003362410523116504 7.856439229433953e-05 0.0008226738228191697 0.0009055902030585372 6.450535326327179e-05 -2.51214665837184e-05 0.0005851277998147691 0.0004955250967289386 -0.000237482120131428 4.204896300922752e-05 0.0004497927996324378 0.0004208851524759299 -0.0002734203040027612 3.882537329194784e-05 0.000831296235191773 0.0004679122592089258 0.0002855236187849835 0.001387765266606727 0.001942965619357485 0.002240109624052143 0.003380479030060774 0.004206698839037437 0.004555524251641945 0.004536904459607316 0.004233612439764075 0.003062932333377485 0.001491368457506875 0.0008286065218058311 5.026917125024784e-05 -0.00115840102413107 -0.002070110392430284 -0.00211879852289661 -0.001643703250310541 -0.002127734904989733 -0.002123317251978834 -0.001607766126262688 -0.001982621525537442 -0.002272562159352727 -0.001303434169275356 -0.0008782617237095675 -0.0007596647296335469 -0.0007267590916153557 -0.0007410396292935925 -0.0007203479394042752 -0.000254920603196153 -0.0005164971286913793 -0.0004255270585891955 -0.0004462455066946256 -0.000228735535365049 6.979215262173919e-05 -0.0003261264123023211 0.0004661796234959573 0.0007572585050138613 -0.0003317897761104195 -0.0003442010530748025 7.433445981904667e-05 -0.0002404939294869816 -0.0005596471412699369 -0.0007352499803135856 -0.0007002130758007 -0.001075563132965206 -0.0009596909812992422 -0.001533380857398497 -0.002804953467924764 -0.004085418983517313 -0.006850630217180003 -0.02083319812512454 -0.03281181737815331 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /ElectrochemicalCellModel/E2model2RCmatlab .mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/ElectrochemicalCellModel/E2model2RCmatlab .mat -------------------------------------------------------------------------------- /ElectrochemicalCellModel/OCVfromSOCtemp.m: -------------------------------------------------------------------------------- 1 | function ocv=OCVfromSOCtemp(soc,temp,model) 2 | % This function returns the fully rested open-circuit-voltage of an LiPB 3 | % cell given its soc. 4 | 5 | soccol = soc(:); % force soc to be col-vector 6 | SOC = model.SOC(:); % force to be col vector... 03/24/10 7 | OCV0 = model.OCV0(:); % force to be col vector... 03/24/10 8 | OCVrel = model.OCVrel(:); % force to be col vector... 03/24/10 9 | if isscalar(temp), 10 | tempcol = temp*ones(size(soccol)); % replicate for all socs 11 | else 12 | tempcol = temp(:); % force to be col vector 13 | end 14 | diffSOC=SOC(2)-SOC(1); 15 | ocv=zeros(size(soccol)); 16 | I1=find(soccol <= SOC(1)); %if ~isempty(I1), disp('low soc'); end 17 | I2=find(soccol >= SOC(end)); %if ~isempty(I2), disp('high soc'); end 18 | I3=find(soccol > SOC(1) & soccol < SOC(end)); 19 | I6=isnan(soccol); 20 | 21 | % for voltages less than 0% soc... 07/26/06 22 | % extrapolate off low end of table (for SOC(1) < 0... 03/23/10) 23 | if ~isempty(I1), 24 | dv = (OCV0(2)+tempcol.*OCVrel(2)) - (OCV0(1)+tempcol.*OCVrel(1)); 25 | ocv(I1)= (soccol(I1)-SOC(1)).*dv(I1)/diffSOC + OCV0(1)+tempcol(I1).*OCVrel(1); 26 | end 27 | 28 | % for voltages greater than 100% soc... 07/26/06 29 | % extrapolate off high end of table (for SOC(end) > 1... 03/23/10) 30 | if ~isempty(I2), 31 | dv = (OCV0(end)+tempcol.*OCVrel(end)) - (OCV0(end-1)+tempcol.*OCVrel(end-1)); 32 | ocv(I2) = (soccol(I2)-SOC(end)).*dv(I2)/diffSOC + OCV0(end)+tempcol(I2).*OCVrel(end); 33 | end 34 | 35 | % for normal soc range... 36 | % manually interpolate (10x faster than "interp1") 37 | I4=(soccol(I3)-SOC(1))/diffSOC; % for SOC(1) < 0... 03/23/10 38 | I5=floor(I4); I45 = I4-I5; omI45 = 1-I45; 39 | ocv(I3)=OCV0(I5+1).*omI45 + OCV0(I5+2).*I45; 40 | ocv(I3)=ocv(I3) + tempcol(I3).*(OCVrel(I5+1).*omI45 + OCVrel(I5+2).*I45); 41 | ocv(I6)=0; % replace NaN SOCs with zero voltage... 03/23/10 42 | ocv = reshape(ocv,size(soc)); -------------------------------------------------------------------------------- /ElectrochemicalCellModel/P14model-ocv.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/ElectrochemicalCellModel/P14model-ocv.mat -------------------------------------------------------------------------------- /ElectrochemicalCellModel/PCMsim.m: -------------------------------------------------------------------------------- 1 | addpath E:\BMS\ECM\Matlabfiles\work\readonly 2 | load E:\BMS\ECM\Matlabfiles\work\readonly\E2model.mat 3 | 4 | % simPCM: Simulate parallel-connected-module packs (cells are connected in 5 | % parallel to make modules; these modules are connected in series to make 6 | % packs). 7 | % The parameters for each cell may be different 8 | 9 | % Initialize some pack configuration parameters... 10 | Ns = 5; % Number of modules connected in series to make a pack 11 | Np = 10; % Number of cells connected in parallel in each module 12 | 13 | % Initialize some simulation configuration parameters... 14 | maxtime = 3600; % Simulation run time in simulated seconds 15 | t0 = 2700; % Pack rests after time t0 16 | storez = zeros([maxtime Ns Np]); % create storage for SOC 17 | storei = zeros([maxtime Ns Np]); % create storage for current 18 | 19 | % Initialize states for ESC cell model 20 | z = 0.25*ones(Ns,Np); 21 | irc = zeros(Ns,Np); 22 | h = zeros(Ns,Np); 23 | 24 | % Default initialization for cells within the pack 25 | T = 25; % Temperature 26 | q = getParamESC('QParam',T,model)*ones(Ns,Np); 27 | rc = exp(-1./abs(getParamESC('RCParam',T,model)))'*ones(Ns,Np); 28 | r = (getParamESC('RParam',T,model))'; 29 | % In the following line, I set the hysteresis magnitudes to zero to make 30 | % it easier to understand results. I assume this value of zero for the 31 | 32 | m = 0*getParamESC('MParam',T,model)*ones(Ns,Np); 33 | g = getParamESC('GParam',T,model)*ones(Ns,Np); 34 | r0 = getParamESC('R0Param',T,model)*ones(Ns,Np); 35 | rt = 0.000125; % 125 microOhm resistance for each tab 36 | 37 | % ===================================================================== 38 | 39 | 40 | % Modified initialization for cell variability 41 | if 1, % set to "if 1," to execute, or "if 0," to skip this code 42 | z = reshape(linspace(0.3,0.8,Ns*Np),Ns,Np); % Different initial SOCs 43 | end 44 | 45 | if 0, % set to "if 1," to execute, or "if 0," to skip this code 46 | q = reshape(linspace(4.5,5.5,Ns*Np),Ns,Np); % Different capacities 47 | end 48 | 49 | if 0, % set to "if 1," to execute, or "if 0," to skip this code 50 | r0 = reshape(linspace(0.005,0.025,Ns*Np),Ns,Np); % Different R0 values 51 | end 52 | r0 = r0 + 2*rt; % add tab resistance to cell resistance 53 | 54 | % END OF MODIFY SECTION 55 | % ===================================================================== 56 | 57 | % Add faults to pack: cells faulted open- and short-circuit 58 | % To delete a PCM (open-circuit fault), set a resistance to Inf 59 | %r0(1,1) = Inf; % for example... 60 | 61 | % To delete a cell from a PCM (short-circuit fault), set its SOC to NaN 62 | %z(1,2) = NaN; % for example, delete cell 2 in PCM 1 63 | Rsc = 0.0025; % Resistance value to use for cell whose SOC < 0% 64 | 65 | % ------------------------------------------------------------------------ 66 | % Get ready to simulate... first compute pack capacity in Ah 67 | totalCap = min(sum(q,2)); % pack capacity = minimum module capacity 68 | I = 10*totalCap; % cycle at 10C... not realistic, faster simulation 69 | 70 | % Okay... now to simulate pack performance using ESC cell model. 71 | for k = 1:maxtime, 72 | v = OCVfromSOCtemp(z,T,model); % get OCV for each cell: Ns * Np matrix 73 | v = v + m.*h - r.*irc; % add in capacitor voltages and hysteresis 74 | r0(isnan(z)) = Rsc; % short-circuit fault has "short-circuit" resistance 75 | V = (sum(v./r0,2) - I)./sum(1./r0,2); 76 | ik = (v-repmat(V,1,Np))./r0; 77 | z = z - (1/3600)*ik./q; % Update each cell SOC 78 | z(z<0) = NaN; % set over-discharged cells to short-circuit fault 79 | irc = rc.*irc + (1-rc).*ik; % Update capacitor voltages 80 | fac = exp(-abs(g.*ik)./(3600*q)); 81 | h = fac.*h + (fac-1).*sign(ik); % Update hysteresis voltages 82 | minz = min(z(:)); maxz = max(z(:)); % Check to see if SOC limit hit 83 | if minz < 0.05, I = -abs(I); end % stop discharging 84 | if maxz > 0.95, I = abs(I); end % stop charging 85 | if k>t0, I = 0; end % rest 86 | storez(k,:,:) = z; % Store SOC for later plotting 87 | storei(k,:,:) = ik; % store current for later plotting 88 | end % for k 89 | fprintf('Finished executing simulation. Ready to produce plots!\n') 90 | 91 | %% 92 | % Plot the individual SOC vs. time for all cells in all 93 | % series PCMs. There is one subplot for each PCM. 94 | t = (0:(length(storez(:,:,1))-1))/60; 95 | xplots = round(1.0*ceil(sqrt(Ns))); yplots = ceil(Ns/xplots); means = []; 96 | for k = 1:Ns, 97 | zr=squeeze(100*storez(:,k,:)); 98 | subplot(yplots,xplots,k); plot(t,zr); axis([0 ceil(maxtime/60) 0 100]); 99 | title(sprintf('Cells in PCM %d',k)); 100 | ylabel('SOC (%)'); xlabel('Time (min)'); 101 | end 102 | 103 | %% 104 | % Plot the individual SOC vs. time for all cells in all 105 | % series PCMs. There is one subplot for each PCM. 106 | t = (0:(length(storez(:,:,1))-1))/60; 107 | xplots = round(1.0*ceil(sqrt(Ns))); yplots = ceil(Ns/xplots); means = []; 108 | for k = 1:Ns, 109 | zr=squeeze(100*storez(:,k,:)); 110 | subplot(yplots,xplots,k); plot(t,zr); axis([0 ceil(maxtime/60) 0 100]); 111 | title(sprintf('Cells in PCM %d',k)); 112 | ylabel('SOC (%)'); xlabel('Time (min)'); 113 | end 114 | -------------------------------------------------------------------------------- /ElectrochemicalCellModel/README.md: -------------------------------------------------------------------------------- 1 | # ElectroChemical Cell Model 2 | 3 | This branch of algorithm aims to exexute the following things. 4 | - Interpret the data obtained from the laboratory tests on a lithium-ion cell, and use this data to obtain the cell parameters,for example: State of Charge, Terminal voltage, Open Circuit Voltage for a cell. 5 | The cell is modelled as a Randless circuit with Warburg impedance. 6 | ![Image of Randless circuit ](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/randlesscircuit.JPG) 7 | The equations for this model are formulated, first in continuous time model and then converted to discrete time model. 8 | The currents from the resistor current pairs are 9 | ![Image of RC current ](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/resistor_current.JPG) 10 | **Hysteresis** is an important parameter that needs to be taken into account while modelling a lithium-ion cell. In an normal cell, if it is allowed to rest long enough, diffusion voltagesdecay to zero, so model voltage decays to OCV, but due to hysteresis, the behaviour of lithium-ion cell is different. 11 | The hysteresis is differentiated as two forms and consequently, the equations are formulated 12 | - a) Dynamic hysteresis 13 | - ![Image of dynamic hysteresis](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/hysteresis.JPG) 14 | - b) Instantaneous hysteresis 15 | - ![Image of instantaneous hysteresis](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/instantaneoushysteresis.JPG) 16 | The **instantenous hysteresis** only changes when there is a change in the direction of the current. 17 | The overall hysteresis is the sum of the two hysteresis effects. 18 | 19 | If all the effects are cumulated, the cell model is said to be an **Enhanced Self Correcting Model**. 20 | The state equation of an ESC is 21 | ![Image of an ESC state equation](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/modelequation.JPG) 22 | 23 | The ESC output equation is 24 | ![Image of ESC output equation](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/opequation.JPG) 25 | 26 | Finally, the state space form of an ESC model is 27 | ![Image of State space form](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/statespaceformofcell.JPG). 28 | 29 | Code **runProcessOCV.m** runs the processOCV for a cell against the recorded laboratory data. 30 | 31 | While **processOCV** simulates the cell for static data/cell without a load, the **processDynamicOCV** simulates the cell for dynamic data/cell under load. 32 | 33 | The overall procedure to deteremine parameter values for a cell under load is as follows: 34 | 1. First, ***Coulombic Efficiency*** and ***Total Cell Capacity*** are calculated from data, directly, as we did for the OCV test results 35 | 2. Compute ***state of charge*** and ***open circuit voltage*** for every data sample; subtract OCV from terminal voltage. 36 | 3. Use ***subspace system identification*** technique to find R–C time constants. 37 | 4. Compute ***instantaneous hysteresis*** and ***R-C currents*** for every data sample 38 | 5. A ***hystereis rate constant*** is calculated that determines the amount of hysteresis present in a cell. 39 | 6. "Unexplained” part of voltage is now linear in parameters—solve for these parameter values using least squares. 40 | - The unexplained part is 41 | - ![Image of unexplained part](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/unexplainedpart.JPG) 42 | - ![Image of lsq method matlab](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/unexplainedpart2.JPG) 43 | - The X matrix is calculated in least square method. 44 | 7. Compute rms voltage-prediction error of present model 45 | 8. Adapt ***hysteresis rate constant*** to minimize this error, iterating steps 5–8 until convergence is reached 46 | 47 | Code **getParamESC.m** retrieves a parameter for a given cell at a given temperature. 48 | 49 | Code **OCVfromSOCtemp.m** interpolates and returns the open circuit voltage for a given state of charge and temperature. 50 | 51 | Simulation of a Constant current/Constant Voltage charging cycle can be performed with **ChargingCycleSimulation.m** 52 | 53 | Simulation of a Constant power/Constant Voltage charging cycle can be performed with **ChargingCycleSimulation(CP_CV).m** 54 | 55 | Simulation of a Parallel Cell module can be performed with **PCMsim.m** 56 | 57 | Simulation of a Series Cell module can be performed with **SCMsim.m** 58 | 59 | To take the algorithms a step further, the battery is simulated for an electric vehicle under various drive cycles and taking under consideration the characteristics of motor,inverter,drivetrain,etc. 60 | The drive cycles used are **Urban Dynamometer Driving Schedule**, **Highway Fuel Efficiency Test**, **New York City Cycle**. 61 | 62 | The approach to simulating an electric vehicle behaviour is as follows: 63 | 64 | ![Image of EVsimulation](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/blockdiagram.JPG) 65 | 66 | - The desired accelerations and desired speed are limited by the actual achievable accelerations and speed of the vehicle and powertrain module. 67 | - The desired road forces are limited by the forces that the tire-road dynamic can actually achieve. 68 | - Desired torque and power values restricted by specifications of motor chosen for vehicle, and thus achievable torques and power values may be lower. The desired torque must be compatible with the speed-torque for the particular motor chosen. 69 | - Consequently the the battery power is computed based on these limitations and the limitation of the motor power. 70 | - The Battery SOC is then updated. 71 | - Finally the Battery range is extrapolated from the rate of depletion of the available battery energy. 72 | 73 | **EQUATIONS USED**: 74 | 75 | - The vehicle desired acceleration is 76 | ![Image of desired acceleration](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/desireedacc.JPG) 77 | 78 | - The desired acceleration force is 79 | ![Image of desired acceleration force](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/desaccforce.JPG) 80 | - The equivalent mass required is 81 | ![Image of equivalent mass](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/eqmass.JPG) 82 | 83 | - The drag forces acting on the vehicle are classified as aerodynamic force and rolling force 84 | ![Image of drag forces](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/dragforces.JPG) 85 | 86 | - The brake drag and grade forces acting on the vehicle are 87 | ![Image of brake drag and rolling force](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/dragf2.JPG) 88 | 89 | - The demanded motor torque can be derived as 90 | ![Image of demanded motor torque](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/desireedacc.JPG) 91 | 92 | - The actual acceleration force and consequently the actual acceleration is as follows 93 | ![Image of actual acceleration and force](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/accforce.JPG) 94 | 95 | - The limit motor speed is 96 | ![Image of limit motor speed](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/motorspeed.JPG) 97 | 98 | - The actual vehicle vehicle speed is then computed from this limit speed 99 | ![Image of vehicle speed](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/actualspeed.JPG) 100 | 101 | - The battery power demand is then computed 102 | ![Image of motor power](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/mototrpower.JPG) 103 | ![Image of battery power](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/battpower.JPG) 104 | 105 | - The current drawn out of the battery is 106 | ![Image of battery current](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/battcurrent.JPG) 107 | 108 | - The State of Charge of the battery for the drive cycle is 109 | ![Image of battery SOC](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/battSOC.JPG) 110 | 111 | - Finally the driving range of the battery according to the different drive cycles is calculated 112 | ![Image of battery range](https://github.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/blob/master/images/range.JPG) 113 | 114 | Code **setupSimVehicle.m** sets up the different blocks for the simulation of the electric vehicle behaviour. A different block is used each for setting up a single cell,PCM/SCM module, Battery pack, Motor, Wheel, Drivetrain and Vehicle. 115 | 116 | Code **simVehicle.m** simulates the Vehicle for the given data and stores the result. 117 | 118 | Code **setupSimVehicleplots.m** plots the obtained results. 119 | -------------------------------------------------------------------------------- /ElectrochemicalCellModel/SCMsim.m: -------------------------------------------------------------------------------- 1 | addpath E:\BMS\ECM\Matlabfiles\work\readonly 2 | load E:\BMS\ECM\Matlabfiles\work\readonly\E2model.mat 3 | 4 | % simSCM: Simulate series-connected-module packs (cells are connected in 5 | % series to make modules; these modules are connected in parallel to make 6 | % packs). 7 | % The parameters for each cell may be different 8 | 9 | % Initialize some pack configuration parameters... 10 | Ns = 9; % Number of modules connected in series to make a pack 11 | Np = 3; % Number of cells connected in parallel in each module 12 | 13 | % Initialize some simulation configuration parameters... 14 | maxtime = 3600; % Simulation run time in simulated seconds 15 | t0 = 2700; % Pack rests after time t0 16 | storez = zeros([maxtime Ns Np]); % create storage for SOC 17 | storei = zeros([maxtime Ns Np]); % create storage for current 18 | 19 | % Initialize states for ESC cell model 20 | z = 0.25*ones(Ns,Np); 21 | irc = zeros(Ns,Np); 22 | h = zeros(Ns,Np); 23 | 24 | % Default initialization for cells within the pack 25 | T = 25; % Temperature 26 | q = getParamESC('QParam',T,model)*ones(Ns,Np); 27 | rc = exp(-1./abs(getParamESC('RCParam',T,model)))'*ones(Ns,Np); 28 | r = (getParamESC('RParam',T,model))'; 29 | % In the following line, I set the hysteresis magnitudes to zero to make 30 | % it easier to understand results. I assume this value of zero for the 31 | 32 | m = 0*getParamESC('MParam',T,model)*ones(Ns,Np); 33 | g = getParamESC('GParam',T,model)*ones(Ns,Np); 34 | r0 = getParamESC('R0Param',T,model)*ones(Ns,Np); 35 | rt = 0.000125; % 125 microOhm resistance for each tab 36 | 37 | % ===================================================================== 38 | 39 | 40 | % Modified initialization for cell variability 41 | if 0, % set to "if 1," to execute, or "if 0," to skip this code 42 | z = reshape(linspace(0.3,0.8,Ns*Np),Ns,Np); % Different initial SOCs 43 | end 44 | 45 | if 1, % set to "if 1," to execute, or "if 0," to skip this code 46 | q = reshape(linspace(4.5,5.5,Ns*Np),Ns,Np); % Different capacities 47 | end 48 | 49 | if 0, % set to "if 1," to execute, or "if 0," to skip this code 50 | r0 = reshape(linspace(0.005,0.025,Ns*Np),Ns,Np); % Different R0 values 51 | end 52 | r0 = r0 + 2*rt; % add tab resistance to cell resistance 53 | 54 | % END OF MODIFY SECTION 55 | % ===================================================================== 56 | 57 | % Add faults to pack: cells faulted open- and short-circuit 58 | % To delete a PCM (open-circuit fault), set a resistance to Inf 59 | %r0(1,1) = Inf; % for example... 60 | 61 | % To delete a cell from a PCM (short-circuit fault), set its SOC to NaN 62 | %z(1,2) = NaN; % for example, delete cell 2 in PCM 1 63 | Rsc = 0.0025; % Resistance value to use for cell whose SOC < 0% 64 | 65 | % ------------------------------------------------------------------------ 66 | % Get ready to simulate... first compute pack capacity in Ah 67 | totalCap = sum(min(q)); % pack capacity = minimum module capacity 68 | I = 10*totalCap; % cycle at 10C... not realistic, faster simulation 69 | 70 | % Okay... now to simulate pack performance using ESC cell model. 71 | for k = 1:maxtime, 72 | v = OCVfromSOCtemp(z,T,model); % get OCV for each cell: Ns * Np matrix 73 | v = v + m.*h - r.*irc; % add in capacitor voltages and hysteresis 74 | r0(isnan(z)) = Rsc; % short-circuit fault has "short-circuit" resistance 75 | V = (sum(sum(v,1)./sum(r0,1),2)-I)./sum(1./sum(r0,1),2); % Bus V 76 | ik = (sum(v,1)-repmat(V,1,Np))./sum(r0,1); % 1*Np cell currents 77 | ik = repmat(ik,Ns,1); % Ns*Np cell currents 78 | z = z - (1/3600)*ik./q; % Update each cell SOC 79 | z(z<0) = NaN; % set over-discharged cells to short-circuit fault 80 | irc = rc.*irc + (1-rc).*ik; % Update capacitor voltages 81 | fac = exp(-abs(g.*ik)./(3600*q)); 82 | h = fac.*h + (1-fac).*sign(ik); % Update hysteresis voltages 83 | minz = min(z(:)); maxz = max(z(:)); % Check to see if SOC limit hit 84 | if minz < 0.05, I = -abs(I); end % stop discharging 85 | if maxz > 0.95, I = abs(I); end % stop charging 86 | if k>t0, I = 0; end % rest 87 | storez(k,:,:) = z; % Store SOC for later plotting 88 | storei(k,:,:) = ik; % store current for later plotting 89 | end % for k 90 | fprintf('Finished executing simulation. Ready to produce plots!\n') 91 | 92 | %% 93 | % Plot the individual SOC vs. time for all cells in all 94 | % parallel SCMs. There is one subplot for each SCM. 95 | t = (0:(length(storez(:,:,1))-1))/60; 96 | xplots = round(1.0*ceil(sqrt(Np))); yplots = ceil(Np/xplots); means = []; 97 | for k = 1:Np, 98 | zr=squeeze(100*storez(:,:,k)); 99 | subplot(yplots,xplots,k); plot(t,zr); axis([0 ceil(maxtime/60) 0 100]); 100 | title(sprintf('Cells in SCM %d',k)); 101 | ylabel('SOC (%)'); xlabel('Time (min)'); 102 | end 103 | 104 | %% 105 | % Plot the individual cell current vs. time for all cells in 106 | % all parallel SCMs. There is one subplot for each SCM. 107 | t = (0:(length(storei(:,:,1))-1))/60; 108 | for k = 1:Np, 109 | zr=squeeze(storei(:,:,k)); 110 | subplot(yplots,xplots,k); plot(t,zr); 111 | axis([0 ceil(maxtime/60) -101 101]); 112 | title(sprintf('Cells in SCM %d',k)); 113 | ylabel('Current (A)'); xlabel('Time (min)'); 114 | end 115 | -------------------------------------------------------------------------------- /ElectrochemicalCellModel/SOCfromOCVtemp.m: -------------------------------------------------------------------------------- 1 | function soc=SOCfromOCVtemp(ocv,temp,model) 2 | % This function returns an estimate of soc from a fully rested open-circuit-voltage 3 | % of an LiPB cell 4 | 5 | ocvcol = ocv(:); % force ocv to be col-vector 6 | OCV = model.OCV(:); % force to be col vector... 03/24/10 7 | SOC0 = model.SOC0(:); % force to be col vector... 03/24/10 8 | SOCrel = model.SOCrel(:); % force to be col vector... 03/24/10 9 | if isscalar(temp), 10 | tempcol = temp*ones(size(ocvcol)); % replicate temperature for all ocvs 11 | else 12 | tempcol = temp(:); % force to be col vector 13 | end 14 | diffOCV=OCV(2)-OCV(1); 15 | soc=zeros(size(ocvcol)); 16 | I1=find(ocvcol <= OCV(1)); 17 | I2=find(ocvcol >= OCV(end)); 18 | I3=find(ocvcol > OCV(1) & ocvcol < OCV(end)); 19 | I6=isnan(ocvcol); 20 | 21 | % for socs lower than lowest voltage 22 | % extrapolate off low end of table 23 | dz = (SOC0(2)+tempcol.*SOCrel(2)) - (SOC0(1)+tempcol.*SOCrel(1)); 24 | soc(I1)= (ocvcol(I1)-OCV(1)).*dz(I1)/diffOCV + SOC0(1)+tempcol(I1).*SOCrel(1); 25 | 26 | % for socs higher than highest voltage 27 | % extrapolate off high end of table 28 | dz = (SOC0(end)+tempcol.*SOCrel(end)) - (SOC0(end-1)+tempcol.*SOCrel(end-1)); 29 | soc(I2) = (ocvcol(I2)-OCV(end)).*dz(I2)/diffOCV + SOC0(end)+tempcol(I2).*SOCrel(end); 30 | 31 | % for normal soc range... 32 | % manually interpolate (10x faster than "interp1") 33 | I4=(ocvcol(I3)-OCV(1))/diffOCV; 34 | I5=floor(I4); 35 | soc(I3)=SOC0(I5+1).*(1-(I4-I5)) + SOC0(I5+2).*(I4-I5); 36 | soc(I3)=soc(I3) + tempcol(I3).*(SOCrel(I5+1).*(1-(I4-I5)) + SOCrel(I5+2).*(I4-I5)); 37 | soc(I6) = 0; % replace NaN OCVs with zero SOC... 03/23/10 38 | soc = reshape(soc,size(ocv)); -------------------------------------------------------------------------------- /ElectrochemicalCellModel/getParamESC.m: -------------------------------------------------------------------------------- 1 | % function theParam = getParamESC(paramName,temperature,model) 2 | % 3 | % This function returns the values of the specified ESC cell-model 4 | % parameter 'paramName' for the temperatures in 'temperature' for the 5 | % cell model data stored in 'model'. 6 | % 7 | % In the standard ESC model, 'paramName' may be one of: 8 | % 'QParam', 'RCParam', 'RParam', 'R0Param', 'MParam', 'M0Param', 9 | % 'etaParam', or 'GParam' 10 | % (not case sensitive). 11 | 12 | 13 | 14 | 15 | 16 | 17 | % This file is provided as a supplement to: Plett, Gregory L., "Battery 18 | % Management Systems, Volume I, Battery Modeling," Artech House, 2015. 19 | 20 | function theParam = getParamESC(paramName,temp,model) 21 | theFields = fieldnames(model); % get list of fields stored in model 22 | match = strcmpi(paramName,theFields); % see if any match desired data 23 | if ~match, % if not, throw an error 24 | error('Parameter "%s" does not exist in model',paramName); 25 | end 26 | fieldName = char(theFields(match)); % case-sensitive field name 27 | 28 | % if model contains data at only one temperature 29 | if isscalar(model.temps), 30 | if model.temps ~= temp, % check whether requested data exists 31 | error('Model does not contain requested data at this temperature'); 32 | end 33 | theParam = model.(fieldName); 34 | return 35 | end 36 | 37 | % Otherwise, model has multiple temperatures. Bound input "temp" between 38 | % mininum and maximum stored temperature to prohibit "NaN" in output 39 | theParamData = model.(fieldName); 40 | temp = max(min(temp,max(model.temps)),min(model.temps)); 41 | ind = find(model.temps == temp); % see if there is an exact match to 42 | if ~isempty(ind), % avoid call to (slow) interp1 whenever possible 43 | if size(theParamData,1) == 1, 44 | theParam = theParamData(ind); 45 | else 46 | theParam = theParamData(ind,:); 47 | end 48 | else % if there is not an exact match, we interpolate between parameter 49 | theParam = interp1(model.temps,theParamData,temp,'spline'); % values 50 | end % stored at different temperatures 51 | -------------------------------------------------------------------------------- /ElectrochemicalCellModel/processDynamic.m: -------------------------------------------------------------------------------- 1 | % -------------------------------------------------------------------- 2 | % function processDynamic 3 | % 4 | % Technical note: PROCESSDYNAMIC assumes that specific Arbin test 5 | % scripts have been executed to generate the input files. 6 | % "makeMATfiles.m" converts the raw Excel data files into "MAT" format 7 | % where the MAT files have fields for time, step, current, voltage, 8 | % chgAh, and disAh for each script run. 9 | % 10 | % The results from three scripts are required at every temperature. 11 | % The steps in each script file are assumed to be: 12 | % Script 1 (thermal chamber set to test temperature): 13 | % Step 1: Rest @ 100% SOC to acclimatize to test temperature 14 | % Step 2: Discharge @ 1C to reach ca. 90% SOC 15 | % Step 3: Repeatedly execute dynamic profiles (and possibly 16 | % intermediate rests) until SOC is around 10% 17 | % Script 2 (thermal chamber set to 25 degC): 18 | % Step 1: Rest ca. 10% SOC to acclimatize to 25 degC 19 | % Step 2: Discharge to min voltage (ca. C/3) 20 | % Step 3: Rest 21 | % Step 4: Constant voltage at vmin until current small (ca. C/30) 22 | % Steps 5-7: Dither around vmin 23 | % Step 8: Rest 24 | % Script 3 (thermal chamber set to 25 degC): 25 | % Step 2: Charge @ 1C to max voltage 26 | % Step 3: Rest 27 | % Step 4: Constant voltage at vmax until current small (ca. C/30) 28 | % Steps 5-7: Dither around vmax 29 | % Step 8: Rest 30 | % 31 | % All other steps (if present) are ignored by PROCESSDYNAMIC. The time 32 | % step between data samples must be uniform -- we assume a 1s sample 33 | % period in this code 34 | % 35 | % The inputs: 36 | % - data: An array, with one entry per temperature to be processed. 37 | % One of the array entries must be at 25 degC. The fields of 38 | % "data" are: temp (the test temperature), script1, 39 | % script 2, and script 3, where the latter comprise data 40 | % collected from each script. The sub-fields of these script 41 | % structures that are used by PROCESSDYNAMIC are the vectors: 42 | % current, voltage, chgAh, and disAh 43 | % - model: The output from processOCV, comprising the OCV model 44 | % - numpoles: The number of R-C pairs in the model 45 | % - doHyst: 0 if no hysteresis model desired; 1 if hysteresis desired 46 | % 47 | % The output: 48 | % - model: A modified model, which now contains the dynamic fields 49 | % filled in. 50 | function model = processDynamic(data,model,numpoles,doHyst) 51 | global bestcost 52 | 53 | % used by fminbnd later on 54 | options=optimset('TolX',1e-8,'TolFun',1e-8,'MaxFunEval',100000, ... 55 | 'MaxIter',1e6,'Jacobian','Off'); % for later optimization 56 | options=optimset('TolX',0.1,'TolFun',1e-2,'MaxFunEval',40, ... 57 | 'MaxIter',20,'Jacobian','Off'); % for later optimization 58 | 59 | % ------------------------------------------------------------------ 60 | % Step 1: Compute capacity and coulombic efficiency for every test 61 | % ------------------------------------------------------------------ 62 | alltemps = [data(:).temp]; 63 | alletas = 0*alltemps; 64 | allQs = 0*alltemps; 65 | 66 | ind25 = find(alltemps == 25); 67 | if isempty(ind25), 68 | error('Must have a test at 25degC'); 69 | end 70 | not25 = find(alltemps ~= 25); 71 | 72 | for k = ind25, 73 | totDisAh = data(k).script1.disAh(end) + ... 74 | data(k).script2.disAh(end) + ... 75 | data(k).script3.disAh(end); 76 | totChgAh = data(k).script1.chgAh(end) + ... 77 | data(k).script2.chgAh(end) + ... 78 | data(k).script3.chgAh(end); 79 | eta25 = totDisAh/totChgAh; 80 | data(k).eta = eta25; alletas(k) = eta25; 81 | data(k).script1.chgAh = data(k).script1.chgAh*eta25; 82 | data(k).script2.chgAh = data(k).script2.chgAh*eta25; 83 | data(k).script3.chgAh = data(k).script3.chgAh*eta25; 84 | 85 | Q25 = data(k).script1.disAh(end) + data(k).script2.disAh(end) -... 86 | data(k).script1.chgAh(end) - data(k).script2.chgAh(end); 87 | data(k).Q = Q25; allQs(k) = Q25; 88 | end 89 | eta25 = mean(alletas(ind25)); 90 | 91 | for k = not25, 92 | data(k).script2.chgAh = data(k).script2.chgAh*eta25; 93 | data(k).script3.chgAh = data(k).script3.chgAh*eta25; 94 | eta = (data(k).script1.disAh(end) + data(k).script2.disAh(end)+... 95 | data(k).script3.disAh(end) - data(k).script2.chgAh(end)-... 96 | data(k).script3.chgAh(end))/data(k).script1.chgAh(end); 97 | data(k).script1.chgAh = eta*data(k).script1.chgAh; 98 | data(k).eta = eta; alletas(k) = eta; 99 | 100 | Q = data(k).script1.disAh(end) + data(k).script2.disAh(end) - ... 101 | data(k).script1.chgAh(end) - data(k).script2.chgAh(end); 102 | data(k).Q = Q; allQs(k) = Q; 103 | end 104 | 105 | model.temps = unique(alltemps); numTemps = length(model.temps); 106 | model.etaParam = NaN(1,numTemps); 107 | model.QParam = NaN(1,numTemps); 108 | for k = 1:numTemps, 109 | model.etaParam(k) = mean(alletas(alltemps == model.temps(k))); 110 | model.QParam(k) = mean(allQs(alltemps == model.temps(k))); 111 | end 112 | 113 | % ------------------------------------------------------------------ 114 | % Step 2: Compute OCV for "discharge portion" of test 115 | % ------------------------------------------------------------------ 116 | for k = 1:length(data), 117 | etaParam = model.etaParam(k); 118 | etaik = data(k).script1.current; 119 | etaik(etaik<0)= etaParam*etaik(etaik<0); 120 | data(k).Z = 1 - cumsum([0,etaik(1:end-1)])*1/(data(k).Q*3600); 121 | data(k).OCV = OCVfromSOCtemp(data(k).Z(:),alltemps(k),model); 122 | end 123 | 124 | % ------------------------------------------------------------------ 125 | % Step 3: Now, optimize! 126 | % ------------------------------------------------------------------ 127 | model.GParam = NaN(1,numTemps); % "gamma" hysteresis parameter 128 | model.M0Param = NaN(1,numTemps); % "M0" hysteresis parameter 129 | model.MParam = NaN(1,numTemps); % "M" hysteresis parameter 130 | model.R0Param = NaN(1,numTemps); % "R0" ohmic resistance parameter 131 | model.RCParam = NaN(numTemps,numpoles); % time const. 132 | model.RParam = NaN(numTemps,numpoles); % Rk 133 | 134 | for theTemp = 1:numTemps, 135 | fprintf('Processing temperature %d\n',model.temps(theTemp)); 136 | bestcost = Inf; 137 | if doHyst, 138 | model.GParam(theTemp) = abs(fminbnd(@(x) optfn(x,data,... 139 | model,model.temps(theTemp),... 140 | doHyst),1,250,options)); 141 | else 142 | model.GParam(theTemp) = 0; 143 | theGParam = 0; 144 | optfn(theGParam,data,model,model.temps(theTemp),doHyst); 145 | end 146 | [~,model] = minfn(data,model,model.temps(theTemp),doHyst); 147 | end 148 | return 149 | 150 | % -------------------------------------------------------------------- 151 | % This minfn works for the enhanced self-correcting cell model 152 | % -------------------------------------------------------------------- 153 | function cost=optfn(theGParam,data,model,theTemp,doHyst) 154 | global bestcost 155 | 156 | model.GParam(model.temps == theTemp) = abs(theGParam); 157 | [cost,model] = minfn(data,model,theTemp,doHyst); 158 | if cost 0 & eigA < 1); % make sure in range 208 | okpoles = length(eigA); np = np+1; 209 | if okpoles >= numpoles, break; end 210 | fprintf('Trying np = %d\n',np); 211 | end 212 | RCfact = sort(eigA); RCfact = RCfact(end-numpoles+1:end); 213 | RC = -1./log(RCfact); 214 | % Simulate the R-C filters to find R-C currents 215 | vrcRaw = zeros(numpoles,length(h)); 216 | for k=2:length(ik), 217 | vrcRaw(:,k) = diag(RCfact)*vrcRaw(:,k-1) + (1-RCfact)*etaik(k-1); 218 | end 219 | vrcRaw = vrcRaw'; 220 | 221 | % Third modeling step: Hysteresis parameters 222 | if doHyst, 223 | H = [h,sik,-etaik,-vrcRaw]; 224 | W = lsqnonneg(H,verr); % W = H\verr; 225 | M = W(1); M0 = W(2); R0 = W(3); Rfact = W(4:end)'; 226 | else 227 | H = [-etaik,-vrcRaw]; 228 | W = H\verr; 229 | M=0; M0=0; R0 = W(1); Rfact = W(2:end)'; 230 | end 231 | ind = find(model.temps == data(ind(thefile)).temp,1); 232 | model.R0Param(ind) = R0; 233 | model.M0Param(ind) = M0; 234 | model.MParam(ind) = M; 235 | model.RCParam(ind,:) = RC'; 236 | model.RParam(ind,:) = Rfact'; 237 | 238 | vest2 = vest1 + M*h + M0*sik - R0*etaik - vrcRaw*Rfact'; 239 | verr = vk - vest2; 240 | 241 | % Compute RMS error only on data roughly in 5% to 95% SOC 242 | v1 = OCVfromSOCtemp(0.95,data(ind(thefile)).temp,model); 243 | v2 = OCVfromSOCtemp(0.05,data(ind(thefile)).temp,model); 244 | N1 = find(vk 0.5); 117 | vDis = flipud(disV(ind) + (1 - disZ(ind))*deltaV50); 118 | zDis = flipud(disZ(ind)); 119 | filedata(ind25).rawocv = interp1([zChg; zDis],[vChg; vDis],SOC,... 120 | 'linear','extrap'); 121 | 122 | filedata(ind25).temp = data25.temp; 123 | 124 | % ------------------------------------------------------------------ 125 | % Process other temperatures to find raw OCV relationship and eta 126 | % ------------------------------------------------------------------ 127 | for k = not25', 128 | data(k).script2.chgAh = data(k).script2.chgAh*eta25; 129 | data(k).script4.chgAh = data(k).script4.chgAh*eta25; 130 | eta(k) = (data(k).script1.disAh(end) + ... 131 | data(k).script2.disAh(end) + ... 132 | data(k).script3.disAh(end) + ... 133 | data(k).script4.disAh(end) - ... 134 | data(k).script2.chgAh(end) - ... 135 | data(k).script4.chgAh(end))/ ... 136 | (data(k).script1.chgAh(end) + ... 137 | data(k).script3.chgAh(end)); 138 | data(k).script1.chgAh = eta(k)*data(k).script1.chgAh; 139 | data(k).script3.chgAh = eta(k)*data(k).script3.chgAh; 140 | 141 | Q(k) = data(k).script1.disAh(end) + data(k).script2.disAh(end) ... 142 | - data(k).script1.chgAh(end) - data(k).script2.chgAh(end); 143 | indD = find(data(k).script1.step == 2); % slow discharge 144 | IR1Da = data(k).script1.voltage(indD(1)-1) - ... 145 | data(k).script1.voltage(indD(1)); 146 | IR2Da = data(k).script1.voltage(indD(end)+1) - ... 147 | data(k).script1.voltage(indD(end)); 148 | indC = find(data(k).script3.step == 2); 149 | IR1Ca = data(k).script3.voltage(indC(1)) - ... 150 | data(k).script3.voltage(indC(1)-1); 151 | IR2Ca = data(k).script3.voltage(indC(end)) - ... 152 | data(k).script3.voltage(indC(end)+1); 153 | IR1D = min(IR1Da,2*IR2Ca); IR2D = min(IR2Da,2*IR1Ca); 154 | IR1C = min(IR1Ca,2*IR2Da); IR2C = min(IR2Ca,2*IR1Da); 155 | 156 | blend = (0:length(indD)-1)/(length(indD)-1); 157 | IRblend = IR1D + (IR2D-IR1D)*blend(:); 158 | disV = data(k).script1.voltage(indD) + IRblend; 159 | disZ = 1 - data(k).script1.disAh(indD)/Q25; 160 | disZ = disZ + (1 - disZ(1)); 161 | filedata(k).disZ = disZ; 162 | filedata(k).disV = data(k).script1.voltage(indD); 163 | filedata(k).disVmod = disV; 164 | 165 | blend = (0:length(indC)-1)/(length(indC)-1); 166 | IRblend = IR1C + (IR2C-IR1C)*blend(:); 167 | chgV = data(k).script3.voltage(indC) - IRblend; 168 | chgZ = data(k).script3.chgAh(indC)/Q25; 169 | chgZ = chgZ - chgZ(1); 170 | filedata(k).chgZ = chgZ; 171 | filedata(k).chgV = data(k).script3.voltage(indC); 172 | filedata(k).chgVmod = chgV; 173 | 174 | deltaV50 = interp1(chgZ,chgV,0.5) - interp1(disZ,disV,0.5); 175 | ind = find(chgZ < 0.5); 176 | vChg = chgV(ind) - chgZ(ind)*deltaV50; 177 | zChg = chgZ(ind); 178 | ind = find(disZ > 0.5); 179 | vDis = flipud(disV(ind) + (1 - disZ(ind))*deltaV50); 180 | zDis = flipud(disZ(ind)); 181 | filedata(k).rawocv = interp1([zChg; zDis],[vChg; vDis],SOC,... 182 | 'linear','extrap'); 183 | 184 | filedata(k).temp = data(k).temp; 185 | end 186 | 187 | % ------------------------------------------------------------------ 188 | % Use the SOC versus OCV data now available at each individual 189 | % temperature to compute an OCV0 and OCVrel relationship 190 | % ------------------------------------------------------------------ 191 | % First, compile the voltages and temperatures into a single array 192 | % rather than a structure 193 | Vraw = []; temps = []; 194 | for k = 1:numtemps, 195 | if filedata(k).temp > 0, 196 | Vraw = [Vraw; filedata(k).rawocv]; %#ok 197 | temps = [temps; filedata(k).temp]; %#ok 198 | end 199 | end 200 | X = [ones(size(temps)), temps] \ Vraw; 201 | model.OCV0 = X(1,:); 202 | model.OCVrel = X(2,:); 203 | model.SOC = SOC; 204 | 205 | % ------------------------------------------------------------------ 206 | % Make SOC0 and SOCrel 207 | % ------------------------------------------------------------------ 208 | z = -0.1:0.01:1.1; % test soc vector 209 | v = minV-0.01:0.01:maxV+0.01; 210 | socs = []; 211 | for T = filetemps', 212 | v1 = OCVfromSOCtemp(z,T,model); 213 | socs = [socs; interp1(v1,z,v)]; %#ok 214 | end 215 | 216 | SOC0 = zeros(size(v)); SOCrel = SOC0; 217 | H = [ones([numtemps,1]), filetemps]; 218 | for k = 1:length(v), 219 | X = H\socs(:,k); % fit SOC(v,T) = 1*SOC0(v) + T*SOCrel(v) 220 | SOC0(k) = X(1); 221 | SOCrel(k) = X(2); 222 | end 223 | model.OCV = v; 224 | model.SOC0 = SOC0; 225 | model.SOCrel = SOCrel; 226 | 227 | % ------------------------------------------------------------------ 228 | % Save data in output directory 229 | % ------------------------------------------------------------------ 230 | model.OCVeta = eta; 231 | model.OCVQ = Q; 232 | model.name = cellID; 233 | 234 | % % ------------------------------------------------------------------ 235 | % % Plot some data... 236 | % % ------------------------------------------------------------------ 237 | % for k = 1:numtemps, 238 | % figure(k); clf; 239 | % plot(100*SOC,OCVfromSOCtemp(SOC,filedata(k).temp,model),... 240 | % 100*SOC,filedata(k).rawocv); hold on 241 | % xlabel('SOC (%)'); ylabel('OCV (V)'); ylim([minV-0.1 maxV+0.1]); 242 | % title(sprintf('%s OCV relationship at temp = %d',... 243 | % cellID,filedata(k).temp)); xlim([0 100]); 244 | % err = filedata(k).rawocv - ... 245 | % OCVfromSOCtemp(SOC,filedata(k).temp,model); 246 | % rmserr = sqrt(mean(err.^2)); 247 | % text(2,maxV-0.15,sprintf('RMS error = %4.1f (mV)',... 248 | % rmserr*1000),'fontsize',14); 249 | % figFormat 250 | % plot(100*filedata(k).disZ,filedata(k).disV,'k--','linewidth',1); 251 | % plot(100*filedata(k).chgZ,filedata(k).chgV,'k--','linewidth',1); 252 | % legend('Model prediction','Approximate OCV from data',... 253 | % 'Raw measured data','location','southeast'); 254 | % 255 | % if savePlots, 256 | % if ~exist('OCV_FIGURES','dir'), mkdir('OCV_FIGURES'); end 257 | % if filetemps(k) < 0, 258 | % filename = sprintf('OCV_FIGURES/%s_N%02d.eps',... 259 | % cellID,abs(filetemps(k))); 260 | % else 261 | % filename = sprintf('OCV_FIGURES/%s_P%02d.eps',... 262 | % cellID,filetemps(k)); 263 | % end 264 | % print(filename,'-deps2c') 265 | % end 266 | % end 267 | 268 | % ------------------------------------------------------------------ 269 | % Plot some data... 270 | % ------------------------------------------------------------------ 271 | if strcmp(cellID,'E1'), 272 | k = find(filetemps == -15); 273 | figure(100); clf; 274 | plot(100*filedata(k).disZ,filedata(k).disV,'-',... 275 | 100*filedata(k).chgZ,filedata(k).chgV,'-','linewidth',1); 276 | hold on 277 | plot(100*SOC,filedata(k).rawocv,'k--'); hold on 278 | 279 | xlabel('State of charge (%)'); ylabel('Voltage (V)'); ylim([minV maxV]); 280 | title('Voltages versus SOC'); 281 | set(gca,'xtick',0:20:100); 282 | set(gca,'ytick',3:0.2:4.2); 283 | grid on 284 | bookFormatMargin; 285 | yt=get(gca,'YTick'); 286 | set(gca, 'YTickLabel', num2str(yt(:), '%.1f')) 287 | legend('Discharge voltage','Charge voltage','Approximate OCV',... 288 | 'location','southeast'); 289 | print -deps2c ../FIGURES/profileVoltage.eps 290 | 291 | figure(102); clf; 292 | plot(100*filedata(k).disZ,filedata(k).disV,'-',... 293 | 100*filedata(k).chgZ,filedata(k).chgV,'-','linewidth',1); 294 | % hold on 295 | % plot(100*SOC,filedata(k).rawocv,'k--'); hold on 296 | 297 | xlabel('State of charge (%)'); ylabel('Voltage (V)'); ylim([minV maxV]); 298 | title('Voltages versus SOC'); 299 | set(gca,'xtick',0:20:100); 300 | set(gca,'ytick',3:0.2:4.2); 301 | grid on 302 | notesFormat([0.1 0 0.05 0]) 303 | yt=get(gca,'YTick'); 304 | set(gca, 'YTickLabel', num2str(yt(:), '%.1f')) 305 | legend('Discharge voltage','Charge voltage','location','southeast'); 306 | print -deps2c ../FIGURES/profileVoltageTrim.eps 307 | 308 | figure(101); clf; 309 | plot(100*filedata(k).disZ,filedata(k).disV,'-',... 310 | 100*filedata(k).chgZ,filedata(k).chgV,'-','linewidth',1); 311 | hold on 312 | plot(100*SOC,filedata(k).rawocv,'k--'); hold on 313 | 314 | xlabel('State of charge (%)'); ylabel('Voltage (V)'); ylim([minV maxV]); 315 | title('Voltages versus SOC'); 316 | set(gca,'xtick',0:20:100); 317 | set(gca,'ytick',3:0.2:4.2); grid on 318 | notesFormat([0.1 0 0.05 0]); 319 | yt=get(gca,'YTick'); 320 | set(gca, 'YTickLabel', num2str(yt(:), '%.1f')) 321 | legend('Discharge voltage','Charge voltage','Approximate OCV',... 322 | 'location','southeast'); 323 | print -deps2c ~/DOSSIER/SOURCE/TEACHING/ece4710/NOTES/CH02-EquivalentCircuit/FIGURES/profileVoltage.eps 324 | 325 | figure(103); clf; 326 | plot(100*filedata(k).disZ,filedata(k).disV,'-',... 327 | 100*filedata(k).chgZ,filedata(k).chgV,'-'); hold on 328 | ax = gca; ax.ColorOrderIndex = 1; 329 | plot(100*filedata(k).disZ,filedata(k).disVmod,'--',... 330 | 100*filedata(k).chgZ,filedata(k).chgVmod,'--'); 331 | 332 | xlabel('State of charge (%)'); ylabel('Voltage (V)'); ylim([minV maxV]); 333 | title('Voltages versus SOC'); 334 | set(gca,'xtick',0:20:100); 335 | set(gca,'ytick',3:0.2:4.2); grid on 336 | notesFormat([0.1 0 0.05 0]); 337 | yt=get(gca,'YTick'); 338 | set(gca, 'YTickLabel', num2str(yt(:), '%.1f')) 339 | legend('Discharge voltage','Charge voltage','Discharge voltage, \itR\rm removed','Charge voltage, \itR\rm removed',... 340 | 'location','southeast'); 341 | print -deps2c ../FIGURES/profileRremoved.eps 342 | 343 | figure(104); clf; 344 | plot(100*filedata(k).disZ,filedata(k).disV,'-',... 345 | 100*filedata(k).chgZ,filedata(k).chgV,'-'); hold on 346 | ax = gca; ax.ColorOrderIndex = 1; 347 | plot(100*filedata(k).disZ,filedata(k).disVmod,'--',... 348 | 100*filedata(k).chgZ,filedata(k).chgVmod,'--'); 349 | plot(100*SOC,filedata(k).rawocv,'k--'); hold on 350 | 351 | xlabel('State of charge (%)'); ylabel('Voltage (V)'); ylim([minV maxV]); 352 | title('Voltages versus SOC'); 353 | set(gca,'xtick',0:20:100); 354 | set(gca,'ytick',3:0.2:4.2); grid on 355 | notesFormat([0.1 0 0.05 0]); 356 | yt=get(gca,'YTick'); 357 | set(gca, 'YTickLabel', num2str(yt(:), '%.1f')) 358 | legend('Discharge voltage','Charge voltage','Discharge voltage, \itR\rm removed',... 359 | 'Charge voltage, \itR\rm removed','Approximate OCV',... 360 | 'location','southeast'); 361 | print -deps2c ../FIGURES/approxOCV.eps 362 | end 363 | 364 | end -------------------------------------------------------------------------------- /ElectrochemicalCellModel/pulseData.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/ElectrochemicalCellModel/pulseData.mat -------------------------------------------------------------------------------- /ElectrochemicalCellModel/pulseModel.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/ElectrochemicalCellModel/pulseModel.mat -------------------------------------------------------------------------------- /ElectrochemicalCellModel/runProcessDynamic.m: -------------------------------------------------------------------------------- 1 | % -------------------------------------------------------------------- 2 | % script runProcessDynamic 3 | % 4 | % RUNPROCESSDYNAMIC reads data files corresponding to dynamic cell 5 | % tests, executes PROCESSDYNAMIC, and then saves the resulting 6 | % model. It relies on SETUPDYNDATA to provide a list of data files 7 | % to be processed. 8 | 9 | setupDynData; % get list of files to be processed 10 | numpoles = 1; % number of resistor--capacitor pairs in final model 11 | doHyst = 1; % whether to include hysteresis in model 12 | 13 | for indID = 1:length(cellIDs), % process each cell type 14 | cellID = cellIDs{indID}; 15 | 16 | % Read model OCV file 17 | modelFile = sprintf('%smodel-ocv.mat',cellID); 18 | load(modelFile); 19 | 20 | % Read MAT raw data files 21 | data = zeros([0 length(mags{indID} > 0)]); dataInd = 0; 22 | for indTemps = 1:length(mags{indID}), 23 | theMag = mags{indID}(indTemps); 24 | if theMag < 0, 25 | continue 26 | else 27 | dataInd = dataInd + 1; 28 | end 29 | if temps(indTemps) < 0, 30 | DYNPrefix = sprintf('E:/BMS/ECM/Matlabfiles/work/readonly/%s_DYN/%s_DYN_%02d_N%02d',... 31 | cellID,cellID,theMag,abs(temps(indTemps))); 32 | else 33 | DYNPrefix = sprintf('E:/BMS/ECM/Matlabfiles/work/readonly/%s_DYN/%s_DYN_%02d_P%02d',... 34 | cellID,cellID,theMag,temps(indTemps)); 35 | end 36 | inFile = sprintf('%s.mat',DYNPrefix); 37 | fprintf('Loading %s\n',inFile); 38 | load(inFile); 39 | data(dataInd).temp = temps(indTemps); % temperature 40 | data(dataInd).script1 = DYNData.script1; 41 | data(dataInd).script2 = DYNData.script2; 42 | data(dataInd).script3 = DYNData.script3; 43 | end 44 | 45 | model = processDynamic(data,model,numpoles,doHyst); 46 | modelFile = sprintf('%smodel.mat',cellID); 47 | save(modelFile,'model'); 48 | 49 | fprintf('\nDynamic model created!\n'); 50 | end -------------------------------------------------------------------------------- /ElectrochemicalCellModel/runProcessOCV.m: -------------------------------------------------------------------------------- 1 | clear all 2 | cellIDs = {'P14'}; 3 | % data files for each cell available at these temperatures 4 | temps = {[-25 -15 -5 5 15 25 35 45]}; % P14 5 | % minimum and maximum voltages for each cell, used for plotting results 6 | minV = [2.50]; 7 | maxV = [4.25]; 8 | 9 | % -------------------------------------------------------------------- 10 | % Load raw data from cell tests, then process 11 | % -------------------------------------------------------------------- 12 | for theID = 1:length(cellIDs), 13 | dirname = cellIDs{theID}; cellID = dirname; 14 | ind = find(dirname == '_'); 15 | if ~isempty(ind), dirname = dirname(1:ind-1); end 16 | OCVDir = sprintf('E:/BMS/ECM/Matlabfiles/work/readonly/%s_OCV',dirname); 17 | 18 | filetemps = temps{theID}(:); 19 | numtemps = length(filetemps); 20 | data = zeros([0 numtemps]); 21 | 22 | for k = 1:numtemps, 23 | if filetemps(k) < 0, 24 | filename = sprintf('%s/%s_OCV_N%02d.mat',... 25 | OCVDir,cellID,abs(filetemps(k))); 26 | else 27 | filename = sprintf('%s/%s_OCV_P%02d.mat',... 28 | OCVDir,cellID,filetemps(k)); 29 | end 30 | fprintf('Loading OCV data collected for test temperature %d degrees C\n',filetemps(k)); 31 | load(filename); 32 | data(k).temp = filetemps(k); 33 | data(k).script1 = OCVData.script1; 34 | data(k).script2 = OCVData.script2; 35 | data(k).script3 = OCVData.script3; 36 | data(k).script4 = OCVData.script4; 37 | end 38 | 39 | fprintf('Processing data to create OCV relationship\n'); 40 | model = processOCV(data,cellID,minV(theID),maxV(theID),0); 41 | save(sprintf('%smodel-ocv.mat',cellID),'model'); 42 | fprintf('Processing complete. Results are available in variable "model"\n'); 43 | fprintf('and are saved in %s\n',sprintf('%smodel-ocv.mat',cellID)); 44 | end -------------------------------------------------------------------------------- /ElectrochemicalCellModel/setupDynData.m: -------------------------------------------------------------------------------- 1 | % Files to use for optimizations at different temperatures 2 | % Temp: -25 -15 -05 05 15 25 35 45 3 | % -------------------------------------------------------- 4 | % A123: 10 10 30 45 50 50 50 50 5 | % ATL: <2 5 15 20 30 40 50 50 6 | % E1: 2 5 10 20 25 35 45 50 7 | % E2: 2 5 5 15 25 35 45 50 8 | % FORD53: 10 25 25 35 40 40 40 9 | % FORD54: 10 25 25 35 40 40 40 10 | % P14: 4 10 20 35 50 50 50 50 11 | % SAM: 2 2 5 10 10 15 15 15 12 | % 13 | % Note that the numeric indicator indicates the maximum C-rate used 14 | % in that test (e.g., "40" = 4.0C). Because of higher resistance at 15 | % colder temperatures, we must scale down the maximum current to avoid 16 | % exceeding voltage limits of the cell. 17 | clear all 18 | cellIDs = {'P14'}; 19 | temps = [ 5 25 45]; 20 | mags = {[30 50 50]}; % P14 21 | 22 | -------------------------------------------------------------------------------- /ElectrochemicalCellModel/setupSimVehicle.m: -------------------------------------------------------------------------------- 1 | % setup simulation of vehicle - pass on to simVehicle.m 2 | function results = setupSimVehicle(grade) 3 | % global cell module pack motor drivetrain vehicle cycle results 4 | addpath E:\BMS\ECM\Matlabfiles\work\readonly 5 | files = {'nycc.txt','udds.txt','us06.txt','hwy.txt'}; 6 | 7 | % Setup the Chevy Volt 8 | % set up cell: capacity [Ah], weight [g], (vmax, vnom, vmin) [V] 9 | cell = setupCell(15,450,4.2,3.8,3.0); 10 | % set up module: numParallel, numSeries, overhead for this cell by weight 11 | module = setupModule(3,8,0.08,cell); 12 | % set up pack: numSeries, overhead by weight, (full SOC, empty SOC) [%], 13 | % efficiency for this module 14 | pack = setupPack(12,0.1,75,25,0.96,module); 15 | % set up motor: Lmax [Nm], (RPMrated, RPMmax) [RPM], efficiency, 16 | % inertia [kg/m2] 17 | motor = setupMotor(275,4000,12000,.95,0.2); 18 | % set up wheel: radius [m], inertia [kg/m2], rollCoef 19 | wheel = setupWheel(0.35,8,0.0111); 20 | % set up drivetrain: inverter efficiency, fractional regen torque limit, 21 | % gear ratio, gear inertia [kg/m2], gear efficiency, for this pack, 22 | % motor, and wheel 23 | drivetrain = setupDrivetrain(0.94,0.9,12,0.05,0.97,pack,motor,wheel); 24 | % set up vehicle: # wheels, roadForce [N], Cd, frontal area [m2], weight 25 | % [kg], payload [kg], overhead power [W] for this drivetrain 26 | vehicle = setupVehicle(4,0,0.22,1.84,1425,75,200,drivetrain); 27 | 28 | fprintf('\n\nStarting sims...\n'); 29 | for theCycle = 1:length(files), 30 | cycle = dlmread(sprintf('readonly/%s',files{theCycle}),'\t',2,0); 31 | results{theCycle} = simVehicle(vehicle,cycle,grade); 32 | range = (vehicle.drivetrain.pack.socFull - vehicle.drivetrain.pack.socEmpty) /... 33 | (vehicle.drivetrain.pack.socFull - results{theCycle}.batterySOC(end)) * ... 34 | results{theCycle}.distance(end); 35 | fprintf('Cycle = %s, range = %6.1f [km]\n',files{theCycle},range); 36 | end 37 | end 38 | 39 | function cell = setupCell(capacity,weight,vmax,vnom,vmin) 40 | cell.capacity = capacity; % ampere hours 41 | cell.weight = weight; % grams 42 | cell.vmax = vmax; % volts 43 | cell.vnom = vnom; % volts 44 | cell.vmin = vmin; % volts 45 | cell.energy = vnom * capacity; % Watt-hours 46 | cell.specificEnergy = 1000*cell.capacity*cell.vnom/cell.weight; % Wh/kg 47 | end 48 | 49 | function module = setupModule(numParallel,numSeries,overhead,cell) 50 | module.numParallel = numParallel; 51 | module.numSeries = numSeries; 52 | module.overhead = overhead; 53 | module.cell = cell; 54 | module.numCells = numParallel * numSeries; 55 | module.capacity = numParallel * cell.capacity; 56 | module.weight = module.numCells * cell.weight * 1/(1 - overhead)/1000; % kg 57 | module.energy = module.numCells * cell.energy/1000; % kWh 58 | module.specificEnergy = 1000 * module.energy / module.weight; % Wh/kg 59 | end 60 | 61 | function pack = setupPack(numSeries,overhead,socFull,socEmpty,efficiency,module) 62 | pack.numSeries = numSeries; 63 | pack.overhead = overhead; 64 | pack.module = module; 65 | pack.socFull = socFull; 66 | pack.socEmpty = socEmpty; % unitless 67 | pack.efficiency = efficiency; % unitless, captures I*I*R losses 68 | pack.numCells = module.numCells * numSeries; 69 | pack.weight = module.weight * numSeries * 1/(1 - overhead); % kg 70 | pack.energy = module.energy * numSeries; % kWh 71 | pack.specificEnergy = 1000 * pack.energy / pack.weight; % Wh/kg 72 | pack.vmax = numSeries*module.numSeries*module.cell.vmax; 73 | pack.vnom = numSeries*module.numSeries*module.cell.vnom; 74 | pack.vmin = numSeries*module.numSeries*module.cell.vmin; 75 | end 76 | 77 | function motor = setupMotor(Lmax,RPMrated,RPMmax,efficiency,inertia) 78 | motor.Lmax = Lmax; % N-m 79 | motor.RPMrated = RPMrated; 80 | motor.RPMmax = RPMmax; 81 | motor.efficiency = efficiency; 82 | motor.inertia = inertia; %kg-m2 83 | motor.maxPower = 2*pi*Lmax*RPMrated/60000; % kW 84 | end 85 | 86 | function wheel = setupWheel(radius,inertia,rollCoef) 87 | wheel.radius = radius; % m 88 | wheel.inertia = inertia; % km-m2 89 | wheel.rollCoef = rollCoef; 90 | end 91 | 92 | function drivetrain = setupDrivetrain(inverterEfficiency,regenTorque,... 93 | gearRatio,gearInertia,gearEfficiency,pack,motor,wheel) 94 | drivetrain.inverterEfficiency = inverterEfficiency; 95 | drivetrain.regenTorque = regenTorque; % fraction of total torque available 96 | drivetrain.pack = pack; 97 | drivetrain.motor = motor; 98 | drivetrain.wheel = wheel; 99 | drivetrain.gearRatio = gearRatio; 100 | drivetrain.gearInertia = gearInertia; % km-m2, measured on motor side 101 | drivetrain.gearEfficiency = gearEfficiency; 102 | drivetrain.efficiency = pack.efficiency * inverterEfficiency * ... 103 | motor.efficiency * gearEfficiency; 104 | end 105 | 106 | function vehicle = setupVehicle(wheels,roadForce,Cd,A,weight,payload,overheadPwr,drivetrain) 107 | vehicle.drivetrain = drivetrain; 108 | vehicle.wheels = wheels; % number of them 109 | vehicle.roadForce = roadForce; % N 110 | vehicle.Cd = Cd; % drag coeff 111 | vehicle.A = A; % frontal area, m2 112 | vehicle.weight = weight; % kg 113 | vehicle.payload = payload; % kg 114 | vehicle.overheadPwr = overheadPwr; % W 115 | vehicle.curbWeight = weight + drivetrain.pack.weight; 116 | vehicle.maxWeight = vehicle.curbWeight + payload; 117 | vehicle.rotWeight = ((drivetrain.motor.inertia + drivetrain.gearInertia) * ... 118 | drivetrain.gearRatio^2 + drivetrain.wheel.inertia*wheels)/drivetrain.wheel.radius^2; 119 | vehicle.equivMass = vehicle.maxWeight + vehicle.rotWeight; 120 | vehicle.maxSpeed = 2 * pi * drivetrain.wheel.radius * ... 121 | drivetrain.motor.RPMmax * 60 / (1000 * drivetrain.gearRatio); % km/h 122 | end 123 | 124 | -------------------------------------------------------------------------------- /ElectrochemicalCellModel/setupSimVehicleplots.m: -------------------------------------------------------------------------------- 1 | %% 2 | % Plot the desired and actual vehicle speed profiles 3 | names = {'NYCC','UDDS','US06','HWY'}; 4 | style = {'b','r','m','g'}; 5 | locations = {'northwest','northeast','south','south'}; 6 | for ii = 1:length(results) 7 | plotData = results{ii}.actualSpeedKPH; 8 | subplot(2,2,ii); plot(0:length(plotData)-1,plotData,style{ii}); 9 | plotData = results{ii}.desSpeedKPH; hold on; grid on; 10 | plot(0:length(plotData)-1,plotData,'k'); 11 | title(sprintf('%s profile: vehicle speed, 0.3%% grade',names{ii})); 12 | xlabel('Time (s)'); ylabel('Speed (km h^{-1})'); 13 | legend('Actual','Desired','location',locations{ii}); 14 | end 15 | 16 | %% 17 | % Plot the Battery Pack Current Profiles 18 | for ii = 1:length(results), 19 | plotData = results{ii}.current; 20 | subplot(2,2,ii); plot(0:length(plotData)-1,plotData,style{ii}); 21 | title(sprintf('%s profile: battery current',names{ii})); 22 | xlabel('Time (s)'); ylabel('Current (A)'); grid on; 23 | end 24 | %% Plot the Battery Pack Demanded Power Profile 25 | for ii = 1:length(results), 26 | plotData = results{ii}.batteryDemand; 27 | subplot(2,2,ii); plot(0:length(plotData)-1,plotData,style{ii}); 28 | title(sprintf('%s profile: battery power',names{ii})); 29 | xlabel('Time (s)'); ylabel('Power (kW)'); grid on; 30 | end 31 | %% Plot Histograms of Battery Pack Demanded Power 32 | edges = -70:20:140; 33 | for ii = 1:length(results), 34 | [N,Bin] = histc(results{ii}.batteryDemand,edges); 35 | subplot(2,2,ii); bar(edges,N,'histc'); xlim([min(edges) max(edges)]); 36 | title(sprintf('%s profile: battery power',names{ii})); 37 | ylabel('Frequency'); xlabel('Power (kW)'); grid on; 38 | end 39 | %% Plot Histograms of Motor Demanded Power 40 | for ii = 1:length(results) 41 | [N,Bin] = histc(results{ii}.limitPower,edges); 42 | subplot(2,2,ii); bar(edges,N,'histc'); xlim([min(edges) max(edges)]); 43 | title(sprintf('%s profile: limited motor power',names{ii})); 44 | ylabel('Frequency'); xlabel('Power (kW)'); grid on; 45 | end 46 | %% Plot Scatter Plot of Motor Torque Vs. RPM 47 | motor = results{1}.vehicle.drivetrain.motor; 48 | regen = results{1}.vehicle.drivetrain.regenTorque; 49 | RPM = motor.RPMrated:50:motor.RPMmax; 50 | TRQ = motor.Lmax*motor.RPMrated./RPM; 51 | for ii = 1:length(results), 52 | subplot(2,2,ii); 53 | scatter(results{ii}.motorSpeed,results{ii}.limitTorque,style{ii}); 54 | title(sprintf('%s profile: motor torque vs. speed',names{ii})); 55 | xlim([0 12000]); ylim([-300 300]); hold on; grid on; 56 | xlabel('Speed (RPM)'); ylabel('Torque (N m)'); 57 | plot([0 motor.RPMrated],motor.Lmax*[1 1],'k'); 58 | plot([0 motor.RPMrated],-regen*motor.Lmax*[1 1],'k'); 59 | plot(RPM,TRQ,'k'); plot(RPM,-regen*TRQ,'k'); 60 | end -------------------------------------------------------------------------------- /ElectrochemicalCellModel/simCell.m: -------------------------------------------------------------------------------- 1 | % function [vk,rck,hk,zk,sik,OCV] = simCell(ik,T,deltaT,model,z0,iR0,h0) 2 | % ik - current, where (+) is discharge 3 | % T - temperature (degC) 4 | % deltaT = sampling interval in data (s) 5 | % model - standard model structure 6 | % z0 - initial SOC 7 | % iR0 - initial resistor currents as column vector 8 | % h0 - initial hysteresis state 9 | 10 | function [vk,rck,hk,zk,sik,OCV] = simCell(ik,T,deltaT,model,z0,iR0,h0) 11 | % Force data to be column vector(s) 12 | ik = ik(:); iR0 = iR0(:); 13 | 14 | % Get model parameters from model structure 15 | RCfact = exp(-deltaT./abs(getParamESC('RCParam',T,model)))'; 16 | G = getParamESC('GParam',T,model); 17 | Q = getParamESC('QParam',T,model); 18 | M = getParamESC('MParam',T,model); 19 | M0 = getParamESC('M0Param',T,model); 20 | RParam = getParamESC('RParam',T,model); 21 | R0Param = getParamESC('R0Param',T,model); 22 | etaParam = getParamESC('etaParam',T,model); 23 | 24 | etaik = ik; etaik(ik<0) = etaParam*ik(ik<0); 25 | 26 | % Simulate the dynamic states of the model 27 | rck = zeros(length(RCfact),length(etaik)); rck(:,1) = iR0; 28 | for k = 2:length(ik), 29 | rck(:,k) = diag(RCfact)*rck(:,k-1) + (1-RCfact)*etaik(k-1); 30 | end 31 | rck = rck'; 32 | zk = z0-cumsum([0;etaik(1:end-1)])*deltaT/(Q*3600); 33 | if any(zk>1.1), 34 | warning('Current may have wrong sign as SOC > 110%'); 35 | end 36 | 37 | % Hysteresis stuff 38 | hk=zeros([length(ik) 1]); hk(1) = h0; sik = 0*hk; 39 | fac=exp(-abs(G*etaik*deltaT/(3600*Q))); 40 | for k=2:length(ik), 41 | hk(k)=fac(k-1)*hk(k-1)-(1-fac(k-1))*sign(ik(k-1)); 42 | sik(k) = sign(ik(k)); 43 | if abs(ik(k)) hours 20 | xlabel('Time (hr)'); ylabel('Voltage (V)'); title('Comparing measured to simulated voltage'); 21 | legend('Measured voltage','Simulated voltage'); 22 | 23 | % Now, plot the voltage prediction error 24 | subplot(1,2,2) 25 | plot(time/3600,1000*(voltage-vest')); 26 | xlabel('Time (hr)'); ylabel('Voltage (mV)'); title('Voltage prediction error'); 27 | %% 28 | % Visualize the change in SOC over the test 29 | subplot(2,2,1); plot(time/3600,zk); 30 | xlabel('Time (hr)'); ylabel('SOC (unitless)'); title('Model prediction of SOC'); 31 | 32 | % Visualize the change in R-C resistor currents over the test 33 | subplot(2,2,2); plot(time/3600,rck); 34 | xlabel('Time (hr)'); ylabel('R-C resistor currents (A)'); title('Model prediction of R-C resistor current'); 35 | 36 | % Visualize the change in cell OCV over the test 37 | subplot(2,2,3); plot(time/3600,OCV); 38 | xlabel('Time (hr)'); ylabel('OCV (V)'); title('Model prediction of OCV'); 39 | 40 | subplot(2,2,4); plot(time/3600,rck); 41 | xlabel('Time (hr)'); ylabel('R-C resistor currents (A)'); title('Model prediction of R-C resistor current'); 42 | %% 43 | % Visualize the change in dynamic hysteresis over the test 44 | subplot(1,2,1); plot(time/3600,hk); 45 | xlabel('Time (hr)'); ylabel('Dynamic hysteresis state (unitless))'); title('Model prediction of hysteresis state'); 46 | 47 | % Visualize the change in instantaneous hysteresis state over the test 48 | subplot(1,2,2); plot(time/3600,sik); 49 | xlabel('Time (hr)'); ylabel('Instantaneous hysteresis state (unitless))'); title('Model prediction of instantaneous hyst.'); -------------------------------------------------------------------------------- /ElectrochemicalCellModel/simVehicle.m: -------------------------------------------------------------------------------- 1 | % results = simVehicle(vehicle,cycle,grade) 2 | % - simulate a vehicle defined by "vehicle", perhaps created using 3 | % setupSimVehicle.m 4 | % - cycle is Nx2, where first column is time in seconds and second column 5 | % is desired speed in miles per hour 6 | % - grade is road grade in percent - either a constant grade for all 7 | % time, or a different grade value for every point in time 8 | function results = simVehicle(vehicle,cycle,grade) 9 | rho = 1.225; % air density, kg/m3 10 | 11 | results.vehicle = vehicle; 12 | results.cycle = cycle; % time in s, desired speed in miles/hour 13 | results.time = cycle(:,1); % s 14 | if isscalar(grade), 15 | results.grade = repmat(atan(grade/100),size(results.time)); % rad 16 | else 17 | results.grade = atan(grade/100); % rad 18 | end 19 | results.desSpeedKPH = cycle(:,2) * 1.609344; % convert to km/h 20 | results.desSpeed = min(vehicle.maxSpeed,results.desSpeedKPH*1000/3600); % m/s 21 | 22 | % pre-allocate storage for results 23 | results.desAccel = zeros(size(results.desSpeed)); % m/s2 24 | results.desAccelForce = zeros(size(results.desSpeed)); % N 25 | results.aeroForce = zeros(size(results.desSpeed)); % N 26 | results.rollGradeForce = zeros(size(results.desSpeed)); % N 27 | results.demandTorque = zeros(size(results.desSpeed)); % N-m 28 | results.maxTorque = zeros(size(results.desSpeed)); % N-m 29 | results.limitRegen = zeros(size(results.desSpeed)); % N-m 30 | results.limitTorque = zeros(size(results.desSpeed)); % N-m 31 | results.motorTorque = zeros(size(results.desSpeed)); % N-m 32 | results.demandPower = zeros(size(results.desSpeed)); % kW 33 | results.limitPower = zeros(size(results.desSpeed)); % kW 34 | results.batteryDemand = zeros(size(results.desSpeed)); % kW 35 | results.current = zeros(size(results.desSpeed)); % A 36 | results.batterySOC = zeros(size(results.desSpeed)); % 0..100 37 | results.actualAccelForce = zeros(size(results.desSpeed)); % N 38 | results.actualAccel = zeros(size(results.desSpeed)); % m/s2 39 | results.motorSpeed = zeros(size(results.desSpeed)); % RPM 40 | results.actualSpeed = zeros(size(results.desSpeed)); % m/s 41 | results.actualSpeedKPH = zeros(size(results.desSpeed)); % km/h 42 | results.distance = zeros(size(results.desSpeed)); % km 43 | 44 | prevSpeed = 0; prevTime = results.time(1) - 1; prevMotorSpeed = 0; 45 | prevSOC = vehicle.drivetrain.pack.socFull; prevDistance = 0; 46 | for k = 1:length(results.desSpeed), 47 | results.desAccel(k) = (results.desSpeed(k) - prevSpeed)/ ... 48 | (results.time(k) - prevTime); 49 | results.desAccelForce(k) = vehicle.equivMass * results.desAccel(k); 50 | results.aeroForce(k) = 0.5 * rho * vehicle.Cd * vehicle.A * prevSpeed^2; 51 | results.rollGradeForce(k) = vehicle.maxWeight * 9.81 * sin(results.grade(k)); 52 | if abs(prevSpeed) > 0, 53 | results.rollGradeForce(k) = results.rollGradeForce(k) + ... 54 | vehicle.drivetrain.wheel.rollCoef * vehicle.maxWeight * 9.81; 55 | end 56 | results.demandTorque(k) = (results.desAccelForce(k) + results.aeroForce(k) + ... 57 | results.rollGradeForce(k) + vehicle.roadForce) * ... 58 | vehicle.drivetrain.wheel.radius / vehicle.drivetrain.gearRatio; 59 | if prevMotorSpeed < vehicle.drivetrain.motor.RPMrated, 60 | results.maxTorque(k) = vehicle.drivetrain.motor.Lmax; 61 | else 62 | results.maxTorque(k) = vehicle.drivetrain.motor.Lmax * ... 63 | vehicle.drivetrain.motor.RPMrated / prevMotorSpeed; 64 | end 65 | results.limitRegen(k) = min(results.maxTorque(k),... 66 | vehicle.drivetrain.regenTorque * vehicle.drivetrain.motor.Lmax); 67 | results.limitTorque(k) = min(results.demandTorque(k),results.maxTorque(k)); 68 | if results.limitTorque(k) > 0, 69 | results.motorTorque(k) = results.limitTorque(k); 70 | else 71 | results.motorTorque(k) = max(-results.limitRegen(k),results.limitTorque(k)); 72 | end 73 | 74 | results.actualAccelForce(k) = results.limitTorque(k) * vehicle.drivetrain.gearRatio / ... 75 | vehicle.drivetrain.wheel.radius - results.aeroForce(k) - results.rollGradeForce(k) - ... 76 | vehicle.roadForce; 77 | results.actualAccel(k) = results.actualAccelForce(k) / vehicle.equivMass; 78 | results.motorSpeed(k) = min(vehicle.drivetrain.motor.RPMmax,... 79 | vehicle.drivetrain.gearRatio * (prevSpeed + results.actualAccel(k) * ... 80 | (results.time(k) - prevTime)) * 60 / (2*pi*vehicle.drivetrain.wheel.radius)); 81 | results.actualSpeed(k) = results.motorSpeed(k) * 2*pi*vehicle.drivetrain.wheel.radius / ... 82 | (60 * vehicle.drivetrain.gearRatio); 83 | results.actualSpeedKPH(k) = results.actualSpeed(k) * 3600/1000; 84 | deltadistance = (results.actualSpeed(k) + prevSpeed)/2 * (results.time(k) - prevTime)/1000; 85 | results.distance(k) = prevDistance + deltadistance; 86 | 87 | if results.limitTorque(k) > 0, 88 | results.demandPower(k) = results.limitTorque(k); 89 | else 90 | results.demandPower(k) = max(results.limitTorque(k),-results.limitRegen(k)); 91 | end 92 | results.demandPower(k) = results.demandPower(k) * 2*pi * (prevMotorSpeed + results.motorSpeed(k))/2 / 60000; 93 | results.limitPower(k) = max(-vehicle.drivetrain.motor.maxPower,min(... 94 | vehicle.drivetrain.motor.maxPower,results.demandPower(k))); 95 | results.batteryDemand(k) = vehicle.overheadPwr/1000; 96 | if results.limitPower(k) > 0, 97 | results.batteryDemand(k) = results.batteryDemand(k) + ... 98 | results.limitPower(k)/vehicle.drivetrain.efficiency; 99 | else 100 | results.batteryDemand(k) = results.batteryDemand(k) + ... 101 | results.limitPower(k)*vehicle.drivetrain.efficiency; 102 | end 103 | results.current(k) = results.batteryDemand(k)*1000/vehicle.drivetrain.pack.vnom; 104 | results.batterySOC(k) = prevSOC - results.current(k) * (results.time(k) - prevTime) / ... 105 | (36*vehicle.drivetrain.pack.module.capacity); 106 | 107 | 108 | prevTime = results.time(k); 109 | prevSpeed = results.actualSpeed(k); 110 | prevMotorSpeed = results.motorSpeed(k); 111 | prevSOC = results.batterySOC(k); 112 | prevDistance = results.distance(k); 113 | end 114 | end 115 | -------------------------------------------------------------------------------- /ElectrochemicalCellModel/tuneModel.m: -------------------------------------------------------------------------------- 1 | %% 2 | 3 | % function rcValues = tuneModel 4 | % 5 | % This function specifies the resistor and capacitor values that 6 | % you choose to use for either an Rint model or a Thévenin model 7 | % (see lesson 2.1.3), or an "extended Thévenin model" having 8 | % additional parallel resistor-capacitor branches. 9 | % 10 | % If you wish to create an Rint model, simply set rcValues equal 11 | % to the value of R0 (in milliohms). 12 | % If you wish to create a Thévenin model, define rcValues to be 13 | % a vector having three elements. The first element is R0 (in 14 | % milliohms), the second element is R1 (in milliohms), and the 15 | % third element is C1 (in kilofarads). 16 | % If you wish to create an extended Thévenin model having 17 | % additional resistor-capacitor branches, then define rcValues to 18 | % be a vector where the first element is R0 in milliohms, the 19 | % even-index elements (rcValues(2:2:end)) are resistor values for 20 | % the resistor-capacitor branches, in milliohms, and the remaining 21 | % odd-index elements (rcValues(3:2:end)) are capacitor values for 22 | % the resistor-capacitor branches, in kilofarads. 23 | % 24 | % It is possible to receive full credit for this assignment with 25 | % a well-tuned Thévenin model, but it is also possible to get a 26 | % much better fit to the data using an extended Thévenin model. 27 | function rcValues = tuneModel 28 | 29 | % BEGIN MODIFYING CODE AFTER THIS 30 | rcValues = [13.8 ;16; 46];% This is a sample value. You will need to change it. 31 | end 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BatteryManagementSystem_Algorithm_usingMatlab 2 | **Overview of the code** 3 | The codes are used in supplement with the books **Battery Management Systems I and II, by Gregory Plett** and the course **Algorithms for Battery Management System, by Coursera** 4 | -------------------------------------------------------------------------------- /State_Of_Charge/CellData.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/State_Of_Charge/CellData.mat -------------------------------------------------------------------------------- /State_Of_Charge/CellModel.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/State_Of_Charge/CellModel.mat -------------------------------------------------------------------------------- /State_Of_Charge/E2model.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/State_Of_Charge/E2model.mat -------------------------------------------------------------------------------- /State_Of_Charge/EKFinAction.m: -------------------------------------------------------------------------------- 1 | % Initialize simulation variables 2 | SigmaW = 1; % Process noise covariance 3 | SigmaV = 2; % Sensor noise covariance 4 | maxIter = 40; 5 | 6 | % Seed the random number generator: Octave's "randn" function produces pseudo 7 | % random numbers having a Gaussian distribution. To get the same random numbers 8 | % every time you run the code, you can "seed" the pseudo random number generator 9 | % with a deterministic value. This allows us to get reproducible results that 10 | % still contain apparent randomness. 11 | % 12 | 13 | randn("seed",-1); 14 | 15 | % Initialize true state, state estimate, error covariance, initial input 16 | xtrue = 2 + randn(1); % Initialize true system initial state 17 | xhat = 2; % Initialize Kalman filter initial estimate 18 | SigmaX = 1; % Initialize Kalman filter covariance 19 | u = 0; % Unknown initial driving input: assume zero 20 | 21 | % Reserve storage for variables we might want to plot/evaluate 22 | xstore = zeros(maxIter+1,length(xtrue)); xstore(1,:) = xtrue; 23 | xhatstore = zeros(maxIter,length(xhat)); 24 | SigmaXstore = zeros(maxIter,length(xhat)^2); 25 | 26 | for k = 1:maxIter, 27 | % EKF Step 0: Compute Ahat, Bhat 28 | % Note: For this example, x(k+1) = sqrt(5+x(k)) + w(k) 29 | Ahat = 0.5/sqrt(5+xhat); Bhat = 1; 30 | 31 | % EKF Step 1: State estimate time update 32 | % Note: You need to insert your system's f(...) equation here 33 | xhat = sqrt(5+xhat); 34 | 35 | % KF Step 2: Error covariance time update 36 | SigmaX = Ahat*SigmaX*Ahat' + Bhat*SigmaW*Bhat'; 37 | 38 | % [Implied operation of system in background, with 39 | % input signal u, and output signal z] 40 | w = chol(SigmaW)'*randn(1); 41 | v = chol(SigmaV)'*randn(1); 42 | ztrue = xtrue^3 + v; % z is based on present x and u 43 | xtrue = sqrt(5+xtrue) + w; % future x is based on present u 44 | 45 | % KF Step 3: Estimate system output 46 | % Note: You need to insert your system's h(...) equation here 47 | Chat = 3*xhat^2; Dhat = 1; 48 | zhat = xhat^3; 49 | 50 | % KF Step 4: Compute Kalman gain matrix 51 | L = SigmaX*Chat'/(Chat*SigmaX*Chat' + Dhat*SigmaV*Dhat'); 52 | 53 | % KF Step 5: State estimate measurement update 54 | xhat = xhat + L*(ztrue - zhat); 55 | xhat = max(-5,xhat); % don't get square root of negative xhat! 56 | 57 | % KF Step 6: Error covariance measurement update 58 | SigmaX = SigmaX - L*Chat*SigmaX; 59 | 60 | % [Store information for evaluation/plotting purposes] 61 | xstore(k+1,:) = xtrue; 62 | xhatstore(k,:) = xhat; 63 | SigmaXstore(k,:) = SigmaX(:); 64 | end 65 | 66 | subplot(1,2,1); 67 | plot(0:maxIter-1,xstore(1:maxIter),'k-',0:maxIter-1,xhatstore,'b--', ... 68 | 0:maxIter-1,xhatstore+3*sqrt(SigmaXstore),'m-.',... 69 | 0:maxIter-1,xhatstore-3*sqrt(SigmaXstore),'m-.'); grid; 70 | legend('true','estimate','bounds'); 71 | title('Extended Kalman filter in action'); 72 | xlabel('Iteration'); ylabel('State'); 73 | 74 | subplot(1,2,2) 75 | estErr = xstore(1:maxIter)-xhatstore; 76 | bounds = 3*sqrt(SigmaXstore); 77 | plot(0:maxIter-1,estErr,'b-',0:maxIter-1, bounds,'m--',0:maxIter-1,-bounds,'m--'); 78 | grid; legend('Error','bounds',0); 79 | title('EKF Error with bounds'); 80 | xlabel('Iteration'); ylabel('Estimation Error'); 81 | -------------------------------------------------------------------------------- /State_Of_Charge/KFinaction.m: -------------------------------------------------------------------------------- 1 | % Initialize simulation variables 2 | SigmaW = 1; % Process noise covariance 3 | SigmaV = 2; % Sensor noise covariance 4 | A = 1; B = 1; C = 1; D = 0; % Plant definition matrices 5 | maxIter = 40; 6 | 7 | % Seed the random number generator: Octave's "randn" function produces pseudo 8 | % random numbers having a Gaussian distribution. To get the same random numbers 9 | % every time you run the code, you can "seed" the pseudo random number generator 10 | % with a deterministic value. This allows us to get reproducible results that 11 | % still contain apparent randomness. 12 | % 13 | randn("seed",10) 14 | 15 | % Initialize true state, state estimate, error covariance, initial input 16 | xtrue = 0; % Initialize true system initial state 17 | xhat = 0; % Initialize Kalman filter initial estimate 18 | SigmaX = 0; % Initialize Kalman filter covariance 19 | u = 0; % Unknown initial driving input: assume zero 20 | 21 | % Reserve storage for variables we might want to plot/evaluate 22 | xstore = zeros(maxIter+1,length(xtrue)); xstore(1,:) = xtrue; 23 | xhatstore = zeros(maxIter,length(xhat)); 24 | SigmaXstore = zeros(maxIter,length(xhat)^2); 25 | 26 | for k = 1:maxIter, 27 | % KF Step 1: State estimate time update 28 | xhat = A*xhat + B*u; % use prior value of "u" 29 | 30 | % KF Step 2: Error covariance time update 31 | SigmaX = A*SigmaX*A' + SigmaW; 32 | 33 | % [Implied operation of system in background, with 34 | % input signal u, and output signal z] 35 | u = 0.5*randn(1) + cos(k/pi); % for example... usually measured 36 | w = chol(SigmaW)'*randn(length(xtrue)); 37 | v = chol(SigmaV)'*randn(length(C*xtrue)); 38 | ztrue = C*xtrue + D*u + v; % y is based on present x and u 39 | xtrue = A*xtrue + B*u + w; % future x is based on present u 40 | 41 | % KF Step 3: Estimate system output 42 | zhat = C*xhat + D*u; 43 | 44 | % KF Step 4: Compute Kalman gain matrix 45 | L = SigmaX*C'/(C*SigmaX*C' + SigmaV); 46 | 47 | % KF Step 5: State estimate measurement update 48 | xhat = xhat + L*(ztrue - zhat); 49 | 50 | % KF Step 6: Error covariance measurement update 51 | SigmaX = SigmaX - L*C*SigmaX; 52 | 53 | % [Store information for evaluation/plotting purposes] 54 | xstore(k+1,:) = xtrue; 55 | xhatstore(k,:) = xhat; 56 | SigmaXstore(k,:) = SigmaX(:); 57 | end; 58 | 59 | subplot(1,2,1); 60 | plot(0:maxIter-1,xstore(1:maxIter),'k-',0:maxIter-1,xhatstore,'b--', ... 61 | 0:maxIter-1,xhatstore+3*sqrt(SigmaXstore),'m-.',... 62 | 0:maxIter-1,xhatstore-3*sqrt(SigmaXstore),'m-.'); grid; 63 | legend('true','estimate','bounds'); 64 | title('Kalman filter in action'); 65 | xlabel('Iteration'); ylabel('State'); 66 | 67 | subplot(1,2,2) 68 | estErr = xstore(1:maxIter)-xhatstore; 69 | plot(0:maxIter-1,estErr,'b-',0:maxIter-1, ... 70 | 3*sqrt(SigmaXstore),'m--',0:maxIter-1,(-3)*sqrt(SigmaXstore),'m--'); 71 | grid; legend('Error','bounds',0); 72 | title('Error with bounds'); 73 | xlabel('Iteration'); ylabel('Estimation Error'); 74 | -------------------------------------------------------------------------------- /State_Of_Charge/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /State_Of_Charge/OCVfromSOCtemp.m: -------------------------------------------------------------------------------- 1 | function ocv=OCVfromSOCtemp(soc,temp,model) 2 | % This function returns the fully rested open-circuit-voltage of an LiPB 3 | % cell given its soc. 4 | % Syntax: ocv=OCVfromSOCtemp(soc,temp,model) 5 | % where soc is cell state of charge between 0 and 1, 6 | % temp is cell temperature in degrees celsius, 7 | % and model is a cell model structure. 8 | 9 | soccol = soc(:); % force soc to be col-vector 10 | SOC = model.SOC(:); % force to be col vector... 03/24/10 11 | OCV0 = model.OCV0(:); % force to be col vector... 03/24/10 12 | OCVrel = model.OCVrel(:); % force to be col vector... 03/24/10 13 | if isscalar(temp), 14 | tempcol = temp*ones(size(soccol)); % replicate for all socs 15 | else 16 | tempcol = temp(:); % force to be col vector 17 | end 18 | diffSOC=SOC(2)-SOC(1); 19 | ocv=zeros(size(soccol)); 20 | I1=find(soccol <= SOC(1)); %if ~isempty(I1), disp('low soc'); end 21 | I2=find(soccol >= SOC(end)); %if ~isempty(I2), disp('high soc'); end 22 | I3=find(soccol > SOC(1) & soccol < SOC(end)); 23 | I6=isnan(soccol); 24 | 25 | % for voltages less than 0% soc... 07/26/06 26 | % extrapolate off low end of table (for SOC(1) < 0... 03/23/10) 27 | if ~isempty(I1), 28 | dv = (OCV0(2)+tempcol.*OCVrel(2)) - (OCV0(1)+tempcol.*OCVrel(1)); 29 | ocv(I1)= (soccol(I1)-SOC(1)).*dv(I1)/diffSOC + OCV0(1)+tempcol(I1).*OCVrel(1); 30 | end 31 | 32 | % for voltages greater than 100% soc... 07/26/06 33 | % extrapolate off high end of table (for SOC(end) > 1... 03/23/10) 34 | if ~isempty(I2), 35 | dv = (OCV0(end)+tempcol.*OCVrel(end)) - (OCV0(end-1)+tempcol.*OCVrel(end-1)); 36 | ocv(I2) = (soccol(I2)-SOC(end)).*dv(I2)/diffSOC + OCV0(end)+tempcol(I2).*OCVrel(end); 37 | end 38 | 39 | % for normal soc range... 40 | % manually interpolate (10x faster than "interp1") 41 | I4=(soccol(I3)-SOC(1))/diffSOC; % for SOC(1) < 0... 03/23/10 42 | I5=floor(I4); I45 = I4-I5; omI45 = 1-I45; 43 | ocv(I3)=OCV0(I5+1).*omI45 + OCV0(I5+2).*I45; 44 | ocv(I3)=ocv(I3) + tempcol(I3).*(OCVrel(I5+1).*omI45 + OCVrel(I5+2).*I45); 45 | ocv(I6)=0; % replace NaN SOCs with zero voltage... 03/23/10 46 | ocv = reshape(ocv,size(soc)); -------------------------------------------------------------------------------- /State_Of_Charge/P14model.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/State_Of_Charge/P14model.mat -------------------------------------------------------------------------------- /State_Of_Charge/PANPackData.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/State_Of_Charge/PANPackData.mat -------------------------------------------------------------------------------- /State_Of_Charge/PAN_CAPSTONE_DATA.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/State_Of_Charge/PAN_CAPSTONE_DATA.mat -------------------------------------------------------------------------------- /State_Of_Charge/PANdata_P25.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/State_Of_Charge/PANdata_P25.mat -------------------------------------------------------------------------------- /State_Of_Charge/PANdata_P45.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/State_Of_Charge/PANdata_P45.mat -------------------------------------------------------------------------------- /State_Of_Charge/PANmodel.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/State_Of_Charge/PANmodel.mat -------------------------------------------------------------------------------- /State_Of_Charge/README.md: -------------------------------------------------------------------------------- 1 | # State_Of_Charge 2 | 3 | **Abbreviations\Symbols used in the code and their physical\actual meaning and units** 4 | - z : State of Charge 5 | - R : Internal Resistance(Ohms) 6 | - Q : Cell capacity(Wh) 7 | - v : Cell terminal voltage(V) 8 | - rc : Resistor Capacitor Parameters 9 | - s : Instantaneous Hysteresis 10 | - h : Dynamic Hysteresis 11 | - t : Time 12 | 13 | 14 | 15 | This branch(State_of_charge) of the main folder(BatteryManagementSystem_Algorithm_usingMatlab) aims to determine the State of Charge of an individual Lithium-Ion Cell and also the State of Charge of an Electric Vehicle Battery. The methods used consist of Kalman Filter methods and its varieties. The Kalman filter based approach is the best method for linear systems. While for the nonlinear systems Unscented Kalman Filter based methods are used. To use these algorithms, a good knowledge of Kalman filter theory is required. 16 | 17 | **A brief introduction to Kalman filter methods** 18 | A dynamic physical system, in addition to the input and output signals also is superimposed of noises. The noises can generally be classified as process noise and sensor noise.The process noise are the noise because of the system modelling error,while the sensor noise is the noise picked up by the output measurements.The Kalman filter is a robust method used to reduce the noise to a minimal amount, which resulting in increasing the accuracy of the system. The Kalman filter generally proceeds in two steps: 19 | - The first step is the **Prediction Step**. In this step the prediction of the states of the current states of the system based on the previous states takes place. 20 | - The second step is the **Correct/Updation Step**. In this the step the future value is estimated with the help of a particular ***Gain Matrix***. 21 | The Gain Matrix is a parameter that disinguishes the Kalman Filter with all the other methods. 22 | 23 | **Extended Kalman filter** 24 | In this method,instead of passing all the input points,along with the noise, through the system and obtaining an output, a set of weighted points that accurately represent the behaviour of the system is passed through the system and an output in terms of the weighted points are obtained. The final state of the system is then extrapolated from these points. This method is very useful for nonlinear physical systems. 25 | 26 | **Bar Delta Filtering** 27 | One importamt assumption in Kalman filter approach is that all the noises are assumed to be zero mean. But physically one cannot predict the behaviour of noise and generally the noise is not zero mean and there is some current-sensor bias which can cause permanent SOC error. 28 | Also,Accumulated ampere-hours of bias tend to move SOC estimate faster than measurement updates can correct. 29 | So, to counter these obstacles **Bar Delta Filtering** method is used. 30 | This method estimates the bias and uses this as the input signal to the Kalman Filters. 31 | 32 | **Explanation of the codes used** 33 | 34 | ***Extended explanation of the code is present in the comments of the code file*** 35 | 1) Code names: **KFinaction** **EKFinAction.m** 36 | - **Explanation** This code is used to simulate the Extended Kalman filter on a simple system. 37 | 38 | 2) Code name: **initEKF.m** **initSPKF.m** **initSPKFbd** 39 | - **Explanation** This code initializes interprets the data and initializes the variable. 40 | 41 | 3) Code name : **iterEKF.m** **iterSPKF.m** **iterSPKFbd** 42 | - **Explanation** This is the section where the actual Filter process takes place and the output is stored. 43 | 44 | 4) Code name : **wrapperEKF.m** **wrapperSPKF.m** **wrapperSPKFbd** 45 | - **Explanation** This section simulates the entire process and plots and visualizes the data. 46 | 47 | 5) Code name : **simCell.m** 48 | - **Explanation** This simulates the behaviour of a single cell. 49 | 50 | 6) Code name : **bardeltadata.m** 51 | - **Explanation** This provides with the data for the Bar delta filtering method. 52 | -------------------------------------------------------------------------------- /State_Of_Charge/SOCfromOCVtemp.m: -------------------------------------------------------------------------------- 1 | function soc=SOCfromOCVtemp(ocv,temp,model) 2 | % This function returns an estimate of soc from a fully rested open-circuit-voltage 3 | % of an LiPB cell 4 | 5 | ocvcol = ocv(:); % force ocv to be col-vector 6 | OCV = model.OCV(:); % force to be col vector... 03/24/10 7 | SOC0 = model.SOC0(:); % force to be col vector... 03/24/10 8 | SOCrel = model.SOCrel(:); % force to be col vector... 03/24/10 9 | if isscalar(temp), 10 | tempcol = temp*ones(size(ocvcol)); % replicate temperature for all ocvs 11 | else 12 | tempcol = temp(:); % force to be col vector 13 | end 14 | diffOCV=OCV(2)-OCV(1); 15 | soc=zeros(size(ocvcol)); 16 | I1=find(ocvcol <= OCV(1)); 17 | I2=find(ocvcol >= OCV(end)); 18 | I3=find(ocvcol > OCV(1) & ocvcol < OCV(end)); 19 | I6=isnan(ocvcol); 20 | 21 | % for socs lower than lowest voltage 22 | % extrapolate off low end of table 23 | dz = (SOC0(2)+tempcol.*SOCrel(2)) - (SOC0(1)+tempcol.*SOCrel(1)); 24 | soc(I1)= (ocvcol(I1)-OCV(1)).*dz(I1)/diffOCV + SOC0(1)+tempcol(I1).*SOCrel(1); 25 | 26 | % for socs higher than highest voltage 27 | % extrapolate off high end of table 28 | dz = (SOC0(end)+tempcol.*SOCrel(end)) - (SOC0(end-1)+tempcol.*SOCrel(end-1)); 29 | soc(I2) = (ocvcol(I2)-OCV(end)).*dz(I2)/diffOCV + SOC0(end)+tempcol(I2).*SOCrel(end); 30 | 31 | % for normal soc range... 32 | % manually interpolate (10x faster than "interp1") 33 | I4=(ocvcol(I3)-OCV(1))/diffOCV; 34 | I5=floor(I4); 35 | soc(I3)=SOC0(I5+1).*(1-(I4-I5)) + SOC0(I5+2).*(I4-I5); 36 | soc(I3)=soc(I3) + tempcol(I3).*(SOCrel(I5+1).*(1-(I4-I5)) + SOCrel(I5+2).*(I4-I5)); 37 | soc(I6) = 0; % replace NaN OCVs with zero SOC... 03/23/10 38 | soc = reshape(soc,size(ocv)); -------------------------------------------------------------------------------- /State_Of_Charge/SimpleEKF.m: -------------------------------------------------------------------------------- 1 | % Initialize simulation variables 2 | SigmaW = 1; % Process noise covariance 3 | SigmaV = 2; % Sensor noise covariance 4 | maxIter = 40; 5 | 6 | % Seed the random number generator: Octave's "randn" function produces pseudo 7 | % random numbers having a Gaussian distribution. To get the same random numbers 8 | % every time you run the code, you can "seed" the pseudo random number generator 9 | % with a deterministic value. This allows us to get reproducible results that 10 | % still contain apparent randomness. 11 | %. 12 | randn("seed",-1); 13 | 14 | % Define size of variables in model 15 | Nx = 1; % state = 1x1 scalar 16 | Nxa = 3; % augmented state has also w(k) and v(k) contributions 17 | Nz = 1; % output = 1x1 scalar 18 | 19 | % Some constants for the SPKF algorithm. Use standard values for 20 | % cases with Gaussian noises. (These are the weighting matrices 21 | % comprising the values of alpha(c) and alpha(m) organized in a way to 22 | % make later computation efficient). 23 | h = sqrt(3); 24 | Wmx(1) = (h*h-Nxa)/(h*h); Wmx(2) = 1/(2*h*h); Wcx=Wmx; 25 | repWmxz = repmat([Wmx(1) repmat(Wmx(2),[1 2*Nxa])],1); 26 | 27 | xtrue = 2 + randn(1); % Initialize true system initial state 28 | xhat = 2; % Initialize Kalman filter initial estimate 29 | SigmaX = 1; % Initialize Kalman filter covariance 30 | 31 | % Reserve storage for variables we might want to plot/evaluate 32 | xstore = zeros(length(xtrue),maxIter+1); xstore(:,1) = xtrue; 33 | zstore = zeros(1,maxIter); 34 | 35 | % Reserve storage for variables we might want to plot/evaluate 36 | xhatstore = zeros(length(xhat),maxIter); 37 | SigmaXstore = zeros(length(xhat)^2,maxIter); 38 | for k = 1:maxIter, 39 | % SPKF Step 1: State estimate time update 40 | % 1a: Calculate augmented state estimate, including ... 41 | xhata = [xhat; 0; 0]; % process and sensor noise mean 42 | % 1b: Get desired Cholesky factor 43 | Pxa = blkdiag(SigmaX,SigmaW,SigmaV); 44 | sPxa = chol(Pxa,'lower'); 45 | % 1c: Calculate sigma points (strange indexing of xhat to avoid 46 | % "repmat" call, which is very inefficient in Matlab) 47 | X = xhata(:,ones([1 2*Nxa+1])) + h*[zeros([Nxa 1]), sPxa, -sPxa]; 48 | % 1d: Calculate state equation for every element 49 | % Hard-code equation here for efficiency 50 | Xx = sqrt(5+X(1,:)) + X(2,:); 51 | xhat = Xx*repWmxz'; 52 | 53 | % SPKF Step 2: Covariance of prediction 54 | Xs = (Xx(:,2:end) - xhat(:,ones([1 2*Nxa])))*sqrt(Wcx(2)); 55 | Xs1 = Xx(:,1) - xhat; 56 | SigmaX = Xs*Xs' + Wcx(1)*Xs1*Xs1'; 57 | 58 | % [Implied operation of system in background, with 59 | w = chol(SigmaW)'*randn(1); 60 | v = chol(SigmaV)'*randn(1); 61 | zmeas = xtrue^3 + v; % present z is based on present x and v 62 | xtrue = sqrt(5+xtrue) + w; % future x is based on present x and w 63 | xstore(:,k+1) = xtrue; 64 | 65 | % SPKF Step 3: Create output estimate 66 | % Hard-code equation here for efficiency 67 | Z = Xx.^3 + X(3,:); 68 | zhat = Z*repWmxz'; 69 | 70 | % SPKF Step 4: Estimator gain matrix 71 | Zs = (Z(:,2:end) - zhat*ones([1 2*Nxa])) * sqrt(Wcx(2)); 72 | Zs1 = Z(:,1) - zhat; 73 | SigmaXZ = Xs*Zs' + Wcx(1)*Xs1*Zs1'; 74 | SigmaZ = Zs*Zs' + Wcx(1)*Zs1*Zs1'; 75 | Lx = SigmaXZ/SigmaZ; 76 | 77 | % SPKF Step 5: Measurement state update 78 | xhat = xhat + Lx*(zmeas-zhat); % update estimate if not too rare 79 | 80 | % SPKF Step 6: Measurement covariance update 81 | SigmaX = SigmaX - Lx*SigmaZ*Lx'; 82 | 83 | % [Store information for evaluation/plotting purposes] 84 | xhatstore(:,k) = xhat; 85 | SigmaXstore(:,k) = SigmaX(:); 86 | end 87 | 88 | subplot(1,2,1); 89 | plot(0:maxIter-1,xstore(1:maxIter),'k-',0:maxIter-1,xhatstore,'b--', ... 90 | 0:maxIter-1,xhatstore+3*sqrt(SigmaXstore),'m-.',... 91 | 0:maxIter-1,xhatstore-3*sqrt(SigmaXstore),'m-.'); grid; 92 | legend('true','estimate','bounds'); 93 | title('Sigma-point Kalman filter in action'); 94 | xlabel('Iteration'); ylabel('State'); 95 | 96 | subplot(1,2,2) 97 | estErr = xstore(1:maxIter)-xhatstore; 98 | bounds = 3*sqrt(SigmaXstore); 99 | plot(0:maxIter-1,estErr,'b-',0:maxIter-1, bounds,'m--',0:maxIter-1,-bounds,'m--'); 100 | grid; legend('Error','bounds'); 101 | title('SPKF Error with bounds'); 102 | xlabel('Iteration'); ylabel('Estimation Error'); 103 | -------------------------------------------------------------------------------- /State_Of_Charge/bardeltadata.m: -------------------------------------------------------------------------------- 1 | % Use a desktop-validation approach to create a synthetic dataset 2 | % This dataset assumes a 4-cell battery pack comprising Panasonic 25 Ah 3 | % cells. The cells have different series resistances, different capacities, 4 | % and different initial states of charge. They are all exercised with the 5 | % same profile of current versus time, which comprises two UDDS profiles 6 | % separated by rest intervals. 7 | addpath E:\BMS\SOC\matlab % set your own path to the algorithm/code 8 | load E:\BMS\SOC\matlab\PANmodel; % ESC model of Panasonic cell 9 | udds = load('E:\BMS\SOC\matlab\udds.txt'); % profile of current versus time for UDDS 10 | T = 25; 11 | 12 | z0 = 0.9:-0.1:0.6; % set initial SOC for each cell in pack 13 | R0 = (1.3:-0.1:1)*1e-3; % set R0 for each cell in pack 14 | Q0 = 25:28; % set Q for each cell in pack 15 | % create current profile: rest/udds/rest/udds/rest 16 | ik = [zeros(300,1); udds(:,2); zeros(300,1); udds(:,2); zeros(241,1)]; 17 | 18 | vk = zeros(length(ik),length(z0)); % reserve storage for cell voltages 19 | zk = zeros(length(ik),length(z0)); % reserve storage for cell SOCs 20 | 21 | for k = 1:length(z0), 22 | model.R0Param = R0(k)*ones(size(model.R0Param)); % overwrite R0 23 | model.QParam = Q0(k)*ones(size(model.QParam)); % overwrite Q 24 | [vcell,rck,hk,zcell,sik,OCV] = simCell(ik,T,1,model,z0(k),0,0); 25 | vk(:,k) = vcell; 26 | zk(:,k) = zcell; 27 | end 28 | save('PANPackData.mat','vk','zk','ik','T','Q0','R0'); 29 | 30 | subplot(1,2,1); t = (0:length(ik)-1)/3600; 31 | plot(t,vk); xlabel('Time (hr)'); ylabel('Voltage (V)'); 32 | title('Voltage versus time for four cells'); 33 | 34 | subplot(1,2,2); 35 | plot(t,100*zk); xlabel('Time (hr)'); ylabel('State of charge (%)'); 36 | title('SOC versus time for four cells'); -------------------------------------------------------------------------------- /State_Of_Charge/dOCVfromSOCtemp.m: -------------------------------------------------------------------------------- 1 | function docv=dOCVfromSOCtemp(soc,temp,model) 2 | % This function returns the derivative of OCV with respect to SOC for 3 | % a lithium-ion cell given its soc. 4 | 5 | soccol = soc(:); % force soc to be col-vector 6 | SOC = model.SOC(:); % force to be col vector... 03/24/10 7 | dOCV0 = model.dOCV0(:); % force to be col vector... 03/24/10 8 | dOCVrel = model.dOCVrel(:); % force to be col vector... 03/24/10 9 | if isscalar(temp), 10 | tempcol = temp*ones(size(soccol)); % replicate for all socs 11 | else 12 | tempcol = temp(:); % force to be col vector 13 | end 14 | diffSOC=SOC(2)-SOC(1); 15 | docv=zeros(size(soccol)); 16 | I1=find(soccol <= SOC(1)); %if ~isempty(I1), disp('low soc'); end 17 | I2=find(soccol >= SOC(end)); %if ~isempty(I2), disp('high soc'); end 18 | I3=find(soccol > SOC(1) & soccol < SOC(end)); 19 | I6=isnan(soccol); 20 | 21 | % for voltages less than 0% soc... 07/26/06 22 | % extrapolate off low end of table (for SOC(1) < 0... 03/23/10) 23 | if ~isempty(I1), 24 | dv = (dOCV0(2)+tempcol.*dOCVrel(2)) - (dOCV0(1)+tempcol.*dOCVrel(1)); 25 | docv(I1)= (soccol(I1)-SOC(1)).*dv(I1)/diffSOC + dOCV0(1)+tempcol(I1).*dOCVrel(1); 26 | end 27 | 28 | % for voltages greater than 100% soc... 07/26/06 29 | % extrapolate off high end of table (for SOC(end) > 1... 03/23/10) 30 | if ~isempty(I2), 31 | dv = (dOCV0(end)+tempcol.*dOCVrel(end)) - (dOCV0(end-1)+tempcol.*dOCVrel(end-1)); 32 | docv(I2) = (soccol(I2)-SOC(end)).*dv(I2)/diffSOC + dOCV0(end)+tempcol(I2).*dOCVrel(end); 33 | end 34 | 35 | % for normal soc range... 36 | % manually interpolate (10x faster than "interp1") 37 | I4=(soccol(I3)-SOC(1))/diffSOC; % for SOC(1) < 0... 03/23/10 38 | I5=floor(I4); I45 = I4-I5; omI45 = 1-I45; 39 | docv(I3)=dOCV0(I5+1).*omI45 + dOCV0(I5+2).*I45; 40 | docv(I3)=docv(I3) + tempcol(I3).*(dOCVrel(I5+1).*omI45 + dOCVrel(I5+2).*I45); 41 | docv(I6)=0; % replace NaN SOCs with zero voltage... 03/23/10 42 | docv = reshape(docv,size(soc)); -------------------------------------------------------------------------------- /State_Of_Charge/getParamESC.m: -------------------------------------------------------------------------------- 1 | % function theParam = getParamESC(paramName,temperature,model) 2 | % 3 | % This function returns the values of the specified ESC cell-model 4 | % parameter 'paramName' for the temperatures in 'temperature' for the 5 | % cell model data stored in 'model'. 6 | % 7 | % In the standard ESC model, 'paramName' may be one of: 8 | % 'QParam', 'RCParam', 'RParam', 'R0Param', 'MParam', 'M0Param', 9 | % 'etaParam', or 'GParam' 10 | % (not case sensitive). 11 | 12 | 13 | % This file is provided as a supplement to: Plett, Gregory L., "Battery 14 | % Management Systems, Volume I, Battery Modeling," Artech House, 2015. 15 | 16 | function theParam = getParamESC(paramName,temp,model) 17 | theFields = fieldnames(model); % get list of fields stored in model 18 | match = strcmpi(paramName,theFields); % see if any match desired data 19 | if ~match, % if not, throw an error 20 | error('Parameter "%s" does not exist in model',paramName); 21 | end 22 | fieldName = char(theFields(match)); % case-sensitive field name 23 | 24 | % if model contains data at only one temperature 25 | if isscalar(model.temps), 26 | if model.temps ~= temp, % check whether requested data exists 27 | error('Model does not contain requested data at this temperature'); 28 | end 29 | theParam = model.(fieldName); 30 | return 31 | end 32 | 33 | % Otherwise, model has multiple temperatures. Bound input "temp" between 34 | % mininum and maximum stored temperature to prohibit "NaN" in output 35 | theParamData = model.(fieldName); 36 | temp = max(min(temp,max(model.temps)),min(model.temps)); 37 | ind = find(model.temps == temp); % see if there is an exact match to 38 | if ~isempty(ind), % avoid call to (slow) interp1 whenever possible 39 | if size(theParamData,1) == 1, 40 | theParam = theParamData(ind); 41 | else 42 | theParam = theParamData(ind,:); 43 | end 44 | else % if there is not an exact match, we interpolate between parameter 45 | theParam = interp1(model.temps,theParamData,temp,'spline'); % values 46 | end % stored at different temperatures 47 | -------------------------------------------------------------------------------- /State_Of_Charge/initEKF.m: -------------------------------------------------------------------------------- 1 | function ekfData = initEKF(v0,T0,SigmaX0,SigmaV,SigmaW,model) 2 | 3 | % Initial state description 4 | ir0 = 0; ekfData.irInd = 1; 5 | hk0 = 0; ekfData.hkInd = 2; 6 | SOC0 = SOCfromOCVtemp(v0,T0,model); ekfData.zkInd = 3; 7 | ekfData.xhat = [ir0 hk0 SOC0]'; % initial state 8 | 9 | % Covariance values 10 | ekfData.SigmaX = SigmaX0; 11 | ekfData.SigmaV = SigmaV; 12 | ekfData.SigmaW = SigmaW; 13 | ekfData.Qbump = 5; 14 | 15 | % previous value of current 16 | ekfData.priorI = 0; 17 | ekfData.signIk = 0; 18 | 19 | % store model data structure too 20 | ekfData.model = model; 21 | end -------------------------------------------------------------------------------- /State_Of_Charge/initSPKF.m: -------------------------------------------------------------------------------- 1 | function spkfData = initSPKF(v0,T0,SigmaX0,SigmaV,SigmaW,model) 2 | 3 | % Initial state description 4 | ir0 = 0; spkfData.irInd = 1; 5 | hk0 = 0; spkfData.hkInd = 2; 6 | SOC0 = SOCfromOCVtemp(v0,T0,model); spkfData.zkInd = 3; 7 | spkfData.xhat = [ir0 hk0 SOC0]'; % initial state 8 | 9 | % Covariance values 10 | spkfData.SigmaX = SigmaX0; 11 | spkfData.SigmaV = SigmaV; 12 | spkfData.SigmaW = SigmaW; 13 | spkfData.Snoise = real(chol(diag([SigmaW; SigmaV]),'lower')); 14 | spkfData.Qbump = 5; 15 | 16 | % SPKF specific parameters 17 | Nx = length(spkfData.xhat); spkfData.Nx = Nx; % state-vector length 18 | Ny = 1; spkfData.Ny = Ny; % measurement-vector length 19 | Nu = 1; spkfData.Nu = Nu; % input-vector length 20 | Nw = size(SigmaW,1); spkfData.Nw = Nw; % process-noise-vector length 21 | Nv = size(SigmaV,1); spkfData.Nv = Nv; % sensor-noise-vector length 22 | Na = Nx+Nw+Nv; spkfData.Na = Na; % augmented-state-vector length 23 | 24 | h = sqrt(3); h = 3; 25 | spkfData.h = h; % SPKF/CDKF tuning factor 26 | Weight1 = (h*h-Na)/(h*h); % weighting factors when computing mean 27 | Weight2 = 1/(2*h*h); % and covariance 28 | spkfData.Wm = [Weight1; Weight2*ones(2*Na,1)]; % mean 29 | spkfData.Wc = spkfData.Wm; % covar 30 | 31 | % previous value of current 32 | spkfData.priorI = 0; 33 | spkfData.signIk = 0; 34 | 35 | % store model data structure too 36 | spkfData.model = model; 37 | end -------------------------------------------------------------------------------- /State_Of_Charge/initSPKFbd.m: -------------------------------------------------------------------------------- 1 | % init SPKF for bar-delta method 2 | function spkfData = initSPKFbd(v0, T0, SigmaX0, SigmaV, SigmaW, model) 3 | % First, initialize the variables for the "bar" filter 4 | % Initial state description 5 | ir0 = 0; spkfData.irInd = 1; 6 | hk0 = 0; spkfData.hkInd = 2; 7 | SOC0 = mean(SOCfromOCVtemp(v0, T0, model)); spkfData.zkInd = 3; 8 | ib0 = 0; spkfData.biasInd = 4; % Add state to estimate current sensor bias 9 | spkfData.xhat = [ ir0 hk0 SOC0 ib0]'; % initial state 10 | 11 | % Covariance values 12 | spkfData.SigmaX = SigmaX0; 13 | spkfData.SigmaV = SigmaV; 14 | spkfData.SigmaW = SigmaW; 15 | spkfData.Snoise = real(chol(blkdiag(SigmaW, SigmaV), 'lower' )); 16 | spkfData.Qbump = 5; 17 | 18 | % SPKF specific parameters 19 | Nx = length(spkfData.xhat); spkfData.Nx = Nx; % state-vector length 20 | Ny = 1; spkfData.Ny = Ny; % measurement-vector length 21 | Nu = 1; spkfData.Nu = Nu; % input-vector length 22 | Nw = size(SigmaW,1); spkfData.Nw = Nw; % process-noise-vector length 23 | Nv = size(SigmaV,1); spkfData.Nv = Nv; % sensor-noise-vector length 24 | Na = Nx+Nw+Nv; spkfData.Na = Na; % augmented-state-vector length 25 | 26 | h = sqrt(3); spkfData.h = h; % SPKF/CDKF tuning factor 27 | Weight1 = (h*h-Na)/(h*h); % weighting factors when computing mean 28 | Weight2 = 1/(2*h*h); % and covariance 29 | spkfData.Wm = [ Weight1; Weight2*ones(2*Na,1)]; % mean 30 | spkfData.Wc = spkfData.Wm; % covar 31 | 32 | % previous value of current 33 | spkfData.priorI = 0; 34 | spkfData.signIk = 0; 35 | 36 | % store model data structure too 37 | spkfData.model = model; 38 | 39 | % Now, initialize variables for the "delta" filters 40 | % SOC is estimated using SPKF 41 | dNx = 1; spkfData.dNx = dNx; % one state per delta filter, for estimating SOC 42 | dNw = 1; spkfData.dNw = dNw; % one process-noise per delta filter 43 | dNa = dNx+dNw; spkfData.dNa = dNa; % augmented length for delta filters 44 | 45 | spkfData.dh = h; 46 | Weight1 = (h*h-dNa)/(h*h); % weighting factors when computing mean 47 | Weight2 = 1/(2*h*h); % and covariance 48 | spkfData.dWm = [ Weight1; Weight2*ones(2*dNa,1)]; % mean 49 | spkfData.dWc = spkfData.dWm; % covar 50 | 51 | spkfData.celldz = 0*v0(:); 52 | spkfData.cellSdz = SigmaX0(spkfData.zkInd,spkfData.zkInd)*ones(size(v0(:))); 53 | end -------------------------------------------------------------------------------- /State_Of_Charge/iterEKF.m: -------------------------------------------------------------------------------- 1 | function [zk,zkbnd,ekfData] = iterEKF(vk,ik,Tk,deltat,ekfData) 2 | model = ekfData.model; 3 | % Load the cell model parameters 4 | Q = getParamESC('QParam',Tk,model); 5 | G = getParamESC('GParam',Tk,model); 6 | M = getParamESC('MParam',Tk,model); 7 | M0 = getParamESC('M0Param',Tk,model); 8 | RC = exp(-deltat./abs(getParamESC('RCParam',Tk,model)))'; 9 | R = getParamESC('RParam',Tk,model)'; 10 | R0 = getParamESC('R0Param',Tk,model); 11 | eta = getParamESC('etaParam',Tk,model); 12 | if ik<0, ik=ik*eta; end; 13 | 14 | % Get data stored in ekfData structure 15 | I = ekfData.priorI; 16 | SigmaX = ekfData.SigmaX; 17 | SigmaV = ekfData.SigmaV; 18 | SigmaW = ekfData.SigmaW; 19 | xhat = ekfData.xhat; 20 | irInd = ekfData.irInd; 21 | hkInd = ekfData.hkInd; 22 | zkInd = ekfData.zkInd; 23 | if abs(ik)>Q/100, ekfData.signIk = sign(ik); end; 24 | signIk = ekfData.signIk; 25 | 26 | % EKF Step 0: Compute Ahat[k-1], Bhat[k-1] 27 | nx = length(xhat); Ahat = zeros(nx,nx); Bhat = zeros(nx,1); 28 | Ahat(zkInd,zkInd) = 1; Bhat(zkInd) = -deltat/(3600*Q); 29 | Ahat(irInd,irInd) = diag(RC); Bhat(irInd) = 1-RC(:); 30 | Ah = exp(-abs(I*G*deltat/(3600*Q))); % hysteresis factor 31 | Ahat(hkInd,hkInd) = Ah; 32 | B = [Bhat, 0*Bhat]; 33 | Bhat(hkInd) = -abs(G*deltat/(3600*Q))*Ah*(1+sign(I)*xhat(hkInd)); 34 | B(hkInd,2) = Ah-1; 35 | 36 | % Step 1a: State estimate time update 37 | xhat = Ahat*xhat + B*[I; sign(I)]; 38 | 39 | % Step 1b: Error covariance time update 40 | % sigmaminus(k) = Ahat(k-1)*sigmaplus(k-1)*Ahat(k-1)' + ... 41 | % Bhat(k-1)*sigmawtilde*Bhat(k-1)' 42 | SigmaX = Ahat*SigmaX*Ahat' + Bhat*SigmaW*Bhat'; 43 | 44 | % Step 1c: Output estimate 45 | yhat = OCVfromSOCtemp(xhat(zkInd),Tk,model) + M0*signIk + ... 46 | M*xhat(hkInd) - R*xhat(irInd) - R0*ik; 47 | 48 | % Step 2a: Estimator gain matrix 49 | Chat = zeros(1,nx); 50 | Chat(zkInd) = dOCVfromSOCtemp(xhat(zkInd),Tk,model); 51 | Chat(hkInd) = M; 52 | Chat(irInd) = -R; 53 | Dhat = 1; 54 | SigmaY = Chat*SigmaX*Chat' + Dhat*SigmaV*Dhat'; 55 | L = SigmaX*Chat'/SigmaY; 56 | 57 | % Step 2b: State estimate measurement update 58 | r = vk - yhat; % residual. Use to check for sensor errors... 59 | if r^2 > 100*SigmaY, L(:)=0.0; end 60 | xhat = xhat + L*r; 61 | xhat(hkInd) = min(1,max(-1,xhat(hkInd))); % Help maintain robustness 62 | xhat(zkInd) = min(1.05,max(-0.05,xhat(zkInd))); 63 | 64 | % Step 2c: Error covariance measurement update 65 | SigmaX = SigmaX - L*SigmaY*L'; 66 | % % Q-bump code 67 | if r^2 > 4*SigmaY, % bad voltage estimate by 2 std. devs, bump Q 68 | fprintf('Bumping SigmaX\n'); 69 | SigmaX(zkInd,zkInd) = SigmaX(zkInd,zkInd)*ekfData.Qbump; 70 | end 71 | [~,S,V] = svd(SigmaX); 72 | HH = V*S*V'; 73 | SigmaX = (SigmaX + SigmaX' + HH + HH')/4; % Help maintain robustness 74 | 75 | % Save data in ekfData structure for next time... 76 | ekfData.priorI = ik; 77 | ekfData.SigmaX = SigmaX; 78 | ekfData.xhat = xhat; 79 | zk = xhat(zkInd); 80 | zkbnd = 3*sqrt(SigmaX(zkInd,zkInd)); 81 | end -------------------------------------------------------------------------------- /State_Of_Charge/iterSPKF.m: -------------------------------------------------------------------------------- 1 | function [zk,zkbnd,spkfData] = iterSPKF(vk,ik,Tk,deltat,spkfData) 2 | model = spkfData.model; 3 | 4 | % Load the cell model parameters 5 | Q = getParamESC('QParam',Tk,model); 6 | G = getParamESC('GParam',Tk,model); 7 | M = getParamESC('MParam',Tk,model); 8 | M0 = getParamESC('M0Param',Tk,model); 9 | RC = exp(-deltat./abs(getParamESC('RCParam',Tk,model)))'; 10 | R = getParamESC('RParam',Tk,model)'; 11 | R0 = getParamESC('R0Param',Tk,model); 12 | eta = getParamESC('etaParam',Tk,model); 13 | if ik<0, ik=ik*eta; end; 14 | 15 | % Get data stored in spkfData structure 16 | I = spkfData.priorI; 17 | SigmaX = spkfData.SigmaX; 18 | xhat = spkfData.xhat; 19 | Nx = spkfData.Nx; 20 | Nw = spkfData.Nw; 21 | Nv = spkfData.Nv; 22 | Na = spkfData.Na; 23 | Snoise = spkfData.Snoise; 24 | Wc = spkfData.Wc; 25 | irInd = spkfData.irInd; 26 | hkInd = spkfData.hkInd; 27 | zkInd = spkfData.zkInd; 28 | if abs(ik)>Q/100, spkfData.signIk = sign(ik); end; 29 | signIk = spkfData.signIk; 30 | 31 | % Step 1a: State estimate time update 32 | % - Create xhatminus augmented SigmaX points 33 | % - Extract xhatminus state SigmaX points 34 | % - Compute weighted average xhatminus(k) 35 | 36 | % Step 1a-1: Create augmented SigmaX and xhat 37 | [sigmaXa,p] = chol(SigmaX,'lower'); 38 | if p>0, 39 | fprintf('Cholesky error. Recovering...\n'); 40 | theAbsDiag = abs(diag(SigmaX)); 41 | sigmaXa = diag(max(SQRT(theAbsDiag),SQRT(spkfData.SigmaW))); 42 | end 43 | sigmaXa=[real(sigmaXa) zeros([Nx Nw+Nv]); zeros([Nw+Nv Nx]) Snoise]; 44 | xhata = [xhat; zeros([Nw+Nv 1])]; 45 | % NOTE: sigmaXa is lower-triangular 46 | 47 | % Step 1a-2: Calculate SigmaX points (strange indexing of xhata to 48 | % avoid "repmat" call, which is very inefficient in MATLAB) 49 | Xa = xhata(:,ones([1 2*Na+1])) + ... 50 | spkfData.h*[zeros([Na 1]), sigmaXa, -sigmaXa]; 51 | 52 | % Step 1a-3: Time update from last iteration until now 53 | % stateEqn(xold,current,xnoise) 54 | Xx = stateEqn(Xa(1:Nx,:),I,Xa(Nx+1:Nx+Nw,:)); 55 | xhat = Xx*spkfData.Wm; 56 | 57 | % Step 1b: Error covariance time update 58 | % - Compute weighted covariance sigmaminus(k) 59 | % (strange indexing of xhat to avoid "repmat" call) 60 | Xs = Xx - xhat(:,ones([1 2*Na+1])); 61 | SigmaX = Xs*diag(Wc)*Xs'; 62 | 63 | % Step 1c: Output estimate 64 | % - Compute weighted output estimate yhat(k) 65 | I = ik; yk = vk; 66 | Y = outputEqn(Xx,I,Xa(Nx+Nw+1:end,:),Tk,model); 67 | yhat = Y*spkfData.Wm; 68 | 69 | % Step 2a: Estimator gain matrix 70 | Ys = Y - yhat(:,ones([1 2*Na+1])); 71 | SigmaXY = Xs*diag(Wc)*Ys'; 72 | SigmaY = Ys*diag(Wc)*Ys'; 73 | L = SigmaXY/SigmaY; 74 | 75 | % Step 2b: State estimate measurement update 76 | r = yk - yhat; % residual. Use to check for sensor errors... 77 | if r^2 > 100*SigmaY, L(:,1)=0.0; end 78 | xhat = xhat + L*r; 79 | xhat(zkInd)=min(1.05,max(-0.05,xhat(zkInd))); 80 | xhat(hkInd) = min(1,max(-1,xhat(hkInd))); 81 | 82 | % Step 2c: Error covariance measurement update 83 | SigmaX = SigmaX - L*SigmaY*L'; 84 | [~,S,V] = svd(SigmaX); 85 | HH = V*S*V'; 86 | SigmaX = (SigmaX + SigmaX' + HH + HH')/4; % Help maintain robustness 87 | 88 | % Q-bump code 89 | if r^2>4*SigmaY, % bad voltage estimate by 2-SigmaX, bump Q 90 | fprintf('Bumping sigmax\n'); 91 | SigmaX(zkInd,zkInd) = SigmaX(zkInd,zkInd)*spkfData.Qbump; 92 | end 93 | 94 | % Save data in spkfData structure for next time... 95 | spkfData.priorI = ik; 96 | spkfData.SigmaX = SigmaX; 97 | spkfData.xhat = xhat; 98 | zk = xhat(zkInd); 99 | zkbnd = 3*sqrt(SigmaX(zkInd,zkInd)); 100 | 101 | % Calculate new states for all of the old state vectors in xold. 102 | function xnew = stateEqn(xold,current,xnoise) 103 | current = current + xnoise; % noise adds to current 104 | xnew = 0*xold; 105 | xnew(irInd,:) = RC*xold(irInd,:) + (1-diag(RC))*current; 106 | Ah = exp(-abs(current*G*deltat/(3600*Q))); % hysteresis factor 107 | xnew(hkInd,:) = Ah.*xold(hkInd,:) + (Ah-1).*sign(current); 108 | xnew(zkInd,:) = xold(zkInd,:) - current/3600/Q; 109 | xnew(hkInd,:) = min(1,max(-1,xnew(hkInd,:))); 110 | xnew(zkInd,:) = min(1.05,max(-0.05,xnew(zkInd,:))); 111 | end 112 | 113 | % Calculate cell output voltage for all of state vectors in xhat 114 | function yhat = outputEqn(xhat,current,ynoise,T,model) 115 | yhat = OCVfromSOCtemp(xhat(zkInd,:),T,model); 116 | yhat = yhat + M*xhat(hkInd,:) + M0*signIk; 117 | yhat = yhat - R*xhat(irInd,:) - R0*current + ynoise(1,:); 118 | end 119 | 120 | % "Safe" square root 121 | function X = SQRT(x) 122 | X = sqrt(max(0,x)); 123 | end 124 | end -------------------------------------------------------------------------------- /State_Of_Charge/iterSPKFbd.m: -------------------------------------------------------------------------------- 1 | function [zkbar, zkbarbnd, dzk, dzkbnd, ib, ibbnd, spkfData] = ... 2 | iterSPKFbd(vk, ik, Tk, deltat, spkfData) 3 | model = spkfData. model; 4 | 5 | % Load the cell model parameters 6 | G = getParamESC('GParam' , Tk, model); 7 | M = getParamESC('MParam' , Tk, model); 8 | RC = exp(-deltat./abs(getParamESC('RCParam' , Tk, model)))'; 9 | R = getParamESC('RParam' , Tk, model)'; 10 | R0 = getParamESC('R0Param' , Tk, model); 11 | 12 | % Get data stored in spkfData structure 13 | I = spkfData.priorI; 14 | SigmaX = spkfData.SigmaX; 15 | xhat = spkfData.xhat; 16 | celldz = spkfData.celldz; 17 | cellSdz = spkfData.cellSdz; 18 | Nx = spkfData.Nx; 19 | Nw = spkfData.Nw; 20 | Nv = spkfData.Nv; 21 | Na = spkfData.Na; 22 | Snoise = spkfData.Snoise; 23 | Wc = spkfData.Wc; 24 | irInd = spkfData.irInd; 25 | hkInd = spkfData.hkInd; 26 | zkInd = spkfData.zkInd; 27 | ibInd = spkfData.biasInd; % Add index for current-sensor bias estimator 28 | 29 | dQinv = spkfData.dQinv; 30 | Qinvbar = spkfData.Qinvbar; 31 | 32 | % Step 1a : State estimate time update 33 | % - Create xhatminus augmented SigmaX points 34 | % - Extract xhatminus state SigmaX points 35 | % - Compute weighted average xhatminus(k) 36 | 37 | % Step 1a-1: Create augmented SigmaX and xhat 38 | [ sigmaXa, p] = chol(SigmaX, 'lower' ); 39 | if p>0, 40 | fprintf('Cholesky error. Recovering...\n' ); 41 | theAbsDiag = abs(diag(SigmaX)); 42 | sigmaXa = diag(max(SQRT(theAbsDiag), SQRT(spkfData.SigmaW(1,1)))); 43 | end 44 | sigmaXa=[real(sigmaXa) zeros([Nx Nw+Nv]); zeros([Nw+Nv Nx]) Snoise]; 45 | xhata = [ xhat; zeros([Nw+Nv 1])]; 46 | % NOTE: sigmaX a is lower-triangular 47 | 48 | % Step 1a-2: Calculate SigmaX points (strange indexing of xhat a to 49 | % avoid "repmat" call, which is very inefficient in MATLAB) 50 | Xa = xhata(:, ones([1 2*Na+1])) + ... 51 | spkfData.h*[zeros([ Na 1]), sigmaXa, -sigmaXa]; 52 | 53 | % Step 1a-3: Time update from last iteration until now 54 | % state Eqn(xold, current, xnoise) 55 | Xx = stateEqn(Xa(1: Nx,:), I, Xa(Nx+1: Nx+Nw,:)); 56 | xhat = Xx*spkfData.Wm; 57 | 58 | % Step 1b: Error covariance time update 59 | % - Compute weighted covariance sigmaminus(k) 60 | % (strange indexing of xhat to avoid "repmat" call) 61 | Xs = Xx - xhat(:,ones([1 2*Na+1])); 62 | SigmaX = Xs*diag(Wc)*Xs'; 63 | 64 | % Step 1c: Output estimate 65 | % - Compute weighted output estimate yhat(k) 66 | I = ik; yk = vk; 67 | Y = outputEqn(Xx, I, Xa(Nx+Nw+1: end,:), Tk, model); 68 | yhat = Y*spkfData.Wm; 69 | 70 | % Step 2a: Estimator gain matrix 71 | Ys = Y - yhat(:, ones([1 2*Na+1])); 72 | SigmaXY = Xs*diag(Wc)*Ys'; 73 | SigmaY = Ys*diag(Wc)*Ys'; 74 | L = SigmaXY/SigmaY; 75 | 76 | % Step 2b: State estimate measurement update 77 | r = mean(yk) - yhat; % residual. Use to check for sensor errors... 78 | if r^2 > 100*SigmaY, L(:,1)=0.0; end 79 | xhat = xhat + L*r; 80 | xhat(zkInd)=min(1.05, max(-0.05, xhat(zkInd))); 81 | 82 | % Step 2c: Error covariance measurement update 83 | SigmaX = SigmaX - L*SigmaY*L'; 84 | [~, S, V] = svd(SigmaX); 85 | HH = V*S*V'; 86 | SigmaX = (SigmaX + SigmaX' + HH + HH')/4; % Help maintain robustness 87 | 88 | % Q-bump code 89 | if r^2>4*SigmaY, % bad voltage estimate by 2-SigmaX, bump Q 90 | fprintf('Bumping sigmax\n' ); 91 | SigmaX(zkInd, zkInd) = SigmaX(zkInd, zkInd)*spkfData.Qbump; 92 | end 93 | 94 | % Save data in spkfData structure for next time... 95 | ib = xhat(ibInd); 96 | ibbnd = 3*sqrt(SigmaX(ibInd, ibInd)); 97 | spkfData.priorI = ik; 98 | spkfData.SigmaX = SigmaX; 99 | spkfData.xhat = xhat; 100 | zkbar = xhat(zkInd); 101 | zkbarbnd = 3*sqrt(SigmaX(zkInd, zkInd)); 102 | 103 | % The "bar" filter update is complete. Now, work on "delta" filter updates 104 | offset = M*xhat(hkInd) - R*xhat(irInd) - R0*(ik-ib); 105 | for thecell = 1:length(vk), 106 | % Implement SPKF for delta-soc 107 | I = spkfData.priorI; 108 | % First, update the delta-SOC SPKFs 109 | % Step 1a - State prediction time update 110 | cellxa = [celldz(thecell); 0]; 111 | cellSxa = diag([SQRT(cellSdz(thecell)), sqrt(spkfData.SigmaW(1,1))]); 112 | cellXa = cellxa(:,ones([1 2*spkfData.dNa+1])) + ... 113 | spkfData.h*[0*cellxa,cellSxa,-cellSxa]; 114 | cellXx = cellStateEqn(cellXa,I-ib,dQinv(thecell)); 115 | celldz(thecell) = cellXx*spkfData.dWm; 116 | % Step 1b - Do error covariance time update 117 | cellXs = cellXx - celldz(thecell); 118 | cellSdz(thecell) = cellXs*diag(spkfData.dWc)*cellXs'; 119 | % Step 1c - output estimate 120 | I = ik; 121 | cellY = cellOutputEqn(cellXx,I-ib,cellXa,zkbar,spkfData.dR0(thecell),offset,Tk,model); 122 | cellyhat = cellY*spkfData.dWm; 123 | % Step 2a - Estimator gain matrix 124 | cellYs = cellY - cellyhat; 125 | cellSy = cellYs*diag(spkfData.dWc)*cellYs' + spkfData.SigmaV; % lin sensor noise 126 | cellSxy = cellXs*diag(spkfData.dWc)*cellYs'; 127 | cellL = cellSxy/cellSy; 128 | % Step 2b - State estimate measurement update 129 | celldz(thecell) = celldz(thecell) + cellL*(vk(thecell) - cellyhat); 130 | % Step 2c - Error covariance measurement update 131 | cellSdz(thecell) = cellSdz(thecell) - cellL*cellSy*cellL; 132 | end 133 | 134 | % Save data in spkfData structure for next time... 135 | spkfData.celldz = celldz; 136 | spkfData.cellSdz = cellSdz; 137 | dzk = celldz; 138 | dzkbnd = 3*sqrt(cellSdz); 139 | 140 | % Calculate new states for all of the old state vectors in xold. 141 | function xnew = stateEqn(xold, current, xnoise) 142 | current = current + xnoise(1,:); % noise adds to current 143 | xnew = 0*xold; 144 | xnew(irInd,:) = RC*xold(irInd,:) + (1-RC)*(current-xold(ibInd,:)); 145 | Ah = exp(-abs((current+xold(ibInd,:))*G*deltat*Qinvbar/3600)); % hysteresis factor 146 | xnew(hkInd,:) = Ah.*xold(hkInd,:) + (Ah-1).*sign(current-xold(ibInd,:)); 147 | xnew(zkInd,:) = xold(zkInd,:) - (current-xold(ibInd,:))*Qinvbar/3600; 148 | xnew(hkInd,:) = min(1, max(-1, xnew(hkInd,:))); 149 | xnew(zkInd,:) = min(1.05, max(-0.05, xnew(zkInd,:))); 150 | xnew(ibInd,:) = xold(ibInd,:) + xnoise(2,:); 151 | end 152 | 153 | function xnew = cellStateEqn(xold,oldI,celldQinv) 154 | xnew = xold(1,:) - (oldI + xold(2,:))*celldQinv/3600; % use "known" inverse capacities 155 | end 156 | 157 | % Calculate cell output voltage for all of state vectors in xhat 158 | function yhat = outputEqn(xhat, current, ynoise, T, model) 159 | yhat = OCVfromSOCtemp(xhat(zkInd,:), T, model); 160 | yhat = yhat + M*xhat(hkInd,:); 161 | yhat = yhat - R*xhat(irInd,:) - R0*(current - xhat(ibInd,:)) + ynoise(1,:); 162 | end 163 | 164 | function yhat = cellOutputEqn(cellXx,I,cellXa,Z,dR,offset,T,model) 165 | I = I + cellXa(2,:); % add current noise to current 166 | yhat = OCVfromSOCtemp(Z+cellXx,T,model); % OCV part 167 | yhat = yhat - I * dR; % delta resistance part 168 | yhat = yhat + offset; % polarization, hysteresis, bar resistance 169 | % note: sensor noise handled separately 170 | end 171 | 172 | % "Safe" square root 173 | function X = SQRT(x) 174 | X = sqrt(max(0, x)); 175 | end 176 | end -------------------------------------------------------------------------------- /State_Of_Charge/simCell.m: -------------------------------------------------------------------------------- 1 | % simCell: Simulate the ESC cell model 2 | % 3 | % [vk,rck,hk,zk,sik,OCV] = simCell(ik,T,deltaT,model,z0,iR0,h0) 4 | % 5 | % Inputs: ik = vector input current (A) 6 | % T = temperature (degC) 7 | % deltaT= sample period (s) 8 | % model = ESC model structure 9 | % z0 = initial cell SOC (0..1) 10 | % iR0 = initial RC-filter current state (A) 11 | % h0 = initial hysteresis state (0..1) 12 | % 13 | % Outputs: vk = cell voltage (V) 14 | % rck = RC-filter resistor current(s) (A) 15 | % hk = hysteresis state (0..1) 16 | % zk = cell state-of-charge (0..1) 17 | % sk = instantaneous hysteresis state [-1,1] 18 | % OCV = cell OCV value (V) 19 | % 20 | % Important note: time vector corresponding to ik assumed to be 21 | % tk = (0:length(ik)-1)*deltaT, 22 | % where deltaT = sampling period of model. Same time vector corresponds 23 | % to output values, so first entry in all outputs corresponds to 24 | % tk = 0 (state has not yet advanced), next entry corresponds to 25 | % tk = deltaT; etc. 26 | 27 | function [vk,rck,hk,zk,sik,OCV] = simCell(ik,T,deltaT,model,z0,iR0,h0) 28 | % Force data to be column vector(s) 29 | ik = ik(:); iR0 = iR0(:); 30 | 31 | % Get model parameters from model structure 32 | RCfact = exp(-deltaT./abs(getParamESC('RCParam',T,model)))'; 33 | G = getParamESC('GParam',T,model); 34 | Q = getParamESC('QParam',T,model); 35 | M = getParamESC('MParam',T,model); 36 | M0 = getParamESC('M0Param',T,model); 37 | RParam = getParamESC('RParam',T,model); 38 | R0Param = getParamESC('R0Param',T,model); 39 | etaParam = getParamESC('etaParam',T,model); 40 | 41 | etaik = ik; etaik(ik<0) = etaParam*ik(ik<0); 42 | 43 | % Simulate the dynamic states of the model 44 | rck = zeros(length(RCfact),length(etaik)); rck(:,1) = iR0; 45 | for k = 2:length(ik), 46 | rck(:,k) = diag(RCfact)*rck(:,k-1) + (1-RCfact)*etaik(k-1); 47 | end 48 | rck = rck'; 49 | zk = z0-cumsum([0;etaik(1:end-1)])*deltaT/(Q*3600); 50 | if any(zk>1.1), 51 | warning('Current may have wrong sign as SOC > 110%'); 52 | end 53 | 54 | % Hysteresis stuff 55 | hk=zeros([length(ik) 1]); hk(1) = h0; sik = 0*hk; 56 | fac=exp(-abs(G*etaik*deltaT/(3600*Q))); 57 | for k=2:length(ik), 58 | hk(k)=fac(k-1)*hk(k-1)-(1-fac(k-1))*sign(ik(k-1)); 59 | sik(k) = sign(ik(k)); 60 | if abs(ik(k)) 0; charge < 0. 10 | voltage = DYNData.script1.voltage(:); 11 | soc = DYNData.script1.soc(:); 12 | 13 | % Load cell-test data to be used for this batch experiment 14 | % Contains variable "DYNData" of which the field "script1" is of 15 | % interest. This has sub-fields time, current, voltage, soc. 16 | load E:\BMS\SOC\matlab\PANmodel.mat; % load ESC model of Panasonic NMC cell 17 | 18 | % Reserve storage for computed results, for plotting 19 | sochat = zeros(size(soc)); 20 | socbound = zeros(size(soc)); 21 | 22 | % Covariance values 23 | SigmaX0 = diag([1e2 1e-2 1e-3]); % uncertainty of initial state 24 | SigmaV = 3e-1; % Uncertainty of voltage sensor, output equation 25 | SigmaW = 4e0; % Uncertainty of current sensor, state equation 26 | 27 | % Create ekfData structure and initialize variables using first 28 | % voltage measurement and first temperature measurement 29 | ekfData = initEKF(voltage(1),T,SigmaX0,SigmaV,SigmaW,model); 30 | 31 | % Now, enter loop for remainder of time, where we update the SPKF 32 | % once per sample interval 33 | fprintf('Please be patient. This code will take several minutes to execute.\n') 34 | for k = 1:length(voltage), 35 | vk = voltage(k); % "measure" voltage 36 | ik = current(k); % "measure" current 37 | Tk = T; % "measure" temperature 38 | 39 | % Update SOC (and other model states) 40 | [sochat(k),socbound(k),ekfData] = iterEKF(vk,ik,Tk,deltat,ekfData); 41 | if mod(k,1000)==0, 42 | fprintf(' Completed %d out of %d iterations...\n',k,length(voltage)); 43 | end 44 | end 45 | 46 | %% 47 | subplot(1,2,1); plot(time/60,100*sochat,time/60,100*soc); hold on 48 | plot([time/60; NaN; time/60],[100*(sochat+socbound); NaN; 100*(sochat-socbound)]); 49 | title('SOC estimation using EKF'); grid on 50 | xlabel('Time (min)'); ylabel('SOC (%)'); legend('Estimate','Truth','Bounds'); 51 | 52 | %% 53 | fprintf('RMS SOC estimation error = %g%%\n',sqrt(mean((100*(soc-sochat)).^2))); 54 | 55 | %% 56 | subplot(1,2,2); plot(time/60,100*(soc-sochat)); hold on 57 | plot([time/60; NaN; time/60],[100*socbound; NaN; -100*socbound]); 58 | title('SOC estimation errors using EKF'); 59 | xlabel('Time (min)'); ylabel('SOC error (%)'); ylim([-4 4]); 60 | legend('Estimation error','Bounds'); 61 | grid on 62 | 63 | ind = find(abs(soc-sochat)>socbound); 64 | fprintf('Percent of time error outside bounds = %g%%\n',length(ind)/length(soc)*100); -------------------------------------------------------------------------------- /State_Of_Charge/wrapperEKF.m: -------------------------------------------------------------------------------- 1 | % Load model file corresponding to a cell of this type 2 | % Has the variables: current, SOC, time, voltage 3 | addpath E:\BMS\SOC\matlab 4 | load E:\BMS\SOC\matlab\PANdata_P45.mat; % load data from Panasonic NMC cell, +25 degC 5 | T = 45; % Test temperature 6 | 7 | time = DYNData.script1.time(:); deltat = time(2)-time(1); 8 | time = time-time(1); % start time at 0 9 | current = DYNData.script1.current(:); % discharge > 0; charge < 0. 10 | voltage = DYNData.script1.voltage(:); 11 | soc = DYNData.script1.soc(:); 12 | 13 | % Load cell-test data to be used for this batch experiment 14 | % Contains variable "DYNData" of which the field "script1" is of 15 | % interest. This has sub-fields time, current, voltage, soc. 16 | load E:\BMS\SOC\matlab\PANmodel.mat; % load ESC model of Panasonic NMC cell 17 | 18 | % Reserve storage for computed results, for plotting 19 | sochat = zeros(size(soc)); 20 | socbound = zeros(size(soc)); 21 | 22 | % Covariance values 23 | SigmaX0 = diag([1e2 1e-2 1e-3]); % uncertainty of initial state 24 | SigmaV = 3e-1; % Uncertainty of voltage sensor, output equation 25 | SigmaW = 4e0; % Uncertainty of current sensor, state equation 26 | 27 | % Create ekfData structure and initialize variables using first 28 | % voltage measurement and first temperature measurement 29 | ekfData = initEKF(voltage(1),T,SigmaX0,SigmaV,SigmaW,model); 30 | 31 | % Now, enter loop for remainder of time, where we update the SPKF 32 | % once per sample interval 33 | fprintf('Please be patient. This code will take several minutes to execute.\n') 34 | for k = 1:length(voltage), 35 | vk = voltage(k); % "measure" voltage 36 | ik = current(k); % "measure" current 37 | Tk = T; % "measure" temperature 38 | 39 | % Update SOC (and other model states) 40 | [sochat(k),socbound(k),ekfData] = iterEKF(vk,ik,Tk,deltat,ekfData); 41 | if mod(k,1000)==0, 42 | fprintf(' Completed %d out of %d iterations...\n',k,length(voltage)); 43 | end 44 | end 45 | 46 | %% 47 | subplot(1,2,1); plot(time/60,100*sochat,time/60,100*soc); hold on 48 | plot([time/60; NaN; time/60],[100*(sochat+socbound); NaN; 100*(sochat-socbound)]); 49 | title('SOC estimation using EKF'); grid on 50 | xlabel('Time (min)'); ylabel('SOC (%)'); legend('Estimate','Truth','Bounds'); 51 | 52 | %% 53 | fprintf('RMS SOC estimation error = %g%%\n',sqrt(mean((100*(soc-sochat)).^2))); 54 | 55 | %% 56 | subplot(1,2,2); plot(time/60,100*(soc-sochat)); hold on 57 | plot([time/60; NaN; time/60],[100*socbound; NaN; -100*socbound]); 58 | title('SOC estimation errors using EKF'); 59 | xlabel('Time (min)'); ylabel('SOC error (%)'); ylim([-4 4]); 60 | legend('Estimation error','Bounds'); 61 | grid on 62 | 63 | ind = find(abs(soc-sochat)>socbound); 64 | fprintf('Percent of time error outside bounds = %g%%\n',length(ind)/length(soc)*100); -------------------------------------------------------------------------------- /State_Of_Charge/wrapperSPKF.m: -------------------------------------------------------------------------------- 1 | % Load model file corresponding to a cell of this type 2 | % Has the variables: current, SOC, time, voltage 3 | addpath E:\BMS\SOC\matlab 4 | load E:\BMS\SOC\matlab\PANdata_P25.mat; % load data from Panasonic NMC cell, +25 degC 5 | T = 25; % Test temperature 6 | 7 | time = DYNData.script1.time(:); deltat = time(2)-time(1); 8 | time = time-time(1); % start time at 0 9 | current = DYNData.script1.current(:); % discharge > 0; charge < 0. 10 | voltage = DYNData.script1.voltage(:); 11 | soc = DYNData.script1.soc(:); 12 | 13 | % Load cell-test data to be used for this batch experiment 14 | % Contains variable "DYNData" of which the field "script1" is of 15 | % interest. This has sub-fields time, current, voltage, soc. 16 | load E:\BMS\SOC\matlab\PANmodel.mat; % load ESC model of Panasonic NMC cell 17 | 18 | % Reserve storage for computed results, for plotting 19 | sochat = zeros(size(soc)); 20 | socbound = zeros(size(soc)); 21 | 22 | % Covariance values 23 | SigmaX0 = diag([1e2 1e-2 1e-3]); % uncertainty of initial state 24 | SigmaV = 3e-1; % Uncertainty of voltage sensor, output equation 25 | SigmaW = 4e0; % Uncertainty of current sensor, state equation 26 | 27 | % Create spkfData structure and initialize variables using first 28 | % voltage measurement and first temperature measurement 29 | spkfData = initSPKF(voltage(1),T,SigmaX0,SigmaV,SigmaW,model); 30 | 31 | % Now, enter loop for remainder of time, where we update the SPKF 32 | % once per sample interval 33 | fprintf('Please be patient. This code will take several minutes to execute.\n') 34 | for k = 1:length(voltage), 35 | vk = voltage(k); % "measure" voltage 36 | ik = current(k); % "measure" current 37 | Tk = T; % "measure" temperature 38 | 39 | % Update SOC (and other model states) 40 | [sochat(k),socbound(k),spkfData] = iterSPKF(vk,ik,Tk,deltat,spkfData); 41 | % update waitbar periodically, but not too often (slow procedure) 42 | if mod(k,1000)==0, 43 | fprintf(' Completed %d out of %d iterations...\n',k,length(voltage)); 44 | end 45 | end 46 | 47 | %% 48 | subplot(1,2,1); plot(time/60,100*sochat,time/60,100*soc); hold on 49 | plot([time/60; NaN; time/60],[100*(sochat+socbound); NaN; 100*(sochat-socbound)]); 50 | title('SOC estimation using SPKF'); grid on 51 | xlabel('Time (min)'); ylabel('SOC (%)'); legend('Estimate','Truth','Bounds'); 52 | 53 | %% 54 | fprintf('RMS SOC estimation error = %g%%\n',sqrt(mean((100*(soc-sochat)).^2))); 55 | 56 | %% 57 | subplot(1,2,2); plot(time/60,100*(soc-sochat)); hold on 58 | plot([time/60; NaN; time/60],[100*socbound; NaN; -100*socbound]); 59 | title('SOC estimation errors using SPKF'); 60 | xlabel('Time (min)'); ylabel('SOC error (%)'); ylim([-4 4]); 61 | legend('Estimation error','Bounds'); 62 | grid on 63 | 64 | ind = find(abs(soc-sochat)>socbound); 65 | fprintf('Percent of time error outside bounds = %g%%\n',length(ind)/length(soc)*100); -------------------------------------------------------------------------------- /State_Of_Charge/wrapperSPKFbd.m: -------------------------------------------------------------------------------- 1 | clear 2 | % Can set an artificial current-sensor bias to see how well the SPKF 3 | % estimates this bias 4 | ibias = 0.5; % can set this (e.g., to 0 or 0.5 A) 5 | 6 | % Load cell-test data . Contains variable "DYNData" of which the field 7 | % "script1" is of interest. It has sub-fields time, current, voltage, soc. 8 | addpath E:\BMS\SOC\matlab 9 | load E:\BMS\SOC\matlab\PANmodel.mat; % loads cell model 10 | load E:\BMS\SOC\matlab\PANPackData.mat; 11 | time = 0:length(ik)-1; time = time(:); deltat = 1; 12 | current = ik + ibias; 13 | voltage = vk; 14 | soc = zk; 15 | 16 | % For this somewhat simplified example, we will not estimate 17 | % capacity inverse. Instead, we assume perfect knowledge of capacity 18 | % of every cell, and hence capacity inverse of every cell 19 | Qinv = 1./Q0; Qinvbar = mean(Qinv); dQinv = Qinv - Qinvbar; 20 | % Similarly, we will not estimate the delta-R0 values, but assume knowledge 21 | dR0 = getParamESC('R0Param',T,model) - R0; 22 | 23 | % Reserve storage for computed results, for plotting 24 | sochat = 0*time; % reserve storage for bar-soc values 25 | socbound = sochat; % and bounds on those values 26 | bias = sochat; % ... also for current-sensor bias estimate 27 | biasBound = sochat; % and bounds on that estimate 28 | 29 | dsochat = zeros(size(voltage)); % reserve storage for delta-soc values 30 | dsocbound = zeros(size(voltage)); % and for bounds on those values 31 | dR = zeros(size(voltage)); % reserve storage for delta-R0 values 32 | dRbound = zeros(size(voltage)); % and for bounds on those values 33 | 34 | % Covariance values 35 | % State ordering: ir,h,z,bias 36 | SigmaX0 = diag([1e2 1e-4 1e-2 5e-2]); % uncertainty of initial state 37 | SigmaV = 1e-3; % uncertainty of voltage sensor, output equation 38 | SigmaW = diag([1e-1, 1e-4]); % uncertainty of current sensor, bias 39 | 40 | spkfData = initSPKFbd(voltage(1,:), T, SigmaX0, SigmaV, SigmaW, model); 41 | spkfData.Qinvbar = Qinvbar; spkfData.dQinv = dQinv; spkfData.dR0 = dR0; 42 | 43 | % Now, enter loop for remainder of time, where we update the SPKF 44 | % once per sample interval 45 | fprintf('Starting SPKF\n'); 46 | for k = 1:size(voltage,1), 47 | vk = voltage(k,:); % "measure" voltage 48 | ik = current(k); % "measure" current 49 | Tk = T; % "measure" temperature 50 | 51 | % Update SOC (and other model states) of bar filter 52 | [sochat(k), socbound(k), dsochat(k,:), dsocbound(k,:), bias(k), biasBound(k), spkfData] = ... 53 | iterSPKFbd(vk, ik, Tk, deltat, spkfData); 54 | 55 | % update waitbar periodically, but not too often (slow procedure) 56 | if mod(k,250)==0, fprintf(' Completed %d out of %d iterations\n',k,size(voltage,1)); end 57 | end 58 | 59 | % Display output 60 | subplot(2,2,1); 61 | plot(time/60,100*sochat, '-r', time/60, 100*mean(soc,2), '--b'); hold on 62 | h = plot([time/60; NaN; time/60],... 63 | [100*(sochat+socbound); NaN; 100*(sochat-socbound)]); 64 | plot(time/60,100*soc, '-k'); 65 | ylim([55 95]); 66 | title('Avg. SOC estimate using bar filter' ); 67 | xlabel('Time (min)' ); ylabel('SOC (%)'); 68 | legend('Average SOC estimate', 'True average SOC', 'Bounds', 'Individual SOCs'); grid on 69 | 70 | subplot(2,2,2); 71 | plot(time/60,bias, '-r', time/60, ibias*ones(size(bias)), '--b'); hold on 72 | h = plot([time/60; NaN; time/60],... 73 | [bias+biasBound; NaN; bias-biasBound]); 74 | title('Current-sensor bias estimate using bar filter' ); 75 | xlabel('Time (min)' ); ylabel('Bias (A)' ); 76 | legend('Bias estimate', 'True bias', 'Bounds'); grid on 77 | 78 | subplot(2,2,3); 79 | sochat = repmat(sochat,1,4); 80 | socbound = repmat(socbound,1,4); 81 | plot(time/60,100*(sochat+dsochat), '-r'); hold on 82 | h = plot([time/60; NaN; time/60],... 83 | [100*(sochat+dsochat+socbound+dsocbound); NaN(1,4); 100*(sochat+dsochat-dsocbound-socbound)]); 84 | plot(time/60,100*soc, '-k'); 85 | ylim([55 95]); 86 | title('Individual SOC estimate using bar-delta filter' ); 87 | xlabel('Time (min)' ); ylabel('SOC (%)'); grid on -------------------------------------------------------------------------------- /images/accforce.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/accforce.JPG -------------------------------------------------------------------------------- /images/actualspeed.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/actualspeed.JPG -------------------------------------------------------------------------------- /images/battSOC.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/battSOC.JPG -------------------------------------------------------------------------------- /images/battcurrent.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/battcurrent.JPG -------------------------------------------------------------------------------- /images/battpower.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/battpower.JPG -------------------------------------------------------------------------------- /images/blockdiagram.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/blockdiagram.JPG -------------------------------------------------------------------------------- /images/demandtorque.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/demandtorque.JPG -------------------------------------------------------------------------------- /images/desaccforce.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/desaccforce.JPG -------------------------------------------------------------------------------- /images/desireedacc.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/desireedacc.JPG -------------------------------------------------------------------------------- /images/dragf2.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/dragf2.JPG -------------------------------------------------------------------------------- /images/dragforces.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/dragforces.JPG -------------------------------------------------------------------------------- /images/eqmass.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/eqmass.JPG -------------------------------------------------------------------------------- /images/hysteresis.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/hysteresis.JPG -------------------------------------------------------------------------------- /images/instantaneoushysteresis.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/instantaneoushysteresis.JPG -------------------------------------------------------------------------------- /images/modelequation.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/modelequation.JPG -------------------------------------------------------------------------------- /images/motorpower.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/motorpower.JPG -------------------------------------------------------------------------------- /images/motorspeed.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/motorspeed.JPG -------------------------------------------------------------------------------- /images/opequation.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/opequation.JPG -------------------------------------------------------------------------------- /images/overallhysteresis.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/overallhysteresis.JPG -------------------------------------------------------------------------------- /images/randlesscircuit.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/randlesscircuit.JPG -------------------------------------------------------------------------------- /images/range.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/range.JPG -------------------------------------------------------------------------------- /images/resistor_current.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/resistor_current.JPG -------------------------------------------------------------------------------- /images/statespaceformofcell.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/statespaceformofcell.JPG -------------------------------------------------------------------------------- /images/unexplainedpart.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/unexplainedpart.JPG -------------------------------------------------------------------------------- /images/unexplainedpart2.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ronakj2904/BatteryManagementSystem_Algorithm_usingMatlab/95bd67ce9b8378bec162cf172c5c9c51863a06c4/images/unexplainedpart2.JPG --------------------------------------------------------------------------------