driving_physics 0.0.0.2 → 0.0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +12 -1
- data/Rakefile +11 -4
- data/VERSION +1 -1
- data/demo/car.rb +165 -18
- data/demo/disk.rb +83 -0
- data/demo/gearbox.rb +52 -0
- data/demo/motor.rb +141 -0
- data/demo/powertrain.rb +47 -0
- data/demo/scalar_force.rb +41 -15
- data/demo/tire.rb +80 -162
- data/demo/vector_force.rb +46 -16
- data/lib/driving_physics/car.rb +77 -248
- data/lib/driving_physics/cli.rb +51 -0
- data/lib/driving_physics/disk.rb +185 -0
- data/lib/driving_physics/gearbox.rb +109 -0
- data/lib/driving_physics/imperial.rb +6 -0
- data/lib/driving_physics/motor.rb +103 -0
- data/lib/driving_physics/power.rb +20 -0
- data/lib/driving_physics/powertrain.rb +50 -0
- data/lib/driving_physics/scalar_force.rb +6 -3
- data/lib/driving_physics/tire.rb +90 -258
- data/lib/driving_physics/vector_force.rb +15 -2
- data/lib/driving_physics.rb +2 -0
- data/test/disk.rb +129 -0
- data/test/scalar_force.rb +7 -5
- data/test/tire.rb +144 -88
- data/test/vector_force.rb +7 -1
- metadata +12 -5
- data/demo/wheel.rb +0 -84
- data/lib/driving_physics/wheel.rb +0 -191
- data/test/car.rb +0 -156
- data/test/wheel.rb +0 -177
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef486c39982798a7331f49f601e648d520a65977161180631cc4c5d3340a9011
|
4
|
+
data.tar.gz: 9e30b74ae34cb77a711d5d9631983b0c070872900123a1dde2d3dd03bdab854b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 46eaf512dad64ca39ec20451d7acce1b1819ecd64125ee858f92c26e5edd7b0ec0e7ef01af8f73daa0d37868e0ffd76bd3b29478093219cd906d8d9e38026bcd
|
7
|
+
data.tar.gz: ae2e01ae8e7986c9d2e659ff5a0934ed5359e20cf2757d7724118bb59e0fb0199180249815f2f28f55f48a2336511a0b43f0d6fab042ed5d20a5ab6a897baef1
|
data/README.md
CHANGED
@@ -6,7 +6,18 @@ Physical simulation of how to make a car go around a track quickly, in pure
|
|
6
6
|
Ruby with minimal dependencies. Physics from first principles, often
|
7
7
|
using `Vector` class from `matrix.rb` in stdlib.
|
8
8
|
|
9
|
-
|
9
|
+
## Work In Progress
|
10
|
+
|
11
|
+
This is very much a **Work In Progress**. Implemented so far:
|
12
|
+
|
13
|
+
* Spinning: rotational inertia and friction / hysteresis
|
14
|
+
* Tires: traction force via friction and the normal force
|
15
|
+
* Vectors: 2D Vector forces and 3D Vector torques
|
16
|
+
* Motor: torque curves, rotating mass with friction
|
17
|
+
* Gearbox: gear ratios, final drive, rotating mass with friction
|
18
|
+
* Powertrain: combines motor and gearbox
|
19
|
+
* Car: 4 tires, powertrain, extra mass presents a load to the powertrain
|
20
|
+
* Acceleration: via drive traction from "first principles"
|
10
21
|
|
11
22
|
## Rationale
|
12
23
|
|
data/Rakefile
CHANGED
@@ -32,10 +32,13 @@ rescue LoadError
|
|
32
32
|
end
|
33
33
|
|
34
34
|
begin
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
# need to stop looking in old/ and also the scoring seems wack
|
36
|
+
if false
|
37
|
+
require 'flay_task'
|
38
|
+
FlayTask.new do |t|
|
39
|
+
t.dirs = ['lib']
|
40
|
+
t.verbose = true
|
41
|
+
end
|
39
42
|
end
|
40
43
|
rescue LoadError
|
41
44
|
warn 'flay_task unavailable'
|
@@ -49,6 +52,10 @@ rescue LoadError
|
|
49
52
|
warn "roodi_task unavailable"
|
50
53
|
end
|
51
54
|
|
55
|
+
#
|
56
|
+
# GEM BUILD / PUBLISH
|
57
|
+
#
|
58
|
+
|
52
59
|
begin
|
53
60
|
require 'buildar'
|
54
61
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.1.2
|
data/demo/car.rb
CHANGED
@@ -1,28 +1,175 @@
|
|
1
1
|
require 'driving_physics/car'
|
2
|
+
require 'driving_physics/imperial'
|
3
|
+
require 'driving_physics/cli'
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
-
env = DP::Environment.new
|
6
|
-
car = DP::Car.new(env)
|
7
|
-
car.add_fuel 10
|
8
|
-
duration = 120 # seconds
|
5
|
+
include DrivingPhysics
|
9
6
|
|
7
|
+
env = Environment.new
|
10
8
|
puts env
|
11
|
-
|
9
|
+
|
10
|
+
tire = Tire.new(env)
|
11
|
+
motor = Motor.new(env)
|
12
|
+
gearbox = Gearbox.new(env)
|
13
|
+
powertrain = Powertrain.new(motor, gearbox)
|
14
|
+
car = Car.new(tire: tire, powertrain: powertrain) { |c|
|
15
|
+
c.body_mass = 850.0
|
16
|
+
c.frontal_area = 2.5
|
17
|
+
c.cd = 0.5
|
18
|
+
}
|
12
19
|
puts car
|
20
|
+
CLI.pause
|
21
|
+
|
22
|
+
duration = 120
|
23
|
+
|
24
|
+
acc = 0.0
|
25
|
+
speed = 0.0
|
26
|
+
dist = 0.0
|
27
|
+
|
28
|
+
tire_alpha = 0.0
|
29
|
+
tire_omega = 0.0
|
30
|
+
tire_theta = 0.0
|
31
|
+
|
32
|
+
crank_alpha = 0.0
|
33
|
+
crank_omega = 0.0
|
34
|
+
crank_theta = 0.0
|
35
|
+
|
36
|
+
start = Timer.now
|
37
|
+
paused = 0.0
|
38
|
+
num_ticks = duration * env.hz + 1
|
39
|
+
|
40
|
+
clutch = :ok
|
41
|
+
phase = :ignition
|
42
|
+
flag = false
|
43
|
+
rpm = 0
|
44
|
+
puts <<EOF
|
45
|
+
|
46
|
+
#
|
47
|
+
# IGNITION
|
48
|
+
#
|
49
|
+
|
50
|
+
EOF
|
51
|
+
|
52
|
+
num_ticks.times { |i|
|
53
|
+
if phase == :ignition
|
54
|
+
# ignition phase
|
55
|
+
crank_alpha = motor.alpha(motor.starter_torque, omega: crank_omega)
|
56
|
+
crank_omega += crank_alpha * env.tick
|
57
|
+
crank_theta += crank_omega * env.tick
|
58
|
+
|
59
|
+
rpm = DrivingPhysics.rpm(crank_omega)
|
60
|
+
|
61
|
+
if i % 100 == 0 or rpm > motor.idle_rpm
|
62
|
+
puts Timer.display(i)
|
63
|
+
puts format("%d rad %d rad/s %d rad/s/s",
|
64
|
+
crank_theta, crank_omega, crank_alpha)
|
65
|
+
puts format("%d RPM %d Nm starter torque", rpm, motor.starter_torque)
|
66
|
+
puts
|
67
|
+
end
|
68
|
+
|
69
|
+
if rpm > motor.idle_rpm
|
70
|
+
car.gear = 1
|
71
|
+
car.throttle = 1.0
|
72
|
+
phase = :running
|
73
|
+
|
74
|
+
puts <<EOF
|
75
|
+
|
76
|
+
#
|
77
|
+
# RUNNING
|
78
|
+
#
|
79
|
+
|
80
|
+
EOF
|
81
|
+
end
|
82
|
+
elsif phase == :running
|
83
|
+
# track crank_alpha/omega/theta
|
84
|
+
|
85
|
+
# cut throttle after 60 s
|
86
|
+
car.throttle = 0 if i > 60 * env.hz and car.throttle == 1.0
|
87
|
+
|
88
|
+
ar = car.air_force(speed)
|
89
|
+
rr = car.tire_rolling_force(tire_omega)
|
90
|
+
rf = car.tire_rotational_force(tire_omega)
|
91
|
+
|
92
|
+
# note, this fails if we're in neutral
|
93
|
+
force = car.drive_force(rpm) + ar + rr + rf
|
94
|
+
|
95
|
+
ir = car.tire_inertial_force(force)
|
96
|
+
force += ir
|
13
97
|
|
14
|
-
|
98
|
+
acc = DrivingPhysics.acc(force, car.total_mass)
|
99
|
+
speed += acc * env.tick
|
100
|
+
dist += speed * env.tick
|
15
101
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
102
|
+
tire_alpha = acc / car.tire.radius
|
103
|
+
tire_omega += tire_alpha * env.tick
|
104
|
+
tire_theta += tire_omega * env.tick
|
105
|
+
|
106
|
+
crank_alpha = tire_alpha / car.powertrain.gearbox.ratio
|
107
|
+
crank_omega += crank_alpha * env.tick
|
108
|
+
crank_theta += crank_omega * env.tick
|
109
|
+
|
110
|
+
axle_torque = car.powertrain.axle_torque(rpm)
|
111
|
+
crank_torque = car.powertrain.motor.torque(rpm)
|
112
|
+
|
113
|
+
if flag or (i < 5000 and i % 100 == 0) or (i % 1000 == 0)
|
114
|
+
puts Timer.display(i)
|
115
|
+
puts format(" Tire: %.1f r/s/s %.2f r/s %.3f r",
|
116
|
+
tire_alpha, tire_omega, tire_theta)
|
117
|
+
puts format(" Car: %.1f m/s/s %.2f m/s %.3f m (%.1f MPH)",
|
118
|
+
acc, speed, dist, Imperial.mph(speed))
|
119
|
+
puts format(" Motor: %d RPM %.1f Nm", rpm, crank_torque)
|
120
|
+
puts format(" Axle: %.1f Nm (%d N) Net Force: %.1f N",
|
121
|
+
axle_torque, car.drive_force(rpm), force)
|
122
|
+
puts "Resist: " + format(%w[Air Roll Spin Inertial].map { |s|
|
123
|
+
"#{s}: %.1f N"
|
124
|
+
}.join(' '), ar, rr, rf, ir)
|
125
|
+
puts
|
126
|
+
flag = false
|
22
127
|
end
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
128
|
+
|
129
|
+
# tire_omega determines new rpm
|
130
|
+
new_rpm = car.powertrain.crank_rpm(tire_omega)
|
131
|
+
new_clutch, proportion = car.powertrain.gearbox.match_rpms(rpm, new_rpm)
|
132
|
+
|
133
|
+
if new_clutch != clutch
|
134
|
+
flag = true
|
135
|
+
puts format("Clutch: [%s] %d RPM is %.1f%% from %d RPM",
|
136
|
+
new_clutch, new_rpm, proportion * 100, rpm)
|
137
|
+
clutch = new_clutch
|
138
|
+
paused += CLI.pause
|
139
|
+
end
|
140
|
+
|
141
|
+
case new_clutch
|
142
|
+
when :ok
|
143
|
+
rpm = new_rpm
|
144
|
+
when :mismatch
|
145
|
+
flag = true
|
146
|
+
puts '#'
|
147
|
+
puts '# LURCH!'
|
148
|
+
puts '#'
|
149
|
+
puts
|
150
|
+
rpm = new_rpm
|
151
|
+
end
|
152
|
+
next_gear = car.powertrain.gearbox.next_gear(rpm)
|
153
|
+
if next_gear != gearbox.gear
|
154
|
+
flag = true
|
155
|
+
puts "Gear Change: #{next_gear}"
|
156
|
+
car.gear = next_gear
|
157
|
+
paused += CLI.pause
|
158
|
+
end
|
159
|
+
|
160
|
+
# maintain idle when revs drop
|
161
|
+
if car.throttle == 0 and rpm < motor.idle_rpm
|
162
|
+
phase = :idling
|
163
|
+
car.gear = 0
|
164
|
+
paused += CLI.pause
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
elsif phase == :idling
|
169
|
+
# fake
|
170
|
+
rpm = motor.idle_rpm
|
171
|
+
break
|
27
172
|
end
|
28
173
|
}
|
174
|
+
|
175
|
+
puts Timer.summary(start, num_ticks, paused)
|
data/demo/disk.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'driving_physics/disk'
|
2
|
+
require 'driving_physics/cli'
|
3
|
+
|
4
|
+
include DrivingPhysics
|
5
|
+
|
6
|
+
env = Environment.new
|
7
|
+
disk = Disk.new(env)
|
8
|
+
|
9
|
+
puts env
|
10
|
+
puts disk
|
11
|
+
puts
|
12
|
+
|
13
|
+
axle_torque = 50
|
14
|
+
alpha = disk.alpha(axle_torque)
|
15
|
+
drive_force = disk.force(axle_torque)
|
16
|
+
|
17
|
+
puts [format("Axle torque: %.1f Nm", axle_torque),
|
18
|
+
format(" Alpha: %.1f rad/s/s", alpha),
|
19
|
+
format("Drive force: %.1f N", drive_force),
|
20
|
+
].join("\n")
|
21
|
+
puts
|
22
|
+
|
23
|
+
puts "* Spin up the disk with #{axle_torque} Nm of torque"
|
24
|
+
puts "* Cut the power at some point"
|
25
|
+
puts "* Observe"
|
26
|
+
CLI.pause
|
27
|
+
|
28
|
+
duration = 750 # sec
|
29
|
+
|
30
|
+
dist = 0.0 # meters
|
31
|
+
speed = 0.0 # meters/s
|
32
|
+
|
33
|
+
theta = 0.0 # radians
|
34
|
+
omega = 0.0 # radians/s
|
35
|
+
|
36
|
+
paused = 0.0 # seconds
|
37
|
+
num_ticks = duration * env.hz + 1
|
38
|
+
flag = false # to display current stats
|
39
|
+
start = Timer.now
|
40
|
+
|
41
|
+
num_ticks.times { |i|
|
42
|
+
# shut off the powah!
|
43
|
+
if i == 18_950
|
44
|
+
flag = true
|
45
|
+
puts '#'
|
46
|
+
puts '# CUT POWER'
|
47
|
+
puts '#'
|
48
|
+
puts
|
49
|
+
axle_torque = 0
|
50
|
+
end
|
51
|
+
|
52
|
+
rotating_friction = disk.rotating_friction(omega)
|
53
|
+
net_torque = axle_torque + rotating_friction
|
54
|
+
net_force = disk.force(net_torque)
|
55
|
+
|
56
|
+
# rotational kinematics
|
57
|
+
alpha = disk.alpha(net_torque)
|
58
|
+
omega += alpha * env.tick
|
59
|
+
omega = 0.0 if omega.abs < 0.0001
|
60
|
+
theta += omega * env.tick
|
61
|
+
|
62
|
+
if flag or i < 10 or
|
63
|
+
(i < 20_000 and i%1000 == 0) or
|
64
|
+
(i % 10_000 == 0) or
|
65
|
+
i == duration * env.hz - 1
|
66
|
+
|
67
|
+
puts Timer.display(ms: i)
|
68
|
+
puts format(" Torque: %.1f Nm (%d Nm) Friction: %.1f Nm",
|
69
|
+
net_torque, axle_torque, rotating_friction)
|
70
|
+
puts format("Radians: %.1f r %.2f r/s %.3f r/s^2", theta, omega, alpha)
|
71
|
+
puts format(" Revs: %d revs %d revs/s %d rpm",
|
72
|
+
DrivingPhysics.revs(theta),
|
73
|
+
DrivingPhysics.revs(omega),
|
74
|
+
DrivingPhysics.rpm(omega))
|
75
|
+
puts
|
76
|
+
if flag
|
77
|
+
paused += CLI.pause
|
78
|
+
flag = false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
}
|
82
|
+
|
83
|
+
puts Timer.summary(start, num_ticks, paused)
|
data/demo/gearbox.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'driving_physics/gearbox'
|
2
|
+
require 'driving_physics/cli'
|
3
|
+
|
4
|
+
include DrivingPhysics
|
5
|
+
|
6
|
+
env = Environment.new
|
7
|
+
gearbox = Gearbox.new(env)
|
8
|
+
|
9
|
+
torque = 30
|
10
|
+
duration = 30
|
11
|
+
|
12
|
+
puts env
|
13
|
+
puts gearbox
|
14
|
+
puts
|
15
|
+
puts "Spin up the gearbox with #{torque} Nm of input torque"
|
16
|
+
puts "How fast will it go in #{duration} seconds?"
|
17
|
+
CLI.pause
|
18
|
+
|
19
|
+
# rotational kinematics
|
20
|
+
alpha = 0.0
|
21
|
+
omega = 0.0
|
22
|
+
theta = 0.0
|
23
|
+
|
24
|
+
start = Timer.now
|
25
|
+
paused = 0
|
26
|
+
|
27
|
+
(duration * env.hz + 1).times { |i|
|
28
|
+
# just for info, not used in the simulation
|
29
|
+
friction = gearbox.spinner.rotating_friction(omega)
|
30
|
+
|
31
|
+
# update rotational kinematics
|
32
|
+
# gearbox.alpha incorporates friction and inertia
|
33
|
+
alpha = gearbox.alpha(torque, omega: omega)
|
34
|
+
omega += alpha * env.tick
|
35
|
+
theta += omega * env.tick
|
36
|
+
|
37
|
+
net_torque = gearbox.implied_torque(alpha)
|
38
|
+
|
39
|
+
# periodic output
|
40
|
+
if i < 10 or
|
41
|
+
(i < 100 and i % 10 == 0) or
|
42
|
+
(i < 1000 and i % 100 == 0) or
|
43
|
+
i % 1000 == 0
|
44
|
+
puts Timer.display(i)
|
45
|
+
puts format("RPM %d Torque: %.3f Nm (%d Nm) Friction: %.3f Nm",
|
46
|
+
DrivingPhysics.rpm(omega), net_torque, torque, friction)
|
47
|
+
puts format("%.1f rad %.1f rad/s %.1f rad/s/s", theta, omega, alpha)
|
48
|
+
puts
|
49
|
+
end
|
50
|
+
}
|
51
|
+
|
52
|
+
puts Timer.summary(start, num_ticks, paused)
|
data/demo/motor.rb
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'driving_physics/motor'
|
2
|
+
require 'driving_physics/cli'
|
3
|
+
require 'driving_physics/power'
|
4
|
+
|
5
|
+
# next fun idea: PID controller
|
6
|
+
|
7
|
+
include DrivingPhysics
|
8
|
+
|
9
|
+
env = Environment.new
|
10
|
+
motor = Motor.new(env)
|
11
|
+
puts env
|
12
|
+
puts motor
|
13
|
+
puts
|
14
|
+
|
15
|
+
puts "Rev it up!"
|
16
|
+
motor.throttle = 1.0
|
17
|
+
[:torque, :power].each { |run|
|
18
|
+
puts
|
19
|
+
puts run.to_s.upcase + ':'
|
20
|
+
|
21
|
+
800.upto(7000) { |rpm|
|
22
|
+
next unless rpm % 200 == 0
|
23
|
+
omega = DrivingPhysics.omega(rpm)
|
24
|
+
torque = motor.torque(rpm)
|
25
|
+
case run
|
26
|
+
when :torque
|
27
|
+
count = (torque.to_f / 10).round
|
28
|
+
char = 'T'
|
29
|
+
val = torque.round.to_s.rjust(5, ' ')
|
30
|
+
fmt = "%s Nm"
|
31
|
+
when :power
|
32
|
+
power = DrivingPhysics.power(torque, omega)
|
33
|
+
kw = power.to_f / 1000
|
34
|
+
count = (kw / 5).round
|
35
|
+
char = 'P'
|
36
|
+
val = kw.round(1).to_s.rjust(5, ' ')
|
37
|
+
fmt = "%s kW"
|
38
|
+
else
|
39
|
+
raise "unknown"
|
40
|
+
end
|
41
|
+
puts format("%s RPM: #{fmt}\t%s",
|
42
|
+
rpm.to_s.rjust(4, ' '),
|
43
|
+
val,
|
44
|
+
char * count)
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
puts
|
49
|
+
puts "Now, the simulation begins..."
|
50
|
+
puts "---"
|
51
|
+
puts "* Spin the motor up to #{motor.idle_rpm} RPM with the starter motor."
|
52
|
+
puts "* Rev it up with the throttle."
|
53
|
+
puts "* Let it die."
|
54
|
+
CLI.pause
|
55
|
+
|
56
|
+
alpha = 0.0
|
57
|
+
omega = 0.0
|
58
|
+
theta = 0.0
|
59
|
+
|
60
|
+
duration = 60
|
61
|
+
|
62
|
+
status = :ignition
|
63
|
+
rpm = 0
|
64
|
+
|
65
|
+
paused = 0.0
|
66
|
+
num_ticks = duration * env.hz + 1
|
67
|
+
start = Timer.now
|
68
|
+
|
69
|
+
num_ticks.times { |i|
|
70
|
+
# this is an input torque; alpha is determined after inertia and friction
|
71
|
+
torque = case status
|
72
|
+
when :ignition
|
73
|
+
motor.starter_torque
|
74
|
+
when :running, :off_throttle, :idling
|
75
|
+
motor.torque(rpm)
|
76
|
+
else
|
77
|
+
0
|
78
|
+
end
|
79
|
+
|
80
|
+
# Motor#alpha incorporates inertia and friction
|
81
|
+
alpha = motor.alpha(torque, omega: omega)
|
82
|
+
omega += alpha * env.tick
|
83
|
+
theta += omega * env.tick
|
84
|
+
|
85
|
+
net_torque = motor.implied_torque(alpha)
|
86
|
+
|
87
|
+
# prevent silly oscillations due to tiny floating point errors
|
88
|
+
omega = 0 if omega < 0.00001
|
89
|
+
rpm = DrivingPhysics.rpm(omega)
|
90
|
+
|
91
|
+
power = DrivingPhysics.power(net_torque, omega)
|
92
|
+
|
93
|
+
if rpm > motor.idle_rpm and status == :ignition
|
94
|
+
status = :running
|
95
|
+
flag = true
|
96
|
+
end
|
97
|
+
|
98
|
+
if rpm > 7000 and status == :running
|
99
|
+
status = :off_throttle
|
100
|
+
motor.throttle = 0.0
|
101
|
+
flag = true
|
102
|
+
end
|
103
|
+
|
104
|
+
if rpm < 1000 and status == :off_throttle
|
105
|
+
status = :idling
|
106
|
+
motor.throttle = 0.06
|
107
|
+
flag = true
|
108
|
+
end
|
109
|
+
|
110
|
+
if status == :idling
|
111
|
+
if rpm < 999
|
112
|
+
motor.throttle += (1.0 - motor.throttle) * 0.005
|
113
|
+
elsif rpm > 1001
|
114
|
+
motor.throttle -= motor.throttle * 0.005
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
if flag or
|
119
|
+
(i < 10) or
|
120
|
+
(i < 100 and i % 10 == 0) or
|
121
|
+
(i < 1000 and i % 100 == 0) or
|
122
|
+
(i < 10_000 and i % 500 == 0) or
|
123
|
+
(i % 5000 == 0) or
|
124
|
+
(status == :idling and i % 100 == 0)
|
125
|
+
puts Timer.display(i)
|
126
|
+
puts format("Throttle: %.1f%%", motor.throttle * 100)
|
127
|
+
puts format("%d RPM %.1f Nm (%d Nm) %.1f kW Friction: %.1f Nm",
|
128
|
+
DrivingPhysics.rpm(omega),
|
129
|
+
net_torque,
|
130
|
+
torque,
|
131
|
+
power / 1000,
|
132
|
+
motor.spinner.rotating_friction(omega))
|
133
|
+
puts format("%d rad %.1f rad/s %.1f rad/s/s", theta, omega, alpha)
|
134
|
+
puts
|
135
|
+
|
136
|
+
paused += CLI.pause if flag
|
137
|
+
flag = false
|
138
|
+
end
|
139
|
+
}
|
140
|
+
|
141
|
+
puts Timer.summary(start, num_ticks, paused)
|
data/demo/powertrain.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'driving_physics/powertrain'
|
2
|
+
require 'driving_physics/imperial'
|
3
|
+
require 'driving_physics/cli'
|
4
|
+
require 'driving_physics/power'
|
5
|
+
|
6
|
+
include DrivingPhysics
|
7
|
+
|
8
|
+
env = Environment.new
|
9
|
+
motor = Motor.new(env)
|
10
|
+
gearbox = Gearbox.new(env)
|
11
|
+
powertrain = Powertrain.new(motor, gearbox)
|
12
|
+
motor.throttle = 1.0
|
13
|
+
puts env
|
14
|
+
puts powertrain
|
15
|
+
CLI.pause
|
16
|
+
|
17
|
+
crank_alpha = 0.0
|
18
|
+
crank_omega = 0.0
|
19
|
+
|
20
|
+
axle_alpha = 0.0
|
21
|
+
axle_omega = 0.0
|
22
|
+
|
23
|
+
# Run through the gears
|
24
|
+
1.upto(6) { |gear|
|
25
|
+
gearbox.gear = gear
|
26
|
+
|
27
|
+
puts <<EOF
|
28
|
+
|
29
|
+
# GEAR #{gear} (#{gearbox.ratio})
|
30
|
+
#
|
31
|
+
EOF
|
32
|
+
|
33
|
+
800.upto(7000) { |rpm|
|
34
|
+
next unless rpm % 200 == 0
|
35
|
+
|
36
|
+
power, axle_torque, axle_omega = powertrain.output(rpm)
|
37
|
+
kw = power / 1000.to_f
|
38
|
+
mph = Imperial.mph(Disk.tangential(axle_omega, 0.32))
|
39
|
+
ps = Imperial.ps(kw)
|
40
|
+
puts format("%s RPM: %s Nm %s kW %s PS\t%s mph",
|
41
|
+
rpm.round.to_s.rjust(4, ' '),
|
42
|
+
axle_torque.round(1).to_s.rjust(5, ' '),
|
43
|
+
kw.round(1).to_s.rjust(5, ' '),
|
44
|
+
ps.round.to_s.rjust(4, ' '),
|
45
|
+
mph.round.to_s.rjust(3, ' '))
|
46
|
+
}
|
47
|
+
}
|
data/demo/scalar_force.rb
CHANGED
@@ -1,26 +1,52 @@
|
|
1
1
|
require 'driving_physics/scalar_force'
|
2
|
+
require 'driving_physics/environment'
|
3
|
+
require 'driving_physics/cli'
|
2
4
|
|
3
|
-
|
5
|
+
include DrivingPhysics
|
6
|
+
|
7
|
+
env = Environment.new
|
8
|
+
puts env
|
4
9
|
|
5
|
-
pos = 0 # m
|
6
|
-
spd = 0 # m/s
|
7
10
|
mass = 1000 # kg
|
8
|
-
weight = mass *
|
11
|
+
weight = mass * env.g # N
|
9
12
|
drive_force = 7000 # N
|
10
13
|
duration = 100 # seconds
|
11
|
-
tick = 1.0 / DP::HZ
|
12
14
|
|
13
|
-
(
|
14
|
-
|
15
|
+
puts format("Force: %d N Mass: %d kg for %d seconds",
|
16
|
+
drive_force, mass, duration)
|
17
|
+
CLI.pause
|
18
|
+
|
19
|
+
spd = 0.0 # m/s
|
20
|
+
pos = 0.0 # m
|
21
|
+
num_ticks = duration * env.hz + 1
|
22
|
+
|
23
|
+
flag = false
|
24
|
+
phase = :accelerate
|
25
|
+
paused = 0.0
|
26
|
+
start = Timer.now
|
27
|
+
|
28
|
+
num_ticks.times { |i|
|
29
|
+
# TODO: make the resistance force negative
|
30
|
+
net_force = drive_force + ScalarForce.all_resistance(spd, nf_mag: weight)
|
15
31
|
|
16
|
-
|
17
|
-
spd
|
18
|
-
pos
|
32
|
+
acc = DrivingPhysics.acc(net_force, mass)
|
33
|
+
spd += acc * env.tick
|
34
|
+
pos += spd * env.tick
|
19
35
|
|
20
|
-
if
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
36
|
+
if phase == :accelerate and spd.magnitude > 100
|
37
|
+
flag = true
|
38
|
+
phase = :coast
|
39
|
+
drive_force = 0
|
40
|
+
end
|
41
|
+
|
42
|
+
if flag or (i % 1000 == 0)
|
43
|
+
puts format("%d %.3f m/s/s %.2f m/s %.1f m",
|
44
|
+
i.to_f / env.hz, acc, spd, pos)
|
45
|
+
if flag
|
46
|
+
paused += CLI.pause
|
47
|
+
flag = false
|
48
|
+
end
|
25
49
|
end
|
26
50
|
}
|
51
|
+
|
52
|
+
puts Timer.summary(start, num_ticks, paused)
|