blob: f956bf06e031124e9d63e308eb109f5ef1059a9a [file] [log] [blame]
Diana Vandenberg19bb9e22016-02-03 21:24:31 -08001#!/usr/bin/python
2
3import numpy
4
5class TrapezoidProfile(object):
6 """Computes a trapezoidal motion profile
7
8 Attributes:
9 _acceleration_time: the amount of time the robot will travel at the
10 specified acceleration (s)
11 _acceleration: the acceleration the robot will use to get to the target
12 (unit/s^2)
13 _constant_time: amount of time to travel at a constant velocity to reach
14 target (s)
15 _deceleration_time: amount of time to decelerate (at specified
16 deceleration) to target (s)
17 _deceleration: decceleration the robot needs to get to goal velocity
18 (units/s^2)
19 _maximum_acceleration: the maximum acceleration (units/s^2)
20 _maximum_velocity: the maximum velocity (unit/s)
21 _timestep: time between calls to Update (delta_time)
22 _output: output array containing distance to goal and velocity
23 """
24 def __init__(self, delta_time):
25 """Constructs a TrapezoidProfile.
26
27 Args:
28 delta_time: time between calls to Update (seconds)
29 """
30 self._acceleration_time = 0
31 self._acceleration = 0
32 self._constant_time = 0
33 self._deceleration_time = 0
34 self._deceleration = 0
35
36 self._maximum_acceleration = 0
37 self._maximum_velocity = 0
38 self._timestep = delta_time
39
40 self._output = numpy.array(numpy.zeros((2,1)))
41
42 # Updates the state
43 def Update(self, goal_position, goal_velocity):
44 self._CalculateTimes(goal_position - self._output[0], goal_velocity)
45
46 next_timestep = self._timestep
47
48 # We now have the amount of time we need to accelerate to follow the
49 # profile, the amount of time we need to move at constant velocity
50 # to follow the profile, and the amount of time we need to decelerate to
51 # follow the profile. Do as much of that as we have time left in dt.
52 if self._acceleration_time > next_timestep:
53 self._UpdateVals(self._acceleration, next_timestep)
54 else:
55 self._UpdateVals(self._acceleration, self._acceleration_time)
56
57 next_timestep -= self._acceleration_time
58 if self._constant_time > next_timestep:
59 self._UpdateVals(0, next_timestep)
60 else:
61 self._UpdateVals(0, self._constant_time)
62 next_timestep -= self._constant_time;
63 if self._deceleration_time > next_timestep:
64 self._UpdateVals(self._deceleration, next_timestep)
65 else:
66 self._UpdateVals(self._deceleration, self._deceleration_time)
67 next_timestep -= self._deceleration_time
68 self._UpdateVals(0, next_timestep)
69
70 return self._output
71
72 # Useful for preventing windup etc.
73 def MoveCurrentState(self, current):
74 self._output = current
75
76 # Useful for preventing windup etc.
77 def MoveGoal(self, dx):
78 self._output[0] += dx
79
80 def SetGoal(self, x):
81 self._output[0] = x
82
83 def set_maximum_acceleration(self, maximum_acceleration):
84 self._maximum_acceleration = maximum_acceleration
85
86 def set_maximum_velocity(self, maximum_velocity):
87 self._maximum_velocity = maximum_velocity
88
89 def _UpdateVals(self, acceleration, delta_time):
90 self._output[0, 0] += (self._output[1, 0] * delta_time
91 + 0.5 * acceleration * delta_time * delta_time)
92 self._output[1, 0] += acceleration * delta_time
93
94 def _CalculateTimes(self, distance_to_target, goal_velocity):
95 if distance_to_target == 0:
96 self._acceleration_time = 0
97 self._acceleration = 0
98 self._constant_time = 0
99 self._deceleration_time = 0
100 self._deceleration = 0
101 return
102 elif distance_to_target < 0:
103 # Recurse with everything inverted.
104 self._output[1] *= -1
105 self._CalculateTimes(-distance_to_target, -goal_velocity)
106 self._output[1] *= -1
107 self._acceleration *= -1
108 self._deceleration *= -1
109 return
110
111 self._constant_time = 0
112 self._acceleration = self._maximum_acceleration
113 maximum_acceleration_velocity = (
114 distance_to_target * 2 * numpy.abs(self._acceleration)
115 + self._output[1] * self._output[1])
116 if maximum_acceleration_velocity > 0:
117 maximum_acceleration_velocity = numpy.sqrt(maximum_acceleration_velocity)
118 else:
119 maximum_acceleration_velocity = -numpy.sqrt(-maximum_acceleration_velocity)
120
121 # Since we know what we'd have to do if we kept after it to decelerate, we
122 # know the sign of the acceleration.
123 if maximum_acceleration_velocity > goal_velocity:
124 self._deceleration = -self._maximum_acceleration
125 else:
126 self._deceleration = self._maximum_acceleration
127
128 # We now know the top velocity we can get to.
129 top_velocity = numpy.sqrt((distance_to_target +
130 (self._output[1] * self._output[1]) /
131 (2.0 * self._acceleration) +
132 (goal_velocity * goal_velocity) /
133 (2.0 * self._deceleration)) /
134 (-1.0 / (2.0 * self._deceleration) +
135 1.0 / (2.0 * self._acceleration)))
136
137 # If it can go too fast, we now know how long we get to accelerate for and
138 # how long to go at constant velocity.
139 if top_velocity > self._maximum_velocity:
140 self._acceleration_time = ((self._maximum_velocity - self._output[1]) /
141 self._maximum_acceleration)
142 self._constant_time = (distance_to_target +
143 (goal_velocity * goal_velocity -
144 self._maximum_velocity * self._maximum_velocity) /
145 (2.0 * self._maximum_acceleration)) / self._maximum_velocity
146 else:
147 self._acceleration_time = (
148 (top_velocity - self._output[1]) / self._acceleration)
149
150 if self._output[1] > self._maximum_velocity:
151 self._constant_time = 0
152 self._acceleration_time = 0
153
154 self._deceleration_time = (
155 (goal_velocity - top_velocity) / self._deceleration)
156