task_closed_loop_control.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  *
3  * Software License Agreement
4  *
5  * Copyright (c) 2020,
6  * TU Dortmund - Institute of Control Theory and Systems Engineering.
7  * All rights reserved.
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <https://www.gnu.org/licenses/>.
21  *
22  * Authors: Christoph Rösmann
23  *********************************************************************/
24 
26 #include <corbo-core/console.h>
27 #include <corbo-core/time.h>
28 #include <corbo-core/time_series.h>
30 #include <string>
31 
32 namespace corbo {
33 
35 {
36  // default reference
37  _xreference = std::make_shared<StaticReference>(Eigen::Matrix<double, 1, 1>::Ones());
38  _ureference = std::make_shared<ZeroReference>(1);
39 }
40 
41 void ClosedLoopControlTask::getAvailableSignals(const Environment& environment, SignalTargetInterface& signal_target, const std::string& ns) const
42 {
43  if (environment.getController())
44  {
45  environment.getController()->getAvailableSignals(signal_target);
46 
47  if (environment.getController()->getControlInputDimension() != property::INHERITED)
48  {
49  signal_target.registerMeasurement(ns + "control_input", environment.getController()->getControlInputDimension(), {}, true);
50  }
51  }
52 
53  if (environment.getPlant())
54  {
55  environment.getPlant()->getAvailableSignals(signal_target);
56  if (environment.getPlant()->getOutputDimension() != property::INHERITED)
57  signal_target.registerMeasurement(ns + "plant_output", environment.getPlant()->getOutputDimension());
58  }
59 
60  if (environment.getObserver())
61  {
62  environment.getObserver()->getAvailableSignals(signal_target);
63  if (environment.getObserver()->getStateDimension() != property::INHERITED)
64  signal_target.registerMeasurement(ns + "observed_states", environment.getObserver()->getStateDimension());
65  }
66 
68  {
69  signal_target.registerMeasurement(ns + "comp_states", environment.getObserver()->getStateDimension());
70  }
71 
72  if (_xreference)
73  {
74  signal_target.registerMeasurement(ns + "reference/x", _xreference->getDimension());
75  }
76  if (_ureference)
77  {
78  signal_target.registerMeasurement(ns + "reference/u", _ureference->getDimension());
79  }
80 }
81 
82 void ClosedLoopControlTask::performTask(Environment& environment, SignalTargetInterface* signal_target, std::string* msg, const std::string& ns)
83 {
84  // environment->reset();
85 
86  if (_dt <= 0 && !environment.getController()->supportsAsynchronousControl())
87  {
88  PRINT_ERROR("ClosedLoopControlTask::performTask(): dt <= 0 selected, but current controller does not support asynchronous control.");
89  return;
90  }
91 
92  // initialize variables
93  Time t(0);
94  Time tf(_sim_time);
95  Duration dt(_dt);
96 
97  ControllerInterface* controller = environment.getController().get();
98  PlantInterface* plant = environment.getPlant().get();
99  ObserverInterface* observer = environment.getObserver().get();
100 
101  using ControlVector = Eigen::VectorXd;
102  using StateVector = Eigen::VectorXd;
103  using OutputVector = Eigen::VectorXd;
104 
105  TimeSeries::Ptr u_sequence = std::make_shared<TimeSeries>();
106  TimeSeries::Ptr x_sequence = std::make_shared<TimeSeries>();
107  u_sequence->add(0, ControlVector::Zero(plant->getOutputDimension()));
108 
109  OutputVector y(plant->getOutputDimension());
110  StateVector x(controller->getStateDimension());
111 
112  Eigen::VectorXd xref(_xreference->getDimension());
113  Eigen::VectorXd uref(_ureference->getDimension());
114 
115  if (!verify(environment, msg)) return;
116 
117  // initialize modules
118  if (!controller->initialize(x, *_xreference, *_ureference, dt, t))
119  {
120  PRINT_ERROR("Controller initialization failed.");
121  return;
122  }
123  if (!plant->initialize())
124  {
125  PRINT_ERROR("Plant initialization failed.");
126  return;
127  }
128 
130  {
131  PRINT_ERROR("Compensator initialization failed.");
132  return;
133  }
134 
135  Rate rate(_realtime_sync ? dt : Duration(1e-6)); // wait a small amount also if realtime sync is disabled
136 
137  Time t_measure_x;
138  Time compensator_meas_start;
139  Duration cpu_time(0);
140  Duration cpu_time_cache(0);
141 
142  double comp_dt = 0;
143  double deadtime = 0;
144 
146 
147  std::vector<std::pair<double, Eigen::VectorXd>> useq_predict;
149 
150  // perform actual closed-loop task
151  Time t_wall_start = Time::now();
152 
153  while (t <= tf && ok())
154  {
155  Duration last_dt = (t == Time(0)) ? Duration(0) : dt;
156 
157  if (_use_wall_time) t = (Time::now() - t_wall_start).toTime();
158 
159  // publish reference signals
160  if (signal_target)
161  {
162  _xreference->getReference(t, xref);
163  _ureference->getReference(t, uref);
164  signal_target->sendMeasurement(ns + "reference/x", t.toSec(), xref);
165  signal_target->sendMeasurement(ns + "reference/u", t.toSec(), uref);
166  }
167 
168  // plant output
169  if (_use_wall_time) t = (Time::now() - t_wall_start).toTime();
170  if (!plant->output(y, t, signal_target, ns)) PRINT_ERROR("ClosedLoopControl::performTask(): error while retreiving plant output.");
171  if (signal_target) signal_target->sendMeasurement(ns + "plant_output", t.toSec(), y);
172 
173  t_measure_x = t;
174  compensator_meas_start = Time::now();
175 
176  // observer
177  if (_use_wall_time) t = (Time::now() - t_wall_start).toTime();
178  if (!observer->observe(y, x, last_dt, t, signal_target, ns)) PRINT_ERROR("ClosedLoopControl::performTask(): observer error.");
179  if (signal_target) signal_target->sendMeasurement(ns + "observed_states", t.toSec(), x);
180 
181  // compensator (we might want to compensate for long controller CPU times)
182 
184  {
185  comp_dt = (_computation_delay < 0) ? cpu_time.toSec() : _computation_delay;
186  }
187 
189  {
190  _time_value_buffer.getValues(t_measure_x.toSec() - deadtime, comp_dt + deadtime, useq_predict);
191  _compensator.predict(x, useq_predict, comp_dt + deadtime, x);
192 
193  // TODO(roesmann) seems not to work in gui
194  // if (signal_target) signal_target->sendMeasurement("comp_states", t_measure_x.toSec() + comp_dt, x);
195  }
196 
197  // controller
198  if (_use_wall_time) t = (Time::now() - t_wall_start).toTime();
199  if (!controller->step(x, *_xreference, *_ureference, last_dt, t, u_sequence, x_sequence, signal_target, nullptr, nullptr, nullptr, ns))
200  {
201  PRINT_ERROR("ClosedLoopControl::performTask(): controller error.");
202  u_sequence->clear();
203  u_sequence->add(t.toSec(), Eigen::VectorXd::Zero(controller->getControlInputDimension()));
204  }
205 
206  // if dt<=0 -> inherit from controller (asynchronous control mode)
207  if (_dt <= 0)
208  {
209  if (controller->getControlDuration() >= _min_dt)
210  {
211  if (controller->getControlDuration() <= _max_dt)
212  {
213  dt.fromSec(controller->getControlDuration());
214  rate.updateCycleTime(dt);
215  }
216  else
217  {
219  "ClosedLoopControl::performTask(): asychnronous control mode: controller returned dt>_max_dt. Setting dt=dt_max. This "
220  "warning is printed once.");
221  dt.fromSec(_max_dt);
222  }
223  }
224  else
225  {
227  "ClosedLoopControl::performTask(): asychnronous control mode: controller returned dt<_min_dt. Setting dt=dt_min. This warning is "
228  "printed once.");
229  dt.fromSec(_min_dt);
230  }
231  }
232 
233  // control plant
234  if (_use_wall_time) t = (Time::now() - t_wall_start).toTime();
235  plant->control(u_sequence, x_sequence, dt, t, signal_target, ns);
236 
238  {
239  _time_value_buffer.appendValues(t.toSec(), u_sequence->getValuesMap(0));
240  }
241 
242  // save cpu time required for the controller step (used for compensation in the next step)
243  cpu_time = Time::now() - compensator_meas_start;
244  cpu_time_cache = cpu_time;
245 
247  {
248  if (_computation_delay_filter) cpu_time.fromSec(_computation_delay_filter->filter(t.toSec(), cpu_time.toSec()));
249  }
250 
251  if (signal_target)
252  {
253  if (_use_wall_time) t.fromSec(t_measure_x.toSec() + deadtime + cpu_time_cache.toSec()); // TODO(kraemer) correct?
254  controller->sendSignals(t.toSec(), *signal_target, ns);
255  if (u_sequence && !u_sequence->isEmpty()) signal_target->sendMeasurement(ns + "control_input", t.toSec(), u_sequence->getValuesMap(0));
256  }
257 
258  if (!rate.sleep() && _realtime_sync)
259  {
260  PRINT_WARNING("ClosedLoopControlTask(): rate exceeded (" << rate.lastCycleTime().toSec() << "s/" << dt << "s).");
261  }
262  if (!_use_wall_time) t += dt;
263  }
264 
265  plant->stop();
266 }
267 
268 bool ClosedLoopControlTask::verify(const Environment& environment, std::string* msg) const
269 {
270  bool ret_val = true;
271 
272  if (msg) msg->clear();
273 
274  // check if all objects are set
275  if (!_xreference)
276  {
277  ret_val = false;
278  if (msg) *msg += "State reference trajectory not specified for ClosedLoopControlTask\n";
279  }
280 
281  // check if all objects are set
282  if (!_ureference)
283  {
284  ret_val = false;
285  if (msg) *msg += "Control reference trajectory not specified for ClosedLoopControlTask\n";
286  }
287 
288  // verify environment
289  std::string environment_msg;
290  ret_val = ret_val && environment.verify(&environment_msg);
291  if (msg) *msg += environment_msg;
292  if (ret_val == false) return false; // we need all objects in environment allocated!
293 
294  // check reference dimension
295  if (environment.getController()->getStateDimension() != _xreference->getDimension())
296  {
297  ret_val = false;
298  if (msg)
299  *msg += "State reference trajectory dimension (" + std::to_string(_xreference->getDimension()) +
300  ") does not match controller state dimension (" + std::to_string(environment.getController()->getStateDimension()) + ").\n";
301  }
302  if (environment.getController()->getControlInputDimension() != _ureference->getDimension())
303  {
304  ret_val = false;
305  if (msg)
306  *msg += "Control reference trajectory dimension (" + std::to_string(_ureference->getDimension()) +
307  ") does not match controller control input dimension (" +
308  std::to_string(environment.getController()->getControlInputDimension()) + ").\n";
309  }
310 
311  if (environment.getPlant()->requiresFutureControls() && !environment.getController()->providesFutureControls())
312  {
313  ret_val = false;
314  if (msg) *msg += "Controller does not support control sequences, that are required by the plant";
315  }
316 
317  if (environment.getPlant()->requiresFutureStates() && !environment.getController()->providesFutureStates())
318  {
319  ret_val = false;
320  if (msg) *msg += "Controller does not support state sequences, that are required by the plant";
321  }
322 
323  return ret_val;
324 }
325 
327 {
330 }
331 
332 #ifdef MESSAGE_SUPPORT
333 void ClosedLoopControlTask::toMessage(corbo::messages::ClosedLoopControlTask& message) const
334 {
335  message.set_sim_time(_sim_time);
336  message.set_dt(_dt);
337  message.set_realtime_sync(_realtime_sync);
338  message.set_use_wall_time(_use_wall_time);
339  message.set_min_dt(_min_dt);
340  message.set_max_dt(_max_dt);
341  if (_xreference) _xreference->toMessage(*message.mutable_xreference());
342  if (_ureference) _ureference->toMessage(*message.mutable_ureference());
343 
344  message.set_compensate_dead_time(_compensate_dead_time);
345  message.set_compensate_cpu_time(_compensate_cpu_time);
346  message.set_computation_delay(_computation_delay);
347 
348  _compensator.toMessage(*message.mutable_compensator());
349  if (_computation_delay_filter) _computation_delay_filter->toMessage(*message.mutable_computation_delay_filter());
350 }
351 void ClosedLoopControlTask::fromMessage(const corbo::messages::ClosedLoopControlTask& message, std::stringstream* issues)
352 {
353  _sim_time = message.sim_time();
354  _dt = message.dt();
355  _realtime_sync = message.realtime_sync();
356  _use_wall_time = message.use_wall_time();
357  _min_dt = message.min_dt();
358  _max_dt = message.max_dt();
359 
360  // xreference
361  _xreference.reset();
362  if (message.has_xreference())
363  {
364  // construct object
365  std::string type;
366  if (util::get_oneof_field_type(message.xreference(), "reference", type, false))
367  {
369  // import parameters
370  if (xreference)
371  {
372  xreference->fromMessage(message.xreference(), issues);
373  setStateReference(xreference);
374  }
375  }
376  }
377 
378  // ureference
379  _ureference.reset();
380  if (message.has_ureference())
381  {
382  // construct object
383  std::string type;
384  if (util::get_oneof_field_type(message.ureference(), "reference", type, false))
385  {
387  // import parameters
388  if (ureference)
389  {
390  ureference->fromMessage(message.ureference(), issues);
391  setControlReference(ureference);
392  }
393  }
394  }
395 
396  // compensator
397  _computation_delay = message.computation_delay();
398  _compensate_dead_time = message.compensate_dead_time();
399  _compensate_cpu_time = message.compensate_cpu_time();
400 
401  _compensator.fromMessage(message.compensator(), issues);
402 
404 
405  if (message.has_computation_delay_filter() && !message.computation_delay_filter().has_no_filter())
406  {
407  // construct object
408  std::string type;
409  if (util::get_oneof_field_type(message.computation_delay_filter(), "filter", type, false))
410  {
412  // import parameters
413  if (filter)
414  {
415  filter->fromMessage(message.computation_delay_filter(), issues);
416  _computation_delay_filter = filter;
417  }
418  }
419  }
420 }
421 #endif
422 
423 } // namespace corbo
virtual double getControlDuration() const
Return the duration for which the control u obtained from step() is valid (useful for asynchronous co...
bool initialize()
initialize the predictor
void appendValues(double t, const Eigen::Ref< const Eigen::VectorXd > &u)
#define PRINT_WARNING_ONCE(msg)
Print msg-stream only once.
Definition: console.h:149
Scalar * x
Standard environment for control tasks.
Definition: environment.h:49
void predict(const Eigen::Ref< const StateVector > &x0, std::vector< std::pair< double, ControlVector >> u_seq, double dt, Eigen::Ref< StateVector > x1)
Predict x1 using t0, x0, u and dt (alias between x0 and x1 allowed)
Duration lastCycleTime() const
Return actual execution time of the last cycle.
Definition: time.h:368
ReferenceTrajectoryInterface::Ptr _xreference
Scalar * y
#define PRINT_WARNING(msg)
Print msg-stream.
Definition: console.h:145
static Time now()
Retrieve current system time.
Definition: time.h:275
FilterInterface::Ptr _computation_delay_filter
std::shared_ptr< FilterInterface > Ptr
virtual void sendSignals(double t, SignalTargetInterface &signal_target, const std::string &ns="") const
void fromSec(double t)
Set duration from seconds (see Duration(double t))
Definition: time.h:140
void setControlReference(ReferenceTrajectoryInterface::Ptr ureference)
Set control input reference trajectory.
virtual void stop()
Stop plant (you might probably use this to set the plant into a safe setpoint)
virtual bool step(const StateVector &x, ReferenceTrajectoryInterface &xref, ReferenceTrajectoryInterface &uref, const Duration &dt, const Time &t, ControlVector &u, SignalTargetInterface *signal_target=nullptr, const std::string &ns="")
Perform actual controller step / control law computation.
virtual void sendMeasurement(Measurement::ConstPtr measurement)=0
Send a measurement to the target.
Interface class for signal targets.
Representation of time stamps.
Definition: time.h:251
void performTask(Environment &environment, SignalTargetInterface *signal_target=nullptr, std::string *msg=nullptr, const std::string &ns="") override
Perform task.
virtual bool observe(const OutputVector &y, StateVector &x, const Duration &dt, const Time &t, SignalTargetInterface *signal_target=nullptr, const std::string &ns="")=0
Perform actual observer step / state estimation.
void setStateReference(ReferenceTrajectoryInterface::Ptr xreference)
Set state reference trajectory.
void getAvailableSignals(const Environment &environment, SignalTargetInterface &signal_target, const std::string &ns="") const override
Retrieve available signals from the task.
Interface class for observers.
const PlantInterface::Ptr & getPlant() const
Read access to the underlying plant.
Definition: environment.h:75
virtual void registerMeasurement(const std::string &unique_name, int value_dimension, const std::vector< std::string > &value_labels={}, bool zero_order_hold=false)=0
Register a measurement type at current target.
Rate objects can be used to run loops at a desired rate.
Definition: time.h:353
bool verify(std::string *msg=nullptr) const
Check if the environment satisfies all requirements (dimensions, ...)
Definition: environment.cpp:46
virtual int getStateDimension() const =0
Return the dimension of the required plant state/output.
bool verify(const Environment &environment, std::string *msg=nullptr) const override
Check if the environment and other settings satisfy all requirements for the given task...
void reset()
Reset internal buffer.
bool sleep()
Sleep for the remaining duration to met the desired frequency (w.r.t previous sleep call) ...
Definition: time.cpp:31
static Factory & instance()
< Retrieve static instance of the factory
Definition: factory.h:72
const ObserverInterface::Ptr & getObserver() const
Read access to the underlying observer.
Definition: environment.h:71
virtual int getControlInputDimension() const =0
Return the control input dimension.
void reset() override
Reset task state.
double toSec() const
Return duration in seconds.
Definition: time.h:138
constexpr const int INHERITED
Inherit property.
virtual bool initialize()
Initialize plant.
double toSec() const
Cast time stamp to seconds.
Definition: time.h:281
virtual int getOutputDimension() const =0
Return the plant output dimension (y)
virtual bool control(const ControlVector &u, const Duration &dt, const Time &t, SignalTargetInterface *signal_target=nullptr, const std::string &ns="")
Send commands to plant.
bool ok()
global method to check whether to proceed or cancel the current action
Definition: global.cpp:32
std::shared_ptr< ReferenceTrajectoryInterface > Ptr
void fromSec(double t)
Set time stamp from seconds.
Definition: time.h:283
void updateCycleTime(const Duration &dt)
Update cycle time without resetting start time.
Definition: time.h:365
Interface class for controllers.
std::shared_ptr< TimeSeries > Ptr
Definition: time_series.h:64
virtual bool output(OutputVector &output, const Time &t, SignalTargetInterface *signal_target=nullptr, const std::string &ns="")=0
Retrieve current plant output (measurements)
ReferenceTrajectoryInterface::Ptr _ureference
virtual bool initialize(const StateVector &x, ReferenceTrajectoryInterface &expected_xref, ReferenceTrajectoryInterface &expected_uref, const Duration &expected_dt, const Time &t, ReferenceTrajectoryInterface *expected_sref=nullptr)
Initialize the controller.
std::shared_ptr< Derived > create(const std::string &name, bool print_error=true) const
Create a shared instance of the desired object.
Definition: factory.h:93
void getValues(double ts, double dt, std::vector< std::pair< double, Eigen::VectorXd >> &useq_out)
Compute the delayed values.
The matrix class, also used for vectors and row-vectors.
Definition: Matrix.h:178
const ControllerInterface::Ptr & getController() const
Read access to the underlying controller.
Definition: environment.h:67
ClosedLoopControlTask()
Default constructor.
Representation of time durations.
Definition: time.h:106
void setInitialValue(const Eigen::Ref< const Eigen::VectorXd > &uinit)
Specify initial value.
Interface class for plants.
#define PRINT_ERROR(msg)
Print msg-stream as error msg.
Definition: console.h:173


control_box_rst
Author(s): Christoph Rösmann
autogenerated on Mon Feb 28 2022 22:07:33