Goal: Build a road load model for an electric two-wheeler and use it to answer one question: how much force and power does the motor need at each speed? Every later post in this Simulation series reuses this model.
The Physics
To move at speed v, the wheel must overcome four forces:
- Rolling resistance: Froll = Crr × m × g × cos(θ)
- Aerodynamic drag: Faero = 0.5 × ρ × Cd × A × v²
- Grade: Fgrade = m × g × sin(θ)
- Inertia: Finertia = (m + mrot) × a
Total tractive force is the sum of the four. Power at the wheel is force times speed. Power at the motor shaft is wheel power divided by drivetrain efficiency (ηdt).
Assumptions
| Modeled | Ignored |
|---|---|
| Constant rolling resistance coefficient | Speed dependence of Crr and tyre pressure effects |
| Quadratic aero drag in still air | Headwind and rider posture changes |
| Constant drivetrain efficiency | Efficiency varying with torque and speed (covered in a later post) |
| Rotating inertia as an equivalent extra mass | Detailed wheel and rotor inertia split |
| Straight-line motion on a constant grade | Cornering, wheel slip, load transfer |
Parameters
| Symbol | Value | Unit | Note |
|---|---|---|---|
| mveh | 110 | kg | Kerb mass including battery (typical electric scooter) |
| mrider | 75 | kg | Rider |
| mrot | 6 | kg | Equivalent mass of wheels and rotor |
| Crr | 0.015 | - | Typical road scooter tyres on asphalt |
| Cd | 0.90 | - | Upright rider and scooter body |
| A | 0.65 | m² | Frontal area including rider |
| ρ | 1.18 | kg/m³ | About 30 °C at 600 m altitude |
| ηdt | 0.92 | - | Motor shaft to wheel |
| g | 9.81 | m/s² | Gravitational acceleration |
These are representative values, not a specific vehicle. Replace them with your own and rerun the script.
The Code
Copy this into a file called road_load.py. It needs only NumPy and Matplotlib.
"""Road load model for an electric 2-wheeler. Mobility Decoder, Simulation #1."""
import numpy as np
import matplotlib.pyplot as plt
# ---- Parameters (SI units) ----
m_veh, m_rider = 110.0, 75.0 # kg, kerb mass incl. battery, and rider
m = m_veh + m_rider # total mass, kg
m_rot = 6.0 # kg, equivalent mass of rotating parts (wheels, rotor)
g, rho = 9.81, 1.18 # m/s^2, air density in kg/m^3 (about 30 C, 600 m altitude)
Crr = 0.015 # rolling resistance coefficient
Cd, A = 0.90, 0.65 # drag coefficient, frontal area in m^2 (rider included)
eta_dt = 0.92 # drivetrain efficiency, motor shaft to wheel
def road_load(v, grade=0.0, a=0.0):
"""Tractive force (N) at the wheel for speed v (m/s), grade (rise/run), accel a (m/s^2)."""
theta = np.arctan(grade)
f_roll = Crr * m * g * np.cos(theta)
f_aero = 0.5 * rho * Cd * A * v**2
f_grade = m * g * np.sin(theta)
f_inertia = (m + m_rot) * a
return f_roll + f_aero + f_grade + f_inertia
v_kmh = np.linspace(0, 80, 161)
v = v_kmh / 3.6
grades = [0.0, 0.05, 0.10, 0.15]
fig, ax = plt.subplots(1, 2, figsize=(11, 4.2))
for gr in grades:
F = road_load(v, grade=gr)
ax[0].plot(v_kmh, F, label=f"grade {gr*100:.0f}%")
ax[1].plot(v_kmh, F * v / 1000, label=f"grade {gr*100:.0f}%")
F_acc = road_load(v, a=1.0)
ax[0].plot(v_kmh, F_acc, "k--", label="flat, a = 1 m/s$^2$")
ax[1].plot(v_kmh, F_acc * v / 1000, "k--", label="flat, a = 1 m/s$^2$")
ax[0].set(xlabel="Speed (km/h)", ylabel="Tractive force at wheel (N)", title="Force needed vs speed")
ax[1].set(xlabel="Speed (km/h)", ylabel="Power at wheel (kW)", title="Power needed vs speed")
for a_ in ax: a_.grid(alpha=0.3); a_.legend()
plt.tight_layout(); plt.savefig("road_load.png", dpi=150)
# ---- Quick numbers for the post ----
for vk in (30, 40, 60, 80):
vv = vk / 3.6
print(f"{vk} km/h flat: F={road_load(vv):.1f} N, wheel P={road_load(vv)*vv/1000:.2f} kW, "
f"shaft P={road_load(vv)*vv/1000/eta_dt:.2f} kW")
for gr in grades[1:]:
vv = 30 / 3.6
print(f"30 km/h, {gr*100:.0f}% grade: F={road_load(vv, gr):.0f} N, shaft P={road_load(vv, gr)*vv/1000/eta_dt:.2f} kW")
vv = 40/3.6
print(f"40 km/h, a=1: shaft P={road_load(vv, 0, 1.0)*vv/1000/eta_dt:.2f} kW")
# top speed for a given shaft power
for P in (3.0, 4.0, 6.0):
vs = np.linspace(1, 40, 4000)
Pw = road_load(vs) * vs / 1000 / eta_dt
print(f"Shaft power {P} kW -> top speed {vs[np.argmin(abs(Pw-P))]*3.6:.0f} km/h")
# speed where aero force equals rolling force
print("Crossover speed (aero = rolling):", round(np.sqrt(2*Crr*m*g/(rho*Cd*A))*3.6,1), "km/h")Download:road_load.py
Results

