John Park | 33858a3 | 2018-09-28 23:05:48 -0700 | [diff] [blame] | 1 | #include "aos/util/trapezoid_profile.h" |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 2 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 3 | #include "glog/logging.h" |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 4 | |
Stephan Pleines | f63bde8 | 2024-01-13 15:59:33 -0800 | [diff] [blame] | 5 | namespace aos::util { |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 6 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 7 | AsymmetricTrapezoidProfile::AsymmetricTrapezoidProfile( |
| 8 | ::std::chrono::nanoseconds delta_time) |
| 9 | : timestep_(delta_time) { |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 10 | output_.setZero(); |
| 11 | } |
| 12 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 13 | void AsymmetricTrapezoidProfile::UpdateVals(double acceleration, |
| 14 | double delta_time) { |
James Kuszmaul | 651fc3f | 2019-05-15 21:14:25 -0700 | [diff] [blame] | 15 | output_(0) += |
| 16 | output_(1) * delta_time + 0.5 * acceleration * delta_time * delta_time; |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 17 | output_(1) += acceleration * delta_time; |
| 18 | } |
| 19 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 20 | const Eigen::Matrix<double, 2, 1> &AsymmetricTrapezoidProfile::Update( |
| 21 | double goal_position, double goal_velocity) { |
| 22 | CalculateTimes(goal_position - output_(0), goal_velocity, output_); |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 23 | |
James Kuszmaul | 651fc3f | 2019-05-15 21:14:25 -0700 | [diff] [blame] | 24 | double next_timestep = ::aos::time::DurationInSeconds(timestep_); |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 25 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 26 | if (deceleration_reversal_time_ > next_timestep) { |
| 27 | UpdateVals(deceleration_reversal_, next_timestep); |
| 28 | return output_; |
| 29 | } |
| 30 | |
| 31 | UpdateVals(deceleration_reversal_, deceleration_reversal_time_); |
| 32 | next_timestep -= deceleration_reversal_time_; |
| 33 | |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 34 | if (acceleration_time_ > next_timestep) { |
| 35 | UpdateVals(acceleration_, next_timestep); |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 36 | return output_; |
| 37 | } |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 38 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 39 | UpdateVals(acceleration_, acceleration_time_); |
| 40 | next_timestep -= acceleration_time_; |
| 41 | |
| 42 | if (constant_time_ > next_timestep) { |
| 43 | UpdateVals(0, next_timestep); |
| 44 | return output_; |
| 45 | } |
| 46 | |
| 47 | UpdateVals(0, constant_time_); |
| 48 | next_timestep -= constant_time_; |
| 49 | if (deceleration_time_ > next_timestep) { |
| 50 | UpdateVals(deceleration_, next_timestep); |
| 51 | } else { |
| 52 | UpdateVals(deceleration_, deceleration_time_); |
| 53 | next_timestep -= deceleration_time_; |
| 54 | UpdateVals(0, next_timestep); |
| 55 | |
| 56 | if (next_timestep >= 0 && goal_velocity == 0) { |
| 57 | output_(0) = goal_position; |
| 58 | output_(1) = goal_velocity; |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 59 | } |
| 60 | } |
| 61 | |
| 62 | return output_; |
| 63 | } |
| 64 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 65 | void AsymmetricTrapezoidProfile::CalculateTimes( |
| 66 | double distance_to_target, double goal_velocity, |
| 67 | Eigen::Matrix<double, 2, 1> current) { |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 68 | if (distance_to_target == 0) { |
| 69 | // We're there. Stop everything. |
| 70 | // TODO(aschuh): Deal with velocity not right. |
| 71 | acceleration_time_ = 0; |
| 72 | acceleration_ = 0; |
| 73 | constant_time_ = 0; |
| 74 | deceleration_time_ = 0; |
| 75 | deceleration_ = 0; |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 76 | deceleration_reversal_time_ = 0; |
| 77 | deceleration_reversal_ = 0; |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 78 | return; |
| 79 | } else if (distance_to_target < 0) { |
| 80 | // Recurse with everything inverted. |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 81 | current(1) *= -1; |
| 82 | CalculateTimes(-distance_to_target, -goal_velocity, current); |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 83 | acceleration_ *= -1; |
| 84 | deceleration_ *= -1; |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 85 | deceleration_reversal_ *= -1; |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 86 | return; |
| 87 | } |
| 88 | |
| 89 | constant_time_ = 0; |
| 90 | acceleration_ = maximum_acceleration_; |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 91 | |
| 92 | // Calculate the fastest speed we could get going to by the distance to |
| 93 | // target. We will have normalized everything out to be a positive distance |
| 94 | // by now so we never have to deal with going "backwards". |
Diana Vandenberg | 19bb9e2 | 2016-02-03 21:24:31 -0800 | [diff] [blame] | 95 | double maximum_acceleration_velocity = |
| 96 | distance_to_target * 2 * std::abs(acceleration_) + |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 97 | current(1) * current(1); |
| 98 | CHECK_GE(maximum_acceleration_velocity, 0); |
| 99 | maximum_acceleration_velocity = sqrt(maximum_acceleration_velocity); |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 100 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 101 | // If we could get going faster than the target, we will need to decelerate |
| 102 | // after accelerating. |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 103 | if (maximum_acceleration_velocity > goal_velocity) { |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 104 | deceleration_ = -maximum_deceleration_; |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 105 | } else { |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 106 | // We couldn't get up to speed by the destination. Set our decel to |
| 107 | // accelerate to keep accelerating to get up to speed. |
| 108 | // |
| 109 | // Note: goal_velocity != 0 isn't well tested, use at your own risk. |
| 110 | LOG(FATAL) << "Untested"; |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 111 | deceleration_ = maximum_acceleration_; |
| 112 | } |
| 113 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 114 | // If we are going away from the goal, we will need to change directions. |
| 115 | if (current(1) < 0) { |
| 116 | deceleration_reversal_time_ = current(1) / deceleration_; |
| 117 | deceleration_reversal_ = -deceleration_; |
| 118 | } else if ((goal_velocity - current(1)) * (goal_velocity + current(1)) < |
| 119 | 2.0 * deceleration_ * distance_to_target) { |
| 120 | // Then, can we stop in time if we get after it? If so, we don't need to |
| 121 | // decel first before running the profile. |
| 122 | deceleration_reversal_time_ = -current(1) / deceleration_; |
| 123 | deceleration_reversal_ = deceleration_; |
| 124 | } else { |
| 125 | // Otherwise, we are all good and don't need to handle reversing. |
| 126 | deceleration_reversal_time_ = 0.0; |
| 127 | deceleration_reversal_ = 0.0; |
| 128 | } |
| 129 | |
| 130 | current(0) += current(1) * deceleration_reversal_time_ + |
| 131 | 0.5 * deceleration_reversal_ * deceleration_reversal_time_ * |
| 132 | deceleration_reversal_time_; |
| 133 | current(1) += deceleration_reversal_ * deceleration_reversal_time_; |
| 134 | // OK, now we've compensated for slowing down. |
| 135 | |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 136 | // We now know the top velocity we can get to. |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 137 | const double top_velocity = sqrt( |
| 138 | (distance_to_target + (current(1) * current(1)) / (2.0 * acceleration_) + |
James Kuszmaul | 651fc3f | 2019-05-15 21:14:25 -0700 | [diff] [blame] | 139 | (goal_velocity * goal_velocity) / (2.0 * deceleration_)) / |
| 140 | (-1.0 / (2.0 * deceleration_) + 1.0 / (2.0 * acceleration_))); |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 141 | |
| 142 | // If it can go too fast, we now know how long we get to accelerate for and |
| 143 | // how long to go at constant velocity. |
| 144 | if (top_velocity > maximum_velocity_) { |
James Kuszmaul | 651fc3f | 2019-05-15 21:14:25 -0700 | [diff] [blame] | 145 | acceleration_time_ = |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 146 | (maximum_velocity_ - current(1)) / maximum_acceleration_; |
Austin Schuh | b3b799e | 2024-02-20 14:20:12 -0800 | [diff] [blame] | 147 | constant_time_ = ((-0.5 * maximum_acceleration_ * acceleration_time_ * |
| 148 | acceleration_time_ - |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 149 | current(1) * acceleration_time_) + |
Austin Schuh | b3b799e | 2024-02-20 14:20:12 -0800 | [diff] [blame] | 150 | distance_to_target + |
| 151 | (goal_velocity * goal_velocity - |
| 152 | maximum_velocity_ * maximum_velocity_) / |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 153 | (2.0 * maximum_deceleration_)) / |
Austin Schuh | b3b799e | 2024-02-20 14:20:12 -0800 | [diff] [blame] | 154 | maximum_velocity_; |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 155 | } else { |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 156 | acceleration_time_ = (top_velocity - current(1)) / acceleration_; |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 157 | } |
| 158 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 159 | CHECK_GT(top_velocity, -maximum_velocity_); |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 160 | |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 161 | if (current(1) > maximum_velocity_) { |
Ben Fredrickson | f33d653 | 2015-03-15 00:29:29 -0700 | [diff] [blame] | 162 | constant_time_ = 0; |
| 163 | acceleration_time_ = 0; |
| 164 | } |
| 165 | |
Austin Schuh | b3b799e | 2024-02-20 14:20:12 -0800 | [diff] [blame] | 166 | deceleration_time_ = |
| 167 | (goal_velocity - ::std::min(top_velocity, maximum_velocity_)) / |
| 168 | deceleration_; |
Austin Schuh | e197a96 | 2024-02-20 18:10:12 -0800 | [diff] [blame^] | 169 | if (acceleration_time_ <= 0) acceleration_time_ = 0; |
Austin Schuh | b3b799e | 2024-02-20 14:20:12 -0800 | [diff] [blame] | 170 | if (constant_time_ <= 0) constant_time_ = 0; |
| 171 | if (deceleration_time_ <= 0) deceleration_time_ = 0; |
brians | f0165ca | 2013-03-02 06:17:47 +0000 | [diff] [blame] | 172 | } |
| 173 | |
Stephan Pleines | f63bde8 | 2024-01-13 15:59:33 -0800 | [diff] [blame] | 174 | } // namespace aos::util |