driving_physics 0.0.0.2 → 0.0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/test/tire.rb CHANGED
@@ -1,125 +1,181 @@
1
- require 'driving_physics/tire'
2
1
  require 'minitest/autorun'
2
+ require 'driving_physics/tire'
3
3
 
4
- include DrivingPhysics
5
-
6
- describe Tire do
7
- TP = Tire::TemperatureProfile
4
+ T = DrivingPhysics::Tire
8
5
 
9
- describe TP do
10
- before do
11
- @tp = TP.new
12
- end
6
+ describe T do
7
+ describe "Tire.traction" do
8
+ it "calculates traction force from normal force and coeff of friction" do
9
+ scalar_nf = 9800 # N
10
+ cof = 1.1
11
+ scalar_t = T.traction(scalar_nf, cof)
12
+ expect(scalar_t).must_equal 10780.0
13
13
 
14
- it "initializes with two same-sized arrays" do
15
- expect(@tp).must_be_kind_of TP
16
- expect(TP.new([0,1,2,3], [0,1.0,0.7,0.4])).wont_be_nil
17
- expect { TP.new('', '') }.must_raise ArgumentError
18
- expect { TP.new([]) }.must_raise ArgumentError
19
- expect { TP.new([0], []) }.must_raise ArgumentError
20
- expect { TP.new([], [0.0]) }.must_raise ArgumentError
14
+ vector_nf = Vector[9800, 0]
15
+ vector_t = T.traction(vector_nf, cof)
16
+ expect(vector_t).must_equal Vector[10780.0, 0.0]
21
17
  end
18
+ end
22
19
 
23
- it "determines a grip number from a temp number" do
24
- { -500 => TP::MIN_GRIP,
25
- -100 => 0.1,
26
- -0.0001 => 0.1,
27
- 0.0 => 0.5
28
- }.each { |temp, gf| expect(@tp.grip_factor(temp)).must_equal gf }
29
- end
20
+ describe "Tire.volume" do
21
+ it "calculates the volume (m^3) of disk given radius and width" do
22
+ cubic_m = T.volume(1.0, 1.0)
23
+ expect(cubic_m).must_equal Math::PI
30
24
 
31
- it "has a critical_temp above the temp for 100%" do
32
- expect(@tp.critical_temp).must_be(:>, 90)
33
- expect(@tp.critical_temp).must_equal 105
25
+ cubic_m = T.volume(0.35, 0.2)
26
+ expect(cubic_m).must_be_within_epsilon 0.076969
34
27
  end
28
+ end
35
29
 
36
- it "has a map that increases to 100% and decreases below 80%" do
37
- expect {
38
- TP.new([0,1,2,3,4], [0.0,0.1,0.2,0.3,0.4])
39
- }.must_raise TP::Error
30
+ describe "Tire.volume_l" do
31
+ it "calculates the volume (L) of a disk given radius and width" do
32
+ liters = T.volume_l(1.0, 1.0)
33
+ expect(liters).must_equal Math::PI * 1000
40
34
 
41
- expect {
42
- TP.new([0,1,2,3,4], [0.0, 1.0, 0.99, 0.98, 0.97])
43
- }.must_raise TP::Error
35
+ liters = T.volume_l(0.35, 0.2)
36
+ expect(liters).must_be_within_epsilon 76.96902
44
37
  end
45
38
  end
46
39
 
47
- before do
48
- @t = Tire.new
40
+ describe "Tire.density" do
41
+ it "calculates the density (kg/L) given mass and volume" do
42
+ expect(T.density(25.0, 25.0)).must_equal 1.0
43
+ expect(T.density(50.0, 25.0)).must_equal 2.0
44
+ end
49
45
  end
50
46
 
51
- it "initializes with default values without a block" do
52
- expect(@t).must_be_kind_of Tire
47
+ describe "Tire.mass" do
48
+ it "calculates the mass (kg) of a disk given radius, width, and density" do
49
+ expect(T.mass(0.35, 0.2, T::DENSITY)).must_be_within_epsilon 25.015
50
+ end
53
51
  end
54
52
 
