31 #include <Eigen/Cholesky> 33 #include <Eigen/Dense> 39 LqrController::LqrController() {}
45 _system_model = system_model;
47 const int p = getStateDimension();
48 const int q = getControlInputDimension();
50 setWeights(Eigen::MatrixXd::Identity(p, p), Eigen::MatrixXd::Identity(q, q));
56 if (!setWeightR(R)) ret_val =
false;
57 if (!setWeightQ(Q)) ret_val =
false;
63 if (!_system_model || R.rows() != _system_model->getInputDimension() || R.cols() != _system_model->getInputDimension())
return false;
70 if (!_system_model || Q.rows() != _system_model->getStateDimension() || Q.cols() != _system_model->getStateDimension())
return false;
75 bool LqrController::initialize(
const StateVector&
x, ReferenceTrajectoryInterface& expected_xref, ReferenceTrajectoryInterface& expected_uref,
76 const Duration& expected_dt,
const Time& t, ReferenceTrajectoryInterface* sref)
78 assert(x.rows() == expected_xref.getDimension() &&
"Dimension mismatch in controller: current state x and reference");
83 PRINT_ERROR(
"LqrController: no system model provided. Cancelling.");
87 if (_system_model->isContinuousTime())
89 _discrete_time =
false;
90 PRINT_INFO(
"LqrController configured for continuous-time system dynamics.");
94 _discrete_time =
true;
95 PRINT_INFO(
"LqrController configured for discrete-time system dynamics.");
100 if (xref.rows() != x.rows())
102 PRINT_WARNING(
"LqrController currently supports only full state reference trajectories");
108 PRINT_INFO_COND(!_system_model->isLinear(),
"LQR is provided with a nonlinear model: linearizing model at target state...");
110 const int p = xref.rows();
111 const int q = uref.rows();
114 _system_model->getLinearA(xref, uref, _A);
116 _system_model->getLinearB(xref, uref, _B);
124 "Eigenvalues of the Hamiltonian are close to the imaginary axis. Numerically unstable results are expected.");
128 PRINT_WARNING_COND(!care_success || !_S.allFinite(),
"Riccati solving failed or NaN coefficients found in solution.");
137 SignalTargetInterface* signal_target, ReferenceTrajectoryInterface* sref, ReferenceTrajectoryInterface* xinit,
138 ReferenceTrajectoryInterface* uinit,
const std::string& ns)
140 const int q = uref.getDimension();
147 if (!initialize(x, xref, uref, dt, t, sref))
155 xref.getReference(t, xref_vec);
157 uref.getReference(t, uref_vec);
159 StateVector state_error = xref_vec -
x;
161 u = _K * state_error + uref_vec;
163 if (signal_target && _publish_error)
165 signal_target->sendMeasurement(ns +
"controller/error_norml2", t.toSec(), {state_error.norm()});
170 u_sequence->add(0.0, u);
171 x_sequence->add(0.0, x);
176 void LqrController::getAvailableSignals(SignalTargetInterface& signal_target,
const std::string& ns)
const 180 signal_target.registerMeasurement(ns +
"controller/error_norml2", 1);
184 void LqrController::reset() { _initialized =
false; }
186 #ifdef MESSAGE_SUPPORT 187 void LqrController::toMessage(corbo::messages::LqrController& message)
const 189 if (_system_model) _system_model->toMessage(*message.mutable_system_model());
194 Eigen::VectorXd Qdiag = _Q.diagonal();
195 message.mutable_q()->Resize(Qdiag.size(), 0);
198 message.set_q_diagonal_only(
true);
202 message.mutable_q()->Resize(_Q.rows() * _Q.cols(), 0);
205 message.set_q_diagonal_only(
false);
211 Eigen::VectorXd Rdiag = _R.diagonal();
212 message.mutable_r()->Resize(Rdiag.size(), 0);
215 message.set_r_diagonal_only(
true);
219 message.mutable_r()->Resize(_R.rows() * _R.cols(), 0);
222 message.set_r_diagonal_only(
false);
226 message.set_publish_error(_publish_error);
229 void LqrController::fromMessage(
const corbo::messages::LqrController& message, std::stringstream* issues)
231 _system_model.reset();
234 if (!message.has_system_model())
236 if (issues) *issues <<
"LqrController: no system model specified.\n";
242 util::get_oneof_field_type_expand_isolated(message.system_model(),
"system_dynamics", type,
false, 1);
247 dynamics->fromMessage(message.system_model(), issues);
248 setSystemModel(dynamics);
252 if (issues) *issues <<
"LqrController: unknown system model specified.\n";
256 const int p = dynamics->getStateDimension();
257 const int q = dynamics->getInputDimension();
260 if (message.q_diagonal_only())
262 if (p == message.q_size())
264 Eigen::MatrixXd Q =
Eigen::Map<
const Eigen::Matrix<double, -1, 1>>(message.q().data(), message.q_size()).asDiagonal();
265 if (!setWeightQ(Q) && issues) *issues <<
"LqrController: invalid size of weight matrix Q: it shout be [" << p <<
" x " << p <<
"].\n";
268 *issues <<
"LqrController: invalid size of weight matrix diag(Q): it shout be [ 1 x " << p <<
"].\n";
272 if (p * p == message.q_size())
275 *issues <<
"LqrController: invalid size of weight matrix Q: it shout be [" << p <<
" x " << p <<
"].\n";
278 *issues <<
"LqrController: invalid size of weight matrix Q: it shout be [" << p <<
" x " << p <<
"].\n";
281 if (message.r_diagonal_only())
283 if (q == message.r_size())
285 Eigen::MatrixXd R =
Eigen::Map<
const Eigen::Matrix<double, -1, 1>>(message.r().data(), message.r_size()).asDiagonal();
286 if (!setWeightR(R) && issues) *issues <<
"LqrController: invalid size of weight matrix R: it shout be [" << q <<
" x " << q <<
"].\n";
289 *issues <<
"LqrController: invalid size of weight matrix diag(R): it shout be [ 1 x " << q <<
"].\n";
293 if (q * q == message.r_size())
296 *issues <<
"LqrController: invalid size of weight matrix R: it shout be [" << q <<
" x " << q <<
"].\n";
299 *issues <<
"LqrController: invalid size of weight matrix R: it shout be [" << q <<
" x " << q <<
"].\n";
303 _publish_error = message.publish_error();
static bool solve(const Eigen::Ref< const Eigen::MatrixXd > &A, const Eigen::Ref< const Eigen::MatrixXd > &B, const Eigen::Ref< const Eigen::MatrixXd > &Q, const Eigen::Ref< const Eigen::MatrixXd > &R, Eigen::MatrixXd &X, Eigen::MatrixXd *G=nullptr)
Solve continuous-time algebraic Riccati equation.
#define PRINT_WARNING_COND_NAMED(cond, msg)
#define PRINT_WARNING(msg)
Print msg-stream.
static bool isNumericallyStable(const Eigen::Ref< const Eigen::MatrixXd > &A, const Eigen::Ref< const Eigen::MatrixXd > &B, const Eigen::Ref< const Eigen::MatrixXd > &Q, const Eigen::Ref< const Eigen::MatrixXd > &R)
Determine if solving the riccati equation seems to be numerically stable.
A matrix or vector expression mapping an existing array of data.
#define PRINT_INFO_COND(cond, msg)
Print msg-stream only if cond == true.
#define PRINT_WARNING_COND(cond, msg)
Print msg-stream only if cond == true.
EIGEN_DEVICE_FUNC const Scalar & q
static Factory & instance()
< Retrieve static instance of the factory
static bool solve(const Eigen::Ref< const Eigen::MatrixXd > &A, const Eigen::Ref< const Eigen::MatrixXd > &B, const Eigen::Ref< const Eigen::MatrixXd > &Q, const Eigen::Ref< const Eigen::MatrixXd > &R, Eigen::MatrixXd &X, Eigen::MatrixXd *G=nullptr)
Solve discrete-time algebraic Riccati equation.
Eigen::VectorXd OutputVector
A matrix or vector expression mapping an existing expression.
constexpr const int INHERITED
Inherit property.
Eigen::VectorXd StateVector
std::shared_ptr< TimeSeries > Ptr
std::shared_ptr< Derived > create(const std::string &name, bool print_error=true) const
Create a shared instance of the desired object.
The matrix class, also used for vectors and row-vectors.
std::shared_ptr< SystemDynamicsInterface > Ptr
#define PRINT_INFO(msg)
Print msg-stream.
#define PRINT_ERROR(msg)
Print msg-stream as error msg.