--[[
Latest Lua version tested - 5.3.6
E-bike Calculator - Print estimated speed and range of an ebike.
Written in 2017 by Luna Rose
[email protected]
To the extent possible under law, the author(s) have dedicated all
copyright and related and neighboring rights to this software to the public
domain worldwide. This software is distributed without any warranty.
You should have received a copy of the CC0 Public Domain Dedication along
with this software. If not, see
<
http://creativecommons.org/publicdomain/zero/1.0/>
--]]
--[[ ############ User Data ############
Modify the variables in this section to match your ebike configuration.
If the priority is loaded touring with lots of hills then a 52V system should
be more than sufficient with the right gearing. If maximum speed is the main
priority then 72V would be best, offering a 50% increas in speed over 48V.
--]]
-- Motor specifications
local kv = 73 -- Motor RPM constant (kV, aka "RPM per Volt"
local reduction = 21.9 -- Internal gear reduction
local efficiency = .85 -- Motor efficiency in percent (from 0 to 1)
local amp_max = 50 -- Maximum rated current capacity
local watt_max = 3000 -- Maximum rated power capacity
-- Battery and controller
local volt_nom = 50.4 -- Nominal voltage of the battery
local amp_hours = 20.0 -- Nominal amp-hour capacity of the battery
local amp_out_max = 20 -- Maximum current draw for battery/controller
-- Drivetrain
local crank_length = 172.5 -- Length of pedal cranks in millimeters
local radius = 347 -- Radius of tires in millimeters
local chainring = 36 -- Tooth count of the chainring
local cadence = 60 -- Average typical cadence of the rider
local cassette = { -- Tooth counts for each gear on the cassette
11,
13,
16,
20,
24,
28,
32,
36,
40,
46,
52,
}
-- ############ End of User Data ############
-- Colorized output
local red = "\027[31;1m"
local green = "\027[32;1m"
local yellow = "\027[33;1m"
local white = "\027[37;1m"
local plain = "\027[0m"
local cyan = "\027[36;1m"
-- System stats
local power = volt_nom * amp_out_max
local watt_hr = volt_nom * amp_hours
local rpm = (kv * volt_nom / reduction) * .7 -- Magic number for loaded weight
os.execute("clear") ; io.write(white, "Volts\tAmps\tWatts\tWh\tRPM\n")
local disp_a, disp_w, disp_wh, disp_rpm
disp_a = (amp_out_max >= amp_max) and red .. amp_out_max or green .. amp_out_max
disp_w = (power >= watt_max) and red .. power or green .. power
disp_wh = (power >= watt_hr) and red .. watt_hr or green .. watt_hr
disp_rpm = (rpm >= 100) and red .. rpm or green .. rpm
io.write(yellow, volt_nom, "\t", disp_a, "\t", disp_w, "\t", disp_wh, "\t",
disp_rpm, '\n')
-- Drivetrain display
io.write(white, "\nRPM\tTeeth\tCranks\n", plain, cadence, '\t', chainring,
'\t', crank_length)
--[[ Gain ratios and speed estimates
Estimated speed uses the calculated motor RPM on all gears with a loaded RPM
reduction factor of 0.7 to account for overall weight, though actual RPM may
be higher. Above 28MPH the real speed is likely much lower since this is not
a physically based model.
The required power is based on the square of the speed which for the most part
seems to match real world values up to about 28MPH. At higher speeds the real
values would be higher than calculated due to additional resistive forces.
Since anything above 30 isn't ideal for a bicycle, no attempt will be made to
refine the calculator.
--]]
local ratio = radius / crank_length
local cc = 2 * 3.14 * crank_length / 1000 -- Circumfrence of pedal travel
io.write("\n\n", cyan, "Gear\tGain\tMotor\tPedal\tWatts\n", plain)
for i = 1, #cassette do
local gain = ("%.4g"):format(ratio * chainring / cassette[i])
local motor = ("%.3g"):format(gain * cc * rpm * 60 / 1000 * .6214)
local pedal = ("%.3g"):format(gain * cc * cadence * 60 / 1000 * .6214)
local watts = ("%.4g"):format(motor * motor)
io.write(cassette[i], "\t", gain, "\t", motor, "\t", pedal, "\t",
watts, '\n')
end
--[[ Range estimates
Real world range should be greater than what is calculated here since
actual continuous power will vary with pedaling, hence these are minimums.
--]]
local range_400 = ("%.3g"):format(watt_hr / 20)
local range_750 = ("%.3g"):format(watt_hr / 28)
local range_1000 = ("%.3g"):format(watt_hr / 31.7)
io.write(green, "\nRange @ 400W: ", plain, range_400, " miles\n",
yellow, "Range @ 750W: ", plain, range_750, " miles\n",
red, "Range @ 1000W: ", plain, range_1000, " miles\n")