55
- it "accepts a block to initialize with custom values" do
56
- t = Tire.new { |x|
57
- x.tread_mm = 9999
58
- x.g_factor = -0.1234
59
- }
60
-
61
- expect(t.tread_mm).must_equal 9999
62
- expect(t.g_factor).must_equal(-0.1234)
53
+ describe "Tire.rotational_inertia" do
54
+ it "calculates rotational inertia for a disk given radius and mass" do
55
+ expect(T.rotational_inertia(0.35, 25.0)).must_be_within_epsilon 1.53125
56
+ end
63
57
  end
64
58
 
65
- it "knows when the tread is gone" do
66
- expect(@t.tread_left?).must_equal true
67
-
68
- @t.condition.tread_mm = 0.00001
69
- expect(@t.tread_left?).must_equal true
70
-
71
- @t.condition.tread_mm = 0.0
72
- expect(@t.tread_left?).must_equal false
59
+ describe "Tire.alpha" do
60
+ it "calculates angular acceleration from torque and inertia" do
61
+ scalar_torque = 1000
62
+ inertia = T.rotational_inertia(0.35, 25.0)
63
+ expect(T.alpha scalar_torque, inertia).must_be_within_epsilon 653.061
64
+
65
+ vector_torque = Vector[0, 0, 1000]
66
+ vector_alpha = T.alpha vector_torque, inertia
67
+ expect(vector_alpha).must_be_instance_of Vector
68
+ expect(vector_alpha.size).must_equal 3
69
+ expect(vector_alpha[2]).must_be_within_epsilon 653.06
70
+ end
73
71
  end
74
72
 
75
- it "has 50% grip when down to the cords" do
76
- expect(@t.tread_factor).must_equal 1.0
73
+ describe "Tire.torque_vector" do
74
+ it "calculates a torque in the 3rd dimension given 2D force and radius" do
75
+ force = Vector[1000, 0]
76
+ radius = Vector[0, 5]
77
+ torque = T.torque_vector(force, radius)
78
+ expect(torque).must_be_instance_of Vector
79
+ expect(torque.size).must_equal 3
80
+ expect(torque[2]).must_be_within_epsilon 5000.0
81
+ end
82
+ end
77
83
 
78
- @t.condition.tread_mm = 0.0
79
- expect(@t.tread_factor).must_be_within_epsilon 0.5
84
+ describe "Tire.force_vector" do
85
+ it "calculates a (3D) force given 3D torque and 2D radius" do
86
+ # let's invert the Tire.torque_vector case from above:
87
+ torque = Vector[0, 0, 5000]
88
+ radius = Vector[0, 5]
89
+ force = T.force_vector(torque, radius)
90
+ expect(force).must_be_instance_of Vector
91
+ expect(force.size).must_equal 3
92
+ expect(force[0]).must_be_within_epsilon 1000.0
93
+
94
+ # now let's rotate the radius into the x-dimension
95
+ # right hand rule, positive torque means thumb into screen, clockwise
96
+ # negative-x radius means positive-y force
97
+ torque = Vector[0, 0, 500]
98
+ radius = Vector[-5, 0]
99
+ force = T.force_vector(torque, radius)
100
+ expect(force).must_be_instance_of Vector
101
+ expect(force.size).must_equal 3
102
+ expect(force[1]).must_be_within_epsilon 100.0
103
+ end
80
104
  end
81
105
 
82
- it "has less than 10% tread factor when the cords start to wear" do
83
- expect(@t.tread_factor).must_equal 1.0
106
+ describe "instance methods" do
107
+ before do
108
+ @env = DrivingPhysics::Environment.new
109
+ @tire = T.new(@env)
110
+ end
84
111
 
85
- @t.condition.tread_mm = 5.0
86
- expect(@t.tread_factor).must_equal 1.0
112
+ it "initializes" do
113
+ expect(@tire).must_be_instance_of T
114
+ expect(@tire.density).must_equal T::DENSITY # sanity check
115
+ expect(@tire.mass).must_be_within_epsilon 25.01
87
116
 