Left: tractive force at the wheel versus speed. Right: power at the wheel versus speed. Solid lines are constant speed at 0, 5, 10 and 15% grade. The dashed line is a flat road with 1 m/s² acceleration.
What the Plots Tell Us
- At low speed, force is almost flat. Rolling resistance and grade dominate. On the flat, the scooter needs only about 27 N at standstill, while a 15% slope needs about 295 N.
- At high speed, drag takes over. Aero force grows with v², so power grows with v³. Aero drag equals rolling resistance at about 32 km/h with these parameters.
- Hills cost more than speed at city pace. At 30 km/h, a 10% grade needs 2.10 kW and a 15% grade needs 2.90 kW, versus 0.46 kW on the flat.
- Acceleration is the real power hog. Accelerating at 1 m/s² at 40 km/h needs 3.15 kW, almost four times the 0.84 kW needed to hold that speed.
Flat road, constant speed:
| Speed | Force at wheel | Power at wheel | Power at motor shaft |
|---|---|---|---|
| 30 km/h | 51 N | 0.43 kW | 0.46 kW |
| 40 km/h | 70 N | 0.78 kW | 0.84 kW |
| 60 km/h | 123 N | 2.05 kW | 2.23 kW |
| 80 km/h | 198 N | 4.39 kW | 4.77 kW |
Top speed from available shaft power (flat road): 3 kW gives about 67 km/h, 4 kW gives about 75 km/h, and 6 kW gives about 87 km/h.
Sanity Check
Electric scooters with roughly 4 kW peak motors are typically rated for top speeds of about 70 to 80 km/h, which matches the model's 75 km/h. If you have a datasheet for your own vehicle, enter its mass and top speed and check that the model agrees within about 10%. Differences usually come from Cd × A, the least certain parameter here.
Limitations
- Crr is not constant in reality. It rises with speed and falls with tyre pressure.
- Peak motor power is not continuous power. A motor can reach top speed briefly but may overheat if held there. The thermal posts later in the series cover this.
- There is no wheel slip or traction limit, so 1 m/s² may not be achievable on wet roads.
What Is Next?
Post 2 uses this model to size motor power for a target top speed and gradeability, and computes the 0 to 40 km/h acceleration time.
