ros2_control - iron
Loading...
Searching...
No Matches
rate_limiter.hpp
1// Copyright 2020 PAL Robotics S.L.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15/*
16 * Author: Enrique Fernández
17 */
18
19#ifndef CONTROL_TOOLBOX__RATE_LIMITER_HPP_
20#define CONTROL_TOOLBOX__RATE_LIMITER_HPP_
21
22#include <cmath>
23#include <limits>
24
25#include <algorithm>
26#include <stdexcept>
27
28namespace control_toolbox
29{
30template <typename T>
32{
33public:
59 T min_value = std::numeric_limits<T>::quiet_NaN(),
60 T max_value = std::numeric_limits<T>::quiet_NaN(),
61 T min_first_derivative_neg = std::numeric_limits<T>::quiet_NaN(),
62 T max_first_derivative_pos = std::numeric_limits<T>::quiet_NaN(),
63 T min_first_derivative_pos = std::numeric_limits<T>::quiet_NaN(),
64 T max_first_derivative_neg = std::numeric_limits<T>::quiet_NaN(),
65 T min_second_derivative = std::numeric_limits<T>::quiet_NaN(),
66 T max_second_derivative = std::numeric_limits<T>::quiet_NaN());
67
76 T limit(T & v, T v0, T v1, T dt);
77
83 T limit_value(T & v);
84
92 T limit_first_derivative(T & v, T v0, T dt);
93
105 T limit_second_derivative(T & v, T v0, T v1, T dt);
106
124 void set_params(
125 T min_value = std::numeric_limits<T>::quiet_NaN(),
126 T max_value = std::numeric_limits<T>::quiet_NaN(),
127 T min_first_derivative_neg = std::numeric_limits<T>::quiet_NaN(),
128 T max_first_derivative_pos = std::numeric_limits<T>::quiet_NaN(),
129 T min_first_derivative_pos = std::numeric_limits<T>::quiet_NaN(),
130 T max_first_derivative_neg = std::numeric_limits<T>::quiet_NaN(),
131 T min_second_derivative = std::numeric_limits<T>::quiet_NaN(),
132 T max_second_derivative = std::numeric_limits<T>::quiet_NaN());
133
134private:
135 // Enable/Disable value/first_derivative/second_derivative limits:
136 bool has_value_limits_ = true;
137 bool has_first_derivative_limits_ = true;
138 bool has_second_derivative_limits_ = true;
139
140 // value limits:
141 T min_value_ = std::numeric_limits<T>::quiet_NaN();
142 T max_value_ = std::numeric_limits<T>::quiet_NaN();
143
144 // first_derivative limits:
145 T min_first_derivative_neg_ = std::numeric_limits<T>::quiet_NaN();
146 T max_first_derivative_pos_ = std::numeric_limits<T>::quiet_NaN();
147 T min_first_derivative_pos_ = std::numeric_limits<T>::quiet_NaN();
148 T max_first_derivative_neg_ = std::numeric_limits<T>::quiet_NaN();
149
150 // second_derivative limits:
151 T min_second_derivative_ = std::numeric_limits<T>::quiet_NaN();
152 T max_second_derivative_ = std::numeric_limits<T>::quiet_NaN();
153};
154
155template <typename T>
166
167// Check if limits are valid, max must be specified, min defaults to -max if unspecified
168template <typename T>
173{
174 auto tmp_has_value_limits = has_value_limits_;
175 auto tmp_has_first_derivative_limits = has_first_derivative_limits_;
176 auto tmp_has_second_derivative_limits = has_second_derivative_limits_;
177
178 if (std::isnan(max_value))
179 {
180 tmp_has_value_limits = false;
181 }
182 if (std::isnan(min_value))
183 {
185 }
187 {
188 throw std::invalid_argument("Invalid value limits");
189 }
190
191 if (std::isnan(max_first_derivative_pos))
192 {
194 }
195 if (std::isnan(min_first_derivative_neg))
196 {
198 }
200 {
201 throw std::invalid_argument("Invalid first derivative limits");
202 }
204 {
205 if (std::isnan(max_first_derivative_neg))
206 {
208 }
209 if (std::isnan(min_first_derivative_pos))
210 {
212 }
214 {
215 throw std::invalid_argument("Invalid first derivative limits");
216 }
217 }
218
219 if (std::isnan(max_second_derivative))
220 {
222 }
223 if (std::isnan(min_second_derivative))
224 {
226 }
228 {
229 throw std::invalid_argument("Invalid second derivative limits");
230 }
231
232 min_value_ = min_value;
233 max_value_ = max_value;
234 min_first_derivative_neg_ = min_first_derivative_neg;
235 max_first_derivative_pos_ = max_first_derivative_pos;
236 min_first_derivative_pos_ = min_first_derivative_pos;
237 max_first_derivative_neg_ = max_first_derivative_neg;
238 min_second_derivative_ = min_second_derivative;
239 max_second_derivative_ = max_second_derivative;
240 has_value_limits_ = tmp_has_value_limits;
241 has_first_derivative_limits_ = tmp_has_first_derivative_limits;
242 has_second_derivative_limits_ = tmp_has_second_derivative_limits;
243}
244
245template <typename T>
246T RateLimiter<T>::limit(T & v, T v0, T v1, T dt)
247{
248 const T tmp = v;
249
250 limit_second_derivative(v, v0, v1, dt);
251 limit_first_derivative(v, v0, dt);
252 limit_value(v);
253
254 return tmp != static_cast<T>(0.0) ? v / tmp : static_cast<T>(1.0);
255}
256
257template <typename T>
259{
260 const T tmp = v;
261
262 if (has_value_limits_)
263 {
264 v = std::clamp(v, min_value_, max_value_);
265 }
266
267 return tmp != static_cast<T>(0.0) ? v / tmp : static_cast<T>(1.0);
268}
269
270template <typename T>
272{
273 const T tmp = v;
274
275 if (has_first_derivative_limits_)
276 {
277 T dv_max, dv_min = 0;
278 if (v0 > static_cast<T>(0.0))
279 {
280 dv_max = max_first_derivative_pos_ * dt;
281 dv_min = min_first_derivative_pos_ * dt;
282 }
283 else if (v0 < static_cast<T>(0.0))
284 {
285 dv_min = min_first_derivative_neg_ * dt;
286 dv_max = max_first_derivative_neg_ * dt;
287 }
288 else
289 {
290 dv_min = min_first_derivative_neg_ * dt;
291 dv_max = max_first_derivative_pos_ * dt;
292 }
293 const T dv = std::clamp(v - v0, dv_min, dv_max);
294
295 v = v0 + dv;
296 }
297
298 return tmp != static_cast<T>(0.0) ? v / tmp : static_cast<T>(1.0);
299}
300
301template <typename T>
303{
304 const T tmp = v;
305
306 if (has_second_derivative_limits_)
307 {
308 const T dv = v - v0;
309 const T dv0 = v0 - v1;
310
311 // Only limit jerk when accelerating or reverse_accelerating
312 // Note: this prevents oscillating closed-loop behavior, see discussion
313 // details in https://github.com/ros-controls/control_toolbox/issues/240.
314 if ((dv - dv0) * (v - v0) > 0)
315 {
316 const T dt2 = dt * dt;
317
318 const T da_min = min_second_derivative_ * dt2;
319 const T da_max = max_second_derivative_ * dt2;
320
321 const T da = std::clamp(dv - dv0, da_min, da_max);
322
323 v = v0 + dv0 + da;
324 }
325 }
326
327 return tmp != static_cast<T>(0.0) ? v / tmp : static_cast<T>(1.0);
328}
329
330} // namespace control_toolbox
331
332#endif // CONTROL_TOOLBOX__RATE_LIMITER_HPP_
A Low-pass filter class.
Definition low_pass_filter.hpp:78
void set_params(double sampling_frequency, double damping_frequency, double damping_intensity)
Internal computation of the feedforward and feedbackward coefficients according to the LowPassFilter ...
Definition low_pass_filter.hpp:114
Definition rate_limiter.hpp:32
T limit_value(T &v)
Limit the value.
Definition rate_limiter.hpp:258
T limit_second_derivative(T &v, T v0, T v1, T dt)
Limit the second_derivative.
Definition rate_limiter.hpp:302
T limit(T &v, T v0, T v1, T dt)
Limit the value, first_derivative, and second_derivative.
Definition rate_limiter.hpp:246
void set_params(T min_value=std::numeric_limits< T >::quiet_NaN(), T max_value=std::numeric_limits< T >::quiet_NaN(), T min_first_derivative_neg=std::numeric_limits< T >::quiet_NaN(), T max_first_derivative_pos=std::numeric_limits< T >::quiet_NaN(), T min_first_derivative_pos=std::numeric_limits< T >::quiet_NaN(), T max_first_derivative_neg=std::numeric_limits< T >::quiet_NaN(), T min_second_derivative=std::numeric_limits< T >::quiet_NaN(), T max_second_derivative=std::numeric_limits< T >::quiet_NaN())
Set the parameters.
Definition rate_limiter.hpp:169
RateLimiter(T min_value=std::numeric_limits< T >::quiet_NaN(), T max_value=std::numeric_limits< T >::quiet_NaN(), T min_first_derivative_neg=std::numeric_limits< T >::quiet_NaN(), T max_first_derivative_pos=std::numeric_limits< T >::quiet_NaN(), T min_first_derivative_pos=std::numeric_limits< T >::quiet_NaN(), T max_first_derivative_neg=std::numeric_limits< T >::quiet_NaN(), T min_second_derivative=std::numeric_limits< T >::quiet_NaN(), T max_second_derivative=std::numeric_limits< T >::quiet_NaN())
Constructor.
Definition rate_limiter.hpp:156
T limit_first_derivative(T &v, T v0, T dt)
Limit the first_derivative.
Definition rate_limiter.hpp:271
Definition dither.hpp:46