driving_physics 0.0.2.1 → 0.0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +17 -14
- data/Rakefile +3 -3
- data/VERSION +1 -1
- data/demo/car.rb +83 -73
- data/demo/motor.rb +7 -4
- data/demo/mruby/car.rb +184 -0
- data/demo/mruby/motor.rb +7 -4
- data/demo/pid_controller.rb +143 -0
- data/demo/powertrain.rb +1 -1
- data/driving_physics.gemspec +2 -1
- data/lib/driving_physics/car.rb +13 -4
- data/lib/driving_physics/cli.rb +7 -35
- data/lib/driving_physics/cockpit.rb +167 -0
- data/lib/driving_physics/disk.rb +5 -25
- data/lib/driving_physics/environment.rb +2 -0
- data/lib/driving_physics/gearbox.rb +60 -30
- data/lib/driving_physics/motor.rb +166 -58
- data/lib/driving_physics/mruby.rb +3 -0
- data/lib/driving_physics/pid_controller.rb +111 -0
- data/lib/driving_physics/powertrain.rb +8 -13
- data/lib/driving_physics/scalar_force.rb +2 -1
- data/lib/driving_physics/timer.rb +34 -0
- data/lib/driving_physics/vector_force.rb +23 -0
- data/lib/driving_physics.rb +28 -22
- data/test/disk.rb +23 -62
- data/test/gearbox.rb +36 -0
- data/test/motor.rb +236 -0
- data/test/powertrain.rb +21 -0
- data/test/scalar_force.rb +3 -6
- data/test/tire.rb +25 -59
- data/test/vector_force.rb +76 -37
- metadata +26 -5
- data/demo/mruby/disk.rb +0 -81
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b9159b499a0082d5af3ef926fceab925572552978b50d020b71bc51df7506fb
|
4
|
+
data.tar.gz: 183eaae3e07e24f38bb1417d96a6310eebfd288a1300d4e8bab96f008abbf3e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9a19ba4646abe21f30ca12c88f43e9ddc7083028e96504260f0277b167af5374364967b1617ab83744f824d6d2aa513ef38627d23ad5e9ccc8ff7e017b8a2ac8
|
7
|
+
data.tar.gz: 5357970cd547b12727bfc20c408df27a07d3aaac5d5b9042cbb10fc98c85484eb7bf054e140158f7e8695ef6c30eb18961fdd6979b4d0e3ab6c343f7767fc296
|
data/README.md
CHANGED
@@ -10,14 +10,28 @@ using `Vector` class from `matrix.rb` in stdlib.
|
|
10
10
|
|
11
11
|
This is very much a **Work In Progress**. Implemented so far:
|
12
12
|
|
13
|
+
### Library Features
|
14
|
+
|
15
|
+
* Spinning: rotational inertia and friction / hysteresis
|
16
|
+
* Tires: traction force via friction (static and kinetic) and the normal force
|
17
|
+
* Vectors: 2D Vector forces and 3D Vector torques
|
18
|
+
* Motor: torque curves, rotating mass with friction
|
19
|
+
* Gearbox: gear ratios, final drive, rotating mass with friction
|
20
|
+
* Powertrain: combines motor and gearbox
|
21
|
+
* Car: 4 tires, powertrain, extra mass presents a load to the powertrain
|
22
|
+
* Acceleration: via drive traction from "first principles"
|
23
|
+
* Variable clutch and throttle
|
24
|
+
* Engine braking
|
25
|
+
* Power and Energy
|
26
|
+
|
13
27
|
### mruby Support
|
14
28
|
|
15
|
-
This
|
16
|
-
|
29
|
+
This library is primarily a Ruby library, intended for use with CRuby (MRI),
|
30
|
+
JRuby, etc. **mruby** is the redheaded stepchild, with significant
|
17
31
|
implications for a codebase that wants to support both sides. Supporting
|
18
32
|
mruby here meant:
|
19
33
|
|
20
|
-
1. Editing the codebase (minimally) to remove *mruby-deadends*.
|
34
|
+
1. Editing the codebase (minimally) to avoid and remove *mruby-deadends*.
|
21
35
|
2. Creating `rake` tasks to process normal .rb files into *mruby-compatible*
|
22
36
|
.rb files
|
23
37
|
3. Assembling all needed library .rb files into a master .rb file
|
@@ -27,17 +41,6 @@ mruby here meant:
|
|
27
41
|
|
28
42
|
See the main Rakefile near the top for mruby stuff.
|
29
43
|
|
30
|
-
### Library Features
|
31
|
-
|
32
|
-
* Spinning: rotational inertia and friction / hysteresis
|
33
|
-
* Tires: traction force via friction (static and kinetic) and the normal force
|
34
|
-
* Vectors: 2D Vector forces and 3D Vector torques
|
35
|
-
* Motor: torque curves, rotating mass with friction
|
36
|
-
* Gearbox: gear ratios, final drive, rotating mass with friction
|
37
|
-
* Powertrain: combines motor and gearbox
|
38
|
-
* Car: 4 tires, powertrain, extra mass presents a load to the powertrain
|
39
|
-
* Acceleration: via drive traction from "first principles"
|
40
|
-
|
41
44
|
## Rationale
|
42
45
|
|
43
46
|
This is a toy project intended to scratch a personal itch: I want to create
|
data/Rakefile
CHANGED
@@ -40,8 +40,8 @@ end
|
|
40
40
|
|
41
41
|
file MRBLIB_FILE do
|
42
42
|
line_count = write_mruby(File.join(%w[lib driving_physics.rb]), MRBLIB_FILE)
|
43
|
-
%w[
|
44
|
-
disk tire motor gearbox powertrain car].each { |name|
|
43
|
+
%w[cli environment imperial power timer
|
44
|
+
disk tire motor gearbox powertrain car pid_controller].each { |name|
|
45
45
|
file = File.join('lib', 'driving_physics', "#{name}.rb")
|
46
46
|
line_count += write_mruby(file, MRBLIB_FILE, append: true)
|
47
47
|
puts "wrote #{file} to #{MRBLIB_FILE}"
|
@@ -67,7 +67,7 @@ task mrblib: MRBLIB_FILE
|
|
67
67
|
desc "Compile mruby/mrblib/driving_physics.rb to .mrb bytecode"
|
68
68
|
task mrbc: MRBLIB_MRB
|
69
69
|
|
70
|
-
%w[disk tire motor gearbox powertrain car].each { |name|
|
70
|
+
%w[disk tire motor gearbox powertrain car pid_controller].each { |name|
|
71
71
|
demo_file = File.join(MRUBY_DEMO_DIR, "#{name}.rb")
|
72
72
|
demo_mrb = File.join(MRUBY_DEMO_DIR, "#{name}.mrb")
|
73
73
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.3.1
|
data/demo/car.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'driving_physics/car'
|
2
|
+
require 'driving_physics/cockpit'
|
2
3
|
require 'driving_physics/imperial'
|
3
4
|
require 'driving_physics/cli'
|
4
5
|
|
@@ -10,13 +11,16 @@ puts env
|
|
10
11
|
tire = Tire.new(env)
|
11
12
|
motor = Motor.new(env)
|
12
13
|
gearbox = Gearbox.new(env)
|
13
|
-
powertrain = Powertrain.new(motor, gearbox)
|
14
|
+
powertrain = Powertrain.new(motor: motor, gearbox: gearbox)
|
14
15
|
car = Car.new(tire: tire, powertrain: powertrain) { |c|
|
15
16
|
c.body_mass = 850.0
|
16
17
|
c.frontal_area = 2.5
|
17
18
|
c.cd = 0.5
|
18
19
|
}
|
19
20
|
puts car
|
21
|
+
|
22
|
+
cockpit = Cockpit.new(car)
|
23
|
+
puts cockpit
|
20
24
|
CLI.pause
|
21
25
|
|
22
26
|
duration = 120
|
@@ -31,18 +35,19 @@ tire_theta = 0.0
|
|
31
35
|
|
32
36
|
crank_alpha = 0.0
|
33
37
|
crank_omega = 0.0
|
34
|
-
crank_theta = 0.0
|
35
38
|
|
36
39
|
start = Timer.now
|
37
40
|
paused = 0.0
|
38
41
|
num_ticks = duration * env.hz + 1
|
39
42
|
|
40
|
-
|
43
|
+
rev_match = :ok
|
41
44
|
phase = :ignition
|
45
|
+
gearbox.clutch = 0.0 # clutch in to fire up motor
|
42
46
|
flag = false
|
43
|
-
rpm = 0
|
44
|
-
|
45
47
|
|
48
|
+
rpm, crank_torque = 0, 0
|
49
|
+
axle_torque, drive_force, net_force = 0, 0, 0
|
50
|
+
ar, rr, rf, ir = 0, 0, 0, 0
|
46
51
|
|
47
52
|
puts <<EOF
|
48
53
|
|
@@ -54,24 +59,15 @@ EOF
|
|
54
59
|
|
55
60
|
num_ticks.times { |i|
|
56
61
|
if phase == :ignition
|
57
|
-
# ignition phase
|
58
62
|
crank_alpha = motor.alpha(motor.starter_torque, omega: crank_omega)
|
59
63
|
crank_omega += crank_alpha * env.tick
|
60
|
-
crank_theta += crank_omega * env.tick
|
61
64
|
|
62
65
|
rpm = DrivingPhysics.rpm(crank_omega)
|
63
66
|
|
64
|
-
if
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
puts format("%d RPM %d Nm starter torque", rpm, motor.starter_torque)
|
69
|
-
puts
|
70
|
-
end
|
71
|
-
|
72
|
-
if rpm > motor.idle_rpm
|
73
|
-
car.gear = 1
|
74
|
-
car.throttle = 1.0
|
67
|
+
if rpm > motor.idle
|
68
|
+
flag = true
|
69
|
+
cockpit.gear = 1
|
70
|
+
cockpit.throttle_pedal = 1.0
|
75
71
|
phase = :running
|
76
72
|
|
77
73
|
puts <<EOF
|
@@ -82,23 +78,19 @@ num_ticks.times { |i|
|
|
82
78
|
|
83
79
|
EOF
|
84
80
|
end
|
85
|
-
elsif phase == :running
|
86
|
-
# track crank_alpha/omega/theta
|
87
|
-
|
88
|
-
# cut throttle after 60 s
|
89
|
-
car.throttle = 0 if i > 60 * env.hz and car.throttle == 1.0
|
90
|
-
|
81
|
+
elsif phase == :running or phase == :off_throttle
|
91
82
|
ar = car.air_force(speed)
|
92
83
|
rr = car.tire_rolling_force(tire_omega)
|
93
84
|
rf = car.tire_rotational_force(tire_omega)
|
94
85
|
|
95
86
|
# note, this fails if we're in neutral
|
96
|
-
|
87
|
+
drive_force = car.drive_force(rpm, axle_omega: tire_omega)
|
88
|
+
net_force = drive_force + ar + rr + rf
|
97
89
|
|
98
|
-
ir = car.tire_inertial_force(
|
99
|
-
|
90
|
+
ir = car.tire_inertial_force(net_force)
|
91
|
+
net_force += ir
|
100
92
|
|
101
|
-
acc = DrivingPhysics.acc(
|
93
|
+
acc = DrivingPhysics.acc(net_force, car.total_mass)
|
102
94
|
speed += acc * env.tick
|
103
95
|
dist += speed * env.tick
|
104
96
|
|
@@ -108,71 +100,89 @@ EOF
|
|
108
100
|
|
109
101
|
crank_alpha = tire_alpha / car.powertrain.gearbox.ratio
|
110
102
|
crank_omega += crank_alpha * env.tick
|
111
|
-
crank_theta += crank_omega * env.tick
|
112
|
-
|
113
|
-
axle_torque = car.powertrain.axle_torque(rpm)
|
114
|
-
crank_torque = car.powertrain.motor.torque(rpm)
|
115
|
-
|
116
|
-
if flag or (i < 5000 and i % 100 == 0) or (i % 1000 == 0)
|
117
|
-
puts Timer.display(ms: i)
|
118
|
-
puts format(" Tire: %.1f r/s/s %.2f r/s %.3f r",
|
119
|
-
tire_alpha, tire_omega, tire_theta)
|
120
|
-
puts format(" Car: %.1f m/s/s %.2f m/s %.3f m (%.1f MPH)",
|
121
|
-
acc, speed, dist, Imperial.mph(speed))
|
122
|
-
puts format(" Motor: %d RPM %.1f Nm", rpm, crank_torque)
|
123
|
-
puts format(" Axle: %.1f Nm (%d N) Net Force: %.1f N",
|
124
|
-
axle_torque, car.drive_force(rpm), force)
|
125
|
-
puts "Resist: " + format(%w[Air Roll Spin Inertial].map { |s|
|
126
|
-
"#{s}: %.1f N"
|
127
|
-
}.join(' '), ar, rr, rf, ir)
|
128
|
-
puts
|
129
|
-
flag = false
|
130
|
-
end
|
131
103
|
|
132
104
|
# tire_omega determines new rpm
|
133
|
-
new_rpm =
|
134
|
-
|
105
|
+
new_rpm = gearbox.crank_rpm(tire_omega, crank_rpm: rpm)
|
106
|
+
new_rev_match, clutch, proportion = Cockpit.rev_match(rpm, new_rpm)
|
135
107
|
|
136
|
-
if
|
108
|
+
if new_rev_match != rev_match
|
137
109
|
flag = true
|
138
|
-
puts format("
|
139
|
-
|
140
|
-
clutch
|
141
|
-
|
110
|
+
puts format("Rev Match: [%s] %d RPM is %.1f%% from %d RPM",
|
111
|
+
new_rev_match, new_rpm, proportion * 100, rpm)
|
112
|
+
# puts format("Recommend clutch to %.1f%%", clutch * 100)
|
113
|
+
rev_match = new_rev_match
|
142
114
|
end
|
143
115
|
|
144
|
-
|
145
|
-
|
146
|
-
rpm = new_rpm
|
147
|
-
when :mismatch
|
116
|
+
clutch_diff = clutch - gearbox.clutch
|
117
|
+
if clutch_diff.abs > 0.1
|
148
118
|
flag = true
|
149
|
-
puts
|
150
|
-
|
151
|
-
puts '#'
|
152
|
-
puts
|
153
|
-
rpm = new_rpm
|
119
|
+
puts format("Clutch: %.1f%% Recommended Clutch: %.1f%%",
|
120
|
+
gearbox.clutch * 100, clutch * 100)
|
154
121
|
end
|
155
|
-
|
156
|
-
|
122
|
+
# the clutch pedal will reflect this change
|
123
|
+
gearbox.clutch += clutch_diff * 0.5
|
124
|
+
|
125
|
+
# update the motor RPM based on new clutch
|
126
|
+
new_rpm = gearbox.crank_rpm(tire_omega, crank_rpm: rpm)
|
127
|
+
rpm = new_rpm if new_rpm > motor.idle
|
128
|
+
|
129
|
+
new_gear = cockpit.choose_gear(rpm)
|
130
|
+
if new_gear != cockpit.gear
|
131
|
+
flag = true
|
132
|
+
puts "Gear Change: #{new_gear}"
|
133
|
+
cockpit.gear = new_gear
|
134
|
+
end
|
135
|
+
|
136
|
+
# cut throttle after 60 s
|
137
|
+
if i > 60 * env.hz and car.throttle == 1.0
|
157
138
|
flag = true
|
158
|
-
|
159
|
-
|
160
|
-
paused += CLI.pause
|
139
|
+
phase = :off_throttle
|
140
|
+
cockpit.throttle_pedal = 0
|
161
141
|
end
|
162
142
|
|
163
143
|
# maintain idle when revs drop
|
164
|
-
if
|
144
|
+
if cockpit.throttle_pedal == 0 and rpm < motor.idle
|
165
145
|
phase = :idling
|
166
146
|
car.gear = 0
|
167
|
-
paused += CLI.pause
|
168
147
|
end
|
169
148
|
|
149
|
+
print "===\n\n" if flag
|
170
150
|
|
171
151
|
elsif phase == :idling
|
172
|
-
# fake
|
173
|
-
rpm = motor.
|
152
|
+
# fake; exit
|
153
|
+
rpm = motor.idle
|
174
154
|
break
|
175
155
|
end
|
156
|
+
|
157
|
+
if flag or (i < 5000 and i % 100 == 0) or (i % 1000 == 0)
|
158
|
+
puts Timer.display(ms: i)
|
159
|
+
puts format(" Phase: %s", phase)
|
160
|
+
puts format(" Tire: %.3f r/s/s %.2f r/s %.1f r",
|
161
|
+
tire_alpha, tire_omega, tire_theta)
|
162
|
+
puts format(" Car: %.3f m/s/s %.2f m/s %.1f m (%.1f MPH)",
|
163
|
+
acc, speed, dist, Imperial.mph(speed))
|
164
|
+
if phase == :ignition
|
165
|
+
puts format(" Motor: %d RPM Starter: %d Nm Friction: %.1f Nm",
|
166
|
+
rpm, motor.starter_torque, motor.friction(crank_omega))
|
167
|
+
else
|
168
|
+
crank_torque = car.powertrain.motor.torque(rpm)
|
169
|
+
puts format(" Motor: %d RPM %.1f Nm Friction: %.1f Nm",
|
170
|
+
rpm, crank_torque, motor.friction(crank_omega))
|
171
|
+
end
|
172
|
+
puts format("Gearbox: %s", gearbox.inputs)
|
173
|
+
if phase != :ignition
|
174
|
+
axle_torque = car.powertrain.axle_torque(rpm, axle_omega: tire_omega)
|
175
|
+
puts format(" Axle: %.1f Nm (%d N) Net Force: %.1f N",
|
176
|
+
axle_torque, drive_force, net_force)
|
177
|
+
end
|
178
|
+
puts " Resist: " + format(%w[Air Roll Spin Inertial].map { |s|
|
179
|
+
"#{s}: %.1f N"
|
180
|
+
}.join(' '), ar, rr, rf, ir)
|
181
|
+
puts cockpit
|
182
|
+
puts
|
183
|
+
paused += CLI.pause if flag
|
184
|
+
flag = false
|
185
|
+
end
|
176
186
|
}
|
177
187
|
|
178
188
|
puts Timer.summary(start, num_ticks, paused)
|
data/demo/motor.rb
CHANGED
@@ -48,7 +48,7 @@ motor.throttle = 1.0
|
|
48
48
|
puts
|
49
49
|
puts "Now, the simulation begins..."
|
50
50
|
puts "---"
|
51
|
-
puts "* Spin the motor up to #{motor.
|
51
|
+
puts "* Spin the motor up to #{motor.idle} RPM with the starter motor."
|
52
52
|
puts "* Rev it up with the throttle."
|
53
53
|
puts "* Let it die."
|
54
54
|
CLI.pause
|
@@ -89,7 +89,7 @@ num_ticks.times { |i|
|
|
89
89
|
rpm = DrivingPhysics.rpm(omega)
|
90
90
|
power = DrivingPhysics.power(net_torque, omega)
|
91
91
|
|
92
|
-
if rpm > motor.
|
92
|
+
if rpm > motor.idle and status == :ignition
|
93
93
|
status = :running
|
94
94
|
flag = true
|
95
95
|
end
|
@@ -106,10 +106,13 @@ num_ticks.times { |i|
|
|
106
106
|
flag = true
|
107
107
|
end
|
108
108
|
|
109
|
+
# we don't need no PID control
|
109
110
|
if status == :idling
|
110
|
-
if rpm
|
111
|
+
if rpm.round <= 999
|
111
112
|
motor.throttle += (1.0 - motor.throttle) * 0.005
|
112
|
-
elsif rpm
|
113
|
+
elsif rpm.round == 1000
|
114
|
+
motor.throttle += (rand - 0.5) * 0.0005
|
115
|
+
else
|
113
116
|
motor.throttle -= motor.throttle * 0.005
|
114
117
|
end
|
115
118
|
end
|
data/demo/mruby/car.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
|
2
|
+
include DrivingPhysics
|
3
|
+
|
4
|
+
env = Environment.new
|
5
|
+
puts env
|
6
|
+
|
7
|
+
tire = Tire.new(env)
|
8
|
+
motor = Motor.new(env)
|
9
|
+
gearbox = Gearbox.new(env)
|
10
|
+
powertrain = Powertrain.new(motor: motor, gearbox: gearbox)
|
11
|
+
car = Car.new(tire: tire, powertrain: powertrain) { |c|
|
12
|
+
c.body_mass = 850.0
|
13
|
+
c.frontal_area = 2.5
|
14
|
+
c.cd = 0.5
|
15
|
+
}
|
16
|
+
puts car
|
17
|
+
|
18
|
+
cockpit = Cockpit.new(car)
|
19
|
+
puts cockpit
|
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
|
+
|
35
|
+
start = Timer.now
|
36
|
+
paused = 0.0
|
37
|
+
num_ticks = duration * env.hz + 1
|
38
|
+
|
39
|
+
rev_match = :ok
|
40
|
+
phase = :ignition
|
41
|
+
gearbox.clutch = 0.0 # clutch in to fire up motor
|
42
|
+
flag = false
|
43
|
+
|
44
|
+
rpm, crank_torque = 0, 0
|
45
|
+
axle_torque, drive_force, net_force = 0, 0, 0
|
46
|
+
ar, rr, rf, ir = 0, 0, 0, 0
|
47
|
+
|
48
|
+
puts <<EOF
|
49
|
+
|
50
|
+
#
|
51
|
+
# IGNITION
|
52
|
+
#
|
53
|
+
|
54
|
+
EOF
|
55
|
+
|
56
|
+
num_ticks.times { |i|
|
57
|
+
if phase == :ignition
|
58
|
+
crank_alpha = motor.alpha(motor.starter_torque, omega: crank_omega)
|
59
|
+
crank_omega += crank_alpha * env.tick
|
60
|
+
|
61
|
+
rpm = DrivingPhysics.rpm(crank_omega)
|
62
|
+
|
63
|
+
if rpm > motor.idle
|
64
|
+
flag = true
|
65
|
+
cockpit.gear = 1
|
66
|
+
cockpit.throttle_pedal = 1.0
|
67
|
+
phase = :running
|
68
|
+
|
69
|
+
puts <<EOF
|
70
|
+
|
71
|
+
#
|
72
|
+
# RUNNING
|
73
|
+
#
|
74
|
+
|
75
|
+
EOF
|
76
|
+
end
|
77
|
+
elsif phase == :running or phase == :off_throttle
|
78
|
+
ar = car.air_force(speed)
|
79
|
+
rr = car.tire_rolling_force(tire_omega)
|
80
|
+
rf = car.tire_rotational_force(tire_omega)
|
81
|
+
|
82
|
+
# note, this fails if we're in neutral
|
83
|
+
drive_force = car.drive_force(rpm, axle_omega: tire_omega)
|
84
|
+
net_force = drive_force + ar + rr + rf
|
85
|
+
|
86
|
+
ir = car.tire_inertial_force(net_force)
|
87
|
+
net_force += ir
|
88
|
+
|
89
|
+
acc = DrivingPhysics.acc(net_force, car.total_mass)
|
90
|
+
speed += acc * env.tick
|
91
|
+
dist += speed * env.tick
|
92
|
+
|
93
|
+
tire_alpha = acc / car.tire.radius
|
94
|
+
tire_omega += tire_alpha * env.tick
|
95
|
+
tire_theta += tire_omega * env.tick
|
96
|
+
|
97
|
+
crank_alpha = tire_alpha / car.powertrain.gearbox.ratio
|
98
|
+
crank_omega += crank_alpha * env.tick
|
99
|
+
|
100
|
+
# tire_omega determines new rpm
|
101
|
+
new_rpm = gearbox.crank_rpm(tire_omega, crank_rpm: rpm)
|
102
|
+
new_rev_match, clutch, proportion = Cockpit.rev_match(rpm, new_rpm)
|
103
|
+
|
104
|
+
if new_rev_match != rev_match
|
105
|
+
flag = true
|
106
|
+
puts format("Rev Match: [%s] %d RPM is %.1f%% from %d RPM",
|
107
|
+
new_rev_match, new_rpm, proportion * 100, rpm)
|
108
|
+
# puts format("Recommend clutch to %.1f%%", clutch * 100)
|
109
|
+
rev_match = new_rev_match
|
110
|
+
end
|
111
|
+
|
112
|
+
clutch_diff = clutch - gearbox.clutch
|
113
|
+
if clutch_diff.abs > 0.1
|
114
|
+
flag = true
|
115
|
+
puts format("Clutch: %.1f%% Recommended Clutch: %.1f%%",
|
116
|
+
gearbox.clutch * 100, clutch * 100)
|
117
|
+
end
|
118
|
+
# the clutch pedal will reflect this change
|
119
|
+
gearbox.clutch += clutch_diff * 0.5
|
120
|
+
|
121
|
+
# update the motor RPM based on new clutch
|
122
|
+
new_rpm = gearbox.crank_rpm(tire_omega, crank_rpm: rpm)
|
123
|
+
rpm = new_rpm if new_rpm > motor.idle
|
124
|
+
|
125
|
+
new_gear = cockpit.choose_gear(rpm)
|
126
|
+
if new_gear != cockpit.gear
|
127
|
+
flag = true
|
128
|
+
puts "Gear Change: #{new_gear}"
|
129
|
+
cockpit.gear = new_gear
|
130
|
+
end
|
131
|
+
|
132
|
+
# cut throttle after 60 s
|
133
|
+
if i > 60 * env.hz and car.throttle == 1.0
|
134
|
+
flag = true
|
135
|
+
phase = :off_throttle
|
136
|
+
cockpit.throttle_pedal = 0
|
137
|
+
end
|
138
|
+
|
139
|
+
# maintain idle when revs drop
|
140
|
+
if cockpit.throttle_pedal == 0 and rpm < motor.idle
|
141
|
+
phase = :idling
|
142
|
+
car.gear = 0
|
143
|
+
end
|
144
|
+
|
145
|
+
print "===\n\n" if flag
|
146
|
+
|
147
|
+
elsif phase == :idling
|
148
|
+
# fake; exit
|
149
|
+
rpm = motor.idle
|
150
|
+
break
|
151
|
+
end
|
152
|
+
|
153
|
+
if flag or (i < 5000 and i % 100 == 0) or (i % 1000 == 0)
|
154
|
+
puts Timer.display(ms: i)
|
155
|
+
puts format(" Phase: %s", phase)
|
156
|
+
puts format(" Tire: %.3f r/s/s %.2f r/s %.1f r",
|
157
|
+
tire_alpha, tire_omega, tire_theta)
|
158
|
+
puts format(" Car: %.3f m/s/s %.2f m/s %.1f m (%.1f MPH)",
|
159
|
+
acc, speed, dist, Imperial.mph(speed))
|
160
|
+
if phase == :ignition
|
161
|
+
puts format(" Motor: %d RPM Starter: %d Nm Friction: %.1f Nm",
|
162
|
+
rpm, motor.starter_torque, motor.friction(crank_omega))
|
163
|
+
else
|
164
|
+
crank_torque = car.powertrain.motor.torque(rpm)
|
165
|
+
puts format(" Motor: %d RPM %.1f Nm Friction: %.1f Nm",
|
166
|
+
rpm, crank_torque, motor.friction(crank_omega))
|
167
|
+
end
|
168
|
+
puts format("Gearbox: %s", gearbox.inputs)
|
169
|
+
if phase != :ignition
|
170
|
+
axle_torque = car.powertrain.axle_torque(rpm, axle_omega: tire_omega)
|
171
|
+
puts format(" Axle: %.1f Nm (%d N) Net Force: %.1f N",
|
172
|
+
axle_torque, drive_force, net_force)
|
173
|
+
end
|
174
|
+
puts " Resist: " + format(%w[Air Roll Spin Inertial].map { |s|
|
175
|
+
"#{s}: %.1f N"
|
176
|
+
}.join(' '), ar, rr, rf, ir)
|
177
|
+
puts cockpit
|
178
|
+
puts
|
179
|
+
paused += CLI.pause if flag
|
180
|
+
flag = false
|
181
|
+
end
|
182
|
+
}
|
183
|
+
|
184
|
+
puts Timer.summary(start, num_ticks, paused)
|
data/demo/mruby/motor.rb
CHANGED
@@ -45,7 +45,7 @@ motor.throttle = 1.0
|
|
45
45
|
puts
|
46
46
|
puts "Now, the simulation begins..."
|
47
47
|
puts "---"
|
48
|
-
puts "* Spin the motor up to #{motor.
|
48
|
+
puts "* Spin the motor up to #{motor.idle} RPM with the starter motor."
|
49
49
|
puts "* Rev it up with the throttle."
|
50
50
|
puts "* Let it die."
|
51
51
|
CLI.pause
|
@@ -86,7 +86,7 @@ num_ticks.times { |i|
|
|
86
86
|
rpm = DrivingPhysics.rpm(omega)
|
87
87
|
power = DrivingPhysics.power(net_torque, omega)
|
88
88
|
|
89
|
-
if rpm > motor.
|
89
|
+
if rpm > motor.idle and status == :ignition
|
90
90
|
status = :running
|
91
91
|
flag = true
|
92
92
|
end
|
@@ -103,10 +103,13 @@ num_ticks.times { |i|
|
|
103
103
|
flag = true
|
104
104
|
end
|
105
105
|
|
106
|
+
# we don't need no PID control
|
106
107
|
if status == :idling
|
107
|
-
if rpm
|
108
|
+
if rpm.round <= 999
|
108
109
|
motor.throttle += (1.0 - motor.throttle) * 0.005
|
109
|
-
elsif rpm
|
110
|
+
elsif rpm.round == 1000
|
111
|
+
motor.throttle += (rand - 0.5) * 0.0005
|
112
|
+
else
|
110
113
|
motor.throttle -= motor.throttle * 0.005
|
111
114
|
end
|
112
115
|
end
|