Added advanced parameters
Added human power (constant power)
This commit is contained in:
parent
a14d37582c
commit
fa6a4f5b09
@ -80,3 +80,18 @@ input[type=number] {
|
||||
.battery-charge-graph text {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,11 @@
|
||||
|
||||
interface SimulationParameters {
|
||||
batteryCapacity: number,
|
||||
emptyVehicleWeight: number,
|
||||
driverWeight: number,
|
||||
additionalWeight: number,
|
||||
humanPower: number,
|
||||
averageSpeed: number,
|
||||
climateZone: string,
|
||||
dailyDistance: number,
|
||||
dailyAscendingElevation: number
|
||||
@ -13,7 +17,11 @@ function runSimulation(parameters: SimulationParameters): Simulator.SimulationRe
|
||||
|
||||
let vehicle = new Simulator.Vehicle();
|
||||
vehicle.batteryCapacity = parameters.batteryCapacity;
|
||||
vehicle.emptyVehicleWeight = parameters.emptyVehicleWeight;
|
||||
vehicle.driverWeight= parameters.driverWeight;
|
||||
vehicle.additionalWeight = parameters.additionalWeight;
|
||||
vehicle.humanPower = parameters.humanPower;
|
||||
vehicle.averageSpeed = parameters.averageSpeed;
|
||||
let solarIrradiance: number[] = climateData[parameters.climateZone.toLowerCase()];
|
||||
let planning = new Simulator.OutingPlanning(parameters.dailyDistance, parameters.dailyAscendingElevation);
|
||||
|
||||
@ -56,7 +64,11 @@ function initializeSimulator(container: HTMLElement) {
|
||||
container.querySelector('.simulate-button').addEventListener('click', e => {
|
||||
let parameters: SimulationParameters = {
|
||||
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),
|
||||
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,
|
||||
dailyDistance: Number((<HTMLInputElement>container.querySelector('[name=daily-distance]')).value),
|
||||
dailyAscendingElevation: Number((<HTMLInputElement>container.querySelector('[name=daily-elevation]')).value),
|
||||
|
@ -1,110 +1,183 @@
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<div class="simulation-parameters">
|
||||
<div class="wide-label">
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label is-normal">
|
||||
<label class="label">Capacité de la batterie</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<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 class="field is-horizontal">
|
||||
<div class="field-label is-normal">
|
||||
<label class="label">Poids (bagages + passagers)</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field has-addons">
|
||||
<p class="control is-expanded">
|
||||
<input name="additional-weight" class="input" type="number" min="1" value="130"/>
|
||||
</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">Zone climatique</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">
|
||||
<div class="select is-fullwidth">
|
||||
<select class="zone-selector">
|
||||
<option>H1a</option>
|
||||
<option>H1b</option>
|
||||
<option>H1c</option>
|
||||
<option>H2a</option>
|
||||
<option>H2b</option>
|
||||
<option>H2c</option>
|
||||
<option>H2d</option>
|
||||
<option>H3</option>
|
||||
</select>
|
||||
|
||||
<!-- -------------- main parameters ---------------- -->
|
||||
<input type="checkbox" id="main-parameters" name="parameters-accordion" checked="checked" disabled="disabled">
|
||||
<div class="panel">
|
||||
<label class="panel-heading" for="main-parameters">Paramètres</label>
|
||||
<div class="panel-block main-parameters-body">
|
||||
<div class="container wide-label">
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label is-normal">
|
||||
<label class="label">Capacité de la batterie</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<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>
|
||||
<p class="control">
|
||||
<a class="button" data-activate-modal="zones-map-modal">Choix sur la carte...</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label is-normal">
|
||||
<label class="label">Poids passagers et chargement</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field has-addons">
|
||||
<p class="control is-expanded">
|
||||
<input name="additional-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">Zone climatique</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">
|
||||
<div class="select is-fullwidth">
|
||||
<select class="zone-selector">
|
||||
<option>H1a</option>
|
||||
<option>H1b</option>
|
||||
<option>H1c</option>
|
||||
<option>H2a</option>
|
||||
<option>H2b</option>
|
||||
<option>H2c</option>
|
||||
<option>H2d</option>
|
||||
<option>H3</option>
|
||||
</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 class="field is-horizontal">
|
||||
<div class="field-label is-normal">
|
||||
<label class="label">Distance quotidienne</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field has-addons">
|
||||
<p class="control is-expanded">
|
||||
<input name="daily-distance" class="input" type="number" min="1" value="10">
|
||||
</p>
|
||||
<p class="control">
|
||||
<a class="button is-static">km/jour</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label is-normal">
|
||||
<label class="label">Dénivelé positif quotidien</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field has-addons">
|
||||
<p class="control is-expanded">
|
||||
<input name="daily-elevation" class="input" type="number" min="0" value="100">
|
||||
</p>
|
||||
<p class="control">
|
||||
<a class="button is-static">m/jour</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label is-normal">
|
||||
<label class="label">Distance quotidienne</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field has-addons">
|
||||
<p class="control is-expanded">
|
||||
<input name="daily-distance" class="input" type="number" min="1" value="10">
|
||||
</p>
|
||||
<p class="control">
|
||||
<a class="button is-static">km/jour</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- ------------------- Advanced parameters ---------------------- -->
|
||||
<!-- set input type to "radio" to change behavior (accordion) -->
|
||||
<input type="checkbox" id="advanced-parameters" name="parameters-accordion">
|
||||
<div class="panel">
|
||||
<label class="panel-heading" for="advanced-parameters">Paramètres avancés</label>
|
||||
<div class="panel-block advanced-parameters-body">
|
||||
<div class="container wide-label">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label is-normal">
|
||||
<label class="label">Dénivelé positif quotidien</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field has-addons">
|
||||
<p class="control is-expanded">
|
||||
<input name="daily-elevation" class="input" type="number" min="0" value="100">
|
||||
</p>
|
||||
<p class="control">
|
||||
<a class="button is-static">m/jour</a>
|
||||
</p>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label is-normal">
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<p class="control is-expanded">
|
||||
<a class="simulate-button button is-primary is-fullwidth">Simuler</a>
|
||||
</p>
|
||||
|
||||
<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>
|
||||
|
||||
<a class="simulate-button button is-primary is-fullwidth">Simuler</a>
|
||||
</div>
|
||||
|
||||
<div class="simulation-results is-hidden">
|
||||
|
@ -9,7 +9,13 @@ namespace Simulator {
|
||||
|
||||
emptyVehicleWeight: number = 80; // 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 {
|
||||
const g = 9.8;
|
||||
@ -18,13 +24,33 @@ namespace Simulator {
|
||||
potentialEnergy = potentialEnergy / 3600; // convert joules to watt-hour
|
||||
|
||||
// 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 additionalConsumptionAtMaxWeight = 5; // in Wh/km (without accounting for ascending elevation, only accelerations and additional friction)
|
||||
|
||||
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 {
|
||||
|
Loading…
Reference in New Issue
Block a user