Browse Source

Added advanced parameters

Added human power (constant power)
master
Youen Toupin 3 years ago
parent
commit
fa6a4f5b09
  1. 15
      simulator/src/app.scss
  2. 12
      simulator/src/simulator-ui.ts
  3. 245
      simulator/src/simulator.html
  4. 32
      simulator/src/simulator.ts

15
simulator/src/app.scss

@ -80,3 +80,18 @@ input[type=number] {
.battery-charge-graph text { .battery-charge-graph text {
font-size: 10px; font-size: 10px;
} }
input[name="parameters-accordion"], .panel-block {
display: none;
}
label.panel-heading {
cursor: pointer;
display: block;
}
#advanced-parameters:checked ~ .panel > .advanced-parameters-body,
#main-parameters:checked ~ .panel > .main-parameters-body {
display: flex;
}

12
simulator/src/simulator-ui.ts

@ -2,7 +2,11 @@
interface SimulationParameters { interface SimulationParameters {
batteryCapacity: number, batteryCapacity: number,
emptyVehicleWeight: number,
driverWeight: number,
additionalWeight: number, additionalWeight: number,
humanPower: number,
averageSpeed: number,
climateZone: string, climateZone: string,
dailyDistance: number, dailyDistance: number,
dailyAscendingElevation: number dailyAscendingElevation: number
@ -13,7 +17,11 @@ function runSimulation(parameters: SimulationParameters): Simulator.SimulationRe
let vehicle = new Simulator.Vehicle(); let vehicle = new Simulator.Vehicle();
vehicle.batteryCapacity = parameters.batteryCapacity; vehicle.batteryCapacity = parameters.batteryCapacity;
vehicle.emptyVehicleWeight = parameters.emptyVehicleWeight;
vehicle.driverWeight= parameters.driverWeight;
vehicle.additionalWeight = parameters.additionalWeight; vehicle.additionalWeight = parameters.additionalWeight;
vehicle.humanPower = parameters.humanPower;
vehicle.averageSpeed = parameters.averageSpeed;
let solarIrradiance: number[] = climateData[parameters.climateZone.toLowerCase()]; let solarIrradiance: number[] = climateData[parameters.climateZone.toLowerCase()];
let planning = new Simulator.OutingPlanning(parameters.dailyDistance, parameters.dailyAscendingElevation); let planning = new Simulator.OutingPlanning(parameters.dailyDistance, parameters.dailyAscendingElevation);
@ -56,7 +64,11 @@ function initializeSimulator(container: HTMLElement) {
container.querySelector('.simulate-button').addEventListener('click', e => { container.querySelector('.simulate-button').addEventListener('click', e => {
let parameters: SimulationParameters = { let parameters: SimulationParameters = {
batteryCapacity: Number((<HTMLInputElement>container.querySelector('[name=battery-capacity]')).value), batteryCapacity: Number((<HTMLInputElement>container.querySelector('[name=battery-capacity]')).value),
emptyVehicleWeight: Number((<HTMLInputElement>container.querySelector('[name=empty-weight]')).value),
driverWeight: Number((<HTMLInputElement>container.querySelector('[name=driver-weight]')).value),
additionalWeight: Number((<HTMLInputElement>container.querySelector('[name=additional-weight]')).value), additionalWeight: Number((<HTMLInputElement>container.querySelector('[name=additional-weight]')).value),
humanPower: Number((<HTMLInputElement>container.querySelector('[name=human-power]')).value),
averageSpeed: Number((<HTMLInputElement>container.querySelector('[name=average-speed]')).value),
climateZone: (<HTMLSelectElement>container.querySelector('.zone-selector')).value, climateZone: (<HTMLSelectElement>container.querySelector('.zone-selector')).value,
dailyDistance: Number((<HTMLInputElement>container.querySelector('[name=daily-distance]')).value), dailyDistance: Number((<HTMLInputElement>container.querySelector('[name=daily-distance]')).value),
dailyAscendingElevation: Number((<HTMLInputElement>container.querySelector('[name=daily-elevation]')).value), dailyAscendingElevation: Number((<HTMLInputElement>container.querySelector('[name=daily-elevation]')).value),

245
simulator/src/simulator.html

@ -1,110 +1,183 @@
<section class="section"> <section class="section">
<div class="container"> <div class="container">
<div class="simulation-parameters"> <div class="simulation-parameters">
<div class="wide-label">
<div class="field is-horizontal"> <!-- -------------- main parameters ---------------- -->
<div class="field-label is-normal"> <input type="checkbox" id="main-parameters" name="parameters-accordion" checked="checked" disabled="disabled">
<label class="label">Capacité de la batterie</label> <div class="panel">
</div> <label class="panel-heading" for="main-parameters">Paramètres</label>
<div class="field-body"> <div class="panel-block main-parameters-body">
<div class="field has-addons"> <div class="container wide-label">
<p class="control is-expanded"> <div class="field is-horizontal">
<input name="battery-capacity" class="input" type="number" min="1" value="700"/> <div class="field-label is-normal">
</p> <label class="label">Capacité de la batterie</label>
<p class="control"> </div>
<a class="button is-static">Wh</a> <div class="field-body">
</p> <div class="field has-addons">
<p class="control is-expanded">
<input name="battery-capacity" class="input" type="number" min="1" value="700"/>
</p>
<p class="control">
<a class="button is-static">Wh</a>
</p>
</div>
</div>
</div> </div>
</div>
</div>
<div class="field is-horizontal"> <div class="field is-horizontal">
<div class="field-label is-normal"> <div class="field-label is-normal">
<label class="label">Poids (bagages + passagers)</label> <label class="label">Poids passagers et chargement</label>
</div> </div>
<div class="field-body"> <div class="field-body">
<div class="field has-addons"> <div class="field has-addons">
<p class="control is-expanded"> <p class="control is-expanded">
<input name="additional-weight" class="input" type="number" min="1" value="130"/> <input name="additional-weight" class="input" type="number" min="1" value="70"/>
</p> </p>
<p class="control"> <p class="control">
<a class="button is-static">kg</a> <a class="button is-static">kg</a>
</p> </p>
</div>
</div>
</div> </div>
</div>
</div>
<div class="field is-horizontal"> <div class="field is-horizontal">
<div class="field-label is-normal"> <div class="field-label is-normal">
<label class="label">Zone climatique</label> <label class="label">Zone climatique</label>
</div> </div>
<div class="field-body"> <div class="field-body">
<div class="field has-addons"> <div class="field has-addons">
<div class="control is-expanded"> <div class="control is-expanded">
<div class="select is-fullwidth"> <div class="select is-fullwidth">
<select class="zone-selector"> <select class="zone-selector">
<option>H1a</option> <option>H1a</option>
<option>H1b</option> <option>H1b</option>
<option>H1c</option> <option>H1c</option>
<option>H2a</option> <option>H2a</option>
<option>H2b</option> <option>H2b</option>
<option>H2c</option> <option>H2c</option>
<option>H2d</option> <option>H2d</option>
<option>H3</option> <option>H3</option>
</select> </select>
</div>
</div>
<p class="control">
<a class="button" data-activate-modal="zones-map-modal">Choix sur la carte...</a>
</p>
</div> </div>
</div> </div>
<p class="control">
<a class="button" data-activate-modal="zones-map-modal">Choix sur la carte...</a>
</p>
</div> </div>
</div>
</div>
<div class="field is-horizontal"> <div class="field is-horizontal">
<div class="field-label is-normal"> <div class="field-label is-normal">
<label class="label">Distance quotidienne</label> <label class="label">Distance quotidienne</label>
</div> </div>
<div class="field-body"> <div class="field-body">
<div class="field has-addons"> <div class="field has-addons">
<p class="control is-expanded"> <p class="control is-expanded">
<input name="daily-distance" class="input" type="number" min="1" value="10"> <input name="daily-distance" class="input" type="number" min="1" value="10">
</p> </p>
<p class="control"> <p class="control">
<a class="button is-static">km/jour</a> <a class="button is-static">km/jour</a>
</p> </p>
</div>
</div>
</div> </div>
</div>
</div>
<div class="field is-horizontal"> <div class="field is-horizontal">
<div class="field-label is-normal"> <div class="field-label is-normal">
<label class="label">Dénivelé positif quotidien</label> <label class="label">Dénivelé positif quotidien</label>
</div> </div>
<div class="field-body"> <div class="field-body">
<div class="field has-addons"> <div class="field has-addons">
<p class="control is-expanded"> <p class="control is-expanded">
<input name="daily-elevation" class="input" type="number" min="0" value="100"> <input name="daily-elevation" class="input" type="number" min="0" value="100">
</p> </p>
<p class="control"> <p class="control">
<a class="button is-static">m/jour</a> <a class="button is-static">m/jour</a>
</p> </p>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="field is-horizontal"> <!-- ------------------- Advanced parameters ---------------------- -->
<div class="field-label is-normal"> <!-- set input type to "radio" to change behavior (accordion) -->
</div> <input type="checkbox" id="advanced-parameters" name="parameters-accordion">
<div class="field-body"> <div class="panel">
<div class="field"> <label class="panel-heading" for="advanced-parameters">Paramètres avancés</label>
<p class="control is-expanded"> <div class="panel-block advanced-parameters-body">
<a class="simulate-button button is-primary is-fullwidth">Simuler</a> <div class="container wide-label">
</p> <div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Poids véhicule</label>
</div>
<div class="field-body">
<div class="field has-addons">
<p class="control is-expanded">
<input name="empty-weight" class="input" type="number" min="1" value="80"/>
</p>
<p class="control">
<a class="button is-static">kg</a>
</p>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Poids pilote</label>
</div>
<div class="field-body">
<div class="field has-addons">
<p class="control is-expanded">
<input name="driver-weight" class="input" type="number" min="1" value="70"/>
</p>
<p class="control">
<a class="button is-static">kg</a>
</p>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Puissance pilote (pédalage)</label>
</div>
<div class="field-body">
<div class="field has-addons">
<p class="control is-expanded">
<input name="human-power" class="input" type="number" min="0" value="100"/>
</p>
<p class="control">
<a class="button is-static">W</a>
</p>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Vitesse moyenne (hors arrêts)</label>
</div>
<div class="field-body">
<div class="field has-addons">
<p class="control is-expanded">
<input name="average-speed" class="input" type="number" min="1" value="20"/>
</p>
<p class="control">
<a class="button is-static">km/h</a>
</p>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<a class="simulate-button button is-primary is-fullwidth">Simuler</a>
</div> </div>
<div class="simulation-results is-hidden"> <div class="simulation-results is-hidden">

32
simulator/src/simulator.ts

@ -9,7 +9,13 @@ namespace Simulator {
emptyVehicleWeight: number = 80; // kg emptyVehicleWeight: number = 80; // kg
driverWeight: number = 60; // kg driverWeight: number = 60; // kg
additionalWeight: number; // additional weight, not counting cyclist and empty vehicle weight, in kg additionalWeight: number = 0; // additional weight, not counting cyclist and empty vehicle weight, in kg
humanPower: number = 100; // W
averageSpeed: number = 20; // average speed in km/h, when the vehicle is moving (this is important, because driver does not provide power when stopped)
nominalMotorPower: number = 250; // W
assistanceSpeedLimit: number = 25; // km/h
motorConsumption(distance: number, ascendingElevation: number): number { motorConsumption(distance: number, ascendingElevation: number): number {
const g = 9.8; const g = 9.8;
@ -18,13 +24,33 @@ namespace Simulator {
potentialEnergy = potentialEnergy / 3600; // convert joules to watt-hour potentialEnergy = potentialEnergy / 3600; // convert joules to watt-hour
// empirical measures // empirical measures
let baseConsumption = 13; // in Wh/km let baseConsumption = 13; // in Wh/km, when human power is 0
let maxWeight = 300; // in kg let maxWeight = 300; // in kg
let additionalConsumptionAtMaxWeight = 5; // in Wh/km (without accounting for ascending elevation, only accelerations and additional friction) let additionalConsumptionAtMaxWeight = 5; // in Wh/km (without accounting for ascending elevation, only accelerations and additional friction)
let weightRelatedConsumption = MathUtils.clamp(totalWeight * additionalConsumptionAtMaxWeight / maxWeight, 0, additionalConsumptionAtMaxWeight); let weightRelatedConsumption = MathUtils.clamp(totalWeight * additionalConsumptionAtMaxWeight / maxWeight, 0, additionalConsumptionAtMaxWeight);
return distance * (baseConsumption + weightRelatedConsumption) + potentialEnergy; let motorPowerLimit = this.nominalMotorPower;
let tripDuration = (distance * (baseConsumption + weightRelatedConsumption) + potentialEnergy) / (motorPowerLimit + this.humanPower);
let actualSpeed = distance / tripDuration;
console.log("Max vehicle speed, according to available power: " + (Math.round(actualSpeed*10)/10) + " km/h")
if(actualSpeed > this.assistanceSpeedLimit) {
tripDuration = distance / this.assistanceSpeedLimit
motorPowerLimit = Math.max(0, ((distance * (baseConsumption + weightRelatedConsumption) + potentialEnergy) - tripDuration * this.humanPower) / tripDuration);
tripDuration = (distance * (baseConsumption + weightRelatedConsumption) + potentialEnergy) / (motorPowerLimit + this.humanPower);
actualSpeed = distance / tripDuration;
console.log("Vehicle speed clamped by assistance speed limit, motor power limited to: " + Math.round(motorPowerLimit) + " W")
}
if(actualSpeed > this.averageSpeed) {
actualSpeed = this.averageSpeed;
tripDuration = distance / actualSpeed;
}
let humanEnergy = tripDuration * this.humanPower;
return Math.max(motorPowerLimit * tripDuration, distance * (baseConsumption + weightRelatedConsumption) + potentialEnergy - humanEnergy);
} }
solarPower(irradiance: number): number { solarPower(irradiance: number): number {

Loading…
Cancel
Save