blob: 76c97589a8fb59cec57ba9010ab2443bcf4824bb [file] [log] [blame]
Austin Schuh085eab92020-11-26 13:54:51 -08001#!/usr/bin/python3
Diana Vandenberg19bb9e22016-02-03 21:24:31 -08002
3import numpy
4
Ravago Jones5127ccc2022-07-31 16:32:45 -07005
Diana Vandenberg19bb9e22016-02-03 21:24:31 -08006class TrapezoidProfile(object):
Ravago Jones5127ccc2022-07-31 16:32:45 -07007 """Computes a trapezoidal motion profile
Diana Vandenberg19bb9e22016-02-03 21:24:31 -08008
9 Attributes:
10 _acceleration_time: the amount of time the robot will travel at the
11 specified acceleration (s)
12 _acceleration: the acceleration the robot will use to get to the target
13 (unit/s^2)
14 _constant_time: amount of time to travel at a constant velocity to reach
15 target (s)
16 _deceleration_time: amount of time to decelerate (at specified
17 deceleration) to target (s)
18 _deceleration: decceleration the robot needs to get to goal velocity
19 (units/s^2)
20 _maximum_acceleration: the maximum acceleration (units/s^2)
21 _maximum_velocity: the maximum velocity (unit/s)
22 _timestep: time between calls to Update (delta_time)
23 _output: output array containing distance to goal and velocity
24 """
Ravago Jones5127ccc2022-07-31 16:32:45 -070025
26 def __init__(self, delta_time):
27 """Constructs a TrapezoidProfile.
Diana Vandenberg19bb9e22016-02-03 21:24:31 -080028
29 Args:
30 delta_time: time between calls to Update (seconds)
31 """
Ravago Jones5127ccc2022-07-31 16:32:45 -070032 self._acceleration_time = 0
33 self._acceleration = 0
34 self._constant_time = 0
35 self._deceleration_time = 0
36 self._deceleration = 0
Diana Vandenberg19bb9e22016-02-03 21:24:31 -080037
Ravago Jones5127ccc2022-07-31 16:32:45 -070038 self._maximum_acceleration = 0
39 self._maximum_velocity = 0
40 self._timestep = delta_time
Diana Vandenberg19bb9e22016-02-03 21:24:31 -080041
Ravago Jones5127ccc2022-07-31 16:32:45 -070042 self._output = numpy.array(numpy.zeros((2, 1)))
Diana Vandenberg19bb9e22016-02-03 21:24:31 -080043
Ravago Jones5127ccc2022-07-31 16:32:45 -070044 # Updates the state
45 def Update(self, goal_position, goal_velocity):
46 self._CalculateTimes(goal_position - self._output[0], goal_velocity)
Diana Vandenberg19bb9e22016-02-03 21:24:31 -080047
Ravago Jones5127ccc2022-07-31 16:32:45 -070048 next_timestep = self._timestep
Diana Vandenberg19bb9e22016-02-03 21:24:31 -080049
Ravago Jones5127ccc2022-07-31 16:32:45 -070050 # We now have the amount of time we need to accelerate to follow the
51 # profile, the amount of time we need to move at constant velocity
52 # to follow the profile, and the amount of time we need to decelerate to
53 # follow the profile. Do as much of that as we have time left in dt.
54 if self._acceleration_time > next_timestep:
55 self._UpdateVals(self._acceleration, next_timestep)
Diana Vandenberg19bb9e22016-02-03 21:24:31 -080056 else:
Ravago Jones5127ccc2022-07-31 16:32:45 -070057 self._UpdateVals(self._acceleration, self._acceleration_time)
Diana Vandenberg19bb9e22016-02-03 21:24:31 -080058
Ravago Jones5127ccc2022-07-31 16:32:45 -070059 next_timestep -= self._acceleration_time
60 if self._constant_time > next_timestep:
61 self._UpdateVals(0, next_timestep)
62 else:
63 self._UpdateVals(0, self._constant_time)
64 next_timestep -= self._constant_time
65 if self._deceleration_time > next_timestep:
66 self._UpdateVals(self._deceleration, next_timestep)
67 else:
68 self._UpdateVals(self._deceleration,
69 self._deceleration_time)
70 next_timestep -= self._deceleration_time
71 self._UpdateVals(0, next_timestep)
Diana Vandenberg19bb9e22016-02-03 21:24:31 -080072
Ravago Jones5127ccc2022-07-31 16:32:45 -070073 return self._output
Diana Vandenberg19bb9e22016-02-03 21:24:31 -080074
Ravago Jones5127ccc2022-07-31 16:32:45 -070075 # Useful for preventing windup etc.
76 def MoveCurrentState(self, current):
77 self._output = current
Diana Vandenberg19bb9e22016-02-03 21:24:31 -080078
Ravago Jones5127ccc2022-07-31 16:32:45 -070079 # Useful for preventing windup etc.
80 def MoveGoal(self, dx):
81 self._output[0] += dx
Diana Vandenberg19bb9e22016-02-03 21:24:31 -080082
Ravago Jones5127ccc2022-07-31 16:32:45 -070083 def SetGoal(self, x):
84 self._output[0] = x
Diana Vandenberg19bb9e22016-02-03 21:24:31 -080085
Ravago Jones5127ccc2022-07-31 16:32:45 -070086 def set_maximum_acceleration(self, maximum_acceleration):
87 self._maximum_acceleration = maximum_acceleration
Diana Vandenberg19bb9e22016-02-03 21:24:31 -080088
Ravago Jones5127ccc2022-07-31 16:32:45 -070089 def set_maximum_velocity(self, maximum_velocity):
90 self._maximum_velocity = maximum_velocity
Diana Vandenberg19bb9e22016-02-03 21:24:31 -080091
Ravago Jones5127ccc2022-07-31 16:32:45 -070092 def _UpdateVals(self, acceleration, delta_time):
93 self._output[0, 0] += (self._output[1, 0] * delta_time +
94 0.5 * acceleration * delta_time * delta_time)
95 self._output[1, 0] += acceleration * delta_time
Diana Vandenberg19bb9e22016-02-03 21:24:31 -080096
Ravago Jones5127ccc2022-07-31 16:32:45 -070097 def _CalculateTimes(self, distance_to_target, goal_velocity):
98 if distance_to_target == 0:
99 self._acceleration_time = 0
100 self._acceleration = 0
101 self._constant_time = 0
102 self._deceleration_time = 0
103 self._deceleration = 0
104 return
105 elif distance_to_target < 0:
106 # Recurse with everything inverted.
107 self._output[1] *= -1
108 self._CalculateTimes(-distance_to_target, -goal_velocity)
109 self._output[1] *= -1
110 self._acceleration *= -1
111 self._deceleration *= -1
112 return
Diana Vandenberg19bb9e22016-02-03 21:24:31 -0800113
Ravago Jones5127ccc2022-07-31 16:32:45 -0700114 self._constant_time = 0
115 self._acceleration = self._maximum_acceleration
116 maximum_acceleration_velocity = (
117 distance_to_target * 2 * numpy.abs(self._acceleration) +
118 self._output[1] * self._output[1])
119 if maximum_acceleration_velocity > 0:
120 maximum_acceleration_velocity = numpy.sqrt(
121 maximum_acceleration_velocity)
122 else:
123 maximum_acceleration_velocity = -numpy.sqrt(
124 -maximum_acceleration_velocity)
Diana Vandenberg19bb9e22016-02-03 21:24:31 -0800125
Ravago Jones5127ccc2022-07-31 16:32:45 -0700126 # Since we know what we'd have to do if we kept after it to decelerate, we
127 # know the sign of the acceleration.
128 if maximum_acceleration_velocity > goal_velocity:
129 self._deceleration = -self._maximum_acceleration
130 else:
131 self._deceleration = self._maximum_acceleration
Diana Vandenberg19bb9e22016-02-03 21:24:31 -0800132
Ravago Jones5127ccc2022-07-31 16:32:45 -0700133 # We now know the top velocity we can get to.
134 top_velocity = numpy.sqrt(
135 (distance_to_target + (self._output[1] * self._output[1]) /
136 (2.0 * self._acceleration) + (goal_velocity * goal_velocity) /
137 (2.0 * self._deceleration)) / (-1.0 /
138 (2.0 * self._deceleration) + 1.0 /
139 (2.0 * self._acceleration)))
Diana Vandenberg19bb9e22016-02-03 21:24:31 -0800140
Ravago Jones5127ccc2022-07-31 16:32:45 -0700141 # If it can go too fast, we now know how long we get to accelerate for and
142 # how long to go at constant velocity.
143 if top_velocity > self._maximum_velocity:
144 self._acceleration_time = (
145 (self._maximum_velocity - self._output[1]) /
146 self._maximum_acceleration)
147 self._constant_time = (
148 distance_to_target +
149 (goal_velocity * goal_velocity -
150 self._maximum_velocity * self._maximum_velocity) /
151 (2.0 * self._maximum_acceleration)) / self._maximum_velocity
152 else:
153 self._acceleration_time = ((top_velocity - self._output[1]) /
154 self._acceleration)
Diana Vandenberg19bb9e22016-02-03 21:24:31 -0800155
Ravago Jones5127ccc2022-07-31 16:32:45 -0700156 if self._output[1] > self._maximum_velocity:
157 self._constant_time = 0
158 self._acceleration_time = 0
Diana Vandenberg19bb9e22016-02-03 21:24:31 -0800159
Ravago Jones5127ccc2022-07-31 16:32:45 -0700160 self._deceleration_time = ((goal_velocity - top_velocity) /
161 self._deceleration)