MultiDimPIDController.h
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2011-2017, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * @package ArmarX
19 * @author Mirko Waechter( mirko.waechter at kit dot edu)
20 * @date 2018
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
24#pragma once
25
26#include <mutex>
27
28#include <Eigen/Core>
29
33
35
36namespace armarx
37{
38 template <int dimensions = Eigen::Dynamic>
40 {
41 public:
43
45 float Ki,
46 float Kd,
47 double maxControlValue = std::numeric_limits<double>::max(),
48 double maxDerivation = std::numeric_limits<double>::max(),
49 bool threadSafe = true,
50 std::vector<bool> limitless = {}) :
51 Kp(Kp),
52 Ki(Ki),
53 Kd(Kd),
54 integral(0),
55 derivative(0),
61 {
62 reset();
63 }
64
65 void
66 preallocate(size_t size)
67 {
68 stackAllocations.zeroVec = PIDVectorX::Zero(size);
69 stackAllocations.errorVec = stackAllocations.zeroVec;
70 stackAllocations.direction = stackAllocations.zeroVec;
71 stackAllocations.oldControlValue = stackAllocations.zeroVec;
72 }
73
77
78 void
79 update(const double deltaSec,
80 const PIDVectorX& measuredValue,
81 const PIDVectorX& targetValue)
82 {
83 ScopedRecursiveLockPtr lock = getLock();
84 if (stackAllocations.zeroVec.rows() == 0)
85 {
86 preallocate(measuredValue.rows());
87 }
88 ARMARX_CHECK_EQUAL(measuredValue.rows(), targetValue.rows());
89 ARMARX_CHECK_EQUAL(measuredValue.rows(), stackAllocations.zeroVec.rows());
90 processValue = measuredValue;
91 target = targetValue;
92
93 stackAllocations.errorVec = target - processValue;
94
95 if (limitless.size() != 0)
96 {
97 ARMARX_CHECK_EQUAL(limitless.size(), (size_t)stackAllocations.errorVec.rows());
98 for (size_t i = 0; i < limitless.size(); i++)
99 {
100 if (limitless.at(i))
101 {
102 stackAllocations.errorVec(i) =
103 math::MathUtils::angleModPI(stackAllocations.errorVec(i));
104 }
105 }
106 }
107
108
109 double error = stackAllocations.errorVec.norm();
110
111 //double dt = (now - lastUpdateTime).toSecondsDouble();
112 // ARMARX_INFO << deactivateSpam() << VAROUT(dt);
113 if (!firstRun)
114 {
115 integral += error * deltaSec;
116 integral = std::min(integral, maxIntegral);
117 if (deltaSec > 0.0)
118 {
119 derivative = (error - previousError) / deltaSec;
120 }
121 }
122
123 firstRun = false;
124 stackAllocations.direction = targetValue; // copy size
125
126 if (error > 0)
127 {
128 stackAllocations.direction = stackAllocations.errorVec.normalized();
129 }
130 else
131 {
132 stackAllocations.direction.setZero();
133 }
134
135 if (controlValue.rows() > 0)
136 {
137 stackAllocations.oldControlValue = controlValue;
138 }
139 else
140 {
141 stackAllocations.oldControlValue = stackAllocations.zeroVec;
142 }
144 stackAllocations.direction * (Kp * error + Ki * integral + Kd * derivative);
145
146 if (deltaSec > 0.0)
147 {
148 PIDVectorX accVec = (controlValue - stackAllocations.oldControlValue) / deltaSec;
149 float maxNewJointAcc = accVec.maxCoeff();
150 float minNewJointAcc = accVec.minCoeff();
151 maxNewJointAcc = std::max<float>(fabs(minNewJointAcc), fabs(maxNewJointAcc));
152 if (maxNewJointAcc > maxDerivation)
153 {
154 auto newValue = stackAllocations.oldControlValue +
155 accVec * maxDerivation / maxNewJointAcc * deltaSec;
157 << VAROUT(maxNewJointAcc) << VAROUT(controlValue)
158 << VAROUT(stackAllocations.oldControlValue) << VAROUT(newValue);
159 controlValue = newValue;
160 }
161 }
162
163
164 float max = controlValue.maxCoeff();
165 float min = controlValue.minCoeff();
166 max = std::max<float>(fabs(min), fabs(max));
167
168
169 if (max > maxControlValue)
170 {
171 auto newValue = controlValue * maxControlValue / max;
172 ARMARX_DEBUG << deactivateSpam(0.5) << " Control value to big: " << controlValue
173 << " max value: " << maxControlValue << " new value: " << newValue;
174 controlValue = newValue;
175 }
176 ARMARX_DEBUG << deactivateSpam(0.5) << " error: " << error << " cV: " << (controlValue)
177 << " i: " << (Ki * integral) << " d: " << (Kd * derivative)
178 << " dt: " << deltaSec;
179
180 previousError = error;
181 lastUpdateTime += IceUtil::Time::seconds(deltaSec);
182 }
183
184 void
185 update(const PIDVectorX& measuredValue, const PIDVectorX& targetValue)
186 {
187 ScopedRecursiveLockPtr lock = getLock();
188 IceUtil::Time now = TimeUtil::GetTime();
189
190 if (firstRun)
191 {
193 }
194
195 double dt = (now - lastUpdateTime).toSecondsDouble();
196 update(dt, measuredValue, targetValue);
197 lastUpdateTime = now;
198 }
199
200 const PIDVectorX&
202 {
203 return controlValue;
204 }
205
206 void
207 setMaxControlValue(double value)
208 {
209 ScopedRecursiveLockPtr lock = getLock();
210 maxControlValue = value;
211 }
212
213 void
215 {
216 ScopedRecursiveLockPtr lock = getLock();
217 firstRun = true;
218 previousError = 0;
219 integral = 0;
221 //reset control
222 controlValue.setZero();
223 processValue.setZero();
224 target.setZero();
225 }
226
227 // protected:
228 float Kp, Ki, Kd;
229 double integral;
230 double maxIntegral = std::numeric_limits<double>::max();
235 IceUtil::Time lastUpdateTime;
240 mutable std::recursive_mutex mutex;
241 bool threadSafe = true;
242 std::vector<bool> limitless;
243
244 private:
245 struct StackAllocationHelper
246 {
247 PIDVectorX errorVec;
248 PIDVectorX direction;
249 PIDVectorX oldControlValue;
250 PIDVectorX zeroVec;
251 } stackAllocations;
252
253 using ScopedRecursiveLock = std::unique_lock<std::recursive_mutex>;
254 using ScopedRecursiveLockPtr = std::unique_ptr<ScopedRecursiveLock>;
255
257 getLock() const
258 {
259 if (threadSafe)
260 {
262 }
263 else
264 {
265 return ScopedRecursiveLockPtr();
266 }
267 }
268 };
269
271 using MultiDimPIDControllerPtr = std::shared_ptr<MultiDimPIDControllerTemplate<>>;
272} // namespace armarx
#define VAROUT(x)
constexpr T dt
SpamFilterDataPtr deactivateSpam(float deactivationDurationSec=10.0f, const std::string &identifier="", bool deactivate=true) const
disables the logging for the current line for the given amount of seconds.
Definition Logging.cpp:99
void update(const double deltaSec, const PIDVectorX &measuredValue, const PIDVectorX &targetValue)
MultiDimPIDControllerTemplate(float Kp, float Ki, float Kd, double maxControlValue=std::numeric_limits< double >::max(), double maxDerivation=std::numeric_limits< double >::max(), bool threadSafe=true, std::vector< bool > limitless={})
void update(const PIDVectorX &measuredValue, const PIDVectorX &targetValue)
Eigen::Matrix< float, dimensions, 1 > PIDVectorX
static IceUtil::Time GetTime(TimeMode timeMode=TimeMode::VirtualTime)
Get the current time.
Definition TimeUtil.cpp:42
static float angleModPI(float value)
Definition MathUtils.h:173
#define ARMARX_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
RecursiveMutex::scoped_lock ScopedRecursiveLock
std::shared_ptr< ScopedRecursiveLock > ScopedRecursiveLockPtr
This file offers overloads of toIce() and fromIce() functions for STL container types.
MultiDimPIDControllerTemplate<> MultiDimPIDController
std::vector< T > max(const std::vector< T > &v1, const std::vector< T > &v2)
std::shared_ptr< MultiDimPIDControllerTemplate<> > MultiDimPIDControllerPtr
std::vector< T > min(const std::vector< T > &v1, const std::vector< T > &v2)