### Copyright Notice / Disclaimer # Wheels Vehicle Simulation Code Example # More information may be found at http://www.virtual-car.org/ # Copyright 2010 by Mike Safoutin. All Rights Reserved # This code may be used and modified free of charge by anyone so long as this # copyright notice and the comments above remain intact and the author is credited. # By using this code you agree to indemnify the author from any liability that might # arise from its use. No warranty of any kind is express or implied and user # assumes all risks associated with the use of this code. # Obtain permission before redistributing this code over any medium. # In all cases this copyright notice and comments above must remain intact. ### End Copyright Notice / Disclaimer # This is an example of Perl code that might be used to compute the equation of motion # for a vehicle on a second-by-second basis. It is adapted from the code used for the # Wheels vehicle simulation at http://www.virtual-car.org/ . # Please report any errors or questions via the contact means provided at that site. # Note: # In Perl, variable names begin with '$' # Comments begin with '#' # Assignment Operator: '=' # Is Equal To: '==' # Not Equal To: '!=' # Variables are global unless preceded by 'my' on first use, in which case they are local to the scope of the 'my'. # Receive raw user params from CGI form input $zCD = $cgiVals{'cd'}; # drag coefficient $zCRR = $cgiVals{'crr'}; # rolling resistance coefficient $zROTFACTOR = $cgiVals{'rot'}; # rotational inertia factor $cwunit = $cgiVals{'cwunit'}; # units for curb weight $zCW = $cgiVals{'cw'}; # curb weight $plunit = $cgiVals{'plunit'}; # payload units $zPAYLOAD = $cgiVals{'payload'}; # payload $faunit = $cgiVals{'faunit'}; # frontal area units $zAREA = $cgiVals{'area'}; # frontal area $zWHDIA = $cgiVals{'whdia'}; # wheel diameter $wdunit = $cgiVals{'wdunit'}; # wheel diameter units $grunit = $cgiVals{'grunit'}; # grade units (not currently in use) $zGRADE = $cgiVals{'grade'}; # grade (not currently in use) $zRHO = $cgiVals{'rho'}; # air density $adunit = $cgiVals{'adunit'}; # air density units $sched = $cgiVals{'sched'}; # driving schedule name $zEE = $cgiVals{'ee'}; # average engine efficiency (percent) $zDE = $cgiVals{'de'}; # average drivetrain efficiency (percent) $zRE = $cgiVals{'re'}; # average regen efficiency (percent) $hv = $cgiVals{'hv'}; # fuel heating value $hvunit = $cgiVals{'hvunit'}; # fuel heating value units $reunit = $cgiVals{'reunit'}; # regen efficiency units (not in use, assumes percent) $eeunit = $cgiVals{'eeunit'}; # engine efficiency units (not in use, assumes percent) $deunit = $cgiVals{'deunit'}; # drivetrain efficiency units (not in use, assumes percent) $target_mpg = $cgiVals{'mpg'}; # target mi/gal $ee = $zEE/100; # convert percent to decimal $de = $zDE/100; $re = $zRE/100; # override grade - not currently in use $zGRADE = 0; $grunit = 'percent'; $enerfactor = 1; $enerunit = 'J'; $fuel_btugal = $hv; # copy input vals for internal use $CW = $zCW; $CD = $zCD; $CRR = $zCRR; $ROTFACTOR = $zROTFACTOR; $WHDIA = $zWHDIA; $PAYLOAD = $zPAYLOAD; $AREA = $zAREA; $grade = $zGRADE; $RHO = $zRHO; # convert funky units into SI units if ($cwunit eq 'lb') { $CW = $zCW/2.20462262 }; if ($plunit eq 'lb') { $PAYLOAD = $zPAYLOAD/2.20462262 }; if ($faunit eq 'ft2') { $AREA = $zAREA/(3.28084*3.28084) }; if ($wdunit eq 'ft') { $WHDIA = $zWHDIA/3.28084 } elsif ($wdunit eq 'in') { $WHDIA = $zWHDIA/39.3700787 }; if ($adunit eq 'lb/ft3') { $RHO = $zRHO * 16.0084 }; $grade = $grade * 0.0174533; #Degrees to radians # input the specified drive cycle &getdrivecycle; # initialize &initrun; # simulate &simdrive; # output results &outscreen; ### END MAIN ################################################################################ sub getdrivecycle { # read driving cycle from file # assumed in mi/hr format # open for input open(SCHED, "<$filepath") or die "can't open $filepath: $!"; my $mysec = -1; # foreach line SCHED: while () { $thisline = $_; my @elements = split(/\,/, $thisline); $mysec++; $MPH[$mysec] = $elements[1]; $VEL[$mysec] = $elements[1] * 63360/141732; #mi/hr to m/sec } #done with file close(SCHED); $NSEC = $mysec; } ################################################################################ sub initrun { if ($NSEC > 0) { #-----Initialize all computed vars for last run my $i; for ($i = 0; $i <= $NSEC; $i++) { $AMODE[$i] = 0; $BMODE[$i] = 0; $CMODE[$i] = 0; $MEANVEL[$i] = 0; $WHLRPM[$i] = 0; $NETTIREPOWER[$i] = 0; $NETAEROPOWER[$i] = 0; $BRAKEPOWER[$i] = 0; $WHLDEMAND[$i] = 0; $GRSTIREPOWER[$i] = 0; $GRSAEROPOWER[$i] = 0; $INERPOWER[$i] = 0; } } } ################################################################################ sub simdrive { #-----Driving Schedule Simulation #---- Mode A: TOTLFORCE > 0 and INERFORCE > 0 (acceleration) #---- Mode B: TOTLFORCE > 0 and INERFORCE < 0 (powered deceleration) #---- Mode C: TOTLFORCE < 0 and INERFORCE < 0 (braking) #-----Initialize $NETAEROENER = 0; $GRSAEROENER = 0; $NETTIREENER = 0; $GRSTIREENER = 0; $POSKENER = 0; $GRSAEROmodeA = 0; $GRSTIREmodeA = 0; $GRSINERmodeA = 0; $GRSBRAKEmodeA = 0; $GRSAEROmodeB = 0; $GRSTIREmodeB = 0; $GRSINERmodeB = 0; $GRSBRAKEmodeB = 0; $GRSAEROmodeC = 0; $GRSTIREmodeC = 0; $GRSINERmodeC = 0; $GRSBRAKEmodeC = 0; $IDLESEC = 0; $COASTSEC = 0; $TOTLENER = 0; $BRAKENER = 0; $SUMEFF = 0; $occur = 0; $METERS = 0; $A = 0; $B = 0; $C = 0; #-----Calculate total (test) mass and inertia mass $TESTMASS = $CW + $PAYLOAD; $INERMASS = ($ROTFACTOR * $CW) + $PAYLOAD; # -----Calculate generalized forces (constant for the run) $F0 = $TESTMASS * 9.806 * $CRR; $F2 = 0.5 * $RHO * $CD * $AREA; #--------------------------------------------------------------------- #------------------------Main driving cycle loop------------------------ #--------------------------------------------------------------------- $SEC = -1; do { #-----Go thru seconds of cycle until SEC = NSEC or cancel requested $SEC++; #-----Calculate mean velocity from current and previous second if ($SEC == 0) { #-----No previous second: set velocity to zero $dvdt = 0; $MEANVEL[$SEC] = 0; } else { $LASTSEC = $SEC - 1; $dvdt = $VEL[$SEC] - $VEL[$LASTSEC]; $ACCELRATE[$SEC] = $dvdt * (141732/63360); $MEANVEL[$SEC] = ($VEL[$SEC] + $VEL[$LASTSEC])/2; # meters/sec } if ($SEC != 0) { #-----Count distance traveled $METERS = $METERS + $MEANVEL[$SEC]; #-----Calculate forces at this second $ROLLFORCE = $F0; $AEROFORCE = $F2 * $MEANVEL[$SEC] * $MEANVEL[$SEC]; $INERFORCE = $INERMASS * $dvdt + $TESTMASS * 9.806 * sin($zGRADE); $TOTLFORCE = $ROLLFORCE + $AEROFORCE + $INERFORCE; #-----Calculate wheel speed $WHLRPM[$SEC] = $MEANVEL[$SEC] * 60/(3.14159*$WHDIA); #-----Identify driving mode: if ($TOTLFORCE < 0) { #-----Mode C (Braking) &modec; } elsif ($INERFORCE <= 0) { #-----Mode B (Powered Deceleration) &modeb; } else { #-----Mode A (Acceleration) &modea; } } } until ($SEC == $NSEC); #--------------------------------------------------------------------- # ------------------------End of driving cycle loop------------------------ #--------------------------------------------------------------------- #-----Compute total distance traveled over cycle $MILES = $METERS/1609.344; } ################################################################################ sub modea { #-----Compute energy demand during Mode A events #-----TOTLFORCE and INERFORCE are positive $A++; $AMODE[$SEC] = 1; $BMODE[$SEC] = 0; $CMODE[$SEC] = 0; # -----Accounting #Force terms #nil #Power terms $GRSTIREPOWER[$SEC] = $ROLLFORCE * $MEANVEL[$SEC]; $GRSAEROPOWER[$SEC] = $AEROFORCE * $MEANVEL[$SEC]; $NETTIREPOWER[$SEC] = $GRSTIREPOWER[$SEC]; $NETAEROPOWER[$SEC] = $GRSAEROPOWER[$SEC]; $INERPOWER[$SEC] = $INERFORCE * $MEANVEL[$SEC]; $BRAKEPOWER[$SEC] = 0; #Cumulative terms $CUM_RR[$SEC] = $LAST_CUM_RR + $GRSTIREPOWER[$SEC]; $CUM_CD[$SEC] = $LAST_CUM_CD + $GRSAEROPOWER[$SEC]; $CUM_BR[$SEC] = $LAST_CUM_BR + $BRAKEPOWER[$SEC]; $LAST_CUM_RR = $CUM_RR[$SEC]; $LAST_CUM_CD = $CUM_CD[$SEC]; $LAST_CUM_BR = $CUM_BR[$SEC]; #Ener terms $NETTIREENER = $NETTIREENER + $NETTIREPOWER[$SEC]; $NETAEROENER = $NETAEROENER + $NETAEROPOWER[$SEC]; $GRSTIREENER = $GRSTIREENER + $GRSTIREPOWER[$SEC]; $GRSAEROENER = $GRSAEROENER + $GRSAEROPOWER[$SEC]; #Mode ener terms $GRSAEROmodeA = $GRSAEROmodeA + $GRSAEROPOWER[$SEC]; $GRSTIREmodeA = $GRSTIREmodeA + $GRSTIREPOWER[$SEC]; $GRSINERmodeA = $GRSINERmodeA + $INERPOWER[$SEC]; ### placeholder GRSBRAKEmodeA = $GRSBRAKEmodeA + 0; #Checks $POSKENER = $POSKENER + $INERPOWER[$SEC]; #Sums $WHLDEMAND[$SEC] = $TOTLFORCE * $MEANVEL[$SEC]; $TOTLENER = $TOTLENER + $WHLDEMAND[$SEC]; } ################################################################################ sub modeb { #-----Compute energy demand during Mode B events #-----TOTLFORCE is positive, INERFORCE is negative $B++; $AMODE[$SEC] = 0; $BMODE[$SEC] = 1; $CMODE[$SEC] = 0; #-----Count seconds in idle or cruise if ($MEANVEL[$SEC] == 0) {$IDLESEC++}; if (($dvdt == 0) && ($MEANVEL[$SEC] > 0)) { $COASTSEC++ }; #-----Accounting #Force terms $POSFORCE = $ROLLFORCE + $AEROFORCE; $NETROLLFORCE = $ROLLFORCE * ($TOTLFORCE/$POSFORCE); $NETAEROFORCE = $AEROFORCE * ($TOTLFORCE/$POSFORCE); #Power terms $INERPOWER[$SEC] = $INERFORCE * $MEANVEL[$SEC]; $NETTIREPOWER[$SEC] = $NETROLLFORCE * $MEANVEL[$SEC]; $NETAEROPOWER[$SEC] = $NETAEROFORCE * $MEANVEL[$SEC]; $GRSTIREPOWER[$SEC] = $ROLLFORCE * $MEANVEL[$SEC]; $GRSAEROPOWER[$SEC] = $AEROFORCE * $MEANVEL[$SEC]; $BRAKEPOWER[$SEC] = 0; #Cumulative terms $CUM_RR[$SEC] = $LAST_CUM_RR + $GRSTIREPOWER[$SEC]; $CUM_CD[$SEC] = $LAST_CUM_CD + $GRSAEROPOWER[$SEC]; $CUM_BR[$SEC] = $LAST_CUM_BR + $BRAKEPOWER[$SEC]; $LAST_CUM_RR = $CUM_RR[$SEC]; $LAST_CUM_CD = $CUM_CD[$SEC]; $LAST_CUM_BR = $CUM_BR[$SEC]; #Ener terms $NETTIREENER = $NETTIREENER + $NETTIREPOWER[$SEC]; $NETAEROENER = $NETAEROENER + $NETAEROPOWER[$SEC]; $GRSTIREENER = $GRSTIREENER + $GRSTIREPOWER[$SEC]; $GRSAEROENER = $GRSAEROENER + $GRSAEROPOWER[$SEC]; #Mode ener terms $GRSAEROmodeB = $GRSAEROmodeB + $GRSAEROPOWER[$SEC]; $GRSTIREmodeB = $GRSTIREmodeB + $GRSTIREPOWER[$SEC]; $GRSINERmodeB = $GRSINERmodeB + $INERPOWER[$SEC]; ### placeholder $GRSBRAKEmodeB = $GRSBRAKEmodeB + 0; #Sums $WHLDEMAND[$SEC] = $NETTIREPOWER[$SEC] + $NETAEROPOWER[$SEC]; $TOTLENER = $TOTLENER + $WHLDEMAND[$SEC]; } ################################################################################ sub modec { #-----Compute energy demand during Mode C events #-----TOTLFORCE is negative or zero $C++; $AMODE[$SEC] = 0; $BMODE[$SEC] = 0; $CMODE[$SEC] = 1; # -----Accounting #Force terms $BRAKEFORCE = $INERFORCE + $AEROFORCE + $ROLLFORCE; #Sum is always negative #Power terms $GRSTIREPOWER[$SEC] = $ROLLFORCE * $MEANVEL[$SEC]; $GRSAEROPOWER[$SEC] = $AEROFORCE * $MEANVEL[$SEC]; $NETTIREPOWER[$SEC] = 0; $NETAEROPOWER[$SEC] = 0; $INERPOWER[$SEC] = $INERFORCE * $MEANVEL[$SEC]; $BRAKEPOWER[$SEC] = $BRAKEFORCE * $MEANVEL[$SEC]; #Cumulative terms $CUM_RR[$SEC] = $LAST_CUM_RR + $GRSTIREPOWER[$SEC]; $CUM_CD[$SEC] = $LAST_CUM_CD + $GRSAEROPOWER[$SEC]; $CUM_BR[$SEC] = $LAST_CUM_BR + $BRAKEPOWER[$SEC]; $LAST_CUM_RR = $CUM_RR[$SEC]; $LAST_CUM_CD = $CUM_CD[$SEC]; $LAST_CUM_BR = $CUM_BR[$SEC]; #Ener terms $BRAKENER = $BRAKENER + $BRAKEPOWER[$SEC]; ### placeholder $NETTIREENER = $NETTIREENER + 0; ### placeholder $NETAEROENER = $NETAEROENER + 0; $GRSTIREENER = $GRSTIREENER + $GRSTIREPOWER[$SEC]; $GRSAEROENER = $GRSAEROENER + $GRSAEROPOWER[$SEC]; #Mode ener terms $GRSAEROmodeC = $GRSAEROmodeC + $GRSAEROPOWER[$SEC]; $GRSTIREmodeC = $GRSTIREmodeC + $GRSTIREPOWER[$SEC]; $GRSINERmodeC = $GRSINERmodeC + $INERPOWER[$SEC]; $GRSBRAKEmodeC = $GRSBRAKEmodeC + $BRAKEPOWER[$SEC]; #Sums $WHLDEMAND[$SEC] = 0; $TOTLENER = $TOTLENER + $WHLDEMAND[$SEC]; } ################################################################################ sub outscreen { $totalenerout = $TOTLENER/$enerfactor; $totalenerout_kwh = $totalenerout/3600000; $totalaeroout = $GRSAEROENER/$enerfactor; $aeropercent = ((int($GRSAEROENER/$TOTLENER*10000))/100); $totalrollout = (($GRSTIREENER/$enerfactor)); $rollpercent = ((int($GRSTIREENER/$TOTLENER*10000))/100); $totalbrakeout = ((-1*$BRAKENER/$enerfactor)); $brakepercent = ((int(-1*$BRAKENER/$TOTLENER*10000))/100); $actualbalance = ($TOTLENER - $GRSAEROENER - $GRSTIREENER + $BRAKENER); $percenterror = (($TOTLENER - $GRSAEROENER - $GRSTIREENER + $BRAKENER) / $TOTLENER) * 100; $balancewarning = "Energy balance successful."; if ($percenterror > 0.01) {$balancewarning = "NOTE: Energy balance failed."}; $totalinertia = (($POSKENER/$enerfactor)); $inertiadrag = (($GRSAEROENER - $NETAEROENER)/$enerfactor); $inertiaroll = (($GRSTIREENER - $NETTIREENER)/$enerfactor); $inertiabraked = ((-1*$BRAKENER/$enerfactor)); $inertiabalance = ($POSKENER+$BRAKENER-($GRSAEROENER - $NETAEROENER)-($GRSTIREENER - $NETTIREENER)); $inertiapercenterror = ($POSKENER+$BRAKENER-($GRSAEROENER - $NETAEROENER)-($GRSTIREENER - $NETTIREENER))/$POSKENER*100; $inertiabalancewarning = "Inertia balance successful."; if ($inertiapercenterror > 0.01) {$inertiabalancewarning = "NOTE: Inertia balance failed."}; $distancetraveled = ($METERS/1609.344); $kjpermile = (int($TOTLENER/($METERS/1609.344))/1000); $kwhpermile = $TOTLENER/($METERS/1609.344)/3600000; $pdaccelsec = $B-$IDLESEC-$COASTSEC; $thermal_mpg = $distancetraveled/$TOTLENER*$fuel_btugal*1054.35; $net_mpg = $thermal_mpg * $ee * $de; $regen_mpg = ($distancetraveled/($TOTLENER-$re*abs($BRAKENER))*$fuel_btugal*1054.35) * $ee * $de; $thermal_mpg = sprintf "%.2f", $thermal_mpg; $net_mpg = sprintf "%.2f", $net_mpg; $regen_mpg = sprintf "%.2f", $regen_mpg; $totalenerout = sprintf "%.2f", $totalenerout; $totalaeroout = sprintf "%.2f", $totalaeroout; $totalrollout = sprintf "%.2f", $totalrollout; $totalbrakeout = sprintf "%.2f", $totalbrakeout; $totalinertia = sprintf "%.2f", $totalinertia; $inertiadrag = sprintf "%.2f", $inertiadrag; $inertiaroll = sprintf "%.2f", $inertiaroll; $inertiabraked = sprintf "%.2f", $inertiabraked; $distancetraveled = sprintf "%.2f", $distancetraveled; $kjpermile = sprintf "%.2f", $kjpermile; $kwhpermile = sprintf "%.4f", $kwhpermile; $totalenerout_kwh = sprintf "%.4f", $totalenerout_kwh; $matrix1 = $GRSAEROmodeA/$enerfactor; $matrix2 = $GRSAEROmodeB/$enerfactor; $matrix3 = $GRSAEROmodeC/$enerfactor; $matrix4 = $GRSTIREmodeA/$enerfactor; $matrix5 = $GRSTIREmodeB/$enerfactor; $matrix6 = $GRSTIREmodeC/$enerfactor; $matrix7 = $GRSBRAKEmodeA/$enerfactor; $matrix8 = $GRSBRAKEmodeB/$enerfactor; $matrix9 = -1*$GRSBRAKEmodeC/$enerfactor; $matrix10 = $GRSINERmodeA/$enerfactor; $matrix11 = $GRSINERmodeB/$enerfactor; $matrix12 = $GRSINERmodeC/$enerfactor; $aero_sum = $matrix1 + $matrix2 + $matrix3; $rolling_sum = $matrix4 + $matrix5 + $matrix6; $braking_sum = $matrix7 + $matrix8 + $matrix9; $inertia_sum = $matrix10 + $matrix11 + $matrix12; $aero_error = $GRSAEROENER - $aero_sum; $rolling_error = $GRSTIREENER - $rolling_sum; $braking_error = $BRAKENER + $braking_sum; $matrix1 = sprintf "%.2f", $matrix1; $matrix2 = sprintf "%.2f", $matrix2; $matrix3 = sprintf "%.2f", $matrix3; $matrix4 = sprintf "%.2f", $matrix4; $matrix5 = sprintf "%.2f", $matrix5; $matrix6 = sprintf "%.2f", $matrix6; $matrix7 = sprintf "%.2f", $matrix7; $matrix8 = sprintf "%.2f", $matrix8; $matrix9 = sprintf "%.2f", $matrix9; $matrix10 = sprintf "%.2f", $matrix10; $matrix11 = sprintf "%.2f", $matrix11; $matrix12 = sprintf "%.2f", $matrix12; $aero_sum = sprintf "%.2f", $aero_sum; $rolling_sum = sprintf "%.2f", $rolling_sum; $braking_sum = sprintf "%.2f", $braking_sum; $inertia_sum = sprintf "%.2f", $inertia_sum; $totalsec = $IDLESEC+$A+$pdaccelsec+$COASTSEC+$C; $exactdistance = $METERS/1609.344; print <Wheels Vehicle Simulation Return to WheelsModeling MethodologyVirtual Car Home Page