88
- @t.condition.tread_mm = 0.0
89
- expect(@t.tread_factor).must_be_within_epsilon 0.5
117
+ with_mass = T.new(@env) { |w|
118
+ w.mass = 99.01
119
+ }
120
+ expect(with_mass.mass).must_equal 99.01
121
+ expect(with_mass.density).wont_equal T::DENSITY
122
+ end
90
123
 
91
- @t.condition.cords_mm = 0.9
92
- expect(@t.tread_factor).must_be_within_epsilon 0.45
93
- end
124
+ it "has a string representation" do
125
+ str = @tire.to_s
126
+ expect(str).must_be_instance_of String
127
+ expect(str.length).must_be(:>, 5)
128
+ end
94
129
 
95
- it "has decreasing heat cycle factor" do
96
- expect(@t.condition.heat_cycles).must_equal 0
97
- expect(@t.heat_cycle_factor).must_equal 1.0
130
+ it "loses radius as it wears" do
131
+ old_r = @tire.radius
132
+ wear_amt = 50/1000r
133
+ @tire.wear! wear_amt
134
+ expect(@tire.radius).must_equal old_r - wear_amt
135
+ end
98
136
 
99
- @t.condition.heat_cycles = 20
100
- expect(@t.heat_cycle_factor).must_be(:<, 1.0)
101
- end
137
+ it "calculates mass from current radius" do
138
+ expect(@tire.mass).must_be_within_epsilon 25.01
139
+ @tire.wear!(50/1000r)
140
+ expect(@tire.mass).must_be_within_epsilon 18.378
141
+ end
102
142
 
103
- it "has a temp factor according to temperature profile" do
104
- expect(@t.condition.temp_c).must_equal 25
105
- expect(@t.temp_factor).must_equal 0.75
143
+ it "has volume" do
144
+ expect(@tire.volume).must_be_within_epsilon 0.07697
145
+ expect(@tire.volume_l).must_be_within_epsilon 76.96902
146
+ end
106
147
 
107
- @t.condition.temp_c = 90
108
- expect(@t.temp_factor).must_equal 1.0
109
- end
148
+ it "has inertia" do
149
+ expect(@tire.rotational_inertia).must_be_within_epsilon 1.5321
150
+ end
110
151
 
111
- it "incorporates temp, heat_cycles, and tread depth into available grip" do
112
- @t.condition.temp_c = 60
113
- expect(@t.temp_factor).must_be_within_epsilon 0.8
152
+ it "has traction force based on normal force" do
153
+ scalar_nf = 9800
154
+ expect(@tire.traction scalar_nf).must_equal 10780.0
155
+ expect(@tire.traction scalar_nf, static: false).must_equal 6860.0
114
156
 
115
- @t.condition.heat_cycles = 10
116
- expect(@t.heat_cycle_factor).must_be_within_epsilon 0.96
157
+ vector_nf = Vector[9800, 0]
158
+ expect(@tire.traction vector_nf).must_equal Vector[10780.0, 0.0]
159
+ expect(@tire.traction vector_nf, static: false).
160
+ must_equal Vector[6860.0, 0.0]
161
+ end
162
+
163
+ it "determines (e.g. thrust) force based on axle torque" do
164
+ expect(@tire.force 1000).must_be_within_epsilon 2857.143
165
+ @tire.wear! 50/1000r
166
+ expect(@tire.force 1000).must_be_within_epsilon 3333.333
167
+ end
117
168
 
118
- @t.condition.tread_mm = 5.0
119
- expect(@t.tread_factor).must_equal 1.0
120
- expect(@t.max_g).must_be_within_epsilon 0.768
169
+ it "determines tractable torque" do
170
+ scalar_nf = 9800
171
+ expect(@tire.tractable_torque scalar_nf).must_be_within_epsilon 3773.0
172
+ kin_tq = @tire.tractable_torque scalar_nf, static: false
173
+ expect(kin_tq).must_be_within_epsilon 2401.0
121
174
 
122
- @t.condition.tread_mm = 0.0
123
- expect(@t.max_g).must_be_within_epsilon 0.384
175
+ # not sure about how torque vectors work, but the "math" "works":
176
+ vector_nf = Vector[9800, 0]
177
+ expect(@tire.tractable_torque(vector_nf)[0]).
178
+ must_be_within_epsilon 3773.0
179
+ end
124
180
  end
