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
data/test/vector_force.rb
CHANGED
@@ -1,52 +1,91 @@
|
|
1
1
|
require 'minitest/autorun'
|
2
2
|
require 'driving_physics/vector_force'
|
3
3
|
|
4
|
-
|
4
|
+
describe DrivingPhysics do
|
5
|
+
describe "random_centered_zero" do
|
6
|
+
it "generates uniformly random numbers centered on zero" do
|
7
|
+
hsh = {}
|
8
|
+
110_000.times {
|
9
|
+
num = DrivingPhysics.random_centered_zero(5)
|
10
|
+
hsh[num] ||= 0
|
11
|
+
hsh[num] += 1
|
12
|
+
}
|
13
|
+
# note, this will fail occasionally due to chance
|
14
|
+
hsh.values.each { |count|
|
15
|
+
expect(count).must_be(:>=, 9000)
|
16
|
+
expect(count).must_be(:<=, 11000)
|
17
|
+
}
|
18
|
+
end
|
5
19
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@mass = 1000
|
11
|
-
@weight = @mass * G
|
20
|
+
it "coerces magnitude=0 to magnitude=1" do
|
21
|
+
a = Array.new(999) { DrivingPhysics.random_centered_zero(0) }
|
22
|
+
expect(a.all? { |i| i == 0 }).must_equal false
|
23
|
+
end
|
12
24
|
end
|
13
25
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
+
describe "random_unit_vector" do
|
27
|
+
it "generates a random unit vector" do
|
28
|
+
low_res = DrivingPhysics.random_unit_vector(2, resolution: 1)
|
29
|
+
if low_res[0] == 0.0
|
30
|
+
expect(low_res[1].abs).must_equal 1.0
|
31
|
+
elsif low_res[0].abs == 1.0
|
32
|
+
expect(low_res[1]).must_equal 0.0
|
33
|
+
elsif low_res[0].abs.round(3) == 0.707
|
34
|
+
expect(low_res[1].abs.round(3)) == 0.707
|
35
|
+
else
|
36
|
+
p low_res
|
37
|
+
raise "unexpected"
|
38
|
+
end
|
39
|
+
|
40
|
+
9.times {
|
41
|
+
high_res = DrivingPhysics.random_unit_vector(3, resolution: 9)
|
42
|
+
expect(high_res.magnitude).must_be_within_epsilon 1.0
|
43
|
+
}
|
44
|
+
end
|
26
45
|
end
|
27
46
|
|
28
|
-
|
29
|
-
a
|
30
|
-
|
47
|
+
describe "torque_vector" do
|
48
|
+
it "calculates a torque in the 3rd dimension given 2D force and radius" do
|
49
|
+
force = Vector[1000, 0]
|
50
|
+
radius = Vector[0, 5]
|
51
|
+
torque = DrivingPhysics.torque_vector(force, radius)
|
52
|
+
expect(torque).must_be_instance_of Vector
|
53
|
+
expect(torque.size).must_equal 3
|
54
|
+
expect(torque[2]).must_be_within_epsilon 5000.0
|
55
|
+
end
|
31
56
|
end
|
32
57
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
expect(
|
41
|
-
|
42
|
-
|
43
|
-
|
58
|
+
describe "force_vector" do
|
59
|
+
it "calculates a (3D) force given 3D torque and 2D radius" do
|
60
|
+
# let's invert the DrivingPhysics.torque_vector case from above:
|
61
|
+
torque = Vector[0, 0, 5000]
|
62
|
+
radius = Vector[0, 5]
|
63
|
+
force = DrivingPhysics.force_vector(torque, radius)
|
64
|
+
expect(force).must_be_instance_of Vector
|
65
|
+
expect(force.size).must_equal 3
|
66
|
+
expect(force[0]).must_be_within_epsilon 1000.0
|
67
|
+
|
68
|
+
# now let's rotate the radius into the x-dimension
|
69
|
+
# right hand rule, positive torque means thumb into screen, clockwise
|
70
|
+
# negative-x radius means positive-y force
|
71
|
+
torque = Vector[0, 0, 500]
|
72
|
+
radius = Vector[-5, 0]
|
73
|
+
force = DrivingPhysics.force_vector(torque, radius)
|
74
|
+
expect(force).must_be_instance_of Vector
|
75
|
+
expect(force.size).must_equal 3
|
76
|
+
expect(force[1]).must_be_within_epsilon 100.0
|
44
77
|
end
|
78
|
+
end
|
79
|
+
end
|
45
80
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
81
|
+
include DrivingPhysics
|
82
|
+
|
83
|
+
describe VectorForce do
|
84
|
+
before do
|
85
|
+
@drive_force = Vector[7000.0, 0.0]
|
86
|
+
@v = Vector[3.0, 0]
|
87
|
+
@mass = 1000
|
88
|
+
@weight = @mass * G
|
50
89
|
end
|
51
90
|
|
52
91
|
it "calculates air resistance as the square of velocity" 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.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rick Hull
|
@@ -9,7 +9,21 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
date: 1980-01-01 00:00:00.000000000 Z
|
12
|
-
dependencies:
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: device_control
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.3'
|
13
27
|
description: WIP
|
14
28
|
email:
|
15
29
|
executables: []
|
@@ -23,8 +37,9 @@ files:
|
|
23
37
|
- demo/disk.rb
|
24
38
|
- demo/gearbox.rb
|
25
39
|
- demo/motor.rb
|
26
|
-
- demo/mruby/
|
40
|
+
- demo/mruby/car.rb
|
27
41
|
- demo/mruby/motor.rb
|
42
|
+
- demo/pid_controller.rb
|
28
43
|
- demo/powertrain.rb
|
29
44
|
- demo/scalar_force.rb
|
30
45
|
- demo/tire.rb
|
@@ -33,19 +48,25 @@ files:
|
|
33
48
|
- lib/driving_physics.rb
|
34
49
|
- lib/driving_physics/car.rb
|
35
50
|
- lib/driving_physics/cli.rb
|
51
|
+
- lib/driving_physics/cockpit.rb
|
36
52
|
- lib/driving_physics/disk.rb
|
37
53
|
- lib/driving_physics/environment.rb
|
38
54
|
- lib/driving_physics/gearbox.rb
|
39
55
|
- lib/driving_physics/imperial.rb
|
40
56
|
- lib/driving_physics/motor.rb
|
41
57
|
- lib/driving_physics/mruby.rb
|
58
|
+
- lib/driving_physics/pid_controller.rb
|
42
59
|
- lib/driving_physics/power.rb
|
43
60
|
- lib/driving_physics/powertrain.rb
|
44
61
|
- lib/driving_physics/scalar_force.rb
|
62
|
+
- lib/driving_physics/timer.rb
|
45
63
|
- lib/driving_physics/tire.rb
|
46
64
|
- lib/driving_physics/vector_force.rb
|
47
65
|
- test/disk.rb
|
48
66
|
- test/driving_physics.rb
|
67
|
+
- test/gearbox.rb
|
68
|
+
- test/motor.rb
|
69
|
+
- test/powertrain.rb
|
49
70
|
- test/scalar_force.rb
|
50
71
|
- test/tire.rb
|
51
72
|
- test/vector_force.rb
|
@@ -68,8 +89,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
89
|
- !ruby/object:Gem::Version
|
69
90
|
version: '0'
|
70
91
|
requirements: []
|
71
|
-
rubygems_version: 3.
|
92
|
+
rubygems_version: 3.4.4
|
72
93
|
signing_key:
|
73
94
|
specification_version: 4
|
74
|
-
summary:
|
95
|
+
summary: 'Physics from first principles: mass, friction, rotation, etc'
|
75
96
|
test_files: []
|
data/demo/mruby/disk.rb
DELETED
@@ -1,81 +0,0 @@
|
|
1
|
-
|
2
|
-
include DrivingPhysics
|
3
|
-
|
4
|
-
env = Environment.new
|
5
|
-
disk = Disk.new(env)
|
6
|
-
|
7
|
-
puts env
|
8
|
-
puts disk
|
9
|
-
puts
|
10
|
-
|
11
|
-
axle_torque = 50
|
12
|
-
alpha = disk.alpha(axle_torque)
|
13
|
-
drive_force = disk.force(axle_torque)
|
14
|
-
|
15
|
-
puts [format("Axle torque: %.1f Nm", axle_torque),
|
16
|
-
format(" Alpha: %.1f rad/s/s", alpha),
|
17
|
-
format("Drive force: %.1f N", drive_force),
|
18
|
-
].join("\n")
|
19
|
-
puts
|
20
|
-
|
21
|
-
puts "* Spin up the disk with #{axle_torque} Nm of torque"
|
22
|
-
puts "* Cut the power at some point"
|
23
|
-
puts "* Observe"
|
24
|
-
CLI.pause
|
25
|
-
|
26
|
-
duration = 750 # sec
|
27
|
-
|
28
|
-
dist = 0.0 # meters
|
29
|
-
speed = 0.0 # meters/s
|
30
|
-
|
31
|
-
theta = 0.0 # radians
|
32
|
-
omega = 0.0 # radians/s
|
33
|
-
|
34
|
-
paused = 0.0 # seconds
|
35
|
-
num_ticks = duration * env.hz + 1
|
36
|
-
flag = false # to display current stats
|
37
|
-
start = Timer.now
|
38
|
-
|
39
|
-
num_ticks.times { |i|
|
40
|
-
# shut off the powah!
|
41
|
-
if i == 18_950
|
42
|
-
flag = true
|
43
|
-
puts '#'
|
44
|
-
puts '# CUT POWER'
|
45
|
-
puts '#'
|
46
|
-
puts
|
47
|
-
axle_torque = 0
|
48
|
-
end
|
49
|
-
|
50
|
-
rotating_friction = disk.rotating_friction(omega)
|
51
|
-
net_torque = axle_torque + rotating_friction
|
52
|
-
net_force = disk.force(net_torque)
|
53
|
-
|
54
|
-
# rotational kinematics
|
55
|
-
alpha = disk.alpha(net_torque)
|
56
|
-
omega += alpha * env.tick
|
57
|
-
omega = 0.0 if omega.abs < 0.0001
|
58
|
-
theta += omega * env.tick
|
59
|
-
|
60
|
-
if flag or i < 10 or
|
61
|
-
(i < 20_000 and i%1000 == 0) or
|
62
|
-
(i % 10_000 == 0) or
|
63
|
-
i == duration * env.hz - 1
|
64
|
-
|
65
|
-
puts Timer.display(ms: i)
|
66
|
-
puts format(" Torque: %.1f Nm (%d Nm) Friction: %.1f Nm",
|
67
|
-
net_torque, axle_torque, rotating_friction)
|
68
|
-
puts format("Radians: %.1f r %.2f r/s %.3f r/s^2", theta, omega, alpha)
|
69
|
-
puts format(" Revs: %d revs %d revs/s %d rpm",
|
70
|
-
DrivingPhysics.revs(theta),
|
71
|
-
DrivingPhysics.revs(omega),
|
72
|
-
DrivingPhysics.rpm(omega))
|
73
|
-
puts
|
74
|
-
if flag
|
75
|
-
paused += CLI.pause
|
76
|
-
flag = false
|
77
|
-
end
|
78
|
-
end
|
79
|
-
}
|
80
|
-
|
81
|
-
puts Timer.summary(start, num_ticks, paused)
|