forked from Team254/FRC-2019-Public
-
Notifications
You must be signed in to change notification settings - Fork 0
/
MotionState.java
161 lines (141 loc) · 5.16 KB
/
MotionState.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package com.team254.lib.motion;
import static com.team254.lib.util.Util.epsilonEquals;
import static com.team254.lib.motion.MotionUtil.kEpsilon;
/**
* A MotionState is a completely specified state of 1D motion through time.
*/
public class MotionState {
protected final double t;
protected final double pos;
protected final double vel;
protected final double acc;
public static MotionState kInvalidState = new MotionState(Double.NaN, Double.NaN, Double.NaN, Double.NaN);
public MotionState(double t, double pos, double vel, double acc) {
this.t = t;
this.pos = pos;
this.vel = vel;
this.acc = acc;
}
public MotionState(MotionState state) {
this(state.t, state.pos, state.vel, state.acc);
}
public double t() {
return t;
}
public double pos() {
return pos;
}
public double vel() {
return vel;
}
public double vel2() {
return vel * vel;
}
public double acc() {
return acc;
}
/**
* Extrapolates this MotionState to the specified time by applying this MotionState's acceleration.
*
* @param t The time of the new MotionState.
* @return A MotionState that is a valid predecessor (if t<=0) or successor (if t>=0) of this state.
*/
public MotionState extrapolate(double t) {
return extrapolate(t, acc);
}
/**
* Extrapolates this MotionState to the specified time by applying a given acceleration to the (t, pos, vel) portion
* of this MotionState.
*
* @param t The time of the new MotionState.
* @param acc The acceleration to apply.
* @return A MotionState that is a valid predecessor (if t<=0) or successor (if t>=0) of this state (with the
* specified accel).
*/
public MotionState extrapolate(double t, double acc) {
final double dt = t - this.t;
return new MotionState(t, pos + vel * dt + .5 * acc * dt * dt, vel + acc * dt, acc);
}
/**
* Find the next time (first time > MotionState.t()) that this MotionState will be at pos. This is an inverse of the
* extrapolate() method.
*
* @param pos The position to query.
* @return The time when we are next at pos() if we are extrapolating with a positive dt. NaN if we never reach pos.
*/
public double nextTimeAtPos(double pos) {
if (epsilonEquals(pos, this.pos, kEpsilon)) {
// Already at pos.
return t;
}
if (epsilonEquals(acc, 0.0, kEpsilon)) {
// Zero acceleration case.
final double delta_pos = pos - this.pos;
if (!epsilonEquals(vel, 0.0, kEpsilon) && Math.signum(delta_pos) == Math.signum(vel)) {
// Constant velocity heading towards pos.
return delta_pos / vel + t;
}
return Double.NaN;
}
// Solve the quadratic formula.
// ax^2 + bx + c == 0
// x = dt
// a = .5 * acc
// b = vel
// c = this.pos - pos
final double disc = vel * vel - 2.0 * acc * (this.pos - pos);
if (disc < 0.0) {
// Extrapolating this MotionState never reaches the desired pos.
return Double.NaN;
}
final double sqrt_disc = Math.sqrt(disc);
final double max_dt = (-vel + sqrt_disc) / acc;
final double min_dt = (-vel - sqrt_disc) / acc;
if (min_dt >= 0.0 && (max_dt < 0.0 || min_dt < max_dt)) {
return t + min_dt;
}
if (max_dt >= 0.0) {
return t + max_dt;
}
// We only reach the desired pos in the past.
return Double.NaN;
}
@Override
public String toString() {
return "(t=" + t + ", pos=" + pos + ", vel=" + vel + ", acc=" + acc + ")";
}
/**
* Checks if two MotionStates are epsilon-equals (all fields are equal within a nominal tolerance).
*/
@Override
public boolean equals(Object other) {
return (other instanceof MotionState) && equals((MotionState) other, kEpsilon);
}
/**
* Checks if two MotionStates are epsilon-equals (all fields are equal within a specified tolerance).
*/
public boolean equals(MotionState other, double epsilon) {
return coincident(other, epsilon) && epsilonEquals(acc, other.acc, epsilon);
}
/**
* Checks if two MotionStates are coincident (t, pos, and vel are equal within a nominal tolerance, but acceleration
* may be different).
*/
public boolean coincident(MotionState other) {
return coincident(other, kEpsilon);
}
/**
* Checks if two MotionStates are coincident (t, pos, and vel are equal within a specified tolerance, but
* acceleration may be different).
*/
public boolean coincident(MotionState other, double epsilon) {
return epsilonEquals(t, other.t, epsilon) && epsilonEquals(pos, other.pos, epsilon)
&& epsilonEquals(vel, other.vel, epsilon);
}
/**
* Returns a MotionState that is the mirror image of this one. Pos, vel, and acc are all negated, but time is not.
*/
public MotionState flipped() {
return new MotionState(t, -pos, -vel, -acc);
}
}