125
181
  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).must_equal rr * 2
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.0.2
4
+ version: 0.0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rick Hull
@@ -20,25 +20,32 @@ files:
20
20
  - Rakefile
21
21
  - VERSION
22
22
  - demo/car.rb
23
+ - demo/disk.rb
24
+ - demo/gearbox.rb
25
+ - demo/motor.rb
26
+ - demo/powertrain.rb
23
27
  - demo/scalar_force.rb
24
28
  - demo/tire.rb
25
29
  - demo/vector_force.rb
26
- - demo/wheel.rb
27
30
  - driving_physics.gemspec
28
31
  - lib/driving_physics.rb
29
32
  - lib/driving_physics/car.rb
33
+ - lib/driving_physics/cli.rb
34
+ - lib/driving_physics/disk.rb
30
35
  - lib/driving_physics/environment.rb
36
+ - lib/driving_physics/gearbox.rb
31
37
  - lib/driving_physics/imperial.rb
38
+ - lib/driving_physics/motor.rb
39
+ - lib/driving_physics/power.rb
40
+ - lib/driving_physics/powertrain.rb
32
41
  - lib/driving_physics/scalar_force.rb
33
42
  - lib/driving_physics/tire.rb
34
43
  - lib/driving_physics/vector_force.rb
35
- - lib/driving_physics/wheel.rb
36
- - test/car.rb
44
+ - test/disk.rb
37
45
  - test/driving_physics.rb
38
46
  - test/scalar_force.rb
39
47
  - test/tire.rb
40
48
  - test/vector_force.rb
41
- - test/wheel.rb
42
49
  homepage: https://github.com/rickhull/driving_physics
43
50
  licenses:
44
51
  - LGPL-3.0
