1
+ # Copyright (c) FIRST and other WPILib contributors.
2
+ # Open Source Software; you can modify and/or share it under the terms of
3
+ # the WPILib BSD license file in the root directory of this project.
4
+
5
+
6
+ import math
7
+
8
+ from wpimath .trajectory import TrapezoidProfile
9
+
10
+ kDt = 0.01 # 10 ms
11
+
12
+ def expect_near_units (val1 , val2 , eps ):
13
+ assert abs (val1 - val2 ) <= eps
14
+
15
+ def expect_lt_or_near_units (val1 , val2 , eps ):
16
+ if val1 <= val2 :
17
+ assert val1 <= val2
18
+ else :
19
+ expect_near_units (val1 , val2 , eps )
20
+
21
+
22
+ def test_reaches_goal ():
23
+ constraints = TrapezoidProfile .Constraints (1.75 , 0.75 )
24
+ goal = TrapezoidProfile .State (3.0 , 0.0 )
25
+ state = TrapezoidProfile .State ()
26
+
27
+ profile = TrapezoidProfile (constraints )
28
+ for _ in range (450 ):
29
+ state = profile .calculate (kDt , state , goal )
30
+ assert state == goal
31
+
32
+
33
+ def test_pos_continuous_under_vel_change ():
34
+ constraints = TrapezoidProfile .Constraints (1.75 , 0.75 )
35
+ goal = TrapezoidProfile .State (12.0 , 0.0 )
36
+ profile = TrapezoidProfile (constraints )
37
+ state = profile .calculate (kDt , TrapezoidProfile .State (), goal )
38
+
39
+ last_pos = state .position
40
+ for i in range (1600 ):
41
+ if i == 400 :
42
+ constraints = TrapezoidProfile .Constraints (0.75 , 0.75 )
43
+ profile = TrapezoidProfile (constraints )
44
+
45
+ state = profile .calculate (kDt , state , goal )
46
+ estimated_vel = (state .position - last_pos ) / kDt
47
+
48
+ if i >= 400 :
49
+ expect_lt_or_near_units (estimated_vel , constraints .maxVelocity , 1e-4 )
50
+ assert state .velocity <= constraints .maxVelocity
51
+
52
+ last_pos = state .position
53
+
54
+ assert state == goal
55
+
56
+
57
+ def test_backwards ():
58
+ constraints = TrapezoidProfile .Constraints (0.75 , 0.75 )
59
+ goal = TrapezoidProfile .State (- 2.0 , 0.0 )
60
+ state = TrapezoidProfile .State ()
61
+ profile = TrapezoidProfile (constraints )
62
+
63
+ for _ in range (400 ):
64
+ state = profile .calculate (kDt , state , goal )
65
+ assert state == goal
66
+
67
+
68
+ def test_switch_goal_in_middle ():
69
+ constraints = TrapezoidProfile .Constraints (0.75 , 0.75 )
70
+ goal = TrapezoidProfile .State (- 2.0 , 0.0 )
71
+ state = TrapezoidProfile .State ()
72
+ profile = TrapezoidProfile (constraints )
73
+
74
+ for _ in range (200 ):
75
+ state = profile .calculate (kDt , state , goal )
76
+ assert state != goal
77
+
78
+ goal = TrapezoidProfile .State (0.0 , 0.0 )
79
+ profile = TrapezoidProfile (constraints )
80
+ for _ in range (550 ):
81
+ state = profile .calculate (kDt , state , goal )
82
+ assert state == goal
83
+
84
+
85
+ def test_top_speed ():
86
+ constraints = TrapezoidProfile .Constraints (0.75 , 0.75 )
87
+ goal = TrapezoidProfile .State (4.0 , 0.0 )
88
+ state = TrapezoidProfile .State ()
89
+ profile = TrapezoidProfile (constraints )
90
+
91
+ for _ in range (200 ):
92
+ state = profile .calculate (kDt , state , goal )
93
+ expect_near_units (constraints .maxVelocity , state .velocity , 1e-4 )
94
+
95
+ profile = TrapezoidProfile (constraints )
96
+ for _ in range (2000 ):
97
+ state = profile .calculate (kDt , state , goal )
98
+ assert state == goal
99
+
100
+
101
+ def test_timing_to_current ():
102
+ constraints = TrapezoidProfile .Constraints (0.75 , 0.75 )
103
+ goal = TrapezoidProfile .State (2.0 , 0.0 )
104
+ state = TrapezoidProfile .State ()
105
+ profile = TrapezoidProfile (constraints )
106
+
107
+ for _ in range (400 ):
108
+ state = profile .calculate (kDt , state , goal )
109
+ expect_near_units (profile .timeLeftUntil (state .position ), 0.0 , 0.02 )
110
+
111
+
112
+ def test_timing_to_goal ():
113
+ constraints = TrapezoidProfile .Constraints (0.75 , 0.75 )
114
+ goal = TrapezoidProfile .State (2.0 , 0.0 )
115
+ profile = TrapezoidProfile (constraints )
116
+
117
+ state = profile .calculate (kDt , goal , TrapezoidProfile .State ())
118
+ predicted_time_left = profile .timeLeftUntil (goal .position )
119
+
120
+ reached_goal = False
121
+ for i in range (400 ):
122
+ state = profile .calculate (kDt , state , goal )
123
+ if not reached_goal and state == goal :
124
+ assert math .isclose (predicted_time_left , i * kDt , abs_tol = 0.25 )
125
+ reached_goal = True
126
+
127
+
128
+ def test_timing_before_goal ():
129
+ constraints = TrapezoidProfile .Constraints (0.75 , 0.75 )
130
+ goal = TrapezoidProfile .State (2.0 , 0.0 )
131
+ profile = TrapezoidProfile (constraints )
132
+
133
+ state = profile .calculate (kDt , goal , TrapezoidProfile .State ())
134
+ predicted_time_left = profile .timeLeftUntil (1.0 )
135
+
136
+ reached_goal = False
137
+ for i in range (400 ):
138
+ state = profile .calculate (kDt , state , goal )
139
+ if not reached_goal and abs (state .velocity - 1.0 ) < 1e-4 :
140
+ assert math .isclose (predicted_time_left , i * kDt , abs_tol = 0.02 )
141
+ reached_goal = True
142
+
143
+
144
+ def test_timing_to_negative_goal ():
145
+ constraints = TrapezoidProfile .Constraints (0.75 , 0.75 )
146
+ goal = TrapezoidProfile .State (- 2.0 , 0.0 )
147
+ profile = TrapezoidProfile (constraints )
148
+
149
+ state = profile .calculate (kDt , goal , TrapezoidProfile .State ())
150
+ predicted_time_left = profile .timeLeftUntil (goal .position )
151
+
152
+ reached_goal = False
153
+ for i in range (400 ):
154
+ state = profile .calculate (kDt , state , goal )
155
+ if not reached_goal and state == goal :
156
+ assert math .isclose (predicted_time_left , i * kDt , abs_tol = 0.25 )
157
+ reached_goal = True
158
+
159
+
160
+ def test_timing_before_negative_goal ():
161
+ constraints = TrapezoidProfile .Constraints (0.75 , 0.75 )
162
+ goal = TrapezoidProfile .State (- 2.0 , 0.0 )
163
+ profile = TrapezoidProfile (constraints )
164
+
165
+ state = profile .calculate (kDt , goal , TrapezoidProfile .State ())
166
+ predicted_time_left = profile .timeLeftUntil (- 1.0 )
167
+
168
+ reached_goal = False
169
+ for i in range (400 ):
170
+ state = profile .calculate (kDt , state , goal )
171
+ if not reached_goal and abs (state .velocity + 1.0 ) < 1e-4 :
172
+ assert math .isclose (predicted_time_left , i * kDt , abs_tol = 0.02 )
173
+ reached_goal = True
174
+
175
+
176
+ def test_initialization_of_current_state ():
177
+ constraints = TrapezoidProfile .Constraints (1.0 , 1.0 )
178
+ profile = TrapezoidProfile (constraints )
179
+ expect_near_units (profile .timeLeftUntil (0.0 ), 0.0 , 1e-10 )
180
+ expect_near_units (profile .totalTime (), 0.0 , 1e-10 )
0 commit comments