driving_physics 0.0.0.3 → 0.0.2.1
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 +31 -1
- data/Rakefile +84 -0
- data/VERSION +1 -1
- data/demo/car.rb +178 -0
- data/demo/disk.rb +83 -0
- data/demo/gearbox.rb +53 -0
- data/demo/motor.rb +140 -0
- data/demo/mruby/disk.rb +81 -0
- data/demo/mruby/motor.rb +137 -0
- data/demo/powertrain.rb +47 -0
- data/demo/scalar_force.rb +41 -15
- data/demo/tire.rb +87 -0
- data/demo/vector_force.rb +46 -16
- data/lib/driving_physics/car.rb +123 -0
- 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 +150 -0
- data/lib/driving_physics/mruby.rb +45 -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 +120 -0
- data/lib/driving_physics/vector_force.rb +15 -2
- data/lib/driving_physics.rb +2 -0
- data/test/disk.rb +132 -0
- data/test/scalar_force.rb +7 -5
- data/test/{wheel.rb → tire.rb} +65 -55
- data/test/vector_force.rb +7 -1
- metadata +20 -4
- data/demo/wheel.rb +0 -84
- data/lib/driving_physics/wheel.rb +0 -191
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'driving_physics/disk'
|
2
|
+
|
3
|
+
module DrivingPhysics
|
4
|
+
|
5
|
+
# a Tire is a Disk with lighter density and meaningful surface friction
|
6
|
+
|
7
|
+
class Tire < Disk
|
8
|
+
# Note, this is not the density of solid rubber. This density
|
9
|
+
# yields a sensible mass for a wheel / tire combo at common radius
|
10
|
+
# and width, assuming a uniform density
|
11
|
+
# e.g. 25kg at 350mm R x 200mm W
|
12
|
+
#
|
13
|
+
DENSITY = 0.325 # kg / L
|
14
|
+
|
15
|
+
# * the traction force opposes the axle torque / drive force
|
16
|
+
# thus, driving the car forward
|
17
|
+
# * if the drive force exceeds the traction force, slippage occurs
|
18
|
+
# * slippage reduces the available traction force further
|
19
|
+
# * if the drive force is not reduced, the slippage increases
|
20
|
+
# until resistance forces equal the drive force
|
21
|
+
def self.traction(normal_force, cof)
|
22
|
+
normal_force * cof
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_accessor :mu_s, :mu_k, :omega_friction, :base_friction, :roll_cof
|
26
|
+
|
27
|
+
def initialize(env)
|
28
|
+
@env = env
|
29
|
+
@radius = 0.35
|
30
|
+
@width = 0.2
|
31
|
+
@density = DENSITY
|
32
|
+
@temp = @env.air_temp
|
33
|
+
@mu_s = 1.1 # static friction
|
34
|
+
@mu_k = 0.7 # kinetic friction
|
35
|
+
@base_friction = 5.0/10_000
|
36
|
+
@omega_friction = 5.0/100_000
|
37
|
+
@roll_cof = DrivingPhysics::ROLL_COF
|
38
|
+
|
39
|
+
yield self if block_given?
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
[[format("%d mm x %d mm (RxW)", @radius * 1000, @width * 1000),
|
44
|
+
format("%.1f kg %.1f C", self.mass, @temp),
|
45
|
+
format("cF: %.1f / %.1f", @mu_s, @mu_k),
|
46
|
+
].join(" | "),
|
47
|
+
].join("\n")
|
48
|
+
end
|
49
|
+
|
50
|
+
def wear!(amount)
|
51
|
+
@radius -= amount
|
52
|
+
end
|
53
|
+
|
54
|
+
def heat!(amount_deg_c)
|
55
|
+
@temp += amount_deg_c
|
56
|
+
end
|
57
|
+
|
58
|
+
def traction(nf, static: true)
|
59
|
+
self.class.traction(nf, static ? @mu_s : @mu_k)
|
60
|
+
end
|
61
|
+
|
62
|
+
# require a normal_force to be be passed in
|
63
|
+
def rotating_friction(omega, normal_force:)
|
64
|
+
super(omega, normal_force: normal_force)
|
65
|
+
end
|
66
|
+
|
67
|
+
# rolling loss in terms of axle torque
|
68
|
+
def rolling_friction(omega, normal_force:)
|
69
|
+
return omega if omega.zero?
|
70
|
+
mag = omega.abs
|
71
|
+
sign = omega / mag
|
72
|
+
-1 * sign * (normal_force * @roll_cof) * @radius
|
73
|
+
end
|
74
|
+
|
75
|
+
# inertial loss in terms of axle torque when used as a drive wheel
|
76
|
+
def inertial_loss(axle_torque, driven_mass:)
|
77
|
+
drive_force = self.force(axle_torque)
|
78
|
+
force_loss = 0
|
79
|
+
# The force loss depends on the acceleration, but the acceleration
|
80
|
+
# depends on the force loss. Converge the value via 5 round trips.
|
81
|
+
# This is a rough way to compute an integral and should be accurate
|
82
|
+
# to 8+ digits.
|
83
|
+
5.times {
|
84
|
+
acc = DrivingPhysics.acc(drive_force - force_loss, driven_mass)
|
85
|
+
alpha = acc / @radius
|
86
|
+
force_loss = self.implied_torque(alpha) / @radius
|
87
|
+
}
|
88
|
+
force_loss * @radius
|
89
|
+
end
|
90
|
+
|
91
|
+
def net_torque(axle_torque, mass:, omega:, normal_force:)
|
92
|
+
# friction forces oppose omega
|
93
|
+
net = axle_torque +
|
94
|
+
self.rolling_friction(omega, normal_force: normal_force) +
|
95
|
+
self.rotating_friction(omega, normal_force: normal_force)
|
96
|
+
|
97
|
+
# inertial loss has interdependencies; calculate last
|
98
|
+
# it opposes net torque, not omega
|
99
|
+
sign = net / net.abs
|
100
|
+
net - sign * self.inertial_loss(net, driven_mass: mass)
|
101
|
+
end
|
102
|
+
|
103
|
+
def net_tractable_torque(axle_torque,
|
104
|
+
mass:, omega:, normal_force:, static: true)
|
105
|
+
net = self.net_torque(axle_torque,
|
106
|
+
mass: mass,
|
107
|
+
omega: omega,
|
108
|
+
normal_force: normal_force)
|
109
|
+
tt = self.tractable_torque(normal_force, static: static)
|
110
|
+
net > tt ? tt : net
|
111
|
+
end
|
112
|
+
|
113
|
+
# this doesn't take inertial losses or internal frictional losses
|
114
|
+
# into account. input torque required to saturate traction will be
|
115
|
+
# higher than what this method returns
|
116
|
+
def tractable_torque(nf, static: true)
|
117
|
+
traction(nf, static: static) * @radius
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -84,12 +84,25 @@ module DrivingPhysics
|
|
84
84
|
frontal_area: FRONTAL_AREA,
|
85
85
|
drag_cof: DRAG_COF,
|
86
86
|
air_density: AIR_DENSITY)
|
87
|
+
return velocity if velocity.zero?
|
87
88
|
-1 * 0.5 * frontal_area * drag_cof * air_density *
|
88
89
|
velocity * velocity.magnitude
|
89
90
|
end
|
90
91
|
|
91
|
-
|
92
|
-
|
92
|
+
# return a force opposing velocity, representing friction / hysteresis
|
93
|
+
def self.rotational_resistance(velocity,
|
94
|
+
rot_const: ROT_CONST,
|
95
|
+
rot_cof: ROT_COF)
|
96
|
+
return velocity if velocity.zero?
|
97
|
+
-1 * velocity * rot_cof + -1 * velocity.normalize * rot_const
|
98
|
+
end
|
99
|
+
|
100
|
+
# return a torque opposing omega, representing friction / hysteresis
|
101
|
+
def self.omega_resistance(omega,
|
102
|
+
rot_const: ROT_TQ_CONST,
|
103
|
+
rot_cof: ROT_TQ_COF)
|
104
|
+
return 0 if omega == 0.0
|
105
|
+
omega * ROT_TQ_COF + ROT_TQ_CONST
|
93
106
|
end
|
94
107
|
|
95
108
|
# dir is drive_force vector or velocity vector; will be normalized
|
data/lib/driving_physics.rb
CHANGED
@@ -26,6 +26,7 @@ module DrivingPhysics
|
|
26
26
|
DRAG_COF = 0.3 # based roughly on 2000s-era Chevrolet Corvette
|
27
27
|
DRAG = 0.4257 # air_resistance at 1 m/s given above numbers
|
28
28
|
ROT_COF = 12.771 # if rotating resistance matches air resistance at 30 m/s
|
29
|
+
ROT_CONST = 0.05 # N opposing drive force / torque
|
29
30
|
ROLL_COF = 0.01 # roughly: street tires on concrete
|
30
31
|
|
31
32
|
#
|
@@ -35,6 +36,7 @@ module DrivingPhysics
|
|
35
36
|
MINS_PER_HOUR = 60
|
36
37
|
SECS_PER_HOUR = SECS_PER_MIN * MINS_PER_HOUR
|
37
38
|
|
39
|
+
# HH::MM::SS.mmm
|
38
40
|
def self.elapsed_display(elapsed_ms)
|
39
41
|
elapsed_s, ms = elapsed_ms.divmod 1000
|
40
42
|
|
data/test/disk.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'driving_physics/disk'
|
3
|
+
|
4
|
+
D = DrivingPhysics::Disk
|
5
|
+
|
6
|
+
describe D do
|
7
|
+
describe "Disk.volume" do
|
8
|
+
it "calculates the volume (m^3) of disk given radius and width" do
|
9
|
+
cubic_m = D.volume(1.0, 1.0)
|
10
|
+
expect(cubic_m).must_equal Math::PI
|
11
|
+
|
12
|
+
cubic_m = D.volume(0.35, 0.2)
|
13
|
+
expect(cubic_m).must_be_within_epsilon 0.076969
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "Disk.volume_l" do
|
18
|
+
it "calculates the volume (L) of a disk given radius and width" do
|
19
|
+
liters = D.volume_l(1.0, 1.0)
|
20
|
+
expect(liters).must_equal Math::PI * 1000
|
21
|
+
|
22
|
+
liters = D.volume_l(0.35, 0.2)
|
23
|
+
expect(liters).must_be_within_epsilon 76.96902
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "Disk.density" do
|
28
|
+
it "calculates the density (kg/L) given mass and volume" do
|
29
|
+
expect(D.density(25.0, 25.0)).must_equal 1.0
|
30
|
+
expect(D.density(50.0, 25.0)).must_equal 2.0
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "Disk.mass" do
|
35
|
+
it "calculates the mass (kg) of a disk given radius, width, and density" do
|
36
|
+
skip
|
37
|
+
expect(D.mass(0.35, 0.2, D::DENSITY)).must_be_within_epsilon 25.015
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "Disk.rotational_inertia" do
|
42
|
+
it "calculates rotational inertia for a disk given radius and mass" do
|
43
|
+
expect(D.rotational_inertia(0.35, 25.0)).must_be_within_epsilon 1.53125
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "Disk.alpha" do
|
48
|
+
it "calculates angular acceleration from torque and inertia" do
|
49
|
+
scalar_torque = 1000
|
50
|
+
inertia = D.rotational_inertia(0.35, 25.0)
|
51
|
+
expect(D.alpha scalar_torque, inertia).must_be_within_epsilon 653.061
|
52
|
+
|
53
|
+
skip # Vector
|
54
|
+
vector_torque = Vector[0, 0, 1000]
|
55
|
+
vector_alpha = D.alpha vector_torque, inertia
|
56
|
+
expect(vector_alpha).must_be_instance_of Vector
|
57
|
+
expect(vector_alpha.size).must_equal 3
|
58
|
+
expect(vector_alpha[2]).must_be_within_epsilon 653.06
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "Disk.torque_vector" do
|
63
|
+
it "calculates a torque in the 3rd dimension given 2D force and radius" do
|
64
|
+
skip # Vector
|
65
|
+
force = Vector[1000, 0]
|
66
|
+
radius = Vector[0, 5]
|
67
|
+
torque = D.torque_vector(force, radius)
|
68
|
+
expect(torque).must_be_instance_of Vector
|
69
|
+
expect(torque.size).must_equal 3
|
70
|
+
expect(torque[2]).must_be_within_epsilon 5000.0
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "Disk.force_vector" do
|
75
|
+
it "calculates a (3D) force given 3D torque and 2D radius" do
|
76
|
+
skip # Vector
|
77
|
+
# let's invert the Disk.torque_vector case from above:
|
78
|
+
torque = Vector[0, 0, 5000]
|
79
|
+
radius = Vector[0, 5]
|
80
|
+
force = D.force_vector(torque, radius)
|
81
|
+
expect(force).must_be_instance_of Vector
|
82
|
+
expect(force.size).must_equal 3
|
83
|
+
expect(force[0]).must_be_within_epsilon 1000.0
|
84
|
+
|
85
|
+
# now let's rotate the radius into the x-dimension
|
86
|
+
# right hand rule, positive torque means thumb into screen, clockwise
|
87
|
+
# negative-x radius means positive-y force
|
88
|
+
torque = Vector[0, 0, 500]
|
89
|
+
radius = Vector[-5, 0]
|
90
|
+
force = D.force_vector(torque, radius)
|
91
|
+
expect(force).must_be_instance_of Vector
|
92
|
+
expect(force.size).must_equal 3
|
93
|
+
expect(force[1]).must_be_within_epsilon 100.0
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "instance methods" do
|
98
|
+
before do
|
99
|
+
@env = DrivingPhysics::Environment.new
|
100
|
+
@disk = D.new(@env)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "initializes" do
|
104
|
+
skip
|
105
|
+
expect(@disk).must_be_instance_of D
|
106
|
+
expect(@disk.density).must_equal D::DENSITY # sanity check
|
107
|
+
expect(@disk.mass).must_be_within_epsilon 25.01
|
108
|
+
|
109
|
+
with_mass = D.new(@env) { |w|
|
110
|
+
w.mass = 99.01
|
111
|
+
}
|
112
|
+
expect(with_mass.mass).must_equal 99.01
|
113
|
+
expect(with_mass.density).wont_equal D::DENSITY
|
114
|
+
end
|
115
|
+
|
116
|
+
it "has a string representation" do
|
117
|
+
str = @disk.to_s
|
118
|
+
expect(str).must_be_instance_of String
|
119
|
+
expect(str.length).must_be(:>, 5)
|
120
|
+
end
|
121
|
+
|
122
|
+
it "has volume" do
|
123
|
+
expect(@disk.volume).must_be_within_epsilon 0.07697
|
124
|
+
expect(@disk.volume_l).must_be_within_epsilon 76.96902
|
125
|
+
end
|
126
|
+
|
127
|
+
it "has inertia" do
|
128
|
+
skip
|
129
|
+
expect(@disk.rotational_inertia).must_be_within_epsilon 1.5321
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/test/scalar_force.rb
CHANGED
@@ -6,25 +6,27 @@ include DrivingPhysics
|
|
6
6
|
describe ScalarForce do
|
7
7
|
# i.e. multiply this number times speed^2 to approximate drag force
|
8
8
|
it "calculates a reasonable drag constant" do
|
9
|
-
expect(ScalarForce.air_resistance 1).must_be_within_epsilon DRAG
|
9
|
+
expect(ScalarForce.air_resistance 1).must_be_within_epsilon(-1 * DRAG)
|
10
10
|
end
|
11
11
|
|
12
12
|
# ROT_COF's value is from observing that rotational resistance
|
13
13
|
# matches air resistance at roughly 30 m/s in street cars
|
14
14
|
it "approximates a reasonable rotational resistance constant" do
|
15
|
-
expect(30 * ScalarForce.air_resistance(1)).
|
15
|
+
expect(30 * ScalarForce.air_resistance(1)).
|
16
|
+
must_be_within_epsilon(-1 * ROT_COF)
|
16
17
|
end
|
17
18
|
|
18
19
|
it "approximates a positive drag force" do
|
19
|
-
expect(ScalarForce.air_resistance 30).must_be_within_epsilon
|
20
|
+
expect(ScalarForce.air_resistance 30).must_be_within_epsilon(-383.13)
|
20
21
|
end
|
21
22
|
|
22
23
|
it "approximates a positive rotational resistance force" do
|
23
|
-
expect(ScalarForce.rotational_resistance 30).
|
24
|
+
expect(ScalarForce.rotational_resistance 30).
|
25
|
+
must_be_within_epsilon(-383.13)
|
24
26
|
end
|
25
27
|
|
26
28
|
it "approximates a positive rolling resistance force" do
|
27
29
|
nf = 1000 * G
|
28
|
-
expect(ScalarForce.rolling_resistance nf).must_be_within_epsilon
|
30
|
+
expect(ScalarForce.rolling_resistance nf).must_be_within_epsilon(-98.0)
|
29
31
|
end
|
30
32
|
end
|
data/test/{wheel.rb → tire.rb}
RENAMED
@@ -1,92 +1,96 @@
|
|
1
1
|
require 'minitest/autorun'
|
2
|
-
require 'driving_physics/
|
2
|
+
require 'driving_physics/tire'
|
3
3
|
|
4
|
-
|
4
|
+
T = DrivingPhysics::Tire
|
5
5
|
|
6
|
-
describe
|
7
|
-
describe "
|
6
|
+
describe T do
|
7
|
+
describe "Tire.traction" do
|
8
8
|
it "calculates traction force from normal force and coeff of friction" do
|
9
9
|
scalar_nf = 9800 # N
|
10
10
|
cof = 1.1
|
11
|
-
scalar_t =
|
11
|
+
scalar_t = T.traction(scalar_nf, cof)
|
12
12
|
expect(scalar_t).must_equal 10780.0
|
13
13
|
|
14
|
+
skip # Vector
|
14
15
|
vector_nf = Vector[9800, 0]
|
15
|
-
vector_t =
|
16
|
+
vector_t = T.traction(vector_nf, cof)
|
16
17
|
expect(vector_t).must_equal Vector[10780.0, 0.0]
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
20
|
-
describe "
|
21
|
+
describe "Tire.volume" do
|
21
22
|
it "calculates the volume (m^3) of disk given radius and width" do
|
22
|
-
cubic_m =
|
23
|
+
cubic_m = T.volume(1.0, 1.0)
|
23
24
|
expect(cubic_m).must_equal Math::PI
|
24
25
|
|
25
|
-
cubic_m =
|
26
|
+
cubic_m = T.volume(0.35, 0.2)
|
26
27
|
expect(cubic_m).must_be_within_epsilon 0.076969
|
27
28
|
end
|
28
29
|
end
|
29
30
|
|
30
|
-
describe "
|
31
|
+
describe "Tire.volume_l" do
|
31
32
|
it "calculates the volume (L) of a disk given radius and width" do
|
32
|
-
liters =
|
33
|
+
liters = T.volume_l(1.0, 1.0)
|
33
34
|
expect(liters).must_equal Math::PI * 1000
|
34
35
|
|
35
|
-
liters =
|
36
|
+
liters = T.volume_l(0.35, 0.2)
|
36
37
|
expect(liters).must_be_within_epsilon 76.96902
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
40
|
-
describe "
|
41
|
+
describe "Tire.density" do
|
41
42
|
it "calculates the density (kg/L) given mass and volume" do
|
42
|
-
expect(
|
43
|
-
expect(
|
43
|
+
expect(T.density(25.0, 25.0)).must_equal 1.0
|
44
|
+
expect(T.density(50.0, 25.0)).must_equal 2.0
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
47
|
-
describe "
|
48
|
+
describe "Tire.mass" do
|
48
49
|
it "calculates the mass (kg) of a disk given radius, width, and density" do
|
49
|
-
expect(
|
50
|
+
expect(T.mass(0.35, 0.2, T::DENSITY)).must_be_within_epsilon 25.015
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
53
|
-
describe "
|
54
|
+
describe "Tire.rotational_inertia" do
|
54
55
|
it "calculates rotational inertia for a disk given radius and mass" do
|
55
|
-
expect(
|
56
|
+
expect(T.rotational_inertia(0.35, 25.0)).must_be_within_epsilon 1.53125
|
56
57
|
end
|
57
58
|
end
|
58
59
|
|
59
|
-
describe "
|
60
|
+
describe "Tire.alpha" do
|
60
61
|
it "calculates angular acceleration from torque and inertia" do
|
61
62
|
scalar_torque = 1000
|
62
|
-
inertia =
|
63
|
-
expect(
|
63
|
+
inertia = T.rotational_inertia(0.35, 25.0)
|
64
|
+
expect(T.alpha scalar_torque, inertia).must_be_within_epsilon 653.061
|
64
65
|
|
66
|
+
skip # Vector
|
65
67
|
vector_torque = Vector[0, 0, 1000]
|
66
|
-
vector_alpha =
|
68
|
+
vector_alpha = T.alpha vector_torque, inertia
|
67
69
|
expect(vector_alpha).must_be_instance_of Vector
|
68
70
|
expect(vector_alpha.size).must_equal 3
|
69
71
|
expect(vector_alpha[2]).must_be_within_epsilon 653.06
|
70
72
|
end
|
71
73
|
end
|
72
74
|
|
73
|
-
describe "
|
75
|
+
describe "Tire.torque_vector" do
|
74
76
|
it "calculates a torque in the 3rd dimension given 2D force and radius" do
|
77
|
+
skip # Vector
|
75
78
|
force = Vector[1000, 0]
|
76
79
|
radius = Vector[0, 5]
|
77
|
-
torque =
|
80
|
+
torque = T.torque_vector(force, radius)
|
78
81
|
expect(torque).must_be_instance_of Vector
|
79
82
|
expect(torque.size).must_equal 3
|
80
83
|
expect(torque[2]).must_be_within_epsilon 5000.0
|
81
84
|
end
|
82
85
|
end
|
83
86
|
|
84
|
-
describe "
|
87
|
+
describe "Tire.force_vector" do
|
85
88
|
it "calculates a (3D) force given 3D torque and 2D radius" do
|
86
|
-
# let's invert the
|
89
|
+
# let's invert the Tire.torque_vector case from above:
|
90
|
+
skip # Vector
|
87
91
|
torque = Vector[0, 0, 5000]
|
88
92
|
radius = Vector[0, 5]
|
89
|
-
force =
|
93
|
+
force = T.force_vector(torque, radius)
|
90
94
|
expect(force).must_be_instance_of Vector
|
91
95
|
expect(force.size).must_equal 3
|
92
96
|
expect(force[0]).must_be_within_epsilon 1000.0
|
@@ -96,7 +100,7 @@ describe W do
|
|
96
100
|
# negative-x radius means positive-y force
|
97
101
|
torque = Vector[0, 0, 500]
|
98
102
|
radius = Vector[-5, 0]
|
99
|
-
force =
|
103
|
+
force = T.force_vector(torque, radius)
|
100
104
|
expect(force).must_be_instance_of Vector
|
101
105
|
expect(force.size).must_equal 3
|
102
106
|
expect(force[1]).must_be_within_epsilon 100.0
|
@@ -106,72 +110,78 @@ describe W do
|
|
106
110
|
describe "instance methods" do
|
107
111
|
before do
|
108
112
|
@env = DrivingPhysics::Environment.new
|
109
|
-
@
|
113
|
+
@tire = T.new(@env)
|
110
114
|
end
|
111
115
|
|
112
116
|
it "initializes" do
|
113
|
-
expect(@
|
114
|
-
expect(@
|
115
|
-
expect(@
|
117
|
+
expect(@tire).must_be_instance_of T
|
118
|
+
expect(@tire.density).must_equal T::DENSITY # sanity check
|
119
|
+
expect(@tire.mass).must_be_within_epsilon 25.01
|
116
120
|
|
117
|
-
with_mass =
|
121
|
+
with_mass = T.new(@env) { |w|
|
122
|
+
w.mass = 99.01
|
123
|
+
}
|
118
124
|
expect(with_mass.mass).must_equal 99.01
|
119
|
-
expect(with_mass.density).wont_equal
|
125
|
+
expect(with_mass.density).wont_equal T::DENSITY
|
120
126
|
end
|
121
127
|
|
122
128
|
it "has a string representation" do
|
123
|
-
str = @
|
129
|
+
str = @tire.to_s
|
124
130
|
expect(str).must_be_instance_of String
|
125
131
|
expect(str.length).must_be(:>, 5)
|
126
132
|
end
|
127
133
|
|
128
134
|
it "loses radius as it wears" do
|
129
|
-
|
130
|
-
|
131
|
-
|
135
|
+
old_r = @tire.radius
|
136
|
+
wear_amt = 50/1000r
|
137
|
+
@tire.wear! wear_amt
|
138
|
+
expect(@tire.radius).must_equal old_r - wear_amt
|
132
139
|
end
|
133
140
|
|
134
141
|
it "calculates mass from current radius" do
|
135
|
-
expect(@
|
136
|
-
@
|
137
|
-
expect(@
|
142
|
+
expect(@tire.mass).must_be_within_epsilon 25.01
|
143
|
+
@tire.wear!(50/1000r)
|
144
|
+
expect(@tire.mass).must_be_within_epsilon 18.378
|
138
145
|
end
|
139
146
|
|
140
147
|
it "has volume" do
|
141
|
-
expect(@
|
142
|
-
expect(@
|
148
|
+
expect(@tire.volume).must_be_within_epsilon 0.07697
|
149
|
+
expect(@tire.volume_l).must_be_within_epsilon 76.96902
|
143
150
|
end
|
144
151
|
|
145
152
|
it "has inertia" do
|
146
|
-
expect(@
|
153
|
+
expect(@tire.rotational_inertia).must_be_within_epsilon 1.5321
|
147
154
|
end
|
148
155
|
|
149
156
|
it "has traction force based on normal force" do
|
150
157
|
scalar_nf = 9800
|
151
|
-
expect(@
|
152
|
-
expect(@
|
158
|
+
expect(@tire.traction scalar_nf).must_equal 10780.0
|
159
|
+
expect(@tire.traction scalar_nf, static: false).must_equal 6860.0
|
153
160
|
|
161
|
+
skip # Vector
|
154
162
|
vector_nf = Vector[9800, 0]
|
155
|
-
expect(@
|
156
|
-
expect(@
|
163
|
+
expect(@tire.traction vector_nf).must_equal Vector[10780.0, 0.0]
|
164
|
+
expect(@tire.traction vector_nf, static: false).
|
157
165
|
must_equal Vector[6860.0, 0.0]
|
158
166
|
end
|
159
167
|
|
160
168
|
it "determines (e.g. thrust) force based on axle torque" do
|
161
|
-
expect(@
|
162
|
-
@
|
163
|
-
expect(@
|
169
|
+
expect(@tire.force 1000).must_be_within_epsilon 2857.143
|
170
|
+
@tire.wear! 50/1000r
|
171
|
+
expect(@tire.force 1000).must_be_within_epsilon 3333.333
|
164
172
|
end
|
165
173
|
|
166
174
|
it "determines tractable torque" do
|
167
175
|
scalar_nf = 9800
|
168
|
-
expect(@
|
169
|
-
kin_tq = @
|
176
|
+
expect(@tire.tractable_torque scalar_nf).must_be_within_epsilon 3773.0
|
177
|
+
kin_tq = @tire.tractable_torque scalar_nf, static: false
|
170
178
|
expect(kin_tq).must_be_within_epsilon 2401.0
|
171
179
|
|
172
180
|
# not sure about how torque vectors work, but the "math" "works":
|
181
|
+
skip # Vector
|
173
182
|
vector_nf = Vector[9800, 0]
|
174
|
-
expect(@
|
183
|
+
expect(@tire.tractable_torque(vector_nf)[0]).
|
184
|
+
must_be_within_epsilon 3773.0
|
175
185
|
end
|
176
186
|
end
|
177
187
|
end
|
data/test/vector_force.rb
CHANGED
@@ -74,9 +74,15 @@ describe VectorForce do
|
|
74
74
|
end
|
75
75
|
|
76
76
|
it "calculates the rotational resistance as a function of velocity" do
|
77
|
+
rr = VectorForce.rotational_resistance(@v, rot_const: 0)
|
78
|
+
rr2 = VectorForce.rotational_resistance(@v * 2, rot_const: 0)
|
79
|
+
expect(rr2).must_equal rr * 2
|
80
|
+
|
81
|
+
# now with rot_const != 0, the relationship is skewed
|
77
82
|
rr = VectorForce.rotational_resistance(@v)
|
78
83
|
rr2 = VectorForce.rotational_resistance(@v * 2)
|
79
|
-
expect(rr2).
|
84
|
+
expect(rr2).wont_equal rr * 2 # because of rot_const
|
85
|
+
expect(rr2.magnitude).must_be(:<, (rr * 2).magnitude)
|
80
86
|
end
|
81
87
|
|
82
88
|
it "sums resistance forces" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: driving_physics
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rick Hull
|
@@ -19,20 +19,36 @@ files:
|
|
19
19
|
- README.md
|
20
20
|
- Rakefile
|
21
21
|
- VERSION
|
22
|
+
- demo/car.rb
|
23
|
+
- demo/disk.rb
|
24
|
+
- demo/gearbox.rb
|
25
|
+
- demo/motor.rb
|
26
|
+
- demo/mruby/disk.rb
|
27
|
+
- demo/mruby/motor.rb
|
28
|
+
- demo/powertrain.rb
|
22
29
|
- demo/scalar_force.rb
|
30
|
+
- demo/tire.rb
|
23
31
|
- demo/vector_force.rb
|
24
|
-
- demo/wheel.rb
|
25
32
|
- driving_physics.gemspec
|
26
33
|
- lib/driving_physics.rb
|
34
|
+
- lib/driving_physics/car.rb
|
35
|
+
- lib/driving_physics/cli.rb
|
36
|
+
- lib/driving_physics/disk.rb
|
27
37
|
- lib/driving_physics/environment.rb
|
38
|
+
- lib/driving_physics/gearbox.rb
|
28
39
|
- lib/driving_physics/imperial.rb
|
40
|
+
- lib/driving_physics/motor.rb
|
41
|
+
- lib/driving_physics/mruby.rb
|
42
|
+
- lib/driving_physics/power.rb
|
43
|
+
- lib/driving_physics/powertrain.rb
|
29
44
|
- lib/driving_physics/scalar_force.rb
|
45
|
+
- lib/driving_physics/tire.rb
|
30
46
|
- lib/driving_physics/vector_force.rb
|
31
|
-
-
|
47
|
+
- test/disk.rb
|
32
48
|
- test/driving_physics.rb
|
33
49
|
- test/scalar_force.rb
|
50
|
+
- test/tire.rb
|
34
51
|
- test/vector_force.rb
|
35
|
-
- test/wheel.rb
|
36
52
|
homepage: https://github.com/rickhull/driving_physics
|
37
53
|
licenses:
|
38
54
|
- LGPL-3.0
|