data/demo/wheel.rb DELETED
@@ -1,84 +0,0 @@
1
- require 'driving_physics/wheel'
2
-
3
- include DrivingPhysics
4
-
5
- env = Environment.new
6
- wheel = Wheel.new(env, mass: 25.0)
7
-
8
- puts env
9
- puts wheel
10
-
11
- # 1000 kg car
12
- # 4 tires
13
- # 250 kg per tire plus tire mass
14
-
15
- supported_mass = 1000 # kg
16
- total_mass = supported_mass + 4 * wheel.mass
17
- corner_mass = Rational(total_mass) / 4
18
- normal_force = corner_mass * env.g
19
- axle_torque = 1000 # N*m
20
- friction_loss = 0.05 # 5% friction / hysteresis loss
21
-
22
- puts [format("Corner mass: %d kg", corner_mass),
23
- format("Normal force: %.1f N", normal_force),
24
- format("Axle torque: %d Nm", axle_torque),
25
- ].join("\n")
26
- puts
27
-
28
- traction = wheel.traction(normal_force)
29
- drive_force = wheel.force(axle_torque)
30
- inertial_loss = wheel.inertial_loss(axle_torque, supported_mass)
31
- friction_loss *= axle_torque # 5% of the axle torque
32
-
33
- # drive force = (axle torque - inertia - friction) limited by traction
34
-
35
- net_axle_torque = axle_torque - inertial_loss - friction_loss
36
- net_drive_force = wheel.force(net_axle_torque)
37
- net_drive_force = traction if net_drive_force > traction # traction limited
38
-
39
- acc = DrivingPhysics.acc(net_drive_force, supported_mass) # translational
40
-
41
- puts [format("Traction: %.1f N", traction),
42
- format("Drive force: %.1f N", drive_force),
43
- format("Inertial loss: %.1f Nm", inertial_loss),
44
- format("Friction loss: %.1f Nm", friction_loss),
45
- format("Net Axle Torque: %.1f Nm", net_axle_torque),
46
- format("Net Drive Force: %.1f N", net_drive_force),
47
- format("Acceleration: %.1f m/s/s", acc),
48
- format("Alpha: %.2f r/s/s", acc / wheel.radius_m),
49
- ].join("\n")
50
- puts
51
-
52
- duration = 100 # sec
53
-
54
- dist = 0.0 # meters
55
- speed = 0.0 # meters/s
56
-
57
- theta = 0.0 # radians
58
- omega = 0.0 # radians/s
59
-
60
- (duration * env.hz).times { |i|
61
- # accumulate frictional losses with speed (omega)
62
- omega_loss_cof = [wheel.omega_friction * omega, 1.0].min
63
- slowed_acc = acc - acc * omega_loss_cof
64
-
65
- # translational kinematics
66
- speed += slowed_acc * env.tick
67
- dist += speed * env.tick
68
-
69
- # rotational kinematics
70
- alpha = slowed_acc / wheel.radius_m
71
- omega += alpha * env.tick
72
- theta += omega * env.tick
73
-
74
- if i < 10 or
75
- (i < 10_000 and i%1000 == 0) or
76
- (i % 10_000 == 0)
77
- puts DrivingPhysics.elapsed_display(i)
78
- puts format("Wheel: %.1f r %.2f r/s %.3f r/s^2", theta, omega, alpha)
79
- puts format(" Car: %.1f m %.2f m/s %.3f m/s^2", dist, speed, slowed_acc)
80
- puts format("Omega Frictional Loss: %.1f%%", omega_loss_cof * 100)
81
- puts "Press [enter]"
82
- gets
83
- end
84
- }
@@ -1,191 +0,0 @@
1
- require 'driving_physics/environment'
2
- require 'driving_physics/vector_force'
3
-
4
- module DrivingPhysics
5
- # Rotational complements to acc/vel/pos
6
- # alpha - angular acceleration
7
- # omega - angular velocity (radians / s)
8
- # theta - radians
9
-
10
- class Wheel
11
- # Note, this is not the density of solid rubber. This density
12
- # yields a sensible mass for a wheel / tire combo at common radius
13
- # and width, assuming a uniform density
14
- # e.g. 25kg at 350mm R x 200mm W
15
- #
16
- DENSITY = 0.325 # kg / L
17
-
18
- # * the traction force opposes the axle torque / drive force
19
- # thus, driving the car forward
20
- # * if the drive force exceeds the traction force, slippage occurs
21
- # * slippage reduces the available traction force further
22
- # * if the drive force is not reduced, the slippage increases
23
- # until resistance forces equal the drive force
24
- def self.traction(normal_force, cof)
25
- normal_force * cof
26
- end
27
-
28
- def self.force(axle_torque, radius_m)
29
- axle_torque / radius_m.to_f
30
- end
31
-
32
- # in m^3
33
- def self.volume(radius_m, width_m)
34
- Math::PI * radius_m ** 2 * width_m.to_f
35
- end
36
-
37
- # in L
38
- def self.volume_l(radius_m, width_m)
39
- volume(radius_m, width_m) * 1000
40
- end
41
-
42
- def self.density(mass, volume_l)
43
- mass.to_f / volume_l
44
- end
45
-
46
- def self.mass(radius_m, width_m, density)
47
- density * volume_l(radius_m, width_m)
48
- end
49
-
50
- # I = 1/2 (m)(r^2) for a disk
51
- def self.rotational_inertia(radius_m, mass)
52
- mass * radius_m**2 / 2.0
53
- end
54
- class << self
55
- alias_method(:moment_of_inertia, :rotational_inertia)
56
- end
57
-
58
- # angular acceleration
59
- def self.alpha(torque, inertia)
60
- torque / inertia
61
- end
62
-
63
- def self.tangential(rotational, radius_m)
64
- rotational * radius_m
65
- end
66
- class << self
67
- alias_method(:tangential_a, :tangential)
68
- alias_method(:tangential_v, :tangential)
69
- alias_method(:tangential_p, :tangential)
70
- end
71
-
72
- # vectors only
73
- def self.torque_vector(force, radius)
74
- if !force.is_a?(Vector) or force.size != 2
75
- raise(ArgumentError, "force must be a 2D vector")
76
- end
77
- if !radius.is_a?(Vector) or radius.size != 2
78
- raise(ArgumentError, "radius must be a 2D vector")
79
- end
80
- force = Vector[force[0], force[1], 0]
81
- radius = Vector[radius[0], radius[1], 0]
82
- force.cross(radius)
83
- end
84
-
85
- # vectors only
86
- def self.force_vector(torque, radius)
87
- if !torque.is_a?(Vector) or torque.size != 3
88
- raise(ArgumentError, "torque must be a 3D vector")
89
- end
90
- if !radius.is_a?(Vector) or radius.size != 2
91
- raise(ArgumentError, "radius must be a 2D vector")
92
- end
93
- radius = Vector[radius[0], radius[1], 0]
94
- radius.cross(torque) / radius.dot(radius)
95
- end
96
-
97
- attr_reader :env, :radius, :radius_m, :width, :width_m, :density, :temp,
98
- :mu_s, :mu_k, :omega_friction
99
-
100
- def initialize(env,
101
- radius: 350, width: 200, density: DENSITY,
102
- temp: nil, mass: nil,
103
- mu_s: 1.1, mu_k: 0.7,
104
- omega_friction: 0.002)
105
- @env = env
106
- @radius = radius.to_f # mm
107
- @radius_m = @radius / 1000
108
- @width = width.to_f # mm
109
- @width_m = @width / 1000
110
- @mu_s = mu_s.to_f # static friction
111
- @mu_k = mu_k.to_f # kinetic friction
112
- @omega_friction = omega_friction # scales with speed
113
- @density = mass.nil? ? density : self.class.density(mass, volume_l)
114
- @temp = temp.to_f || @env.air_temp
115
- end
116
-
117
- def to_s
118
- [[format("%d mm (R) x %d mm (W)", @radius, @width),
119
- format("Mass: %.1f kg %.3f kg/L", self.mass, @density),
120
- format("cF: %.1f / %.1f", @mu_s, @mu_k),
121
- ].join(" | "),
122
- [format("Temp: %.1f C", @temp),
123
- ].join(" | "),
124
- ].join("\n")
125
- end
126
-
127
- def wear!(amount_mm)
128
- @radius -= amount_mm
129
- @radius_m = @radius / 1000
130
- end
131
-
132
- def heat!(amount_deg_c)
133
- @temp += amount_deg_c
134
- end
135
-
136
- def mass
137
- self.class.mass(@radius_m, @width_m, @density)
138
- end
139
-
140
- # in m^3
141
- def volume
142
- self.class.volume(@radius_m, @width_m)
143
- end
144
-
145
- # in L
146
- def volume_l
147
- self.class.volume_l(@radius_m, @width_m)
148
- end
149
-
150
- def rotational_inertia
151
- self.class.rotational_inertia(@radius_m, self.mass)
152
- end
153
- alias_method(:moment_of_inertia, :rotational_inertia)
154
-
155
- def traction(nf, static: true)
156
- self.class.traction(nf, static ? @mu_s : @mu_k)
157
- end
158
-
159
- def force(axle_torque)
160
- self.class.force(axle_torque, @radius_m)
161
- end
162
-
163
- # how much torque to accelerate rotational inertia at alpha
164
- def inertial_torque(alpha)
165
- alpha * self.rotational_inertia
166
- end
167
-
168
- # this doesn't take inertial losses or internal frictional losses
169
- # into account. input torque required to saturate traction will be
170
- # higher than what this method returns
171
- def tractable_torque(nf, static: true)
172
- traction(nf, static: static) * @radius_m
173
- end
174
-
175
- # inertial loss in terms of axle torque when used as a drive wheel
176
- def inertial_loss(axle_torque, total_driven_mass)
177
- drive_force = self.force(axle_torque)
178
- force_loss = 0
179
- # The force loss depends on the acceleration, but the acceleration
180
- # depends on the force loss. Converge the value via 5 round trips.
181
- # This is a rough way to compute an integral and should be accurate
182
- # to 8+ digits.
183
- 5.times {
184
- acc = DrivingPhysics.acc(drive_force - force_loss, total_driven_mass)
185
- alpha = acc / @radius_m
186
- force_loss = self.inertial_torque(alpha) / @radius_m
187
- }
188
- force_loss * @radius_m
189
- end
190
- end
191
- end