Wheels Vehicle Simulation

Input Parameters
Curb Weight:$zCW $cwunit
Payload:$zPAYLOAD $plunit
Drag Coefficient:$CD
Frontal Area:$zAREA $faunit
Rolling Resistance Coefficient:$CRR
Wheel Diameter:$zWHDIA $wdunit
Rotation Factor:$ROTFACTOR
Grade$zGRADE $grunit
Air Density:$zRHO $adunit
Ave. engine efficiency:$zEE $eeunit
Ave. drivetrain efficiency:$zDE $deunit
Ave. regen efficiency:$zRE $reunit
Fuel heating value:$hv $hvunit
Driving Cycle:$sched


Energy Breakdown - $sched Cycle
Total energy demanded at wheels:$totalenerout $enerunit ($totalenerout_kwh kWh)
 Consumed by Aerodynamic Drag:$totalaeroout $enerunit ($aeropercent %)
 Consumed by Rolling Resistance:$totalrollout $enerunit ($rollpercent %)
 Consumed by Braking:$totalbrakeout $enerunit ($brakepercent %)
Distance traveled:$distancetraveled miles
Gross efficiency:$kjpermile kJ/mile ($kwhpermile kWh/mile)
Gross thermal fuel economy (at wheels):$thermal_mpg mi/gal
Net fuel economy (engine $zEE%, drivetrain $zDE%):$net_mpg mi/gal
With regenerative braking at $zRE percent:$regen_mpg mi/gal
Time in Idle:$IDLESEC sec
Time in Acceleration:$A sec
Time in Powered Deceleration:$pdaccelsec sec
Time in Cruise:$COASTSEC sec
Time in Braking:$C sec
This accounts for $totalsec of $NSEC seconds in the cycle.

endofhtml ; #-----If energy balance is not valid, explain why if ($percenterror > 0.01) { print "NOTE: Energy balance failed due to:
\n"; if ($zGRADE != 0) { print "Nonzero grade.
"; } elsif (($VEL[$NSEC] != 0) || ($VEL[0] != 0)) { print "Nonzero initial or final velocity in driving cycle.
"; } else { print "Unknown reason.
"; } print "
"; }