Lodestar
An integrated real-time control package in C++
StateSpace.hpp
1 //
2 // Created by Hamza El-Kebir on 4/17/21.
3 //
4 
5 #ifndef LODESTAR_STATESPACE_HPP
6 #define LODESTAR_STATESPACE_HPP
7 
8 #include "SystemStateless.hpp"
9 #include <Eigen/Dense>
10 #include "Lodestar/aux/CompileTimeQualifiers.hpp"
11 
12 namespace ls {
13  namespace systems {
14  template<typename TScalar = double, const int TStateDim = Eigen::Dynamic, const int TInputDim = Eigen::Dynamic, const int TOutputDim = Eigen::Dynamic>
15  class StateSpace : public SystemStateless {
16  public:
17  typedef Eigen::Matrix<TScalar, TStateDim, TStateDim> TDStateMatrix;
18  typedef Eigen::Matrix<TScalar, TStateDim, TInputDim> TDInputMatrix;
19  typedef Eigen::Matrix<TScalar, TOutputDim, TStateDim> TDOutputMatrix;
20  typedef Eigen::Matrix<TScalar, TOutputDim, TInputDim> TDFeedforwardMatrix;
21 
25  StateSpace() : A_(new TDStateMatrix),
26  B_(new TDInputMatrix),
27  C_(new TDOutputMatrix),
28  D_(new TDFeedforwardMatrix),
29  dt_(-1), isDiscrete_(false)
30  {
31  A_->setIdentity();
32  B_->setIdentity();
33  C_->setIdentity();
34  D_->setZero();
35  };
36 
48  StateSpace(TDStateMatrix *A, TDInputMatrix *B,
49  TDOutputMatrix *C, TDOutputMatrix *D);
50 
51  // /**
52  // * @brief Construct a state space system with the given matrices.
53  // *
54  // * @note TState space systems are assumed to be in continuous time by
55  // * default.
56  // *
57  // * @param A TState matrix.
58  // * @param B Input matrix.
59  // * @param C Output matrix.
60  // * @param D Feedforward matrix.
61  // */
62  // StateSpace(const TDStateMatrix &A, const TDInputMatrix &B,
63  // const TDOutputMatrix &C, const TDOutputMatrix &D);
64 
65  template<typename DerivedA, typename DerivedB, typename DerivedC, typename DerivedD>
66  StateSpace(const Eigen::EigenBase<DerivedA> &A, const Eigen::EigenBase<DerivedB> &B,
67  const Eigen::EigenBase<DerivedC> &C, const Eigen::EigenBase<DerivedD> &D);
68 
74  StateSpace(const StateSpace &other);
75 
81  const TDStateMatrix &getA() const;
82 
88  void setA(TDStateMatrix *A);
89 
90  template<typename Derived>
91  void setA(Eigen::EigenBase<Derived> *A);
92 
98  void setA(const TDStateMatrix &A);
99 
100  template<typename Derived>
101  void setA(const Eigen::EigenBase<Derived> &A);
102 
108  const TDInputMatrix &getB() const;
109 
115  void setB(TDInputMatrix *B);
116 
117  template<typename Derived>
118  void setB(Eigen::EigenBase<Derived> *B);
119 
125  void setB(const TDInputMatrix &B);
126 
127  template<typename Derived>
128  void setB(const Eigen::EigenBase<Derived> &B);
129 
135  const TDOutputMatrix &getC() const;
136 
142  void setC(TDOutputMatrix *C);
143 
144  template<typename Derived>
145  void setC(Eigen::EigenBase<Derived> *C);
146 
152  void setC(const TDOutputMatrix &C);
153 
154  template<typename Derived>
155  void setC(const Eigen::EigenBase<Derived> &C);
156 
162  const TDFeedforwardMatrix &getD() const;
163 
169  void setD(TDFeedforwardMatrix *D);
170 
171  template<typename Derived>
172  void setD(Eigen::EigenBase<Derived> *D);
173 
179  void setD(const TDFeedforwardMatrix &D);
180 
181  template<typename Derived>
182  void setD(const Eigen::EigenBase<Derived> &D);
183 
190  void copyMatrices(const StateSpace &other);
191 
197  void setDiscreteParams(double dt);
198 
206  void setDiscreteParams(double dt, bool discrete);
207 
213  inline bool isDiscrete() const;
214 
220  inline double getSamplingPeriod() const;
221 
227  void setSamplingPeriod(double dt);
228 
234  IF_DYNAMIC_RETURN(TStateDim, TInputDim, TOutputDim, long)
235  inline stateDim() const
236  {
237  return stateDimDynamic();
238  }
239 
240  long inline stateDimDynamic() const
241  {
242  return A_->rows();
243  }
244 
245  IF_STATIC_RETURN(TStateDim, TInputDim, TOutputDim, long)
246  inline stateDim() const
247  {
248  return stateDimStatic();
249  }
250 
251  long inline stateDimStatic() const
252  {
253  return TStateDim;
254  }
255 
261  IF_DYNAMIC_RETURN(TStateDim, TInputDim, TOutputDim, long)
262  inline inputDim() const
263  {
264  return inputDimDynamic();
265  }
266 
267  long inline inputDimDynamic() const
268  {
269  return B_->cols();
270  }
271 
272  IF_STATIC_RETURN(TStateDim, TInputDim, TOutputDim, long)
273  inline inputDim() const
274  {
275  return inputDimStatic();
276  }
277 
278  long inline inputDimStatic() const
279  {
280  return TInputDim;
281  }
282 
288  IF_DYNAMIC_RETURN(TStateDim, TInputDim, TOutputDim, long)
289  inline outputDim() const
290  {
291  return outputDimDynamic();
292  }
293 
294  long inline outputDimDynamic() const
295  {
296  return C_->rows();
297  }
298 
299  IF_STATIC_RETURN(TStateDim, TInputDim, TOutputDim, long)
300  inline outputDim() const
301  {
302  return outputDimStatic();
303  }
304 
305  long inline outputDimStatic() const
306  {
307  return TOutputDim;
308  }
309 
318  template<int TStateDim2, int TOutputDim2>
319  void append(const StateSpace<TScalar, TStateDim2, TOutputDim, TOutputDim2> &ss);
320 
331  inline bool isStable(double tolerance = 0) const;
332 
351  template<typename T_TScalar = TScalar, const int T_TStateDim = TStateDim, const int T_TInputDim = TInputDim, const int T_TOutputDim = TOutputDim>
352  ls::systems::StateSpace<T_TScalar, LS_STATIC_UNLESS_DYNAMIC(
353  T_TStateDim + T_TOutputDim), T_TInputDim, T_TOutputDim>
354  addIntegralAction(LS_IS_DYNAMIC_DEFAULT(T_TStateDim, T_TInputDim, T_TOutputDim)) const;
355 
374  template<typename T_TScalar = TScalar, const int T_TStateDim = TStateDim, const int T_TInputDim = TInputDim, const int T_TOutputDim = TOutputDim>
375  ls::systems::StateSpace<T_TScalar, LS_STATIC_UNLESS_DYNAMIC(
376  T_TStateDim + T_TOutputDim), T_TInputDim, T_TOutputDim>
377  addIntegralAction(LS_IS_STATIC_DEFAULT(T_TStateDim, T_TInputDim, T_TOutputDim)) const;
378 
379  protected:
380  TDStateMatrix *A_;
381  TDInputMatrix *B_;
382  TDOutputMatrix *C_;
383  TDFeedforwardMatrix *D_;
384  double dt_;
385  bool isDiscrete_;
386  };
387  }
388 }
389 
390 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
393  A_(new TDStateMatrix(other.getA())),
394  B_(new TDInputMatrix(other.getB())),
395  C_(new TDOutputMatrix(other.getC())),
396  D_(new TDFeedforwardMatrix(other.getD())),
397  dt_(other.getSamplingPeriod()),
398  isDiscrete_(other.isDiscrete())
399 {}
400 
401 // TODO: Replace constructors by Eigen::EigenBase<*>
402 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
404  TDStateMatrix *A, TDInputMatrix *B,
405  TDOutputMatrix *C, TDOutputMatrix *D):
406  A_(new TDStateMatrix(*A)),
407  B_(new TDInputMatrix(*B)),
408  C_(new TDOutputMatrix(*C)),
409  D_(new TDFeedforwardMatrix(*D))
410 {
411  // *A_ = *A;
412  // *B_ = *B;
413  // *C_ = *C;
414  // *D_ = *D;
415  dt_ = -1;
416  isDiscrete_ = false;
417 }
418 
419 //template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
420 //ls::systems::StateSpace<TScalar, TStateDim, TInputDim, TOutputDim>::StateSpace(
421 // const TDStateMatrix &A, const TDInputMatrix &B,
422 // const TDOutputMatrix &C, const TDOutputMatrix &D) : A_(new TDStateMatrix(A)), B_(new TDInputMatrix(B)),
423 // C_(new TDOutputMatrix(C)), D_(new TDFeedforwardMatrix(D))
424 //{
429 // dt_ = -1;
430 // isDiscrete_ = false;
431 //}
432 
433 template<typename TScalar, const int TStateDim, const int TInputDim, const int TOutputDim>
434 template<typename DerivedA, typename DerivedB, typename DerivedC, typename DerivedD>
436  const Eigen::EigenBase<DerivedB> &B,
437  const Eigen::EigenBase<DerivedC> &C,
438  const Eigen::EigenBase<DerivedD> &D):
439  A_(new TDStateMatrix(A)),
440  B_(new TDInputMatrix(B)),
441  C_(new TDOutputMatrix(C)),
442  D_(new TDFeedforwardMatrix(D))
443 {
444  *A_ = A;
445  *B_ = B;
446  *C_ = C;
447  *D_ = D;
448  dt_ = -1;
449  isDiscrete_ = false;
450 }
451 
452 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
453 const typename ls::systems::StateSpace<TScalar, TStateDim, TInputDim, TOutputDim>::TDStateMatrix &
455 {
456  return *A_;
457 }
458 
459 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
461 {
462  *A_ = *A;
463 }
464 
465 template<typename TScalar, const int TStateDim, const int TInputDim, const int TOutputDim>
466 template<typename Derived>
468 {
469  *A_ = *A;
470 }
471 
472 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
474  const TDStateMatrix &A)
475 {
476  *A_ = A;
477 }
478 
479 template<typename TScalar, const int TStateDim, const int TInputDim, const int TOutputDim>
480 template<typename Derived>
482 {
483  *A_ = A;
484 }
485 
486 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
487 const typename ls::systems::StateSpace<TScalar, TStateDim, TInputDim, TOutputDim>::TDInputMatrix &
489 {
490  return *B_;
491 }
492 
493 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
495 {
496  *B_ = *B;
497 }
498 
499 template<typename TScalar, const int TStateDim, const int TInputDim, const int TOutputDim>
500 template<typename Derived>
502 {
503  *B_ = *B;
504 }
505 
506 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
508  const TDInputMatrix &B)
509 {
510  *B_ = B;
511 }
512 
513 template<typename TScalar, const int TStateDim, const int TInputDim, const int TOutputDim>
514 template<typename Derived>
516 {
517  *B_ = B;
518 }
519 
520 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
521 const typename ls::systems::StateSpace<TScalar, TStateDim, TInputDim, TOutputDim>::TDOutputMatrix &
523 {
524  return *C_;
525 }
526 
527 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
528 void
530 {
531  *C_ = *C;
532 }
533 
534 template<typename TScalar, const int TStateDim, const int TInputDim, const int TOutputDim>
535 template<typename Derived>
537 {
538  *C_ = *C;
539 }
540 
541 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
543  const TDOutputMatrix &C)
544 {
545  *C_ = C;
546 }
547 
548 template<typename TScalar, const int TStateDim, const int TInputDim, const int TOutputDim>
549 template<typename Derived>
551 {
552  *C_ = C;
553 }
554 
555 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
556 const typename ls::systems::StateSpace<TScalar, TStateDim, TInputDim, TOutputDim>::TDFeedforwardMatrix &
558 {
559  return *D_;
560 }
561 
562 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
564  TDFeedforwardMatrix *D)
565 {
566  *D_ = *D;
567 }
568 
569 template<typename TScalar, const int TStateDim, const int TInputDim, const int TOutputDim>
570 template<typename Derived>
572 {
573  *D_ = *D;
574 }
575 
576 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
578  const TDFeedforwardMatrix &D)
579 {
580  *D_ = D;
581 }
582 
583 template<typename TScalar, const int TStateDim, const int TInputDim, const int TOutputDim>
584 template<typename Derived>
586 {
587  *D_ = D;
588 }
589 
590 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
592 {
593  return isDiscrete_;
594 }
595 
596 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
598 {
599  return dt_;
600 }
601 
602 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
604 {
605  dt_ = dt;
606 }
607 
608 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
610 {
611  dt_ = dt;
612  isDiscrete_ = true;
613 }
614 
615 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
617 {
618  dt_ = dt;
619  isDiscrete_ = discrete;
620 }
621 
622 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
623 template<int TStateDim2, int TOutputDim2>
626 {
627  // TODO: Implement state space append function.
628 }
629 
630 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
632 {
633  auto eig = A_->eigenvalues();
634 
635  if (isDiscrete()) {
636  double tol = (tolerance < 0 ? -tolerance * tolerance : tolerance * tolerance);
637 
638  for (int i = 0; i < eig.size(); i++) {
639  if (eig(i).real() * eig(i).real() + eig(i).imag() * eig(i).imag() > 1 + tol)
640  return false;
641  }
642  } else {
643  for (int i = 0; i < eig.size(); i++) {
644  if (eig(i).real() > -tolerance)
645  return false;
646  }
647  }
648 
649  return true;
650 }
651 
652 template<typename TScalar, int TStateDim, int TInputDim, int TOutputDim>
655 {
656  setA(other.getA());
657  setB(other.getB());
658  setC(other.getC());
659  setD(other.getD());
660 
661 }
662 
663 template<typename TScalar, const int TStateDim, const int TInputDim, const int TOutputDim>
664 template<typename T_TScalar, const int T_TStateDim, const int T_TInputDim, const int T_TOutputDim>
665 ls::systems::StateSpace<T_TScalar, LS_STATIC_UNLESS_DYNAMIC(T_TStateDim + T_TOutputDim), T_TInputDim, T_TOutputDim>
667  LS_IS_DYNAMIC(T_TStateDim, T_TInputDim, T_TOutputDim)) const
668 {
669  Eigen::Matrix<TScalar, Eigen::Dynamic, Eigen::Dynamic> A(stateDim() + outputDim(), stateDim() + outputDim());
670  A.setZero();
671  A.topLeftCorner(stateDim(), stateDim()) = getA();
672  A.bottomLeftCorner(outputDim(), stateDim()) = getC();
673  A.bottomRightCorner(outputDim(), outputDim()).setIdentity();
674 
675  Eigen::Matrix<TScalar, Eigen::Dynamic, Eigen::Dynamic> B(stateDim() + outputDim(), inputDim());
676  B.setZero();
677  B.topRows(stateDim()) = getB();
678 
679  Eigen::Matrix<TScalar, Eigen::Dynamic, Eigen::Dynamic> C(outputDim(), stateDim() + outputDim());
680  C.setZero();
681  C.leftCols(stateDim()) = getC();
682  C.rightCols(outputDim()).setIdentity();
683 
684  Eigen::Matrix<TScalar, Eigen::Dynamic, Eigen::Dynamic> D(outputDim(), inputDim());
685  D = getD();
686 
687  ls::systems::StateSpace<T_TScalar, LS_STATIC_UNLESS_DYNAMIC(
688  T_TStateDim + T_TOutputDim), T_TInputDim, T_TOutputDim> ssi{A, B, C, D};
689 
690  return ssi;
691 }
692 
693 template<typename TScalar, const int TStateDim, const int TInputDim, const int TOutputDim>
694 template<typename T_TScalar, const int T_TStateDim, const int T_TInputDim, const int T_TOutputDim>
695 ls::systems::StateSpace<T_TScalar, LS_STATIC_UNLESS_DYNAMIC(T_TStateDim + T_TOutputDim), T_TInputDim, T_TOutputDim>
697  LS_IS_STATIC(T_TStateDim, T_TInputDim, T_TOutputDim)) const
698 {
699  Eigen::Matrix<TScalar, TStateDim + TOutputDim, TStateDim + TOutputDim> A{};
700  A.setZero();
701  A.template topLeftCorner<TStateDim, TStateDim>() = getA();
702  A.template bottomLeftCorner<TOutputDim, TStateDim>() = getC();
703  A.template bottomRightCorner<TOutputDim, TOutputDim>().setIdentity();
704 
705  Eigen::Matrix<TScalar, TStateDim + TOutputDim, TInputDim> B{};
706  B.setZero();
707  B.template topRows<TStateDim>() = getB();
708 
709  Eigen::Matrix<TScalar, TOutputDim, TStateDim + TOutputDim> C{};
710  C.setZero();
711  C.template leftCols<TStateDim>() = getC();
712  C.template rightCols<TOutputDim>().setIdentity();
713 
714  Eigen::Matrix<TScalar, TOutputDim, TInputDim> D{};
715  D = getD();
716 
717  ls::systems::StateSpace<T_TScalar, LS_STATIC_UNLESS_DYNAMIC(
718  T_TStateDim + T_TOutputDim), T_TInputDim, T_TOutputDim> ssi{A, B, C, D};
719 
720  return ssi;
721 }
722 
723 #endif //LODESTAR_STATESPACE_HPP
ls::systems::StateSpace::getB
const TDInputMatrix & getB() const
Gets the input matrix.
Definition: StateSpace.hpp:488
ls::systems::StateSpace::getD
const TDFeedforwardMatrix & getD() const
Gets the feedforward matrix.
Definition: StateSpace.hpp:557
ls::systems::StateSpace::StateSpace
StateSpace()
Default constructor.
Definition: StateSpace.hpp:25
ls::systems::StateSpace::D_
TDFeedforwardMatrix * D_
Output matrix.
Definition: StateSpace.hpp:383
ls::systems::StateSpace::isDiscrete_
bool isDiscrete_
Sampling period.
Definition: StateSpace.hpp:385
ls::systems::StateSpace::B_
TDInputMatrix * B_
TState matrix.
Definition: StateSpace.hpp:381
ls::systems::StateSpace::getA
const TDStateMatrix & getA() const
Gets the state matrix.
Definition: StateSpace.hpp:454
ls::systems::StateSpace::setA
void setA(TDStateMatrix *A)
Sets the state matrix.
Definition: StateSpace.hpp:460
ls::systems::StateSpace::stateDim
stateDim() const
Returns the state dimension.
Definition: StateSpace.hpp:235
ls::systems::StateSpace::getC
const TDOutputMatrix & getC() const
Gets the output matrix.
Definition: StateSpace.hpp:522
ls
Main Lodestar code.
Definition: BilinearTransformation.hpp:12
ls::systems::StateSpace::setDiscreteParams
void setDiscreteParams(double dt)
Sets the discrete time system parameters.
Definition: StateSpace.hpp:609
ls::systems::StateSpace::C_
TDOutputMatrix * C_
Input matrix.
Definition: StateSpace.hpp:382
ls::systems::StateSpace::isDiscrete
bool isDiscrete() const
Returns a bool that tells if the system is discrete.
Definition: StateSpace.hpp:591
ls::systems::StateSpace
Definition: StateSpace.hpp:15
ls::systems::StateSpace::append
void append(const StateSpace< TScalar, TStateDim2, TOutputDim, TOutputDim2 > &ss)
Appends to state space systems.
Definition: StateSpace.hpp:624
ls::systems::StateSpace::dt_
double dt_
Feedforward matrix.
Definition: StateSpace.hpp:384
ls::systems::SystemStateless
Definition: SystemStateless.hpp:9
ls::systems::StateSpace::setC
void setC(TDOutputMatrix *C)
Sets the output matrix.
Definition: StateSpace.hpp:529
ls::systems::StateSpace::inputDim
inputDim() const
Returns the input dimension.
Definition: StateSpace.hpp:262
ls::systems::StateSpace::getSamplingPeriod
double getSamplingPeriod() const
Returns the sampling period.
Definition: StateSpace.hpp:597
ls::systems::StateSpace::setD
void setD(TDFeedforwardMatrix *D)
Sets the feedforward matrix.
Definition: StateSpace.hpp:563
ls::systems::StateSpace::outputDim
outputDim() const
Returns the output dimension.
Definition: StateSpace.hpp:289
ls::systems::StateSpace::copyMatrices
void copyMatrices(const StateSpace &other)
Copies matrices from one state space object to the current instance.
Definition: StateSpace.hpp:653
ls::systems::StateSpace::addIntegralAction
ls::systems::StateSpace< T_TScalar, LS_STATIC_UNLESS_DYNAMIC(T_TStateDim+T_TOutputDim), T_TInputDim, T_TOutputDim > addIntegralAction(LS_IS_DYNAMIC_DEFAULT(T_TStateDim, T_TInputDim, T_TOutputDim)) const
Returns a state space representation that adds integral action to the original system.
ls::systems::StateSpace::setB
void setB(TDInputMatrix *B)
Sets the input matrix.
Definition: StateSpace.hpp:494
ls::systems::StateSpace::isStable
bool isStable(double tolerance=0) const
Determines if the system is stable based on its eigenvalues.
Definition: StateSpace.hpp:631
ls::systems::StateSpace::setSamplingPeriod
void setSamplingPeriod(double dt)
Sets the sampling period.
Definition: StateSpace.hpp:603