From 6ee68c5978b02557a6b5aed8bc4284495dd038ce Mon Sep 17 00:00:00 2001 From: mgiftthaler Date: Fri, 24 Apr 2020 18:31:15 +0200 Subject: [PATCH 01/21] backing up the test --- .../test/solver/linear/ManifoldLQOCTest.cpp | 229 ++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp diff --git a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp new file mode 100644 index 000000000..01918bc24 --- /dev/null +++ b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp @@ -0,0 +1,229 @@ + +#include + +using namespace ct::core; +using namespace ct::optcon; + + +const bool verbose = true; + +using ManifoldState_t = ManifoldState; +const size_t state_dim = ManifoldState_t::TangentDim; +const size_t control_dim = 3; + +class DiscrSO3LTITestSystem final : public LTISystem +{ +public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + + DiscrSO3LTITestSystem(const Eigen::Matrix A_test, + const Eigen::Matrix B_test, + const ManifoldState_t& m_ref = ManifoldState_t::NeutralElement()) + : m_ref_(m_ref) + { + this->A() << A_test; + this->B() << B_test; + } + + /** + * @brief the log operator is defined as expressing the tangent vector w.r.t. m_ref_ + */ + virtual ManifoldState_t::Tangent lift(const ManifoldState_t& m) override + { + return ManifoldState_t::Tangent::Zero(); // the system stays where it is (combination with A = identity()) + /*return m.rminus(m);*/ + } + // virtual ManifoldState_t retract(const ManifoldState_t::Tangent& t) override { + // throw std::runtime_error("not implemetned."); + // /*return m_ref_.rplus(t);*/ } +protected: + ManifoldState_t m_ref_; +}; + + +int main(int argc, char** argv) +{ + std::cout << std::fixed; + + const size_t N = 5; + const double dt = 0.25; + + const ManifoldState_t x0 = manif::SO3(0, 0, 0); + ct::core::DiscreteArray x_traj(N + 1, x0); // init state trajectory, will be overwritten + ct::core::DiscreteArray> u_traj_init(N); // init control traj + for (size_t i = 0; i < N; i++) + u_traj_init[i] = ct::core::ControlVector::Zero() * dt; + + + // create instances of HPIPM and an unconstrained Gauss-Newton Riccati solver + std::shared_ptr> hpipmSolver( + new HPIPMInterface); + std::shared_ptr> gnRiccatiSolver( + new GNRiccatiSolver); + + // store them, and identifying names, in a vectors + std::vector>> lqocSolvers; + lqocSolvers.push_back(gnRiccatiSolver); + lqocSolvers.push_back(hpipmSolver); + std::vector solverNames = {"Riccati", "HPIPM"}; + + // create linear-quadratic optimal control problem containers + std::vector>> problems; + std::shared_ptr> lqocProblem1( + new LQOCProblem(N)); + std::shared_ptr> lqocProblem2( + new LQOCProblem(N)); + + problems.push_back(lqocProblem1); + problems.push_back(lqocProblem2); + + // create a discrete-time manifold system + Eigen::Matrix A = Eigen::Matrix::Zero(); + Eigen::Matrix B; + A << 1.0, 0, 0, 0, 1.0, 0, 0, 0, 1.0; // a slightly unstable system + B << 1, 0, 0, 0, 1, 0, 0, 0, 1; // direct action + std::shared_ptr> exampleSystem( + new DiscrSO3LTITestSystem(A, B, x0)); + + + // create a cost function + Eigen::Matrix Q, Q_final; + Eigen::Matrix R; + Q_final << 100, 0, 0, 0, 100, 0, 0, 0, 100; + //Q << 1, 0, 0, 0, 1, 0, 0, 0, 1; + Q.setZero(); + R << 1, 0, 0, 0, 1, 0, 0, 0, 1; + ManifoldState_t x_final = manif::SO3(M_PI / 2, 0, 0); + std::cout << "desired final state: " << x_final << std::endl; + ManifoldState_t x_nominal = x0; + ct::core::ControlVector u_nom = ct::core::ControlVector::Zero(); + std::shared_ptr> costFunction( + new CostFunctionQuadraticSimple(Q, R, x_nominal, u_nom, x_final, Q_final)); + + ManifoldState_t::Tangent b; + b.setZero(); // todo why this? + + // integrate an initial state with the open-loop system to get initial trajectories + ManifoldState_t x_curr; + ManifoldState_t::Tangent dx; + x_curr = x0; + x_traj.front() = x0; + std::cout << "integrate an random initial state with the unstable system" << std::endl; + std::cout << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; + for (size_t i = 0; i < N; i++) + { + exampleSystem->computeControlledDynamics(x_curr, 0, u_traj_init[i], dx); + x_curr = x_curr + dx; + x_traj[i + 1] = x_curr; + std::cout << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; + } + + + // initialize the optimal control problems for both solvers + problems[0]->setFromTimeInvariantLinearQuadraticProblem(x_traj, u_traj_init, *exampleSystem, *costFunction, b, dt); + problems[1]->setFromTimeInvariantLinearQuadraticProblem(x_traj, u_traj_init, *exampleSystem, *costFunction, b, dt); + + + // HACKY corrections // TODO: move somewhere meaningful + for (size_t idx : {0, 1}) // for both solvers + { + // intermediate stages cost transportation + for (size_t i = 0; i < N; i++) + { + Eigen::Matrix Jl, Jr; + auto e = x_nominal.rminus(x_traj[i], Jl, Jr); // compute PT matrix w.r.t. current ref traj + auto e_adj = (e.exp()).adj(); + problems[idx]->Q_[i] = e_adj.transpose() * problems[idx]->Q_[i] * e_adj; + problems[idx]->qv_[i] = e_adj.transpose() * problems[idx]->qv_[i]; + } + // terminal stage transportation + Eigen::Matrix Jl, Jr; + auto e = x_final.rminus(x_traj[N], Jl, Jr); // compute PT matrix w.r.t. current ref traj + auto e_adj = (e.exp()).adj(); + std::cout << "cost adj" << std::endl << e_adj << std::endl; + std::cout << "cost Jr" << std::endl << Jr << std::endl; + problems[idx]->Q_.back() = e_adj.transpose() * problems[idx]->Q_.back() * e_adj; + problems[idx]->qv_.back() = e_adj.transpose() * problems[idx]->qv_.back(); + + // dynamics transportation + for (size_t i = 0; i < N; i++) + { + // std::cout << "dyn transport matrices" << std::endl; + Eigen::Matrix Jl, Jr; + + auto l = x_traj[i + 1].rminus(x_traj[i], Jl, Jr); // TODO: is it the right way round? + auto l_adj = (l.exp()).adj(); + //std::cout << "Jl" << std::endl << Jl << std::endl; + //std::cout << "Jr" << std::endl << Jr << std::endl; + //std::cout << "l_adj" << std::endl << l_adj << std::endl; + //std::cout << std::endl; + problems[idx]->A_[i] = l_adj.transpose() * problems[idx]->A_[i]; // todo: are those the right + problems[idx]->B_[i] = l_adj.transpose() * problems[idx]->B_[i]; // TODO: are those the right + } + + + // set the problem pointers + lqocSolvers[idx]->setProblem(problems[idx]); + + // allocate memory (if required) + lqocSolvers[idx]->initializeAndAllocate(); + + // solve the problems... + lqocSolvers[idx]->solve(); + + // postprocess data + lqocSolvers[idx]->computeStatesAndControls(); + lqocSolvers[idx]->computeFeedbackMatrices(); + lqocSolvers[idx]->compute_lv(); + } + + // retrieve solutions from both solvers + auto xSol_riccati = lqocSolvers[0]->getSolutionState(); + auto uSol_riccati = lqocSolvers[0]->getSolutionControl(); + ct::core::FeedbackArray KSol_riccati = lqocSolvers[0]->getSolutionFeedback(); + ct::core::ControlVectorArray lv_sol_riccati = lqocSolvers[0]->get_lv(); + auto xSol_hpipm = lqocSolvers[1]->getSolutionState(); + auto uSol_hpipm = lqocSolvers[1]->getSolutionControl(); + ct::core::FeedbackArray KSol_hpipm = lqocSolvers[1]->getSolutionFeedback(); + ct::core::ControlVectorArray lv_sol_hpipm = lqocSolvers[1]->get_lv(); + + std::cout << std::endl << std::endl; + std::cout << std::setprecision(3) << "Forward integrated closed-loop solution:" << std::endl; + x_curr = x0; + std::cout << "m init: " << x_curr << std::endl; + for (size_t i = 0; i < N; i++) + { + Eigen::Matrix j1, j2; + auto x_err = x_curr.rminus(x_traj[i], j1, j2); + // std::cout << "lv_sol_riccati[i]: " << lv_sol_riccati[i].transpose() << std::endl; + // std::cout << "uSol_riccati[i]: " << uSol_riccati[i].transpose() << std::endl; + // std::cout << "KSol_riccati: " << std::endl << KSol_riccati[i] << std::endl; + // TODO: some term is missing here; + auto u = u_traj_init[i] + lv_sol_riccati[i] + KSol_riccati[i] * (x_err /*- eucl. part here*/); + exampleSystem->computeControlledDynamics(x_curr, i * dt, u, dx); + Eigen::Quaterniond old_rot(x_curr.w(), x_curr.x(), x_curr.y(), x_curr.z()); + x_curr = x_curr + dx; + Eigen::Quaterniond new_rot(x_curr.w(), x_curr.x(), x_curr.y(), x_curr.z()); + std::cout << "m: " << x_curr << "\t -- rot diff norm(): " << old_rot.angularDistance(new_rot) << std::endl; + } + std::cout << std::endl << std::endl; + + std::cout << std::setprecision(3) << "dx solution from riccati solver and directly added state_traj" << std::endl; + ct::core::DiscreteArray x_solver_direct(N + 1); + for (size_t j = 0; j < xSol_riccati.size(); j++) + { + x_solver_direct[j] = x_traj[j] + xSol_riccati[j]; + double angularDiff = 0; + if (j > 0) + { + Eigen::Quaterniond old_rot(x_solver_direct[j - 1].w(), x_solver_direct[j - 1].x(), + x_solver_direct[j - 1].y(), x_solver_direct[j - 1].z()); + Eigen::Quaterniond new_rot( + x_solver_direct[j].w(), x_solver_direct[j].x(), x_solver_direct[j].y(), x_solver_direct[j].z()); + angularDiff = old_rot.angularDistance(new_rot); + } + std::cout << "\t m:" << x_solver_direct[j] << "\t dx:" << xSol_riccati[j].transpose() + << "\t -- rot diff norm(): " << angularDiff << std::endl; + } + return 1; +} \ No newline at end of file From 55d2d87e11a2bc18348450b98348d4ccf8c6cb2b Mon Sep 17 00:00:00 2001 From: mgiftthaler Date: Sun, 26 Apr 2020 11:12:03 +0200 Subject: [PATCH 02/21] backing up working example --- .../control/StateFeedbackController-impl.h | 79 ++-- .../ct/core/control/StateFeedbackController.h | 46 +- .../optcon/costfunction/CostFunction-impl.hpp | 2 +- .../costfunction/CostFunctionAD-impl.hpp | 6 +- .../ct/optcon/costfunction/CostFunctionAD.hpp | 6 +- .../CostFunctionAnalytical-impl.hpp | 6 +- .../costfunction/CostFunctionAnalytical.hpp | 4 +- .../CostFunctionQuadratic-impl.hpp | 11 +- .../costfunction/CostFunctionQuadratic.hpp | 18 +- .../CostFunctionQuadraticSimple-impl.hpp | 103 +++-- .../CostFunctionQuadraticSimple.hpp | 37 +- .../ct/optcon/costfunction/costfun-impl.hpp | 2 +- .../ct/optcon/costfunction/costfun.hpp | 2 +- .../ct/optcon/nloc/NLOCBackendBase-impl.hpp | 405 ++++++++---------- .../ct/optcon/nloc/NLOCBackendBase.hpp | 86 ++-- ct_optcon/include/ct/optcon/optcon.h | 12 +- .../ct/optcon/problem/LQOCProblem-impl.hpp | 131 ++++-- .../include/ct/optcon/problem/LQOCProblem.hpp | 27 +- .../solver/lqp/AugGNRiccatiSolver-impl.hpp | 293 +++++++++++++ .../optcon/solver/lqp/AugGNRiccatiSolver.hpp | 111 +++++ .../solver/lqp/GNRiccatiSolver-impl.hpp | 74 ++-- .../ct/optcon/solver/lqp/GNRiccatiSolver.hpp | 13 +- .../optcon/solver/lqp/HPIPMInterface-impl.hpp | 87 ++-- .../ct/optcon/solver/lqp/HPIPMInterface.hpp | 32 +- .../ct/optcon/solver/lqp/LQOCSolver.hpp | 21 +- ct_optcon/test/CMakeLists.txt | 11 +- .../test/solver/linear/HPIPMInterfaceTest.h | 18 +- ct_optcon/test/solver/linear/LQOCSolverTest.h | 27 +- .../test/solver/linear/ManifoldLQOCTest.cpp | 293 +++++++------ ct_optcon/test/testSystems/LinkedMasses.h | 15 +- ct_optcon/test/testSystems/SpringLoadedMass.h | 36 +- 31 files changed, 1223 insertions(+), 791 deletions(-) create mode 100644 ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver-impl.hpp create mode 100644 ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver.hpp diff --git a/ct_core/include/ct/core/control/StateFeedbackController-impl.h b/ct_core/include/ct/core/control/StateFeedbackController-impl.h index 397527405..fcb6e61ee 100644 --- a/ct_core/include/ct/core/control/StateFeedbackController-impl.h +++ b/ct_core/include/ct/core/control/StateFeedbackController-impl.h @@ -6,17 +6,17 @@ Licensed under the BSD-2 license (see LICENSE file in main directory) namespace ct { namespace core { -template +template StateFeedbackController::StateFeedbackController() : Base() { } -template +template StateFeedbackController::~StateFeedbackController() { } -template +template StateFeedbackController::StateFeedbackController(const MANIFOLD& x_ref, const ct::core::ControlVector& uff, const ct::core::FeedbackMatrix& K) @@ -29,16 +29,15 @@ StateFeedbackController::StateFeedbackController( { } -template +template StateFeedbackController::StateFeedbackController( const StateFeedbackController& other) : Base(), x_ref_(other.x_ref_), uff_(other.uff_), K_(other.K_) { } -template -StateFeedbackController::StateFeedbackController( - const StateVectorArray& x_ref, +template +StateFeedbackController::StateFeedbackController(const DiscreteArray& x_ref, const ControlVectorArray& uff, const FeedbackArray& K, const SCALAR deltaT, @@ -48,8 +47,8 @@ StateFeedbackController::StateFeedbackController( { } -template -void StateFeedbackController::update(const DiscreteArray& x_ref, +template +void StateFeedbackController::update(const DiscreteArray& x_ref, const DiscreteArray& uff, const DiscreteArray>& K, const tpl::TimeArray& t) @@ -70,7 +69,7 @@ void StateFeedbackController::update(const Discre K_.setTime(tshort); } -template +template void StateFeedbackController::computeControl(const MANIFOLD& state, const Time_t& tn, control_vector_t& u) @@ -78,87 +77,83 @@ void StateFeedbackController::computeControl(cons computeControl_specialized(state, tn, u); } - -template +template StateFeedbackController* StateFeedbackController::clone() const { return new StateFeedbackController(*this); } - -template -const DiscreteArray::state_vector_t>& -StateFeedbackController::x_ref() const +template +const DiscreteArray& StateFeedbackController::x_ref() const { return x_ref_.getDataArray(); } -template +template const DiscreteArray::control_vector_t>& StateFeedbackController::uff() const { return uff_.getDataArray(); } -template -const DiscreteArray>& -StateFeedbackController::K() const +template +auto StateFeedbackController::K() const + -> const DiscreteArray>& { return K_.getDataArray(); } -template -const tpl::TimeArray& StateFeedbackController::time() const +template +auto StateFeedbackController::time() const -> const tpl::TimeArray& { return x_ref_.getTimeArray(); } -template -StateTrajectory& -StateFeedbackController::getReferenceStateTrajectory() +template +DiscreteTrajectory& StateFeedbackController::getReferenceStateTrajectory() { return x_ref_; } -template -const StateTrajectory& -StateFeedbackController::getReferenceStateTrajectory() const +template +auto StateFeedbackController::getReferenceStateTrajectory() const + -> const DiscreteTrajectory& { return x_ref_; } -template -ControlTrajectory& -StateFeedbackController::getFeedforwardTrajectory() +template +auto StateFeedbackController::getFeedforwardTrajectory() + -> ControlTrajectory& { return uff_; } -template -const ControlTrajectory& -StateFeedbackController::getFeedforwardTrajectory() const +template +auto StateFeedbackController::getFeedforwardTrajectory() const + -> const ControlTrajectory& { return uff_; } -template -FeedbackTrajectory& -StateFeedbackController::getFeedbackTrajectory() +template +auto StateFeedbackController::getFeedbackTrajectory() + -> FeedbackTrajectory& { return K_; } -template -const FeedbackTrajectory& -StateFeedbackController::getFeedbackTrajectory() const +template +auto StateFeedbackController::getFeedbackTrajectory() const + -> const FeedbackTrajectory& { return K_; } -template +template void StateFeedbackController::extractControlTrajectory( - const StateTrajectory& x_traj, + const DiscreteTrajectory& x_traj, ControlTrajectory& u_traj) { u_traj.clear(); diff --git a/ct_core/include/ct/core/control/StateFeedbackController.h b/ct_core/include/ct/core/control/StateFeedbackController.h index a93c978dc..3478e3148 100644 --- a/ct_core/include/ct/core/control/StateFeedbackController.h +++ b/ct_core/include/ct/core/control/StateFeedbackController.h @@ -6,6 +6,7 @@ Licensed under the BSD-2 license (see LICENSE file in main directory) #pragma once #include "Controller.h" +#include namespace ct { namespace core { @@ -37,8 +38,8 @@ namespace core { * @tparam CONTROL_DIM control vector size * @tparam SCALAR primitive type */ -template -class StateFeedbackController : public Controller, +template +class StateFeedbackController : public Controller { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW @@ -87,6 +88,22 @@ class StateFeedbackController : public Controller //! deep cloning StateFeedbackController* clone() const override; + template + typename std::enable_if::type computeControl_specialized(const MANIFOLD& state, + const Time_t& t, + control_vector_t& u) + { + u = uff_.eval(t) + K_.eval(t) * (state - x_ref_.eval(t)); // TODO: move to impl + } + + template + typename std::enable_if::type computeControl_specialized(const MANIFOLD& state, + const Time_t& n, + control_vector_t& u) + { + u = uff_[n] + K_[n] * (state - x_ref_[n]); // TODO: move to impl + } + //! computes the control action in the continuous case /*! * evaluates the controller using interpolation where required using interpolation @@ -96,13 +113,6 @@ class StateFeedbackController : public Controller */ virtual void computeControl(const MANIFOLD& state, const Time_t& tn, control_vector_t& controlAction) override; - typename std::enable_if::type computeControl_specialized(const MANIFOLD& state, - const Time_t& tn, - control_vector_t& controlAction); - typename std::enable_if::type computeControl_specialized(const MANIFOLD& state, - const Time_t& tn, - control_vector_t& controlAction); - //! updates the controller /*! * sets a new feedforward and feedback controller @@ -156,23 +166,5 @@ class StateFeedbackController : public Controller }; -template -typename std::enable_if::type -StateFeedbackController::computeControl_specialized(const state_vector_t& state, - const SCALAR& t, - control_vector_t& controlAction) -{ - controlAction = uff_.eval(t) + K_.eval(t) * (state - x_ref_.eval(t)); -} - -template -typename std::enable_if::type -StateFeedbackController::computeControl_specialized(const state_vector_t& state, - const int n, - control_vector_t& controlAction) -{ - controlAction = uff_[n] + K_[n] * (state - x_ref_[n]); -} - } // namespace core } // namespace ct diff --git a/ct_optcon/include/ct/optcon/costfunction/CostFunction-impl.hpp b/ct_optcon/include/ct/optcon/costfunction/CostFunction-impl.hpp index 3a3807460..28454e1d6 100644 --- a/ct_optcon/include/ct/optcon/costfunction/CostFunction-impl.hpp +++ b/ct_optcon/include/ct/optcon/costfunction/CostFunction-impl.hpp @@ -12,7 +12,7 @@ template CostFunction::CostFunction() : t_(0.0), t_shift_(0.0) { - x_.setZero(); + x_ = MANIFOLD::NeutralElement(); u_.setZero(); } diff --git a/ct_optcon/include/ct/optcon/costfunction/CostFunctionAD-impl.hpp b/ct_optcon/include/ct/optcon/costfunction/CostFunctionAD-impl.hpp index 447acd332..e9b6e21b0 100644 --- a/ct_optcon/include/ct/optcon/costfunction/CostFunctionAD-impl.hpp +++ b/ct_optcon/include/ct/optcon/costfunction/CostFunctionAD-impl.hpp @@ -219,8 +219,7 @@ auto CostFunctionAD::evaluateTerminal() -> SCALAR_EVAL } template -auto CostFunctionAD::stateDerivativeIntermediate() - -> ct::core::StateVector +auto CostFunctionAD::stateDerivativeIntermediate() -> typename EVAL_MANIFOLD::Tangent { Eigen::Matrix jacTot = intermediateCostCodegen_->jacobian(stateControlTime_); @@ -228,8 +227,7 @@ auto CostFunctionAD::stateDerivativeIntermediate() } template -auto CostFunctionAD::stateDerivativeTerminal() - -> ct::core::StateVector +auto CostFunctionAD::stateDerivativeTerminal() -> typename EVAL_MANIFOLD::Tangent { Eigen::Matrix jacTot = finalCostCodegen_->jacobian(stateControlTime_); return jacTot.template leftCols().transpose() + this->stateDerivativeTerminalBase(); diff --git a/ct_optcon/include/ct/optcon/costfunction/CostFunctionAD.hpp b/ct_optcon/include/ct/optcon/costfunction/CostFunctionAD.hpp index 5c8beccb2..c77c54240 100644 --- a/ct_optcon/include/ct/optcon/costfunction/CostFunctionAD.hpp +++ b/ct_optcon/include/ct/optcon/costfunction/CostFunctionAD.hpp @@ -89,13 +89,11 @@ class CostFunctionAD */ CostFunctionAD(const CostFunctionAD& arg); - /** * \brief Destructor */ virtual ~CostFunctionAD(); - /** * @brief Initializes the AD costfunction, generates and compiles * source code @@ -133,8 +131,8 @@ class CostFunctionAD SCALAR_EVAL evaluateIntermediate() override; SCALAR_EVAL evaluateTerminal() override; - ct::core::StateVector stateDerivativeIntermediate() override; - ct::core::StateVector stateDerivativeTerminal() override; + typename EVAL_MANIFOLD::Tangent stateDerivativeIntermediate() override; + typename EVAL_MANIFOLD::Tangent stateDerivativeTerminal() override; control_vector_t controlDerivativeIntermediate() override; control_vector_t controlDerivativeTerminal() override; diff --git a/ct_optcon/include/ct/optcon/costfunction/CostFunctionAnalytical-impl.hpp b/ct_optcon/include/ct/optcon/costfunction/CostFunctionAnalytical-impl.hpp index 1138c48ae..a92462332 100644 --- a/ct_optcon/include/ct/optcon/costfunction/CostFunctionAnalytical-impl.hpp +++ b/ct_optcon/include/ct/optcon/costfunction/CostFunctionAnalytical-impl.hpp @@ -101,15 +101,13 @@ auto CostFunctionAnalytical::evaluateTerminal() -> SCALAR } template -auto CostFunctionAnalytical::stateDerivativeIntermediate() - -> ct::core::StateVector +auto CostFunctionAnalytical::stateDerivativeIntermediate() -> typename MANIFOLD::Tangent { return this->stateDerivativeIntermediateBase(); } template -auto CostFunctionAnalytical::stateDerivativeTerminal() - -> ct::core::StateVector +auto CostFunctionAnalytical::stateDerivativeTerminal() -> typename MANIFOLD::Tangent { return this->stateDerivativeTerminalBase(); } diff --git a/ct_optcon/include/ct/optcon/costfunction/CostFunctionAnalytical.hpp b/ct_optcon/include/ct/optcon/costfunction/CostFunctionAnalytical.hpp index e90622153..901441d5a 100644 --- a/ct_optcon/include/ct/optcon/costfunction/CostFunctionAnalytical.hpp +++ b/ct_optcon/include/ct/optcon/costfunction/CostFunctionAnalytical.hpp @@ -75,8 +75,8 @@ class CostFunctionAnalytical : public CostFunctionQuadratic stateDerivativeIntermediate() override; - ct::core::StateVector stateDerivativeTerminal() override; + typename MANIFOLD::Tangent stateDerivativeIntermediate() override; + typename MANIFOLD::Tangent stateDerivativeTerminal() override; state_matrix_t stateSecondDerivativeIntermediate() override; state_matrix_t stateSecondDerivativeTerminal() override; diff --git a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadratic-impl.hpp b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadratic-impl.hpp index ad35a9505..13f452307 100644 --- a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadratic-impl.hpp +++ b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadratic-impl.hpp @@ -207,7 +207,6 @@ auto CostFunctionQuadratic::evaluateIntermediateBase() -> return y; } - template auto CostFunctionQuadratic::evaluateTerminalBase() -> SCALAR_EVAL { @@ -219,11 +218,10 @@ auto CostFunctionQuadratic::evaluateTerminalBase() -> SCA return y; } - template -auto CostFunctionQuadratic::stateDerivativeIntermediateBase() -> Tangent_t +auto CostFunctionQuadratic::stateDerivativeIntermediateBase() -> typename EVAL_MANIFOLD::Tangent { - Tangent_t derivative; + typename EVAL_MANIFOLD::Tangent derivative; derivative.setZero(); for (auto it : this->intermediateCostAnalytical_) @@ -238,11 +236,10 @@ auto CostFunctionQuadratic::stateDerivativeIntermediateBa return derivative; } - template -auto CostFunctionQuadratic::stateDerivativeTerminalBase() -> Tangent_t +auto CostFunctionQuadratic::stateDerivativeTerminalBase() -> typename EVAL_MANIFOLD::Tangent { - Tangent_t derivative; + typename EVAL_MANIFOLD::Tangent derivative; derivative.setZero(); for (auto it : this->finalCostAnalytical_) diff --git a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadratic.hpp b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadratic.hpp index c41bd9fbb..326af56d9 100644 --- a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadratic.hpp +++ b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadratic.hpp @@ -33,7 +33,7 @@ class CostFunctionQuadratic : public CostFunction static constexpr size_t STATE_DIM = MANIFOLD::TangentDim; using SCALAR_EVAL = typename MANIFOLD::Scalar; // TODO: is there a better name than scalar_eval? - using Tangent_t = typename MANIFOLD::Tangent; + using EVAL_MANIFOLD = typename MANIFOLD::template RedefineScalar::type>; using Base = CostFunction; using control_vector_t = typename Base::control_vector_t; @@ -91,13 +91,13 @@ class CostFunctionQuadratic : public CostFunction * \brief Computes intermediate-cost first-order derivative with respect to state * @return derivative vector (Jacobian) */ - virtual ct::core::StateVector stateDerivativeIntermediate() = 0; + virtual typename MANIFOLD::Tangent stateDerivativeIntermediate() = 0; /** * Computes terminal-cost first-order derivative with respect to state * @return derivative vector (Jacobian) */ - virtual ct::core::StateVector stateDerivativeTerminal() = 0; + virtual typename MANIFOLD::Tangent stateDerivativeTerminal() = 0; /** * \brief Computes intermediate-cost second-order derivative with respect to state @@ -184,10 +184,10 @@ class CostFunctionQuadratic : public CostFunction SCALAR_EVAL evaluateTerminalBase(); //! evaluate intermediate analytical state derivatives - Tangent_t stateDerivativeIntermediateBase(); + typename EVAL_MANIFOLD::Tangent stateDerivativeIntermediateBase(); //! evaluate terminal analytical state derivatives - Tangent_t stateDerivativeTerminalBase(); + typename EVAL_MANIFOLD::Tangent stateDerivativeTerminalBase(); //! evaluate intermediate analytical state second derivatives state_matrix_t stateSecondDerivativeIntermediateBase(); @@ -217,10 +217,10 @@ class CostFunctionQuadratic : public CostFunction //! compute the state derivative by numerical differentiation (can be used for testing) // euclidean case template ::value, bool>::type> - Tangent_t stateDerivativeIntermediateNumDiff(); + typename EVAL_MANIFOLD::Tangent stateDerivativeIntermediateNumDiff(); // non-euclidean case template ::value), bool>::type> - Tangent_t stateDerivativeIntermediateNumDiff(); + typename EVAL_MANIFOLD::Tangent stateDerivativeIntermediateNumDiff(); //! compute the control derivative by numerical differentiation (can be used for testing) // euclidean case @@ -247,7 +247,7 @@ class CostFunctionQuadratic : public CostFunction // impl for euclidean case (do not move to *-impl.h file) template template ::value, bool>::type> -auto CostFunctionQuadratic::stateDerivativeIntermediateNumDiff() -> Tangent_t +auto CostFunctionQuadratic::stateDerivativeIntermediateNumDiff() -> typename EVAL_MANIFOLD::Tangent { using state_vector_t = ct::core::StateVector; @@ -297,7 +297,7 @@ auto CostFunctionQuadratic::stateDerivativeIntermediateNu // impl for manifold case (do not move to *-impl.h file) template template ::value), bool>::type> -auto CostFunctionQuadratic::stateDerivativeIntermediateNumDiff() -> Tangent_t +auto CostFunctionQuadratic::stateDerivativeIntermediateNumDiff() -> typename EVAL_MANIFOLD::Tangent { throw std::runtime_error("stateDerivativeIntermediateNumDiff not implemented for manifold case."); } diff --git a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp index d85f5d016..133868cab 100644 --- a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp +++ b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp @@ -8,8 +8,8 @@ Licensed under the BSD-2 license (see LICENSE file in main directory) namespace ct { namespace optcon { -template -CostFunctionQuadraticSimple::CostFunctionQuadraticSimple() +template +CostFunctionQuadraticSimple::CostFunctionQuadraticSimple() { x_nominal_.setZero(); Q_.setZero(); @@ -21,12 +21,12 @@ CostFunctionQuadraticSimple::CostFunctionQuadrat u_deviation_.setZero(); } -template -CostFunctionQuadraticSimple::CostFunctionQuadraticSimple(const state_matrix_t& Q, +template +CostFunctionQuadraticSimple::CostFunctionQuadraticSimple(const state_matrix_t& Q, const control_matrix_t& R, - const state_vector_t& x_nominal, + const MANIFOLD& x_nominal, const control_vector_t& u_nominal, - const state_vector_t& x_final, + const MANIFOLD& x_final, const state_matrix_t& Q_final) : x_nominal_(x_nominal), Q_(Q), u_nominal_(u_nominal), R_(R), x_final_(x_final), Q_final_(Q_final) { @@ -34,14 +34,13 @@ CostFunctionQuadraticSimple::CostFunctionQuadrat u_deviation_.setZero(); } -template -CostFunctionQuadraticSimple::~CostFunctionQuadraticSimple() +template +CostFunctionQuadraticSimple::~CostFunctionQuadraticSimple() { } -template -CostFunctionQuadraticSimple::CostFunctionQuadraticSimple( - const CostFunctionQuadraticSimple& arg) +template +CostFunctionQuadraticSimple::CostFunctionQuadraticSimple(const CostFunctionQuadraticSimple& arg) : x_deviation_(arg.x_deviation_), x_nominal_(arg.x_nominal_), Q_(arg.Q_), @@ -53,16 +52,14 @@ CostFunctionQuadraticSimple::CostFunctionQuadrat { } - -template -CostFunctionQuadraticSimple* -CostFunctionQuadraticSimple::clone() const +template +CostFunctionQuadraticSimple* CostFunctionQuadraticSimple::clone() const { - return new CostFunctionQuadraticSimple(*this); + return new CostFunctionQuadraticSimple(*this); } -template -void CostFunctionQuadraticSimple::setCurrentStateAndControl(const state_vector_t& x, +template +void CostFunctionQuadraticSimple::setCurrentStateAndControl(const MANIFOLD& x, const control_vector_t& u, const SCALAR& t) { @@ -70,87 +67,85 @@ void CostFunctionQuadraticSimple::setCurrentStat this->u_ = u; this->t_ = t; - this->x_deviation_ = x - x_nominal_; + this->x_deviation_ = x - x_nominal_; // TODO: switch order and evluate jacs as well this->u_deviation_ = u - u_nominal_; } -template -SCALAR CostFunctionQuadraticSimple::evaluateIntermediate() +template +auto CostFunctionQuadraticSimple::evaluateIntermediate() -> SCALAR { SCALAR costQ = SCALAR(0.5) * (x_deviation_.transpose() * Q_ * x_deviation_)(0); SCALAR costR = SCALAR(0.5) * (u_deviation_.transpose() * R_ * u_deviation_)(0); return costQ + costR; } -template -typename CostFunctionQuadraticSimple::state_vector_t -CostFunctionQuadraticSimple::stateDerivativeIntermediate() +template +typename MANIFOLD::Tangent CostFunctionQuadraticSimple::stateDerivativeIntermediate() { return Q_ * x_deviation_; } -template -typename CostFunctionQuadraticSimple::state_matrix_t -CostFunctionQuadraticSimple::stateSecondDerivativeIntermediate() +template +auto CostFunctionQuadraticSimple::stateSecondDerivativeIntermediate() -> state_matrix_t { return Q_; } -template -typename CostFunctionQuadraticSimple::control_vector_t -CostFunctionQuadraticSimple::controlDerivativeIntermediate() +template +auto CostFunctionQuadraticSimple::controlDerivativeIntermediate() -> control_vector_t { return R_ * u_deviation_; } -template -typename CostFunctionQuadraticSimple::control_matrix_t -CostFunctionQuadraticSimple::controlSecondDerivativeIntermediate() +template +auto CostFunctionQuadraticSimple::controlSecondDerivativeIntermediate() -> control_matrix_t { return R_; } -template -typename CostFunctionQuadraticSimple::control_state_matrix_t -CostFunctionQuadraticSimple::stateControlDerivativeIntermediate() +template +auto CostFunctionQuadraticSimple::stateControlDerivativeIntermediate() -> control_state_matrix_t { return control_state_matrix_t::Zero(); } -template -SCALAR CostFunctionQuadraticSimple::evaluateTerminal() +template +auto CostFunctionQuadraticSimple::evaluateTerminal() -> SCALAR { - state_vector_t x_deviation_final = this->x_ - x_final_; - return SCALAR(0.5) * x_deviation_final.transpose() * Q_final_ * x_deviation_final; + typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_); + return SCALAR(0.5) * (x_deviation_final.transpose() * Q_final_ * x_deviation_final)(0); } -template -typename CostFunctionQuadraticSimple::state_vector_t -CostFunctionQuadraticSimple::stateDerivativeTerminal() +template +typename MANIFOLD::Tangent CostFunctionQuadraticSimple::stateDerivativeTerminal() { - state_vector_t x_deviation_final = this->x_ - x_final_; - return Q_final_ * x_deviation_final; + Eigen::Matrix J_curr, J_ref; + typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_, J_ref, J_curr); + return Q_final_ * J_curr.transpose() * x_deviation_final; + //typename MANIFOLD::Tangent x_deviation_final = this->x_.rminus(x_final_, J_ref, J_curr); } -template -typename CostFunctionQuadraticSimple::state_matrix_t -CostFunctionQuadraticSimple::stateSecondDerivativeTerminal() +template +auto CostFunctionQuadraticSimple::stateSecondDerivativeTerminal() -> state_matrix_t { - return Q_final_; + Eigen::Matrix J_curr, J_ref; + typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_, J_ref, J_curr); + return J_curr * Q_final_ * J_curr.transpose(); + //typename MANIFOLD::Tangent x_deviation_final = this->x_.rminus(x_final_, J_ref, J_curr); + //return Q_final_; } -template -void CostFunctionQuadraticSimple::updateReferenceState(const state_vector_t& x_ref) +template +void CostFunctionQuadraticSimple::updateReferenceState(const MANIFOLD& x_ref) { x_nominal_ = x_ref; } -template -void CostFunctionQuadraticSimple::updateFinalState(const state_vector_t& x_final) +template +void CostFunctionQuadraticSimple::updateFinalState(const MANIFOLD& x_final) { x_final_ = x_final; } - } // namespace optcon } // namespace ct diff --git a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple.hpp b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple.hpp index 2b472d25b..d638cafb8 100644 --- a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple.hpp +++ b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple.hpp @@ -19,17 +19,18 @@ namespace optcon { * \f$ J(x,u,t) = \bar{x}^T Q \bar{x} + \bar{u}^T R \bar{u} + \bar{x}^T_f Q_f \bar{x}^T_f \f$ * where \f$ \bar{x}, \bar{u} \f$ indicate deviations from a nominal (desired) state and control */ -template -class CostFunctionQuadraticSimple : public CostFunctionQuadratic +template +class CostFunctionQuadraticSimple : public CostFunctionQuadratic { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW - typedef Eigen::Matrix state_matrix_t; - typedef Eigen::Matrix control_matrix_t; - typedef Eigen::Matrix control_state_matrix_t; + using SCALAR = typename MANIFOLD::Scalar; + static constexpr size_t STATE_DIM = MANIFOLD::TangentDim; - typedef core::StateVector state_vector_t; + typedef core::StateMatrix state_matrix_t; + typedef core::ControlMatrix control_matrix_t; + typedef core::ControlStateMatrix control_state_matrix_t; typedef core::ControlVector control_vector_t; /** @@ -47,9 +48,9 @@ class CostFunctionQuadraticSimple : public CostFunctionQuadratic* clone() const override; + CostFunctionQuadraticSimple* clone() const override; - virtual void setCurrentStateAndControl(const state_vector_t& x, - const control_vector_t& u, - const SCALAR& t) override; + virtual void setCurrentStateAndControl(const MANIFOLD& x, const control_vector_t& u, const SCALAR& t) override; virtual SCALAR evaluateIntermediate() override; - virtual state_vector_t stateDerivativeIntermediate() override; + virtual typename MANIFOLD::Tangent stateDerivativeIntermediate() override; virtual state_matrix_t stateSecondDerivativeIntermediate() override; @@ -80,24 +79,24 @@ class CostFunctionQuadraticSimple : public CostFunctionQuadratic -NLOCBackendBase::NLOCBackendBase( - const OptConProblem_t& optConProblem, +template +NLOCBackendBase::NLOCBackendBase(const OptConProblem_t& optConProblem, const Settings_t& settings) : NLOCBackendBase(createSystemInterface(optConProblem, settings), settings) { } -template -NLOCBackendBase::NLOCBackendBase( - const OptConProblem_t& optConProblem, +template +NLOCBackendBase::NLOCBackendBase(const OptConProblem_t& optConProblem, const std::string& settingsFile, bool verbose, const std::string& ns) @@ -27,9 +25,8 @@ NLOCBackendBase::NLOCB { } -template -NLOCBackendBase::NLOCBackendBase( - const systemInterfacePtr_t& systemInterface, +template +NLOCBackendBase::NLOCBackendBase(const systemInterfacePtr_t& systemInterface, const Settings_t& settings) : initialized_(false), configured_(false), @@ -83,9 +80,8 @@ NLOCBackendBase::NLOCB } } -template -NLOCBackendBase::NLOCBackendBase( - const systemInterfacePtr_t& systemInterface, +template +NLOCBackendBase::NLOCBackendBase(const systemInterfacePtr_t& systemInterface, const std::string& settingsFile, bool verbose, const std::string& ns) @@ -93,38 +89,31 @@ NLOCBackendBase::NLOCB { } -template -NLOCBackendBase::~NLOCBackendBase() +template +NLOCBackendBase::~NLOCBackendBase() { } -template -template -typename std::enable_if>::value, - typename NLOCBackendBase::systemInterfacePtr_t>::type -NLOCBackendBase::createSystemInterface( - const OptConProblem_t& optConProblem, +template +typename std::enable_if::systemInterfacePtr_t>::type +NLOCBackendBase::createSystemInterface(const OptConProblem_t& optConProblem, const Settings_t& settings) { - return systemInterfacePtr_t( - new OptconContinuousSystemInterface(optConProblem, settings)); + return systemInterfacePtr_t(new OptconContinuousSystemInterface(optConProblem, settings)); } -template -template -typename std::enable_if>::value, - typename NLOCBackendBase::systemInterfacePtr_t>::type -NLOCBackendBase::createSystemInterface( - const OptConProblem_t& optConProblem, +template +typename std::enable_if::systemInterfacePtr_t>::type +NLOCBackendBase::createSystemInterface(const OptConProblem_t& optConProblem, const Settings_t& settings) { - return systemInterfacePtr_t( - new OptconDiscreteSystemInterface(optConProblem, settings)); + return systemInterfacePtr_t(new OptconDiscreteSystemInterface(optConProblem, settings)); } -template -void NLOCBackendBase::setInitialGuess( - const Policy_t& initialGuess) +template +void NLOCBackendBase::setInitialGuess(const Policy_t& initialGuess) { if (initialGuess.x_ref().size() < (size_t)K_ + 1) { @@ -186,8 +175,8 @@ void NLOCBackendBase:: finalCostPrevious_ = finalCostBest_; } -template -void NLOCBackendBase::changeTimeHorizon(int numStages) +template +void NLOCBackendBase::changeTimeHorizon(int numStages) { if (numStages == K_) return; @@ -225,8 +214,8 @@ void NLOCBackendBase:: lqocSolver_->setProblem(lqocProblem_); } -template -void NLOCBackendBase::changeTimeHorizon(const SCALAR& tf) +template +void NLOCBackendBase::changeTimeHorizon(const SCALAR& tf) { if (tf < 0) throw std::runtime_error("negative time horizon specified"); @@ -234,9 +223,8 @@ void NLOCBackendBase:: changeTimeHorizon(settings_.computeK(tf)); } -template -void NLOCBackendBase::changeInitialState( - const core::StateVector& x0) +template +void NLOCBackendBase::changeInitialState(const MANIFOLD& x0) { if (x_.size() == 0) x_.resize(1); @@ -245,8 +233,8 @@ void NLOCBackendBase:: reset(); // since initial state changed, we have to start fresh, i.e. with a rollout } -template -void NLOCBackendBase::changeCostFunction( +template +void NLOCBackendBase::changeCostFunction( const typename OptConProblem_t::CostFunctionPtr_t& cf) { if (cf == nullptr) @@ -266,15 +254,15 @@ void NLOCBackendBase:: computeCostsOfTrajectory(settings_.nThreads, x_, u_ff_, intermediateCostBest_, finalCostBest_); } -template -void NLOCBackendBase::changeNonlinearSystem( +template +void NLOCBackendBase::changeNonlinearSystem( const typename OptConProblem_t::DynamicsPtr_t& dyn) { systemInterface_->changeNonlinearSystem(dyn); } -template -void NLOCBackendBase::changeInputBoxConstraints( +template +void NLOCBackendBase::changeInputBoxConstraints( const typename OptConProblem_t::ConstraintPtr_t& con) { if (con == nullptr) @@ -295,8 +283,8 @@ void NLOCBackendBase:: setInputBoxConstraintsForLQOCProblem(); } -template -void NLOCBackendBase::changeStateBoxConstraints( +template +void NLOCBackendBase::changeStateBoxConstraints( const typename OptConProblem_t::ConstraintPtr_t& con) { if (con == nullptr) @@ -318,8 +306,8 @@ void NLOCBackendBase:: } -template -void NLOCBackendBase::changeGeneralConstraints( +template +void NLOCBackendBase::changeGeneralConstraints( const typename OptConProblem_t::ConstraintPtr_t& con) { if (con == nullptr) @@ -360,15 +348,15 @@ void NLOCBackendBase:: computeGeneralConstraintErrorOfTrajectory(settings_.nThreads, x_, u_ff_, e_gen_norm_); } -template -void NLOCBackendBase::changeLinearSystem( +template +void NLOCBackendBase::changeLinearSystem( const typename OptConProblem_t::LinearPtr_t& lin) { systemInterface_->changeLinearSystem(lin); } -template -void NLOCBackendBase::checkProblem() +template +void NLOCBackendBase::checkProblem() { if (K_ == 0) throw std::runtime_error("Time horizon too small resulting in 0 steps"); @@ -381,8 +369,8 @@ void NLOCBackendBase:: } } -template -void NLOCBackendBase::configure(const Settings_t& settings) +template +void NLOCBackendBase::configure(const Settings_t& settings) { if (!settings.parametersOk()) { @@ -432,8 +420,8 @@ void NLOCBackendBase:: configured_ = true; } -template -bool NLOCBackendBase::rolloutSingleShot(const size_t threadId, +template +bool NLOCBackendBase::rolloutSingleShot(const size_t threadId, const size_t k, //! the starting index of the shot ControlVectorArray& u_local, StateVectorArray& x_local, @@ -520,9 +508,8 @@ bool NLOCBackendBase:: return true; } -template -bool NLOCBackendBase::rolloutShotsSingleThreaded( - size_t threadId, +template +bool NLOCBackendBase::rolloutShotsSingleThreaded(size_t threadId, size_t firstIndex, size_t lastIndex, ControlVectorArray& u_ff_local, @@ -552,8 +539,8 @@ bool NLOCBackendBase:: return true; } -template -void NLOCBackendBase::computeSingleDefect(size_t k, +template +void NLOCBackendBase::computeSingleDefect(size_t k, const StateVectorArray& x_local, const StateVectorArray& xShot, StateVectorArray& d) const @@ -568,10 +555,9 @@ void NLOCBackendBase:: //! else ... all other entries of d remain zero. } -template -void NLOCBackendBase::computeCostsOfTrajectory( - size_t threadId, - const ct::core::StateVectorArray& x_local, +template +void NLOCBackendBase::computeCostsOfTrajectory(size_t threadId, + const ct::core::DiscreteArray& x_local, const ct::core::ControlVectorArray& u_local, scalar_t& intermediateCost, scalar_t& finalCost) const @@ -592,10 +578,9 @@ void NLOCBackendBase:: finalCost = costFunctions_[threadId]->evaluateTerminal(); } -template -void NLOCBackendBase::computeBoxConstraintErrorOfTrajectory( - size_t threadId, - const ct::core::StateVectorArray& x_local, +template +void NLOCBackendBase::computeBoxConstraintErrorOfTrajectory(size_t threadId, + const ct::core::DiscreteArray& x_local, const ct::core::ControlVectorArray& u_local, scalar_t& e_tot) const { @@ -642,12 +627,11 @@ void NLOCBackendBase:: } } -template -void NLOCBackendBase:: - computeGeneralConstraintErrorOfTrajectory(size_t threadId, - const ct::core::StateVectorArray& x_local, - const ct::core::ControlVectorArray& u_local, - scalar_t& e_tot) const +template +void NLOCBackendBase::computeGeneralConstraintErrorOfTrajectory(size_t threadId, + const ct::core::DiscreteArray& x_local, + const ct::core::ControlVectorArray& u_local, + scalar_t& e_tot) const { e_tot = 0; @@ -681,9 +665,8 @@ void NLOCBackendBase:: } } -template -void NLOCBackendBase::executeLQApproximation(size_t threadId, - size_t k) +template +void NLOCBackendBase::executeLQApproximation(size_t threadId, size_t k) { LQOCProblem_t& p = *lqocProblem_; const scalar_t& dt = settings_.dt; @@ -723,8 +706,8 @@ void NLOCBackendBase:: } -template -void NLOCBackendBase::setInputBoxConstraintsForLQOCProblem() +template +void NLOCBackendBase::setInputBoxConstraintsForLQOCProblem() { // set box constraints if there are any if (inputBoxConstraints_[settings_.nThreads] != nullptr) @@ -748,8 +731,8 @@ void NLOCBackendBase:: } } -template -void NLOCBackendBase::setStateBoxConstraintsForLQOCProblem() +template +void NLOCBackendBase::setStateBoxConstraintsForLQOCProblem() { if (stateBoxConstraints_[settings_.nThreads] != nullptr) { @@ -786,10 +769,8 @@ void NLOCBackendBase:: } } -template -void NLOCBackendBase::computeLinearizedConstraints( - size_t threadId, - size_t k) +template +void NLOCBackendBase::computeLinearizedConstraints(size_t threadId, size_t k) { // set general if there are any if (generalConstraints_[threadId] != nullptr) @@ -815,8 +796,8 @@ void NLOCBackendBase:: } } -template -void NLOCBackendBase::initializeCostToGo() +template +void NLOCBackendBase::initializeCostToGo() { LQOCProblem_t& p = *lqocProblem_; @@ -846,8 +827,8 @@ void NLOCBackendBase:: } } -template -void NLOCBackendBase::printSummary() +template +void NLOCBackendBase::printSummary() { SCALAR d_norm_l1 = computeDefectsNorm<1>(d_); SCALAR d_norm_l2 = computeDefectsNorm<2>(d_); @@ -891,8 +872,8 @@ void NLOCBackendBase:: } } -template -void NLOCBackendBase::logToMatlab(const size_t& iteration) +template +void NLOCBackendBase::logToMatlab(const size_t& iteration) { #ifdef MATLAB_FULL_LOG @@ -942,8 +923,8 @@ void NLOCBackendBase:: } -template -void NLOCBackendBase::logInitToMatlab() +template +void NLOCBackendBase::logInitToMatlab() { #ifdef MATLAB if (settings_.logToMatlab) @@ -975,9 +956,9 @@ void NLOCBackendBase:: } -template +template const core::ControlTrajectory -NLOCBackendBase::getControlTrajectory() const +NLOCBackendBase::getControlTrajectory() const { //! \todo this method currently copies the time array (suboptimal) @@ -988,26 +969,25 @@ NLOCBackendBase::getCo } -template -const core::StateTrajectory -NLOCBackendBase::getStateTrajectory() const +template +const core::DiscreteTrajectory NLOCBackendBase::getStateTrajectory() const { //! \todo this method currently copies the time array (suboptimal) core::tpl::TimeArray t_control = t_; - return core::StateTrajectory(t_control, x_); + return core::DiscreteTrajectory(t_control, x_); } -template -SCALAR NLOCBackendBase::getCost() const +template +SCALAR NLOCBackendBase::getCost() const { return intermediateCostBest_ + finalCostBest_; } -template -bool NLOCBackendBase::lineSearch() +template +bool NLOCBackendBase::lineSearch() { // lowest cost scalar_t lowestCostPrevious; @@ -1103,12 +1083,12 @@ bool NLOCBackendBase:: } -template -void NLOCBackendBase::executeLineSearch(const size_t threadId, +template +void NLOCBackendBase::executeLineSearch(const size_t threadId, const scalar_t alpha, - ct::core::StateVectorArray& x_alpha, - ct::core::StateVectorArray& x_shot_alpha, - ct::core::StateVectorArray& defects_recorded, + ct::core::DiscreteArray& x_alpha, + ct::core::DiscreteArray& x_shot_alpha, + ct::core::DiscreteArray& defects_recorded, ct::core::ControlVectorArray& u_alpha, scalar_t& intermediateCost, scalar_t& finalCost, @@ -1135,7 +1115,7 @@ void NLOCBackendBase:: x_alpha = delta_x_ * alpha + x_prev_; // update x_lqr reference - ct::core::StateVectorArray x_ref_lqr = delta_x_ref_lqr_ * alpha + x_prev_; + ct::core::DiscreteArray x_ref_lqr = delta_x_ref_lqr_ * alpha + x_prev_; if (terminationFlag && *terminationFlag) return; @@ -1183,8 +1163,8 @@ void NLOCBackendBase:: } -template -bool NLOCBackendBase::acceptStep(const SCALAR alpha, +template +bool NLOCBackendBase::acceptStep(const SCALAR alpha, const SCALAR intermediateCost, const SCALAR finalCost, const SCALAR defectNorm, @@ -1276,8 +1256,8 @@ bool NLOCBackendBase:: } -template -void NLOCBackendBase::prepareSolveLQProblem(size_t startIndex) +template +void NLOCBackendBase::prepareSolveLQProblem(size_t startIndex) { lqpCounter_++; @@ -1300,8 +1280,8 @@ void NLOCBackendBase:: } -template -void NLOCBackendBase::finishSolveLQProblem(size_t endIndex) +template +void NLOCBackendBase::finishSolveLQProblem(size_t endIndex) { lqpCounter_++; @@ -1324,8 +1304,8 @@ void NLOCBackendBase:: } -template -void NLOCBackendBase::solveFullLQProblem() +template +void NLOCBackendBase::solveFullLQProblem() { lqpCounter_++; @@ -1334,17 +1314,16 @@ void NLOCBackendBase:: lqocSolver_->solve(); } -template -void NLOCBackendBase::updateCosts() +template +void NLOCBackendBase::updateCosts() { intermediateCostPrevious_ = intermediateCostBest_; finalCostPrevious_ = finalCostBest_; computeCostsOfTrajectory(settings_.nThreads, x_, u_ff_, intermediateCostBest_, finalCostBest_); } -template -void NLOCBackendBase::retrieveLastAffineModel( - StateMatrixArray& A, +template +void NLOCBackendBase::retrieveLastAffineModel(StateMatrixArray& A, StateControlMatrixArray& B, StateVectorArray& b) { @@ -1353,16 +1332,16 @@ void NLOCBackendBase:: b = lqocProblem_->b_; } -template -const typename NLOCBackendBase::Policy_t& -NLOCBackendBase::getSolution() +template +const typename NLOCBackendBase::Policy_t& +NLOCBackendBase::getSolution() { policy_.update(x_, u_ff_, L_, t_); return policy_; } -template -void NLOCBackendBase::reset() +template +void NLOCBackendBase::reset() { firstRollout_ = true; iteration_ = 0; @@ -1377,8 +1356,8 @@ void NLOCBackendBase:: } -template -void NLOCBackendBase::extractSolution() +template +void NLOCBackendBase::extractSolution() { L_.setConstant(core::FeedbackMatrix::Zero()); // TODO should eventually go away x_ref_lqr_ = x_; @@ -1402,7 +1381,7 @@ void NLOCBackendBase:: lqocSolver_->computeStatesAndControls(); delta_u_ff_ = lqocSolver_->getSolutionControl(); delta_x_ = lqocSolver_->getSolutionState(); - delta_x_ref_lqr_.setConstant(ct::core::StateVector::Zero()); + delta_x_ref_lqr_.setConstant(MANIFOLD::NeutralElement()); break; } case NLOptConSettings::NLOCP_ALGORITHM::ILQR: @@ -1411,15 +1390,15 @@ void NLOCBackendBase:: delta_u_ff_ = lqocSolver_->get_lv(); lqocSolver_->computeFeedbackMatrices(); L_ = lqocSolver_->getSolutionFeedback(); - delta_x_.setConstant(ct::core::StateVector::Zero()); - delta_x_ref_lqr_.setConstant(ct::core::StateVector::Zero()); + delta_x_.setConstant(MANIFOLD::NeutralElement()); + delta_x_ref_lqr_.setConstant(MANIFOLD::NeutralElement()); break; } case NLOptConSettings::NLOCP_ALGORITHM::MS_ILQR: { lqocSolver_->computeStatesAndControls(); delta_x_ = lqocSolver_->getSolutionState(); - delta_x_ref_lqr_.setConstant(ct::core::StateVector::Zero()); + delta_x_ref_lqr_.setConstant(MANIFOLD::NeutralElement()); lqocSolver_->compute_lv(); delta_u_ff_ = lqocSolver_->get_lv(); lqocSolver_->computeFeedbackMatrices(); @@ -1443,8 +1422,8 @@ void NLOCBackendBase:: } -template -void NLOCBackendBase::doFullStepUpdate() +template +void NLOCBackendBase::doFullStepUpdate() { alphaBest_ = 1.0; @@ -1453,44 +1432,42 @@ void NLOCBackendBase:: x_ref_lqr_ += delta_x_ref_lqr_; } -template -void NLOCBackendBase::logSummaryToMatlab( - const std::string& fileName) +template +void NLOCBackendBase::logSummaryToMatlab(const std::string& fileName) { summaryAllIterations_.logToMatlab(fileName); } -template -const SummaryAllIterations& -NLOCBackendBase::getSummary() const +template +const SummaryAllIterations& NLOCBackendBase::getSummary() const { return summaryAllIterations_; } -template -void NLOCBackendBase::resetDefects() +template +void NLOCBackendBase::resetDefects() { d_.setConstant(state_vector_t::Zero()); } -template -void NLOCBackendBase::computeDefectsNorm() +template +void NLOCBackendBase::computeDefectsNorm() { d_norm_ = computeDefectsNorm<1>(d_); } -template -size_t& NLOCBackendBase::iteration() +template +size_t& NLOCBackendBase::iteration() { return iteration_; } -template -bool NLOCBackendBase::nominalRollout() +template +bool NLOCBackendBase::nominalRollout() { bool success = rolloutSingleShot(settings_.nThreads, 0, u_ff_, x_, x_prev_, xShot_, *this->substepsX_, *this->substepsU_); @@ -1500,163 +1477,151 @@ bool NLOCBackendBase:: return success; } -template -const typename NLOCBackendBase::TimeArray& -NLOCBackendBase::getTimeArray() +template +const typename NLOCBackendBase::TimeArray& +NLOCBackendBase::getTimeArray() { return t_; } -template -bool NLOCBackendBase::isConfigured() +template +bool NLOCBackendBase::isConfigured() { return configured_; } -template -bool NLOCBackendBase::isInitialized() +template +bool NLOCBackendBase::isInitialized() { return initialized_; } -template -SCALAR NLOCBackendBase::getTotalDefect() const +template +SCALAR NLOCBackendBase::getTotalDefect() const { return d_norm_; } -template -bool NLOCBackendBase::testConsistency() +template +bool NLOCBackendBase::testConsistency() { return true; // TODO this is currently meaningless } -template -std::vector< - typename NLOCBackendBase::OptConProblem_t::DynamicsPtr_t>& -NLOCBackendBase::getNonlinearSystemsInstances() +template +std::vector::OptConProblem_t::DynamicsPtr_t>& +NLOCBackendBase::getNonlinearSystemsInstances() { return systemInterface_->getNonlinearSystemsInstances(); } -template -const std::vector< - typename NLOCBackendBase::OptConProblem_t::DynamicsPtr_t>& -NLOCBackendBase::getNonlinearSystemsInstances() const +template +const std::vector::OptConProblem_t::DynamicsPtr_t>& +NLOCBackendBase::getNonlinearSystemsInstances() const { return systemInterface_->getNonlinearSystemsInstances(); } -template -std::vector< - typename NLOCBackendBase::OptConProblem_t::LinearPtr_t>& -NLOCBackendBase::getLinearSystemsInstances() +template +std::vector::OptConProblem_t::LinearPtr_t>& +NLOCBackendBase::getLinearSystemsInstances() { return systemInterface_->getLinearSystemsInstances(); } -template -const std::vector< - typename NLOCBackendBase::OptConProblem_t::LinearPtr_t>& -NLOCBackendBase::getLinearSystemsInstances() const +template +const std::vector::OptConProblem_t::LinearPtr_t>& +NLOCBackendBase::getLinearSystemsInstances() const { return systemInterface_->getLinearSystemsInstances(); } -template -std::vector::OptConProblem_t:: - CostFunctionPtr_t>& -NLOCBackendBase::getCostFunctionInstances() +template +std::vector::OptConProblem_t::CostFunctionPtr_t>& +NLOCBackendBase::getCostFunctionInstances() { return costFunctions_; } -template -const std::vector::OptConProblem_t:: - CostFunctionPtr_t>& -NLOCBackendBase::getCostFunctionInstances() const +template +const std::vector::OptConProblem_t::CostFunctionPtr_t>& +NLOCBackendBase::getCostFunctionInstances() const { return costFunctions_; } -template -std::vector::OptConProblem_t:: - ConstraintPtr_t>& -NLOCBackendBase::getInputBoxConstraintsInstances() +template +std::vector::OptConProblem_t::ConstraintPtr_t>& +NLOCBackendBase::getInputBoxConstraintsInstances() { return inputBoxConstraints_; } -template -const std::vector::OptConProblem_t:: - ConstraintPtr_t>& -NLOCBackendBase::getInputBoxConstraintsInstances() const +template +const std::vector::OptConProblem_t::ConstraintPtr_t>& +NLOCBackendBase::getInputBoxConstraintsInstances() const { return inputBoxConstraints_; } -template -std::vector::OptConProblem_t:: - ConstraintPtr_t>& -NLOCBackendBase::getStateBoxConstraintsInstances() +template +std::vector::OptConProblem_t::ConstraintPtr_t>& +NLOCBackendBase::getStateBoxConstraintsInstances() { return stateBoxConstraints_; } -template -const std::vector::OptConProblem_t:: - ConstraintPtr_t>& -NLOCBackendBase::getStateBoxConstraintsInstances() const +template +const std::vector::OptConProblem_t::ConstraintPtr_t>& +NLOCBackendBase::getStateBoxConstraintsInstances() const { return stateBoxConstraints_; } -template -std::vector::OptConProblem_t:: - ConstraintPtr_t>& -NLOCBackendBase::getGeneralConstraintsInstances() +template +std::vector::OptConProblem_t::ConstraintPtr_t>& +NLOCBackendBase::getGeneralConstraintsInstances() { return generalConstraints_; } -template -const std::vector::OptConProblem_t:: - ConstraintPtr_t>& -NLOCBackendBase::getGeneralConstraintsInstances() const +template +const std::vector::OptConProblem_t::ConstraintPtr_t>& +NLOCBackendBase::getGeneralConstraintsInstances() const { return generalConstraints_; } -template -SCALAR NLOCBackendBase::getTimeHorizon() +template +SCALAR NLOCBackendBase::getTimeHorizon() { return K_ * settings_.dt; } -template -int NLOCBackendBase::getNumSteps() +template +int NLOCBackendBase::getNumSteps() { return K_; } -template -int NLOCBackendBase::getNumStepsPerShot() const +template +int NLOCBackendBase::getNumStepsPerShot() const { if (settings_.isSingleShooting()) return K_; @@ -1664,9 +1629,9 @@ int NLOCBackendBase::g return settings_.K_shot; } -template -const typename NLOCBackendBase::Settings_t& -NLOCBackendBase::getSettings() const +template +const typename NLOCBackendBase::Settings_t& +NLOCBackendBase::getSettings() const { return settings_; } diff --git a/ct_optcon/include/ct/optcon/nloc/NLOCBackendBase.hpp b/ct_optcon/include/ct/optcon/nloc/NLOCBackendBase.hpp index 7726909d5..f0559552a 100644 --- a/ct_optcon/include/ct/optcon/nloc/NLOCBackendBase.hpp +++ b/ct_optcon/include/ct/optcon/nloc/NLOCBackendBase.hpp @@ -39,32 +39,29 @@ namespace optcon { * */ -template +template class NLOCBackendBase { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW - static const size_t state_dim = STATE_DIM; + static const size_t STATE_DIM = MANIFOLD::TangentDim; + static const size_t state_dim = MANIFOLD::TangentDim; static const size_t control_dim = CONTROL_DIM; + using SCALAR = typename MANIFOLD::Scalar; + using scalar_t = SCALAR; + using scalar_array_t = std::vector>; typedef NLOptConSettings Settings_t; - typedef typename ct::core::StateFeedbackController Policy_t; + typedef typename ct::core::StateFeedbackController Policy_t; - typedef typename std::conditional, - DiscreteOptConProblem>::type OptConProblem_t; + typedef OptConProblem OptConProblem_t; typedef LQOCProblem LQOCProblem_t; typedef LQOCSolver LQOCSolver_t; - typedef ct::core::StateVectorArray StateVectorArray; + typedef ct::core::DiscreteArray StateVectorArray; typedef std::shared_ptr StateVectorArrayPtr; typedef ct::core::ControlVectorArray ControlVectorArray; @@ -76,7 +73,7 @@ class NLOCBackendBase typedef std::vector> ControlSubsteps; typedef std::shared_ptr ControlSubstepsPtr; - typedef OptconSystemInterface systemInterface_t; + typedef OptconSystemInterface systemInterface_t; typedef std::shared_ptr systemInterfacePtr_t; using ControlMatrix = ct::core::ControlMatrix; @@ -86,17 +83,13 @@ class NLOCBackendBase using FeedbackArray = ct::core::FeedbackArray; using TimeArray = ct::core::tpl::TimeArray; - using state_matrix_t = Eigen::Matrix; - using control_matrix_t = Eigen::Matrix; - using control_state_matrix_t = Eigen::Matrix; - using state_control_matrix_t = Eigen::Matrix; - - using state_vector_t = core::StateVector; + using state_matrix_t = ct::core::StateMatrix; + using control_matrix_t = ct::core::ControlMatrix; + using control_state_matrix_t = ct::core::ControlStateMatrix; + using state_control_matrix_t = ct::core::StateControlMatrix; using control_vector_t = core::ControlVector; using feedback_matrix_t = core::FeedbackMatrix; - using scalar_t = SCALAR; - using scalar_array_t = std::vector>; NLOCBackendBase(const OptConProblem_t& optConProblem, const Settings_t& settings); NLOCBackendBase(const OptConProblem_t& optConProblem, @@ -112,21 +105,18 @@ class NLOCBackendBase virtual ~NLOCBackendBase(); - template // do not use this template argument - typename std::enable_if>::value, - systemInterfacePtr_t>::type + typename std::enable_if::type createSystemInterface(const OptConProblem_t& optConProblem, const Settings_t& settings); - template // do not use this template argument - typename std::enable_if>::value, - systemInterfacePtr_t>::type - createSystemInterface(const OptConProblem_t& optConProblem, const Settings_t& settings); + typename std::enable_if::type createSystemInterface( + const OptConProblem_t& optConProblem, + const Settings_t& settings); //! configure the solver /** - * Configure the solver - * @param settings solver settings - */ + * @brief solver settings + * + */ virtual void configure(const Settings_t& settings); //! get the current SLQsolver settings @@ -283,7 +273,7 @@ class NLOCBackendBase void reset(); - const core::StateTrajectory getStateTrajectory() const; + const core::DiscreteTrajectory getStateTrajectory() const; const core::ControlTrajectory getControlTrajectory() const; @@ -466,7 +456,7 @@ class NLOCBackendBase * \param finalCost the accumulated final cost */ void computeCostsOfTrajectory(size_t threadId, - const core::StateVectorArray& x_local, + const core::DiscreteArray& x_local, const core::ControlVectorArray& u_local, scalar_t& intermediateCost, scalar_t& finalCost) const; @@ -480,7 +470,7 @@ class NLOCBackendBase * \param e_tot the total accumulated box constraint violation */ void computeBoxConstraintErrorOfTrajectory(size_t threadId, - const ct::core::StateVectorArray& x_local, + const ct::core::DiscreteArray& x_local, const ct::core::ControlVectorArray& u_local, scalar_t& e_tot) const; @@ -493,16 +483,16 @@ class NLOCBackendBase * \param e_tot the total accumulated general constraint violation */ void computeGeneralConstraintErrorOfTrajectory(size_t threadId, - const ct::core::StateVectorArray& x_local, + const ct::core::DiscreteArray& x_local, const ct::core::ControlVectorArray& u_local, scalar_t& e_tot) const; //! Check if controller with particular alpha is better void executeLineSearch(const size_t threadId, const scalar_t alpha, - ct::core::StateVectorArray& x_recorded, - ct::core::StateVectorArray& x_shot_recorded, - ct::core::StateVectorArray& defects_recorded, + ct::core::DiscreteArray& x_recorded, + ct::core::DiscreteArray& x_shot_recorded, + ct::core::DiscreteArray& defects_recorded, ct::core::ControlVectorArray& u_recorded, scalar_t& intermediateCost, scalar_t& finalCost, @@ -515,8 +505,7 @@ class NLOCBackendBase //! in case of line-search compute new merit and check if to accept step. Returns true if accept step - bool acceptStep( - const SCALAR alpha, + bool acceptStep(const SCALAR alpha, const SCALAR intermediateCost, const SCALAR finalCost, const SCALAR defectNorm, @@ -619,17 +608,16 @@ class NLOCBackendBase SummaryAllIterations summaryAllIterations_; - //! if building with MATLAB support, include matfile +//! if building with MATLAB support, include matfile #ifdef MATLAB matlab::MatFile matFile_; #endif //MATLAB }; -template +template template -SCALAR NLOCBackendBase::computeDiscreteArrayNorm( - const ARRAY_TYPE& d) const +SCALAR NLOCBackendBase::computeDiscreteArrayNorm(const ARRAY_TYPE& d) const { SCALAR norm = 0.0; @@ -641,10 +629,9 @@ SCALAR NLOCBackendBase } -template +template template -SCALAR NLOCBackendBase::computeDiscreteArrayNorm( - const ARRAY_TYPE& a, +SCALAR NLOCBackendBase::computeDiscreteArrayNorm(const ARRAY_TYPE& a, const ARRAY_TYPE& b) const { assert(a.size() == b.size()); @@ -658,10 +645,9 @@ SCALAR NLOCBackendBase return norm; } -template +template template -SCALAR NLOCBackendBase::computeDefectsNorm( - const StateVectorArray& d) const +SCALAR NLOCBackendBase::computeDefectsNorm(const StateVectorArray& d) const { return computeDiscreteArrayNorm(d); } diff --git a/ct_optcon/include/ct/optcon/optcon.h b/ct_optcon/include/ct/optcon/optcon.h index b10f29178..631b09c79 100644 --- a/ct_optcon/include/ct/optcon/optcon.h +++ b/ct_optcon/include/ct/optcon/optcon.h @@ -13,7 +13,7 @@ Licensed under the BSD-2 license (see LICENSE file in main directory) //#include "constraint/constraint.h" #include "problem/OptConProblem.h" -//#include "problem/LQOCProblem.hpp" +#include "problem/LQOCProblem.hpp" #include "solver/NLOptConSettings.hpp" #include "system_interface/OptconSystemInterface.h" @@ -27,8 +27,8 @@ Licensed under the BSD-2 license (see LICENSE file in main directory) //#include "nloc/algorithms/SingleShooting.hpp" // //#include "solver/OptConSolver.h" -//#include "solver/lqp/HPIPMInterface.hpp" -//#include "solver/lqp/GNRiccatiSolver.hpp" +#include "solver/lqp/HPIPMInterface.hpp" +#include "solver/lqp/GNRiccatiSolver.hpp" //#include "solver/NLOptConSolver.hpp" // //#include "lqr/riccati/CARE.hpp" @@ -52,13 +52,13 @@ Licensed under the BSD-2 license (see LICENSE file in main directory) //#include "constraint/constraint-impl.h" // #include "problem/OptConProblem-impl.h" -//#include "problem/LQOCProblem-impl.hpp" +#include "problem/LQOCProblem-impl.hpp" #include "system_interface/OptconContinuousSystemInterface-impl.h" #include "system_interface/OptconDiscreteSystemInterface-impl.h" -//#include "solver/lqp/GNRiccatiSolver-impl.hpp" -//#include "solver/lqp/HPIPMInterface-impl.hpp" +#include "solver/lqp/GNRiccatiSolver-impl.hpp" +#include "solver/lqp/HPIPMInterface-impl.hpp" //#include "solver/NLOptConSolver-impl.hpp" // //#include "lqr/riccati/CARE-impl.hpp" diff --git a/ct_optcon/include/ct/optcon/problem/LQOCProblem-impl.hpp b/ct_optcon/include/ct/optcon/problem/LQOCProblem-impl.hpp index 9fd043145..c9bb43b73 100644 --- a/ct_optcon/include/ct/optcon/problem/LQOCProblem-impl.hpp +++ b/ct_optcon/include/ct/optcon/problem/LQOCProblem-impl.hpp @@ -8,20 +8,24 @@ Licensed under the BSD-2 license (see LICENSE file in main directory) namespace ct { namespace optcon { - -template -LQOCProblem::LQOCProblem(int N) : nbu_(N, 0), nbx_(N + 1, 0) +template +LQOCProblem::LQOCProblem(int N) : nbu_(N, 0), nbx_(N + 1, 0) { changeNumStages(N); } -template -bool LQOCProblem::isConstrained() const +template +LQOCProblem::~LQOCProblem() +{ +} + +template +bool LQOCProblem::isConstrained() const { return (isInputBoxConstrained() | isStateBoxConstrained() | isGeneralConstrained()); } -template -bool LQOCProblem::isInputBoxConstrained() const +template +bool LQOCProblem::isInputBoxConstrained() const { if (std::accumulate(nbu_.begin(), nbu_.end(), 0) > 0) return true; @@ -29,8 +33,8 @@ bool LQOCProblem::isInputBoxConstrained() const return false; } -template -bool LQOCProblem::isStateBoxConstrained() const +template +bool LQOCProblem::isStateBoxConstrained() const { if (std::accumulate(nbx_.begin(), nbx_.end(), 0) > 0) return true; @@ -38,8 +42,8 @@ bool LQOCProblem::isStateBoxConstrained() const return false; } -template -bool LQOCProblem::isGeneralConstrained() const +template +bool LQOCProblem::isGeneralConstrained() const { if (std::accumulate(ng_.begin(), ng_.end(), 0) > 0) return true; @@ -47,14 +51,14 @@ bool LQOCProblem::isGeneralConstrained() const return false; } -template -int LQOCProblem::getNumberOfStages() +template +int LQOCProblem::getNumberOfStages() { return K_; } -template -void LQOCProblem::changeNumStages(int N) +template +void LQOCProblem::changeNumStages(int N) { K_ = N; @@ -66,6 +70,7 @@ void LQOCProblem::changeNumStages(int N) q_.resize(N + 1); qv_.resize(N + 1); Q_.resize(N + 1); + Acal_.resize(N+1); rv_.resize(N); R_.resize(N); @@ -88,8 +93,8 @@ void LQOCProblem::changeNumStages(int N) } -template -void LQOCProblem::setZero(const int& nGenConstr) +template +void LQOCProblem::setZero(const int& nGenConstr) { A_.setConstant(core::StateMatrix::Zero()); B_.setConstant(core::StateControlMatrix::Zero()); @@ -97,6 +102,7 @@ void LQOCProblem::setZero(const int& nGenConstr) P_.setConstant(core::FeedbackMatrix::Zero()); qv_.setConstant(core::StateVector::Zero()); Q_.setConstant(core::StateMatrix::Zero()); + Acal_.setConstant(core::StateMatrix::Identity()); rv_.setConstant(core::ControlVector::Zero()); R_.setConstant(core::ControlMatrix::Zero()); q_.setConstant((SCALAR)0.0); @@ -125,8 +131,8 @@ void LQOCProblem::setZero(const int& nGenConstr) } -template -void LQOCProblem::setInputBoxConstraint(const int index, +template +void LQOCProblem::setInputBoxConstraint(const int index, const int nConstr, const constr_vec_t& u_lb, const constr_vec_t& u_ub, @@ -157,8 +163,8 @@ void LQOCProblem::setInputBoxConstraint(const in } } -template -void LQOCProblem::setInputBoxConstraints(const int nConstr, +template +void LQOCProblem::setInputBoxConstraints(const int nConstr, const constr_vec_t& u_lb, const constr_vec_t& u_ub, const VectorXi& sp, @@ -169,8 +175,8 @@ void LQOCProblem::setInputBoxConstraints(const i } -template -void LQOCProblem::setIntermediateStateBoxConstraint(const int index, +template +void LQOCProblem::setIntermediateStateBoxConstraint(const int index, const int nConstr, const constr_vec_t& x_lb, const constr_vec_t& x_ub, @@ -201,8 +207,8 @@ void LQOCProblem::setIntermediateStateBoxConstra } } -template -void LQOCProblem::setIntermediateStateBoxConstraints(const int nConstr, +template +void LQOCProblem::setIntermediateStateBoxConstraints(const int nConstr, const constr_vec_t& x_lb, const constr_vec_t& x_ub, const VectorXi& sp, @@ -213,8 +219,8 @@ void LQOCProblem::setIntermediateStateBoxConstra } -template -void LQOCProblem::setTerminalBoxConstraints(const int nConstr, +template +void LQOCProblem::setTerminalBoxConstraints(const int nConstr, const constr_vec_t& x_lb, const constr_vec_t& x_ub, const VectorXi& sp, @@ -244,8 +250,8 @@ void LQOCProblem::setTerminalBoxConstraints(cons } } -template -void LQOCProblem::setGeneralConstraints(const constr_vec_t& d_lb, +template +void LQOCProblem::setGeneralConstraints(const constr_vec_t& d_lb, const constr_vec_t& d_ub, const constr_state_jac_t& C, const constr_control_jac_t& D) @@ -266,28 +272,23 @@ void LQOCProblem::setGeneralConstraints(const co D_.setConstant(D); } - -template -void LQOCProblem::setFromTimeInvariantLinearQuadraticProblem( - ct::core::DiscreteLinearSystem& linearSystem, - ct::optcon::CostFunctionQuadratic& costFunction, - const ct::core::StateVector& offset, +template +void LQOCProblem::setFromTimeInvariantLinearQuadraticProblem(const MANIFOLD& x0, + const core::ControlVector& u0, + ct::core::LinearSystem& linearSystem, + ct::optcon::CostFunctionQuadratic& costFunction, + const typename MANIFOLD::Tangent& stateOffset, const double dt) { setZero(); - core::StateVector x0; - x0.setZero(); // by definition - core::ControlVector u0; - u0.setZero(); // by definition - core::StateMatrix A; core::StateControlMatrix B; - linearSystem.getAandB(x0, u0, 0, A, B); + linearSystem.getDerivatives(A, B, x0, u0, 0); A_ = core::StateMatrixArray(K_, A); B_ = core::StateControlMatrixArray(K_, B); - b_ = core::StateVectorArray(K_ + 1, offset); + b_ = core::DiscreteArray(K_ + 1, stateOffset); // feed current state and control to cost function costFunction.setCurrentStateAndControl(x0, u0, 0); @@ -298,12 +299,52 @@ void LQOCProblem::setFromTimeInvariantLinearQuad core::FeedbackArray(K_, costFunction.stateControlDerivativeIntermediate() * dt); R_ = core::ControlMatrixArray(K_, costFunction.controlSecondDerivativeIntermediate() * dt); - qv_ = core::StateVectorArray(K_ + 1, costFunction.stateDerivativeIntermediate() * dt); + qv_ = core::DiscreteArray(K_ + 1, costFunction.stateDerivativeIntermediate() * dt); rv_ = core::ControlVectorArray(K_, costFunction.controlDerivativeIntermediate() * dt); // final stage - Q_[K_] = costFunction.stateSecondDerivativeTerminal(); - qv_[K_] = costFunction.stateDerivativeTerminal(); + Q_[K_] = costFunction.stateSecondDerivativeTerminal() * dt; + qv_[K_] = costFunction.stateDerivativeTerminal() * dt; +} + + +template +void LQOCProblem::setFromTimeInvariantLinearQuadraticProblem( + const ct::core::DiscreteArray& x_traj, + const ct::core::DiscreteArray>& u_traj, + ct::core::LinearSystem& linearSystem, + ct::optcon::CostFunctionQuadratic& costFunction, + const typename MANIFOLD::Tangent& stateOffset, + const double dt) +{ + setZero(); + + // get LTI dynamics + core::StateMatrix A; + core::StateControlMatrix B; + linearSystem.getDerivatives(A, B, x_traj.front(), u_traj.front(), 0); + A_ = core::StateMatrixArray(K_, A); + B_ = core::StateControlMatrixArray(K_, B); + b_ = core::DiscreteArray(K_ + 1, stateOffset); + + // eval costs around the reference trajectory + for (int i = 0; i < K_; i++) + { + // feed current state and control to cost function + costFunction.setCurrentStateAndControl(x_traj[i], u_traj[i], i * dt); + + // intermediate stage + Q_[i] = costFunction.stateSecondDerivativeIntermediate() * dt; + P_[i] = costFunction.stateControlDerivativeIntermediate() * dt; + R_[i] = costFunction.controlSecondDerivativeIntermediate() * dt; + + qv_[i] = costFunction.stateDerivativeIntermediate() * dt; + rv_[i] = costFunction.controlDerivativeIntermediate() * dt; + } + // final stage + costFunction.setCurrentStateAndControl(x_traj.back(), u_traj.back(), K_ * dt); + Q_[K_] = costFunction.stateSecondDerivativeTerminal() * dt; + qv_[K_] = costFunction.stateDerivativeTerminal() * dt; } } // namespace optcon diff --git a/ct_optcon/include/ct/optcon/problem/LQOCProblem.hpp b/ct_optcon/include/ct/optcon/problem/LQOCProblem.hpp index 631b8d79d..df5407d33 100644 --- a/ct_optcon/include/ct/optcon/problem/LQOCProblem.hpp +++ b/ct_optcon/include/ct/optcon/problem/LQOCProblem.hpp @@ -53,12 +53,15 @@ namespace optcon { * * \todo Refactor the initializing methods such that const-references can be handed over. */ -template +template class LQOCProblem { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW + using SCALAR = typename MANIFOLD::Scalar; + static constexpr size_t STATE_DIM = MANIFOLD::TangentDim; + using constr_vec_t = Eigen::Matrix; using constr_state_jac_t = Eigen::Matrix; using constr_control_jac_t = Eigen::Matrix; @@ -84,6 +87,8 @@ class LQOCProblem //! constructor LQOCProblem(int N = 0); + ~LQOCProblem(); + //! returns the number of discrete time steps in the LQOCP, including terminal stage int getNumberOfStages(); @@ -189,10 +194,17 @@ class LQOCProblem * @param stateOffset the offset for the affine system dynamics demanded by the LQOC Solver * @param dt the sampling time, required for discretization */ - void setFromTimeInvariantLinearQuadraticProblem( - ct::core::DiscreteLinearSystem& linearSystem, - ct::optcon::CostFunctionQuadratic& costFunction, - const ct::core::StateVector& stateOffset, + void setFromTimeInvariantLinearQuadraticProblem(const MANIFOLD& x0, + const core::ControlVector& u0, + ct::core::LinearSystem& linearSystem, + ct::optcon::CostFunctionQuadratic& costFunction, + const typename MANIFOLD::Tangent& stateOffset, + const double dt); + void setFromTimeInvariantLinearQuadraticProblem(const ct::core::DiscreteArray& x_traj, + const ct::core::DiscreteArray>& u_traj, + ct::core::LinearSystem& linearSystem, + ct::optcon::CostFunctionQuadratic& costFunction, + const typename MANIFOLD::Tangent& stateOffset, const double dt); //! return a flag indicating whether this LQOC Problem is constrained or not @@ -205,14 +217,15 @@ class LQOCProblem //! affine, time-varying system dynamics in discrete time ct::core::StateMatrixArray A_; ct::core::StateControlMatrixArray B_; - ct::core::StateVectorArray b_; + ct::core::DiscreteArray b_; //! constant term of in the LQ approximation of the cost function ct::core::ScalarArray q_; //! LQ approximation of the pure state penalty, including terminal state penalty - ct::core::StateVectorArray qv_; + ct::core::DiscreteArray qv_; ct::core::StateMatrixArray Q_; + ct::core::StateMatrixArray Acal_; // TODO: temp //! LQ approximation of the pure control penalty ct::core::ControlVectorArray rv_; diff --git a/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver-impl.hpp b/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver-impl.hpp new file mode 100644 index 000000000..39591d47b --- /dev/null +++ b/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver-impl.hpp @@ -0,0 +1,293 @@ +/********************************************************************************************************************** +This file is part of the Control Toolbox (https://github.com/ethz-adrl/control-toolbox), copyright by ETH Zurich. +Licensed under the BSD-2 license (see LICENSE file in main directory) + **********************************************************************************************************************/ + +#pragma once + +namespace ct { +namespace optcon { + + +template +AugGNRiccatiSolver::AugGNRiccatiSolver(const std::shared_ptr& lqocProblem) + : LQOCSolver(lqocProblem), N_(-1) +{ + Eigen::initParallel(); + Eigen::setNbThreads(settings_.nThreadsEigen); +} + + +template +AugGNRiccatiSolver::AugGNRiccatiSolver(int N) +{ + changeNumberOfStages(N); +} + +template +void AugGNRiccatiSolver::solve() +{ + for (int i = this->lqocProblem_->getNumberOfStages() - 1; i >= 0; i--) + solveSingleStage(i); +} + +template +void AugGNRiccatiSolver::solveSingleStage(int N) +{ + if (N == this->lqocProblem_->getNumberOfStages() - 1) + initializeCostToGo(); + + designController(N); + + if (N > 0) + computeCostToGo(N); +} + +template +void AugGNRiccatiSolver::configure(const NLOptConSettings& settings) +{ + settings_ = settings; + H_corrFix_ = settings_.epsilon * ControlMatrix::Identity(); +} + +template +void AugGNRiccatiSolver::computeStatesAndControls() +{ + LQOCProblem_t& p = *this->lqocProblem_; + + this->x_sol_[0].setZero(); // should always be zero (fixed init state) + + for (int k = 0; k < this->lqocProblem_->getNumberOfStages(); k++) + { + //! control update rule in diff coordinates + this->u_sol_[k] = this->lv_[k] + this->L_[k] * this->x_sol_[k]; + + //! state update rule in diff coordinates + this->x_sol_[k + 1] = p.A_[k] * this->x_sol_[k] + p.B_[k] * (this->u_sol_[k]) + p.b_[k]; // TODO: transport + } +} + +template +void AugGNRiccatiSolver::computeFeedbackMatrices() +{ /*no action required, already computed in backward pass */ +} + +template +void AugGNRiccatiSolver::compute_lv() +{ /*no action required, already computed in backward pass*/ +} + +template +auto AugGNRiccatiSolver::getSmallestEigenvalue() -> SCALAR +{ + return smallestEigenvalue_; +} + +template +void AugGNRiccatiSolver::setProblemImpl(std::shared_ptr lqocProblem) +{ + if (lqocProblem->isConstrained()) + { + throw std::runtime_error( + "Selected wrong solver - AugGNRiccatiSolver cannot handle constrained problems. Use a different solver"); + } + + const int& N = lqocProblem->getNumberOfStages(); + changeNumberOfStages(N); +} + +template +void AugGNRiccatiSolver::changeNumberOfStages(int N) +{ + if (N <= 0) + return; + + if (N_ == N) + return; + + gv_.resize(N); + G_.resize(N); + + H_.resize(N); + Hi_.resize(N); + Hi_inverse_.resize(N); + + this->lv_.resize(N); + this->L_.resize(N); + + this->x_sol_.resize(N + 1); + this->u_sol_.resize(N); + + sv_.resize(N + 1); + S_.resize(N + 1); + sv_tilda_.resize(N + 1); + S_tilda_.resize(N + 1); + + N_ = N; +} + +template +void AugGNRiccatiSolver::initializeCostToGo() +{ + //! since intializeCostToGo is the first call, we initialize the smallestEigenvalue here. + smallestEigenvalue_ = std::numeric_limits::infinity(); + + // initialize quadratic approximation of cost to go + const int& N = this->lqocProblem_->getNumberOfStages(); + LQOCProblem_t& p = *this->lqocProblem_; + + S_[N] = p.Q_[N]; + sv_[N] = p.qv_[N]; + + S_tilda_[N] = p.Acal_[N] * S_[N] * p.Acal_[N].transpose(); + sv_tilda_[N] = p.Acal_[N] * sv_[N]; +} + +template +void AugGNRiccatiSolver::computeCostToGo(size_t k) +{ + LQOCProblem_t& p = *this->lqocProblem_; + + S_[k] = p.Q_[k]; + S_[k].noalias() += p.A_[k].transpose() * S_tilda_[k + 1] * p.A_[k]; + S_[k].noalias() -= this->L_[k].transpose() * Hi_[k] * this->L_[k]; + + S_[k] = 0.5 * (S_[k] + S_[k].transpose()).eval(); + + sv_[k] = p.qv_[k]; + sv_[k].noalias() += p.A_[k].transpose() * sv_tilda_[k + 1]; + sv_[k].noalias() += p.A_[k].transpose() * S_tilda_[k + 1] * p.b_[k]; + sv_[k].noalias() += this->L_[k].transpose() * Hi_[k] * this->lv_[k]; + sv_[k].noalias() += this->L_[k].transpose() * gv_[k]; + sv_[k].noalias() += G_[k].transpose() * this->lv_[k]; + + S_tilda_[k] = p.Acal_[k] * S_[k] * p.Acal_[k].transpose(); + sv_tilda_[k] = p.Acal_[k] * sv_[k]; +} + +template +void AugGNRiccatiSolver::designController(size_t k) +{ + LQOCProblem_t& p = *this->lqocProblem_; + + gv_[k] = p.rv_[k]; + gv_[k].noalias() += p.B_[k].transpose() * sv_tilda_[k + 1]; + //gv_[k].noalias() += p.B_[k].transpose() * S_tilda_[k + 1].template selfadjointView() * p.b_[k]; + + G_[k] = p.P_[k]; + //G_[k].noalias() += B_[k].transpose() * S_[k+1] * A_[k]; + G_[k].noalias() += p.B_[k].transpose() * S_tilda_[k + 1].template selfadjointView() * p.A_[k]; + + H_[k] = p.R_[k]; + //H_[k].noalias() += B_[k].transpose() * S_[k+1] * B_[k]; + H_[k].noalias() += p.B_[k].transpose() * S_tilda_[k + 1].template selfadjointView() * p.B_[k]; + + if (settings_.fixedHessianCorrection) + { + if (settings_.epsilon > 1e-10) + Hi_[k] = H_[k] + settings_.epsilon * ControlMatrix::Identity(); + else + Hi_[k] = H_[k]; + + if (settings_.recordSmallestEigenvalue) + { + // compute eigenvalues with eigenvectors enabled + eigenvalueSolver_.compute(Hi_[k], Eigen::ComputeEigenvectors); + const ControlMatrix& V = eigenvalueSolver_.eigenvectors().real(); + const ControlVector& lambda = eigenvalueSolver_.eigenvalues(); + + smallestEigenvalue_ = std::min(smallestEigenvalue_, lambda.minCoeff()); + + // Corrected Eigenvalue Matrix + ControlMatrix D = ControlMatrix::Zero(); + // make D positive semi-definite (as described in IV. B.) + D.diagonal() = lambda.cwiseMax(settings_.epsilon); + + // reconstruct H + ControlMatrix Hi_regular = V * D * V.transpose(); + + // invert D + ControlMatrix D_inverse = ControlMatrix::Zero(); + // eigenvalue-wise inversion + D_inverse.diagonal() = -1.0 * D.diagonal().cwiseInverse(); + ControlMatrix Hi_inverse_regular = V * D_inverse * V.transpose(); + + if (!Hi_inverse_[k].isApprox(Hi_inverse_regular, 1e-4)) + { + std::cout << "warning, inverses not identical at " << k << std::endl; + std::cout << "Hi_inverse_fixed - Hi_inverse_regular: " << std::endl + << Hi_inverse_[k] - Hi_inverse_regular << std::endl + << std::endl; + } + } + + Hi_inverse_[k] = -Hi_[k].template selfadjointView().llt().solve(ControlMatrix::Identity()); + + // calculate FB gain update + this->L_[k].noalias() = Hi_inverse_[k].template selfadjointView() * G_[k]; + + // calculate FF update + this->lv_[k].noalias() = Hi_inverse_[k].template selfadjointView() * gv_[k]; + } + else + { + // compute eigenvalues with eigenvectors enabled + eigenvalueSolver_.compute(H_[k], Eigen::ComputeEigenvectors); + const ControlMatrix& V = eigenvalueSolver_.eigenvectors().real(); + const ControlVector& lambda = eigenvalueSolver_.eigenvalues(); + + if (settings_.recordSmallestEigenvalue) + { + smallestEigenvalue_ = std::min(smallestEigenvalue_, lambda.minCoeff()); + } + + // Corrected Eigenvalue Matrix + ControlMatrix D = ControlMatrix::Zero(); + // make D positive semi-definite (as described in IV. B.) + D.diagonal() = lambda.cwiseMax(settings_.epsilon); + + // reconstruct H + Hi_[k].noalias() = V * D * V.transpose(); + + // invert D + ControlMatrix D_inverse = ControlMatrix::Zero(); + // eigenvalue-wise inversion + D_inverse.diagonal() = -1.0 * D.diagonal().cwiseInverse(); + Hi_inverse_[k].noalias() = V * D_inverse * V.transpose(); + + // calculate FB gain update + this->L_[k].noalias() = Hi_inverse_[k] * G_[k]; + + // calculate FF update + this->lv_[k].noalias() = Hi_inverse_[k] * gv_[k]; + } +} + +template +void AugGNRiccatiSolver::logToMatlab() +{ +#ifdef MATLAB_FULL_LOG + + matFile_.open("AugGNRiccatiSolver.mat"); + + matFile_.put("sv", sv_.toImplementation()); + matFile_.put("S", S_.toImplementation()); + matFile_.put("L", this->L_.toImplementation()); + matFile_.put("H", H_.toImplementation()); + matFile_.put("Hi_", Hi_.toImplementation()); + matFile_.put("Hi_inverse", Hi_inverse_.toImplementation()); + matFile_.put("G", G_.toImplementation()); + matFile_.put("gv", gv_.toImplementation()); + + matFile_.close(); +#endif +} + +template +void AugGNRiccatiSolver::initializeAndAllocate() +{ + // do nothing +} + +} // namespace optcon +} // namespace ct diff --git a/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver.hpp b/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver.hpp new file mode 100644 index 000000000..846c251a5 --- /dev/null +++ b/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver.hpp @@ -0,0 +1,111 @@ +/********************************************************************************************************************** +This file is part of the Control Toolbox (https://github.com/ethz-adrl/control-toolbox), copyright by ETH Zurich. +Licensed under the BSD-2 license (see LICENSE file in main directory) +**********************************************************************************************************************/ + +#pragma once + +#include "LQOCSolver.hpp" + +#ifdef MATLAB_FULL_LOG +#include +#endif + +namespace ct { +namespace optcon { + +/*! + * This class implements an general Riccati backward pass for solving an unconstrained + * linear-quadratic Optimal Control problem + */ +template +class AugGNRiccatiSolver : public LQOCSolver +{ +public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + + static const int STATE_DIM = MANIFOLD::TangentDim; + static const int state_dim = STATE_DIM; + static const int control_dim = CONTROL_DIM; + + using Base = LQOCSolver; + using SCALAR = typename Base::SCALAR; + typedef typename Base::LQOCProblem_t LQOCProblem_t; + + typedef ct::core::StateMatrix StateMatrix; + typedef ct::core::StateMatrixArray StateMatrixArray; + typedef ct::core::ControlVector ControlVector; + typedef ct::core::ControlMatrix ControlMatrix; + typedef ct::core::ControlMatrixArray ControlMatrixArray; + typedef ct::core::StateControlMatrixArray StateControlMatrixArray; + typedef ct::core::FeedbackArray FeedbackArray; + typedef ct::core::ControlVectorArray ControlVectorArray; + + AugGNRiccatiSolver(const std::shared_ptr& lqocProblem = nullptr); + + AugGNRiccatiSolver(int N); + + virtual void solve() override; + + virtual void initializeAndAllocate() override; + + virtual void solveSingleStage(int N) override; + + virtual void configure(const NLOptConSettings& settings) override; + + virtual void computeStatesAndControls() override; + + virtual void computeFeedbackMatrices() override; + + virtual void compute_lv() override; + + virtual SCALAR getSmallestEigenvalue() override; + +protected: + /*! + * resize matrices + * @param lqocProblem + */ + virtual void setProblemImpl(std::shared_ptr lqocProblem) override; + + void changeNumberOfStages(int N); + + void initializeCostToGo(); + + void computeCostToGo(size_t k); + + void designController(size_t k); + + void logToMatlab(); + + NLOptConSettings settings_; + + ControlVectorArray gv_; + FeedbackArray G_; + + ControlMatrixArray H_; + ControlMatrixArray Hi_; + ControlMatrixArray Hi_inverse_; + ControlMatrix H_corrFix_; + + ct::core::DiscreteArray sv_; + StateMatrixArray S_; + ct::core::DiscreteArray sv_tilda_; + StateMatrixArray S_tilda_; + + int N_; + + SCALAR smallestEigenvalue_; + + //! Eigenvalue solver, used for inverting the Hessian and for regularization + Eigen::SelfAdjointEigenSolver> eigenvalueSolver_; + +//! if building with MATLAB support, include matfile +#ifdef MATLAB_FULL_LOG + matlab::MatFile matFile_; +#endif //MATLAB +}; + + +} // namespace optcon +} // namespace ct diff --git a/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp b/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp index 74021f1ef..9f49c2d46 100644 --- a/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp +++ b/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp @@ -9,31 +9,30 @@ namespace ct { namespace optcon { -template -GNRiccatiSolver::GNRiccatiSolver(const std::shared_ptr& lqocProblem) - : LQOCSolver(lqocProblem), N_(-1) +template +GNRiccatiSolver::GNRiccatiSolver(const std::shared_ptr& lqocProblem) + : LQOCSolver(lqocProblem), N_(-1) { Eigen::initParallel(); Eigen::setNbThreads(settings_.nThreadsEigen); } -template -GNRiccatiSolver::GNRiccatiSolver(int N) +template +GNRiccatiSolver::GNRiccatiSolver(int N) { changeNumberOfStages(N); } -template -void GNRiccatiSolver::solve() +template +void GNRiccatiSolver::solve() { for (int i = this->lqocProblem_->getNumberOfStages() - 1; i >= 0; i--) solveSingleStage(i); } - -template -void GNRiccatiSolver::solveSingleStage(int N) +template +void GNRiccatiSolver::solveSingleStage(int N) { if (N == this->lqocProblem_->getNumberOfStages() - 1) initializeCostToGo(); @@ -44,16 +43,15 @@ void GNRiccatiSolver::solveSingleStage(int N) computeCostToGo(N); } - -template -void GNRiccatiSolver::configure(const NLOptConSettings& settings) +template +void GNRiccatiSolver::configure(const NLOptConSettings& settings) { settings_ = settings; H_corrFix_ = settings_.epsilon * ControlMatrix::Identity(); } -template -void GNRiccatiSolver::computeStatesAndControls() +template +void GNRiccatiSolver::computeStatesAndControls() { LQOCProblem_t& p = *this->lqocProblem_; @@ -69,24 +67,24 @@ void GNRiccatiSolver::computeStatesAndControls() } } -template -void GNRiccatiSolver::computeFeedbackMatrices() +template +void GNRiccatiSolver::computeFeedbackMatrices() { /*no action required, already computed in backward pass */ } -template -void GNRiccatiSolver::compute_lv() +template +void GNRiccatiSolver::compute_lv() { /*no action required, already computed in backward pass*/ } -template -SCALAR GNRiccatiSolver::getSmallestEigenvalue() +template +auto GNRiccatiSolver::getSmallestEigenvalue() -> SCALAR { return smallestEigenvalue_; } -template -void GNRiccatiSolver::setProblemImpl(std::shared_ptr lqocProblem) +template +void GNRiccatiSolver::setProblemImpl(std::shared_ptr lqocProblem) { if (lqocProblem->isConstrained()) { @@ -98,9 +96,8 @@ void GNRiccatiSolver::setProblemImpl(std::shared changeNumberOfStages(N); } - -template -void GNRiccatiSolver::changeNumberOfStages(int N) +template +void GNRiccatiSolver::changeNumberOfStages(int N) { if (N <= 0) return; @@ -127,9 +124,8 @@ void GNRiccatiSolver::changeNumberOfStages(int N N_ = N; } - -template -void GNRiccatiSolver::initializeCostToGo() +template +void GNRiccatiSolver::initializeCostToGo() { //! since intializeCostToGo is the first call, we initialize the smallestEigenvalue here. smallestEigenvalue_ = std::numeric_limits::infinity(); @@ -142,9 +138,8 @@ void GNRiccatiSolver::initializeCostToGo() sv_[N] = p.qv_[N]; } - -template -void GNRiccatiSolver::computeCostToGo(size_t k) +template +void GNRiccatiSolver::computeCostToGo(size_t k) { LQOCProblem_t& p = *this->lqocProblem_; @@ -162,9 +157,8 @@ void GNRiccatiSolver::computeCostToGo(size_t k) sv_[k].noalias() += G_[k].transpose() * this->lv_[k]; } - -template -void GNRiccatiSolver::designController(size_t k) +template +void GNRiccatiSolver::designController(size_t k) { LQOCProblem_t& p = *this->lqocProblem_; @@ -261,9 +255,8 @@ void GNRiccatiSolver::designController(size_t k) } } - -template -void GNRiccatiSolver::logToMatlab() +template +void GNRiccatiSolver::logToMatlab() { #ifdef MATLAB_FULL_LOG @@ -282,12 +275,11 @@ void GNRiccatiSolver::logToMatlab() #endif } -template -void GNRiccatiSolver::initializeAndAllocate() +template +void GNRiccatiSolver::initializeAndAllocate() { // do nothing } - } // namespace optcon } // namespace ct diff --git a/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver.hpp b/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver.hpp index c6d59a1e5..7caf75f45 100644 --- a/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver.hpp +++ b/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver.hpp @@ -18,16 +18,19 @@ namespace optcon { * This class implements an general Riccati backward pass for solving an unconstrained * linear-quadratic Optimal Control problem */ -template -class GNRiccatiSolver : public LQOCSolver +template +class GNRiccatiSolver : public LQOCSolver { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW + static const int STATE_DIM = MANIFOLD::TangentDim; static const int state_dim = STATE_DIM; static const int control_dim = CONTROL_DIM; - typedef LQOCProblem LQOCProblem_t; + using Base = LQOCSolver; + using SCALAR = typename Base::SCALAR; + typedef typename Base::LQOCProblem_t LQOCProblem_t; typedef ct::core::StateMatrix StateMatrix; typedef ct::core::StateMatrixArray StateMatrixArray; @@ -36,8 +39,6 @@ class GNRiccatiSolver : public LQOCSolver typedef ct::core::ControlMatrixArray ControlMatrixArray; typedef ct::core::StateControlMatrixArray StateControlMatrixArray; typedef ct::core::FeedbackArray FeedbackArray; - - typedef ct::core::StateVectorArray StateVectorArray; typedef ct::core::ControlVectorArray ControlVectorArray; GNRiccatiSolver(const std::shared_ptr& lqocProblem = nullptr); @@ -87,7 +88,7 @@ class GNRiccatiSolver : public LQOCSolver ControlMatrixArray Hi_inverse_; ControlMatrix H_corrFix_; - StateVectorArray sv_; + ct::core::DiscreteArray sv_; StateMatrixArray S_; int N_; diff --git a/ct_optcon/include/ct/optcon/solver/lqp/HPIPMInterface-impl.hpp b/ct_optcon/include/ct/optcon/solver/lqp/HPIPMInterface-impl.hpp index c1f018ed0..929b9c55c 100644 --- a/ct_optcon/include/ct/optcon/solver/lqp/HPIPMInterface-impl.hpp +++ b/ct_optcon/include/ct/optcon/solver/lqp/HPIPMInterface-impl.hpp @@ -10,8 +10,8 @@ Licensed under the BSD-2 license (see LICENSE file in main directory) namespace ct { namespace optcon { -template -HPIPMInterface::HPIPMInterface() +template +HPIPMInterface::HPIPMInterface() : N_(-1), settings_(NLOptConSettings()), dim_mem_(nullptr), @@ -26,14 +26,14 @@ HPIPMInterface::HPIPMInterface() configure(settings_); } -template -HPIPMInterface::~HPIPMInterface() +template +HPIPMInterface::~HPIPMInterface() { freeHpipmMemory(); } -template -void HPIPMInterface::initializeAndAllocate() +template +void HPIPMInterface::initializeAndAllocate() { freeHpipmMemory(); @@ -91,8 +91,8 @@ void HPIPMInterface::initializeAndAllocate() } } -template -void HPIPMInterface::freeHpipmMemory() +template +void HPIPMInterface::freeHpipmMemory() { free(dim_mem_); free(qp_mem_); @@ -107,16 +107,16 @@ void HPIPMInterface::freeHpipmMemory() } -template -void HPIPMInterface::configure(const NLOptConSettings& settings) +template +void HPIPMInterface::configure(const NLOptConSettings& settings) { settings_ = settings; ::d_ocp_qp_ipm_arg_set_iter_max(&settings_.lqoc_solver_settings.num_lqoc_iterations, &arg_); } -template -void HPIPMInterface::solve() +template +void HPIPMInterface::solve() { // optional printout #ifdef HPIPM_PRINT_MATRICES @@ -212,8 +212,8 @@ void HPIPMInterface::solve() } } -template -void HPIPMInterface::computeStatesAndControls() +template +void HPIPMInterface::computeStatesAndControls() { // extract solution for x and u for (int ii = 0; ii < N_; ii++) @@ -234,9 +234,8 @@ void HPIPMInterface::computeStatesAndControls() } } - -template -void HPIPMInterface::computeFeedbackMatrices() +template +void HPIPMInterface::computeFeedbackMatrices() { // extract data which is mandatory for computing either feedback or iLQR feedforward Eigen::Matrix Lr; @@ -244,7 +243,7 @@ void HPIPMInterface::computeFeedbackMatrices() ct::core::ControlMatrix Lr0_inv = (Lr.template triangularView()).solve(Eigen::Matrix::Identity()); - LQOCProblem& p = *this->lqocProblem_; + LQOCProblem_t& p = *this->lqocProblem_; this->L_.resize(p.getNumberOfStages()); for (int i = 1; i < this->lqocProblem_->getNumberOfStages(); i++) @@ -266,15 +265,15 @@ void HPIPMInterface::computeFeedbackMatrices() this->L_[0] = (-Lr0_inv.transpose() * Lr0_inv * G); } -template -void HPIPMInterface::compute_lv() +template +void HPIPMInterface::compute_lv() { for (int i = 0; i < this->lqocProblem_->getNumberOfStages(); i++) ::d_ocp_qp_ipm_get_ric_k(&qp_, &arg_, &workspace_, i, this->lv_[i].data()); } -template -void HPIPMInterface::printSolution() +template +void HPIPMInterface::printSolution() { #ifdef HPIPM_PRINT_MATRICES int ii; @@ -299,9 +298,8 @@ void HPIPMInterface::printSolution() } -template -void HPIPMInterface::setProblemImpl( - std::shared_ptr> lqocProblem) +template +void HPIPMInterface::setProblemImpl(std::shared_ptr lqocProblem) { // check if the number of stages N changed and adapt problem dimensions bool dimsChanged = changeProblemSize(lqocProblem); @@ -329,9 +327,8 @@ void HPIPMInterface::setProblemImpl( } -template -bool HPIPMInterface::configureInputBoxConstraints( - std::shared_ptr> lqocProblem) +template +bool HPIPMInterface::configureInputBoxConstraints(std::shared_ptr lqocProblem) { bool configChanged = false; @@ -366,9 +363,8 @@ bool HPIPMInterface::configureInputBoxConstraints( } -template -bool HPIPMInterface::configureStateBoxConstraints( - std::shared_ptr> lqocProblem) +template +bool HPIPMInterface::configureStateBoxConstraints(std::shared_ptr lqocProblem) { bool configChanged = false; @@ -410,9 +406,8 @@ bool HPIPMInterface::configureStateBoxConstraints( } -template -bool HPIPMInterface::configureGeneralConstraints( - std::shared_ptr> lqocProblem) +template +bool HPIPMInterface::configureGeneralConstraints(std::shared_ptr lqocProblem) { bool configChanged = false; @@ -466,12 +461,12 @@ bool HPIPMInterface::configureGeneralConstraints( } -template -void HPIPMInterface::setupCostAndDynamics(StateMatrixArray& A, +template +void HPIPMInterface::setupCostAndDynamics(StateMatrixArray& A, StateControlMatrixArray& B, - StateVectorArray& b, + core::DiscreteArray& b, FeedbackArray& P, - StateVectorArray& qv, + core::DiscreteArray& qv, StateMatrixArray& Q, ControlVectorArray& rv, ControlMatrixArray& R) @@ -512,8 +507,8 @@ void HPIPMInterface::setupCostAndDynamics(StateMatrixArr } -template -bool HPIPMInterface::changeProblemSize(std::shared_ptr> p) +template +bool HPIPMInterface::changeProblemSize(std::shared_ptr p) { const int N = p->getNumberOfStages(); @@ -598,8 +593,8 @@ bool HPIPMInterface::changeProblemSize(std::shared_ptr -void HPIPMInterface::d_print_mat(int m, int n, double* A, int lda) +template +void HPIPMInterface::d_print_mat(int m, int n, double* A, int lda) { int i, j; for (i = 0; i < m; i++) @@ -614,8 +609,8 @@ void HPIPMInterface::d_print_mat(int m, int n, double* A } -template -void HPIPMInterface::d_print_e_tran_mat(int row, int col, double* A, int lda) +template +void HPIPMInterface::d_print_e_tran_mat(int row, int col, double* A, int lda) { int i, j; for (j = 0; j < col; j++) @@ -630,8 +625,8 @@ void HPIPMInterface::d_print_e_tran_mat(int row, int col } -template -const ct::core::ControlVectorArray& HPIPMInterface::get_lv() +template +const ct::core::ControlVectorArray& HPIPMInterface::get_lv() { return this->lv_; } diff --git a/ct_optcon/include/ct/optcon/solver/lqp/HPIPMInterface.hpp b/ct_optcon/include/ct/optcon/solver/lqp/HPIPMInterface.hpp index f44dc5a22..b3a263199 100644 --- a/ct_optcon/include/ct/optcon/solver/lqp/HPIPMInterface.hpp +++ b/ct_optcon/include/ct/optcon/solver/lqp/HPIPMInterface.hpp @@ -37,14 +37,20 @@ namespace optcon { * the configuration of the box and general constraints must be done independently * from setProblem() */ -template -class HPIPMInterface : public LQOCSolver +template +class HPIPMInterface : public LQOCSolver { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW - static const int state_dim = STATE_DIM; - static const int control_dim = CONTROL_DIM; + static constexpr size_t STATE_DIM = MANIFOLD::TangentDim; + static constexpr int state_dim = STATE_DIM; + static constexpr int control_dim = CONTROL_DIM; + + using Base = LQOCSolver; + using SCALAR = typename Base::SCALAR; + typedef typename Base::LQOCProblem_t LQOCProblem_t; + static const int max_box_constr_dim = STATE_DIM + CONTROL_DIM; using StateMatrix = ct::core::StateMatrix; @@ -81,13 +87,11 @@ class HPIPMInterface : public LQOCSolver void printSolution(); //! brief setup and configure the box constraints - virtual bool configureInputBoxConstraints( - std::shared_ptr> lqocProblem) override; - virtual bool configureStateBoxConstraints( - std::shared_ptr> lqocProblem) override; + virtual bool configureInputBoxConstraints(std::shared_ptr lqocProblem) override; + virtual bool configureStateBoxConstraints(std::shared_ptr lqocProblem) override; //! brief setup and configure the general (in)equality constraints - virtual bool configureGeneralConstraints(std::shared_ptr> lqocProblem) override; + virtual bool configureGeneralConstraints(std::shared_ptr lqocProblem) override; /*! * @brief allocate memory for HPIPM @@ -116,7 +120,7 @@ class HPIPMInterface : public LQOCSolver * * \warning If you wish to */ - void setProblemImpl(std::shared_ptr> lqocProblem) override; + void setProblemImpl(std::shared_ptr lqocProblem) override; /*! * @brief transcribe the problem for HPIPM @@ -139,9 +143,9 @@ class HPIPMInterface : public LQOCSolver */ void setupCostAndDynamics(StateMatrixArray& A, StateControlMatrixArray& B, - StateVectorArray& b, + core::DiscreteArray& b, FeedbackArray& P, - StateVectorArray& qv, + core::DiscreteArray& qv, StateMatrixArray& Q, ControlVectorArray& rv, ControlMatrixArray& R); @@ -150,7 +154,7 @@ class HPIPMInterface : public LQOCSolver * @brief change the size of the optimal control problem * @return true if the problem size changed, otherwise return false */ - bool changeProblemSize(std::shared_ptr> lqocProblem); + bool changeProblemSize(std::shared_ptr lqocProblem); //! prints a matrix in column-major format void d_print_mat(int m, int n, double* A, int lda); @@ -179,7 +183,7 @@ class HPIPMInterface : public LQOCSolver std::vector hA_; //! system state sensitivities std::vector hB_; //! system input sensitivities std::vector hb_; //! system offset term - Eigen::Matrix hb0_; //! intermediate container for intuitive transcription of first stage + typename MANIFOLD::Tangent hb0_; //! intermediate container for intuitive transcription of first stage std::vector hQ_; //! pure state penalty hessian std::vector hS_; //! state-control cross-terms diff --git a/ct_optcon/include/ct/optcon/solver/lqp/LQOCSolver.hpp b/ct_optcon/include/ct/optcon/solver/lqp/LQOCSolver.hpp index 87f06b734..4ae700686 100644 --- a/ct_optcon/include/ct/optcon/solver/lqp/LQOCSolver.hpp +++ b/ct_optcon/include/ct/optcon/solver/lqp/LQOCSolver.hpp @@ -18,20 +18,22 @@ namespace optcon { * * \todo uncouple from NLOptConSettings */ -template +template class LQOCSolver { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW - typedef LQOCProblem LQOCProblem_t; + static constexpr size_t STATE_DIM = MANIFOLD::TangentDim; + using SCALAR = typename MANIFOLD::Scalar; + + typedef LQOCProblem LQOCProblem_t; /*! * Constructor. Initialize by handing over an LQOCProblem, or otherwise by calling setProblem() * @param lqocProblem shared_ptr to the LQOCProblem to be solved. */ LQOCSolver(const std::shared_ptr& lqocProblem = nullptr) : lqocProblem_(lqocProblem) {} - virtual ~LQOCSolver() = default; /*! @@ -49,20 +51,20 @@ class LQOCSolver //! setup and configure the box constraints // return true if configuration changed, otherwise false - virtual bool configureInputBoxConstraints(std::shared_ptr> lqocProblem) + virtual bool configureInputBoxConstraints(std::shared_ptr lqocProblem) { throw std::runtime_error("input box constraints are not available for this solver."); } // return true if configuration changed, otherwise false - virtual bool configureStateBoxConstraints(std::shared_ptr> lqocProblem) + virtual bool configureStateBoxConstraints(std::shared_ptr lqocProblem) { throw std::runtime_error("state box constraints are not available for this solver."); } //! setup and configure the general (in)equality constraints // return true if configuration changed, otherwise false - virtual bool configureGeneralConstraints(std::shared_ptr> lqocProblem) + virtual bool configureGeneralConstraints(std::shared_ptr lqocProblem) { throw std::runtime_error("general constraints are not available for this solver."); } @@ -81,19 +83,16 @@ class LQOCSolver //! extract the solution (can be overriden if additional extraction steps required in specific solver) virtual void computeStatesAndControls() = 0; //! return solution for state - const ct::core::StateVectorArray& getSolutionState() { return x_sol_; } + const core::DiscreteArray& getSolutionState() { return x_sol_; } //! return solution for control const ct::core::ControlVectorArray& getSolutionControl() { return u_sol_; } - //! return TVLQR feedback matrices virtual void computeFeedbackMatrices() = 0; const ct::core::FeedbackArray& getSolutionFeedback() { return L_; } - //! compute iLQR-style lv virtual void compute_lv() = 0; //! return iLQR-style feedforward lv virtual const ct::core::ControlVectorArray& get_lv() { return lv_; } - //! return the smallest eigenvalue virtual SCALAR getSmallestEigenvalue() { @@ -106,7 +105,7 @@ class LQOCSolver std::shared_ptr lqocProblem_; - core::StateVectorArray x_sol_; // solution in x + core::DiscreteArray x_sol_; // solution in x core::ControlVectorArray u_sol_; // solution in u ct::core::FeedbackArray L_; // solution feedback ct::core::ControlVectorArray lv_; // feedforward increment (iLQR-style) diff --git a/ct_optcon/test/CMakeLists.txt b/ct_optcon/test/CMakeLists.txt index 44ae14c93..49c1c189a 100644 --- a/ct_optcon/test/CMakeLists.txt +++ b/ct_optcon/test/CMakeLists.txt @@ -45,14 +45,19 @@ if(BUILD_TESTS) message(STATUS "ct_optcon: building unit tests requiring CPPADCG") # package_add_test(constraint_comparison constraint/ConstraintComparison.cpp) # package_add_test(constraint_test constraint/ConstraintTest.cpp) - package_add_test(CostFunctionTests costfunction/CostFunctionTests.cpp) - package_add_test(LoadFromFileTest costfunction/LoadFromFileTest.cpp) + # package_add_test(CostFunctionTests costfunction/CostFunctionTests.cpp) + # package_add_test(LoadFromFileTest costfunction/LoadFromFileTest.cpp) endif() #package_add_test(dms_test dms/oscillator/oscDMSTest.cpp) #package_add_test(dms_test_all_var dms/oscillator/oscDMSTestAllVariants.cpp) - package_add_test(system_interface_test system_interface/SystemInterfaceTest.cpp) + #package_add_test(system_interface_test system_interface/SystemInterfaceTest.cpp) + add_executable(ManifoldLQOCTest solver/linear/ManifoldLQOCTest.cpp) + target_link_libraries(ManifoldLQOCTest ct_optcon) + set_target_properties(ManifoldLQOCTest PROPERTIES FOLDER test) + list(APPEND UNIT_TEST_TARGETS ManifoldLQOCTest) + if(HPIPM) message(STATUS "ct_optcon: building unit tests requiring HPIPM") ## some legacy executables (TODO: make example or make test) diff --git a/ct_optcon/test/solver/linear/HPIPMInterfaceTest.h b/ct_optcon/test/solver/linear/HPIPMInterfaceTest.h index 685d5a746..a98b4d5e3 100644 --- a/ct_optcon/test/solver/linear/HPIPMInterfaceTest.h +++ b/ct_optcon/test/solver/linear/HPIPMInterfaceTest.h @@ -17,11 +17,12 @@ TEST(HPIPMInterfaceTest, compareSolvers) { const size_t state_dim = 8; const size_t control_dim = 3; + using State = ct::core::EuclideanState; int N = 5; double dt = 0.5; - typedef ct::optcon::LQOCProblem LQOCProblem_t; + typedef ct::optcon::LQOCProblem LQOCProblem_t; std::shared_ptr lqocProblem_hpipm(new LQOCProblem_t(N)); std::shared_ptr lqocProblem_gnriccati(new LQOCProblem_t(N)); @@ -46,12 +47,11 @@ TEST(HPIPMInterfaceTest, compareSolvers) R *= 2 * 2.0; // create a cost function - ct::optcon::CostFunctionQuadraticSimple costFunction( - Q, R, -stateOffset, u0, -stateOffset, Q); + ct::optcon::CostFunctionQuadraticSimple costFunction(Q, R, -stateOffset, u0, -stateOffset, Q); // create a continuous-time example system and discretize it - std::shared_ptr> system(new LinkedMasses()); - ct::core::SensitivityApproximation discretizedSystem( + std::shared_ptr> system(new LinkedMasses()); + ct::core::SensitivityApproximation discretizedSystem( dt, system, ct::optcon::NLOptConSettings::APPROXIMATION::MATRIX_EXPONENTIAL); // initialize the linear quadratic optimal control problems @@ -60,7 +60,7 @@ TEST(HPIPMInterfaceTest, compareSolvers) // create hpipm solver instance, set and solve problem - ct::optcon::HPIPMInterface hpipm; + ct::optcon::HPIPMInterface hpipm; hpipm.setProblem(lqocProblem_hpipm); hpipm.solve(); hpipm.computeStatesAndControls(); @@ -68,7 +68,7 @@ TEST(HPIPMInterfaceTest, compareSolvers) hpipm.compute_lv(); // create GNRiccati solver instance, set and solve problem - ct::optcon::GNRiccatiSolver gnriccati; + ct::optcon::GNRiccatiSolver gnriccati; gnriccati.setProblem(lqocProblem_gnriccati); gnriccati.solve(); gnriccati.computeStatesAndControls(); @@ -76,8 +76,8 @@ TEST(HPIPMInterfaceTest, compareSolvers) gnriccati.compute_lv(); // compute and retrieve solutions - ct::core::StateVectorArray x_sol_hpipm = hpipm.getSolutionState(); - ct::core::StateVectorArray x_sol_gnrccati = gnriccati.getSolutionState(); + ct::core::DiscreteArray x_sol_hpipm = hpipm.getSolutionState(); + ct::core::DiscreteArray x_sol_gnrccati = gnriccati.getSolutionState(); ct::core::ControlVectorArray u_sol_hpipm = hpipm.getSolutionControl(); ct::core::ControlVectorArray u_sol_gnrccati = gnriccati.getSolutionControl(); ct::core::FeedbackArray K_sol_hpipm = hpipm.getSolutionFeedback(); diff --git a/ct_optcon/test/solver/linear/LQOCSolverTest.h b/ct_optcon/test/solver/linear/LQOCSolverTest.h index 57d204d8f..d97f89231 100644 --- a/ct_optcon/test/solver/linear/LQOCSolverTest.h +++ b/ct_optcon/test/solver/linear/LQOCSolverTest.h @@ -17,45 +17,46 @@ TEST(LQOCSolverTest, compareHPIPMandRiccati) const size_t N = 5; const double dt = 0.5; + using State = core::EuclideanState; + bool verbose = false; // optional verbose output // create instances of HPIPM and an unconstrained Gauss-Newton Riccati solver - std::shared_ptr> hpipmSolver(new HPIPMInterface); - std::shared_ptr> gnRiccatiSolver(new GNRiccatiSolver); + std::shared_ptr> hpipmSolver(new HPIPMInterface); + std::shared_ptr> gnRiccatiSolver(new GNRiccatiSolver); // store them, and identifying names, in a vectors - std::vector>> lqocSolvers; + std::vector>> lqocSolvers; lqocSolvers.push_back(gnRiccatiSolver); lqocSolvers.push_back(hpipmSolver); std::vector solverNames = {"Riccati", "HPIPM"}; // create linear-quadratic optimal control problem containers - std::vector>> problems; - std::shared_ptr> lqocProblem1(new LQOCProblem(N)); - std::shared_ptr> lqocProblem2(new LQOCProblem(N)); + std::vector>> problems; + std::shared_ptr> lqocProblem1(new LQOCProblem(N)); + std::shared_ptr> lqocProblem2(new LQOCProblem(N)); problems.push_back(lqocProblem1); problems.push_back(lqocProblem2); // create a continuous-time example system and discretize it - std::shared_ptr> exampleSystem(new example::SpringLoadedMassLinear()); - core::SensitivityApproximation discreteExampleSystem( + std::shared_ptr> exampleSystem( + new example::SpringLoadedMassLinear()); + core::SensitivityApproximation discreteExampleSystem( dt, exampleSystem, core::SensitivityApproximationSettings::APPROXIMATION::MATRIX_EXPONENTIAL); // nominal control ct::core::ControlVector u0; u0.setZero(); // by definition - // initial state - ct::core::StateVector x0; + // initial state and desired final state + State x0, xf; x0.setZero(); // by definition - // desired final state - ct::core::StateVector xf; xf << -1, 0; // create pointer to a cost function auto costFunction = example::createSpringLoadedMassCostFunction(xf); - ct::core::StateVector b; + State b; b << 0.1, 0.1; // initialize the optimal control problems for both solvers diff --git a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp index 01918bc24..7efcd6535 100644 --- a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp +++ b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp @@ -1,5 +1,7 @@ #include +#include +#include using namespace ct::core; using namespace ct::optcon; @@ -11,25 +13,27 @@ using ManifoldState_t = ManifoldState; const size_t state_dim = ManifoldState_t::TangentDim; const size_t control_dim = 3; -class DiscrSO3LTITestSystem final : public LTISystem +class DiscrSO3LTITestSystem final : public ct::core::ControlledSystem { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW - DiscrSO3LTITestSystem(const Eigen::Matrix A_test, - const Eigen::Matrix B_test, - const ManifoldState_t& m_ref = ManifoldState_t::NeutralElement()) - : m_ref_(m_ref) + DiscrSO3LTITestSystem() {} + virtual void computeControlledDynamics(const ManifoldState_t& m, + const Time_t& n, + const ct::core::ControlVector& u, + ManifoldState_t::Tangent& dx) override { - this->A() << A_test; - this->B() << B_test; + dx = u; } + virtual DiscrSO3LTITestSystem* clone() const override { return new DiscrSO3LTITestSystem(); } /** * @brief the log operator is defined as expressing the tangent vector w.r.t. m_ref_ */ virtual ManifoldState_t::Tangent lift(const ManifoldState_t& m) override { + //throw std::runtime_error("not impl"); return ManifoldState_t::Tangent::Zero(); // the system stays where it is (combination with A = identity()) /*return m.rminus(m);*/ } @@ -37,29 +41,66 @@ class DiscrSO3LTITestSystem final : public LTISystem(0, 0, 0); + Eigen::Matrix3d b; + b.setIdentity(); // some basis, assumed to be expressed in the manifolds over which we iterate + + for (double theta = M_PI / 2; theta <= 2 * M_PI; theta += M_PI / 2) + { + std::cout << "case theta = " << theta << std::endl; + auto m = manif::SE2(0, 0, theta); + Eigen::Matrix3d Jl, Jr; // jacobians + auto d_rminus = m.rminus(origin, Jl, Jr); + auto adj_rminus = d_rminus.exp().adj(); // the adjoint for transportation to the origin + + auto d_between = m.between(origin); + auto adj_between = d_rminus.exp().adj(); + + // ASSERT_TRUE(adj_rminus.isApprox(adj_between)); + + std::cout << adj_rminus * b << std::endl << std::endl; // express basis b in origin-manifold + std::cout << adj_between * b << std::endl << std::endl; // express basis b in origin-manifold + // std::cout << Jr * Jl * b << std::endl << std::endl; // TODO: how does the Jac relate to the adjoint here? + std::cout << std::endl; + } + + // auto m_pi_2 = manif::SE2(0, 0, M_PI / 2); + // auto m_pi = manif::SE2(0, 0, M_PI); + // auto m_mpi_2 = manif::SE2(0, 0, -M_PI / 2); + // + // + // std::cout << m_0.adj() * b << std::endl << std::endl; + // std::cout << m_pi_2.adj() * b << std::endl << std::endl; + // std::cout << m_pi.adj() * b << std::endl << std::endl; + // std::cout << m_mpi_2.adj() * b << std::endl << std::endl; +} + int main(int argc, char** argv) { std::cout << std::fixed; - const size_t N = 5; - const double dt = 0.25; + const size_t N = 20*3; + const double dt = 0.125 / 3.0; - const ManifoldState_t x0 = manif::SO3(0, 0, 0); + const ManifoldState_t x0 = manif::SO3(M_PI, 0, M_PI); ct::core::DiscreteArray x_traj(N + 1, x0); // init state trajectory, will be overwritten - ct::core::DiscreteArray> u_traj_init(N); // init control traj + ct::core::DiscreteArray> u_traj(N); // init control traj for (size_t i = 0; i < N; i++) - u_traj_init[i] = ct::core::ControlVector::Zero() * dt; + u_traj[i] = ct::core::ControlVector::Ones() * dt; // create instances of HPIPM and an unconstrained Gauss-Newton Riccati solver std::shared_ptr> hpipmSolver( new HPIPMInterface); std::shared_ptr> gnRiccatiSolver( - new GNRiccatiSolver); + new AugGNRiccatiSolver); // store them, and identifying names, in a vectors std::vector>> lqocSolvers; @@ -78,22 +119,20 @@ int main(int argc, char** argv) problems.push_back(lqocProblem2); // create a discrete-time manifold system - Eigen::Matrix A = Eigen::Matrix::Zero(); - Eigen::Matrix B; - A << 1.0, 0, 0, 0, 1.0, 0, 0, 0, 1.0; // a slightly unstable system - B << 1, 0, 0, 0, 1, 0, 0, 0, 1; // direct action - std::shared_ptr> exampleSystem( - new DiscrSO3LTITestSystem(A, B, x0)); + std::shared_ptr> exampleSystem( + new DiscrSO3LTITestSystem()); + std::shared_ptr> linearizer( + new ct::core::SystemLinearizer(exampleSystem)); // create a cost function Eigen::Matrix Q, Q_final; Eigen::Matrix R; - Q_final << 100, 0, 0, 0, 100, 0, 0, 0, 100; + Q_final << 10000, 0, 0, 0, 10000, 0, 0, 0, 10000; //Q << 1, 0, 0, 0, 1, 0, 0, 0, 1; Q.setZero(); R << 1, 0, 0, 0, 1, 0, 0, 0, 1; - ManifoldState_t x_final = manif::SO3(M_PI / 2, 0, 0); + ManifoldState_t x_final = manif::SO3(0, 0, 0); std::cout << "desired final state: " << x_final << std::endl; ManifoldState_t x_nominal = x0; ct::core::ControlVector u_nom = ct::core::ControlVector::Zero(); @@ -101,7 +140,7 @@ int main(int argc, char** argv) new CostFunctionQuadraticSimple(Q, R, x_nominal, u_nom, x_final, Q_final)); ManifoldState_t::Tangent b; - b.setZero(); // todo why this? + b.setZero(); // TODO why this? // integrate an initial state with the open-loop system to get initial trajectories ManifoldState_t x_curr; @@ -109,121 +148,125 @@ int main(int argc, char** argv) x_curr = x0; x_traj.front() = x0; std::cout << "integrate an random initial state with the unstable system" << std::endl; - std::cout << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; + std::cout << std::setprecision(4) << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; for (size_t i = 0; i < N; i++) { - exampleSystem->computeControlledDynamics(x_curr, 0, u_traj_init[i], dx); + exampleSystem->computeControlledDynamics(x_curr, 0, u_traj[i], dx); x_curr = x_curr + dx; x_traj[i + 1] = x_curr; - std::cout << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; + std::cout << std::setprecision(4) << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; } - - // initialize the optimal control problems for both solvers - problems[0]->setFromTimeInvariantLinearQuadraticProblem(x_traj, u_traj_init, *exampleSystem, *costFunction, b, dt); - problems[1]->setFromTimeInvariantLinearQuadraticProblem(x_traj, u_traj_init, *exampleSystem, *costFunction, b, dt); + size_t nIter = 6; + for (size_t iter = 0; iter < nIter; iter++) + { + // initialize the optimal control problems for both solvers + problems[0]->setFromTimeInvariantLinearQuadraticProblem(x_traj, u_traj, *linearizer, *costFunction, b, dt); + problems[1]->setFromTimeInvariantLinearQuadraticProblem(x_traj, u_traj, *linearizer, *costFunction, b, dt); - // HACKY corrections // TODO: move somewhere meaningful - for (size_t idx : {0, 1}) // for both solvers - { - // intermediate stages cost transportation - for (size_t i = 0; i < N; i++) + // HACKY corrections // TODO: move somewhere meaningful + for (size_t idx : {0}) // for riccati solver only { - Eigen::Matrix Jl, Jr; - auto e = x_nominal.rminus(x_traj[i], Jl, Jr); // compute PT matrix w.r.t. current ref traj + // intermediate stages cost transportation + for (size_t i = 0; i < N; i++) + { + auto e = x_nominal.rminus(x_traj[i]); + // compute PT matrix w.r.t. current ref traj // TODO: clarifiy formulation of error in cost function + auto e_adj = (e.exp()).adj(); + problems[idx]->Q_[i] = e_adj * problems[idx]->Q_[i] * e_adj.transpose(); + problems[idx]->qv_[i] = e_adj * problems[idx]->qv_[i]; + } + // terminal stage transportation + // TODO: clarifiy formulation of error in cost function + auto e = x_final.rminus(x_traj[N]); // compute PT matrix w.r.t. current ref traj auto e_adj = (e.exp()).adj(); - problems[idx]->Q_[i] = e_adj.transpose() * problems[idx]->Q_[i] * e_adj; - problems[idx]->qv_[i] = e_adj.transpose() * problems[idx]->qv_[i]; - } - // terminal stage transportation - Eigen::Matrix Jl, Jr; - auto e = x_final.rminus(x_traj[N], Jl, Jr); // compute PT matrix w.r.t. current ref traj - auto e_adj = (e.exp()).adj(); - std::cout << "cost adj" << std::endl << e_adj << std::endl; - std::cout << "cost Jr" << std::endl << Jr << std::endl; - problems[idx]->Q_.back() = e_adj.transpose() * problems[idx]->Q_.back() * e_adj; - problems[idx]->qv_.back() = e_adj.transpose() * problems[idx]->qv_.back(); - - // dynamics transportation - for (size_t i = 0; i < N; i++) - { - // std::cout << "dyn transport matrices" << std::endl; - Eigen::Matrix Jl, Jr; - - auto l = x_traj[i + 1].rminus(x_traj[i], Jl, Jr); // TODO: is it the right way round? - auto l_adj = (l.exp()).adj(); - //std::cout << "Jl" << std::endl << Jl << std::endl; - //std::cout << "Jr" << std::endl << Jr << std::endl; - //std::cout << "l_adj" << std::endl << l_adj << std::endl; - //std::cout << std::endl; - problems[idx]->A_[i] = l_adj.transpose() * problems[idx]->A_[i]; // todo: are those the right - problems[idx]->B_[i] = l_adj.transpose() * problems[idx]->B_[i]; // TODO: are those the right + //std::cout << "cost adj" << std::endl << e_adj << std::endl; + problems[idx]->Q_.back() = /*e_adj * */ problems[idx]->Q_.back(); // * e_adj.transpose(); + problems[idx]->qv_.back() = /*e_adj * */ problems[idx]->qv_.back(); + + // dynamics transportation + for (size_t i = 0; i < N; i++) + { + // std::cout << "dyn transport matrices" << std::endl; + Eigen::Matrix3d Jl, Jr; + auto l = x_traj[i + 1].rminus(x_traj[i], Jl, Jr); // TODO: is it the right way round? + auto l_adj = (l.exp()).adj(); + //std::cout << "l_adj" << std::endl << l_adj << std::endl; + //std::cout << std::endl; + // problems[idx]->A_[i] = l_adj.transpose() * problems[idx]->A_[i]; // todo: are those the right + // problems[idx]->B_[i] = l_adj.transpose() * problems[idx]->B_[i]; // TODO: are those the right + problems[idx]->Acal_[i + 1] = l_adj; + } + + + // set the problem pointers + lqocSolvers[idx]->setProblem(problems[idx]); + + // allocate memory (if required) + lqocSolvers[idx]->initializeAndAllocate(); + + // solve the problems... + lqocSolvers[idx]->solve(); + + // postprocess data + lqocSolvers[idx]->computeStatesAndControls(); + lqocSolvers[idx]->computeFeedbackMatrices(); + lqocSolvers[idx]->compute_lv(); } - - // set the problem pointers - lqocSolvers[idx]->setProblem(problems[idx]); - - // allocate memory (if required) - lqocSolvers[idx]->initializeAndAllocate(); - - // solve the problems... - lqocSolvers[idx]->solve(); - - // postprocess data - lqocSolvers[idx]->computeStatesAndControls(); - lqocSolvers[idx]->computeFeedbackMatrices(); - lqocSolvers[idx]->compute_lv(); - } - - // retrieve solutions from both solvers - auto xSol_riccati = lqocSolvers[0]->getSolutionState(); - auto uSol_riccati = lqocSolvers[0]->getSolutionControl(); - ct::core::FeedbackArray KSol_riccati = lqocSolvers[0]->getSolutionFeedback(); - ct::core::ControlVectorArray lv_sol_riccati = lqocSolvers[0]->get_lv(); - auto xSol_hpipm = lqocSolvers[1]->getSolutionState(); - auto uSol_hpipm = lqocSolvers[1]->getSolutionControl(); - ct::core::FeedbackArray KSol_hpipm = lqocSolvers[1]->getSolutionFeedback(); - ct::core::ControlVectorArray lv_sol_hpipm = lqocSolvers[1]->get_lv(); - - std::cout << std::endl << std::endl; - std::cout << std::setprecision(3) << "Forward integrated closed-loop solution:" << std::endl; - x_curr = x0; - std::cout << "m init: " << x_curr << std::endl; - for (size_t i = 0; i < N; i++) - { - Eigen::Matrix j1, j2; - auto x_err = x_curr.rminus(x_traj[i], j1, j2); - // std::cout << "lv_sol_riccati[i]: " << lv_sol_riccati[i].transpose() << std::endl; - // std::cout << "uSol_riccati[i]: " << uSol_riccati[i].transpose() << std::endl; - // std::cout << "KSol_riccati: " << std::endl << KSol_riccati[i] << std::endl; - // TODO: some term is missing here; - auto u = u_traj_init[i] + lv_sol_riccati[i] + KSol_riccati[i] * (x_err /*- eucl. part here*/); - exampleSystem->computeControlledDynamics(x_curr, i * dt, u, dx); - Eigen::Quaterniond old_rot(x_curr.w(), x_curr.x(), x_curr.y(), x_curr.z()); - x_curr = x_curr + dx; - Eigen::Quaterniond new_rot(x_curr.w(), x_curr.x(), x_curr.y(), x_curr.z()); - std::cout << "m: " << x_curr << "\t -- rot diff norm(): " << old_rot.angularDistance(new_rot) << std::endl; - } - std::cout << std::endl << std::endl; - - std::cout << std::setprecision(3) << "dx solution from riccati solver and directly added state_traj" << std::endl; - ct::core::DiscreteArray x_solver_direct(N + 1); - for (size_t j = 0; j < xSol_riccati.size(); j++) - { - x_solver_direct[j] = x_traj[j] + xSol_riccati[j]; - double angularDiff = 0; - if (j > 0) + // retrieve solutions from both solvers + auto xSol_riccati = lqocSolvers[0]->getSolutionState(); + auto uSol_riccati = lqocSolvers[0]->getSolutionControl(); + ct::core::FeedbackArray KSol_riccati = lqocSolvers[0]->getSolutionFeedback(); + ct::core::ControlVectorArray lv_sol_riccati = lqocSolvers[0]->get_lv(); + auto xSol_hpipm = lqocSolvers[1]->getSolutionState(); + auto uSol_hpipm = lqocSolvers[1]->getSolutionControl(); + ct::core::FeedbackArray KSol_hpipm = lqocSolvers[1]->getSolutionFeedback(); + ct::core::ControlVectorArray lv_sol_hpipm = lqocSolvers[1]->get_lv(); + + std::cout << std::endl << std::endl; + //std::cout << std::setprecision(4) << "dx solution from riccati solver and directly added state_traj, iter " + // << iter << std::endl; + //ct::core::DiscreteArray x_solver_direct(N + 1); + //for (size_t j = 0; j < xSol_riccati.size(); j++) + //{ + // x_solver_direct[j] = x_traj[j] + xSol_riccati[j]; + // double angularDiff = 0; + // if (j > 0) + // { + // Eigen::Quaterniond old_rot(x_solver_direct[j - 1].w(), x_solver_direct[j - 1].x(), + // x_solver_direct[j - 1].y(), x_solver_direct[j - 1].z()); + // Eigen::Quaterniond new_rot( + // x_solver_direct[j].w(), x_solver_direct[j].x(), x_solver_direct[j].y(), x_solver_direct[j].z()); + // angularDiff = old_rot.angularDistance(new_rot); + // } + // std::cout << "m:" << x_solver_direct[j] << "\t dx:" << xSol_riccati[j].transpose() + // << "\t -- rot diff norm(): " << angularDiff << std::endl; + //} + + std::cout << std::setprecision(4) << "Forward integrated closed-loop solution, iter :" << iter << std::endl; + x_curr = x0; + std::cout << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; + ct::core::DiscreteArray x_traj_prev = x_traj; + for (size_t i = 0; i < N; i++) { - Eigen::Quaterniond old_rot(x_solver_direct[j - 1].w(), x_solver_direct[j - 1].x(), - x_solver_direct[j - 1].y(), x_solver_direct[j - 1].z()); - Eigen::Quaterniond new_rot( - x_solver_direct[j].w(), x_solver_direct[j].x(), x_solver_direct[j].y(), x_solver_direct[j].z()); - angularDiff = old_rot.angularDistance(new_rot); + ManifoldState_t::Tangent x_err = x_curr.rminus(x_traj_prev[i]); + // TODO: some term is missing here; + const ct::core::ControlVector u = + u_traj[i] + lv_sol_riccati[i] + KSol_riccati[i] * (x_err /*- eucl. part here*/); + u_traj[i] = u; + + exampleSystem->computeControlledDynamics(x_curr, i * dt, u, dx); + Eigen::Quaterniond old_rot(x_curr.w(), x_curr.x(), x_curr.y(), x_curr.z()); + x_curr = x_curr + dx; + Eigen::Quaterniond new_rot(x_curr.w(), x_curr.x(), x_curr.y(), x_curr.z()); + std::cout << "m: " << x_curr << "\t tan: " << x_curr.log() + << "\t -- rot diff norm(): " << old_rot.angularDistance(new_rot) << std::endl; + + x_traj[i + 1] = x_curr; } - std::cout << "\t m:" << x_solver_direct[j] << "\t dx:" << xSol_riccati[j].transpose() - << "\t -- rot diff norm(): " << angularDiff << std::endl; - } + std::cout << std::endl << std::endl; + } // end iter return 1; } \ No newline at end of file diff --git a/ct_optcon/test/testSystems/LinkedMasses.h b/ct_optcon/test/testSystems/LinkedMasses.h index 452c0bd73..ee366ebb1 100644 --- a/ct_optcon/test/testSystems/LinkedMasses.h +++ b/ct_optcon/test/testSystems/LinkedMasses.h @@ -26,7 +26,7 @@ void dmcopy(int row, int col, double* A, int lda, double* B, int ldb) } } -class LinkedMasses : public LinearSystem<8, 3> +class LinkedMasses : public LinearSystem, 3, CONTINUOUS_TIME> { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW @@ -35,6 +35,8 @@ class LinkedMasses : public LinearSystem<8, 3> static const int control_dim = 3; static const int pp = state_dim / 2; // number of masses + using State = EuclideanState; + LinkedMasses() { A_.setZero(); @@ -76,14 +78,14 @@ class LinkedMasses : public LinearSystem<8, 3> } - const state_matrix_t& getDerivativeState(const StateVector& x, + const state_matrix_t& getDerivativeState(const State& x, const ControlVector& u, const double t = 0.0) override { return A_; } - const state_control_matrix_t& getDerivativeControl(const StateVector& x, + const state_control_matrix_t& getDerivativeControl(const State& x, const ControlVector& u, const double t = 0.0) override { @@ -97,11 +99,12 @@ class LinkedMasses : public LinearSystem<8, 3> }; -class LinkedMasses2 : public ControlledSystem<8, 3> +class LinkedMasses2 : public ControlledSystem, 3, CONTINUOUS_TIME> { public: static const int state_dim = 8; static const int control_dim = 3; + using State = EuclideanState; EIGEN_MAKE_ALIGNED_OPERATOR_NEW @@ -150,10 +153,10 @@ class LinkedMasses2 : public ControlledSystem<8, 3> } LinkedMasses2* clone() const override { return new LinkedMasses2(); }; - void computeControlledDynamics(const ct::core::StateVector& state, + void computeControlledDynamics(const State& state, const double& t, const ct::core::ControlVector& control, - ct::core::StateVector& derivative) override + State::Tangent& derivative) override { derivative = A_ * state + B_ * control + b_; } diff --git a/ct_optcon/test/testSystems/SpringLoadedMass.h b/ct_optcon/test/testSystems/SpringLoadedMass.h index b85a8a41e..3c9335325 100644 --- a/ct_optcon/test/testSystems/SpringLoadedMass.h +++ b/ct_optcon/test/testSystems/SpringLoadedMass.h @@ -11,17 +11,23 @@ namespace example { //! Dynamics class for the GNMS unit test -class SpringLoadedMass : public core::ControlledSystem<2, 1> +class SpringLoadedMass : public core::ControlledSystem, 1, core::CONTINUOUS_TIME> { public: static const size_t state_dim = 2; // position, velocity static const size_t control_dim = 1; // force - SpringLoadedMass() : core::ControlledSystem(core::SYSTEM_TYPE::SECOND_ORDER) {} - void computeControlledDynamics(const core::StateVector& state, - const core::Time& t, + SpringLoadedMass() + : core::ControlledSystem, control_dim, core::CONTINUOUS_TIME>( + core::SYSTEM_TYPE::SECOND_ORDER) + { + } + + // compute dynamics + void computeControlledDynamics(const core::EuclideanState& state, + const double& t, const core::ControlVector& control, - core::StateVector& derivative) override + core::EuclideanState::Tangent& derivative) override { derivative(0) = state(1); derivative(1) = control(0) - kStiffness * state(0) + 0.1; // mass is 1 kg @@ -32,7 +38,7 @@ class SpringLoadedMass : public core::ControlledSystem<2, 1> }; //! Linear system class for the GNMS unit test -class SpringLoadedMassLinear : public core::LinearSystem<2, 1> +class SpringLoadedMassLinear : public core::LinearSystem, 1, core::CONTINUOUS_TIME> { public: static const size_t state_dim = 2; // position, velocity @@ -44,7 +50,7 @@ class SpringLoadedMassLinear : public core::LinearSystem<2, 1> state_control_matrix_t B_; - const state_matrix_t& getDerivativeState(const core::StateVector& x, + const state_matrix_t& getDerivativeState(const core::EuclideanState& x, const core::ControlVector& u, const double t = 0.0) override { @@ -52,7 +58,7 @@ class SpringLoadedMassLinear : public core::LinearSystem<2, 1> return A_; } - const state_control_matrix_t& getDerivativeControl(const core::StateVector& x, + const state_control_matrix_t& getDerivativeControl(const core::EuclideanState& x, const core::ControlVector& u, const double t = 0.0) override { @@ -64,7 +70,8 @@ class SpringLoadedMassLinear : public core::LinearSystem<2, 1> }; -std::shared_ptr> createSpringLoadedMassCostFunction(const core::StateVector<2>& x_final) +std::shared_ptr, 1>> createSpringLoadedMassCostFunction( + const core::EuclideanState<2>& x_final) { Eigen::Matrix Q; Q << 1.0, 0, 0, 1.0; @@ -79,11 +86,12 @@ std::shared_ptr> createSpringLoadedMassCostFunction( Eigen::Matrix Q_final; Q_final << 10.0, 0, 0, 10.0; - std::shared_ptr> quadraticCostFunction( - new CostFunctionQuadraticSimple<2, 1>(Q, R, x_nominal, u_nominal, x_final, Q_final)); + std::shared_ptr, 1>> quadraticCostFunction( + new CostFunctionQuadraticSimple, 1>(Q, R, x_nominal, u_nominal, x_final, Q_final)); return quadraticCostFunction; } -} -} -} + +} // namespace example +} // namespace optcon +} // namespace ct From 2f7a11684fc07c1bb322938adb5c7d381d290fe2 Mon Sep 17 00:00:00 2001 From: mgiftthaler Date: Sun, 26 Apr 2020 22:02:06 +0200 Subject: [PATCH 03/21] working ex --- .../ct/optcon/problem/LQOCProblem-impl.hpp | 15 +- .../include/ct/optcon/problem/LQOCProblem.hpp | 9 +- .../solver/lqp/AugGNRiccatiSolver-impl.hpp | 58 +++++--- .../optcon/solver/lqp/AugGNRiccatiSolver.hpp | 6 +- .../solver/lqp/GNRiccatiSolver-impl.hpp | 5 +- .../ct/optcon/solver/lqp/GNRiccatiSolver.hpp | 1 + .../test/solver/linear/ManifoldLQOCTest.cpp | 140 +++++++++++++----- 7 files changed, 162 insertions(+), 72 deletions(-) diff --git a/ct_optcon/include/ct/optcon/problem/LQOCProblem-impl.hpp b/ct_optcon/include/ct/optcon/problem/LQOCProblem-impl.hpp index c9bb43b73..1178bfde2 100644 --- a/ct_optcon/include/ct/optcon/problem/LQOCProblem-impl.hpp +++ b/ct_optcon/include/ct/optcon/problem/LQOCProblem-impl.hpp @@ -70,7 +70,7 @@ void LQOCProblem::changeNumStages(int N) q_.resize(N + 1); qv_.resize(N + 1); Q_.resize(N + 1); - Acal_.resize(N+1); + Adj_x_.resize(N + 1); rv_.resize(N); R_.resize(N); @@ -102,7 +102,7 @@ void LQOCProblem::setZero(const int& nGenConstr) P_.setConstant(core::FeedbackMatrix::Zero()); qv_.setConstant(core::StateVector::Zero()); Q_.setConstant(core::StateMatrix::Zero()); - Acal_.setConstant(core::StateMatrix::Identity()); + Adj_x_.setConstant(core::StateMatrix::Identity()); rv_.setConstant(core::ControlVector::Zero()); R_.setConstant(core::ControlMatrix::Zero()); q_.setConstant((SCALAR)0.0); @@ -277,7 +277,7 @@ void LQOCProblem::setFromTimeInvariantLinearQuadraticProb const core::ControlVector& u0, ct::core::LinearSystem& linearSystem, ct::optcon::CostFunctionQuadratic& costFunction, - const typename MANIFOLD::Tangent& stateOffset, + const typename MANIFOLD::Tangent& b, const double dt) { setZero(); @@ -288,7 +288,7 @@ void LQOCProblem::setFromTimeInvariantLinearQuadraticProb A_ = core::StateMatrixArray(K_, A); B_ = core::StateControlMatrixArray(K_, B); - b_ = core::DiscreteArray(K_ + 1, stateOffset); + b_ = core::DiscreteArray(K_ + 1, b); // feed current state and control to cost function costFunction.setCurrentStateAndControl(x0, u0, 0); @@ -314,18 +314,21 @@ void LQOCProblem::setFromTimeInvariantLinearQuadraticProb const ct::core::DiscreteArray>& u_traj, ct::core::LinearSystem& linearSystem, ct::optcon::CostFunctionQuadratic& costFunction, - const typename MANIFOLD::Tangent& stateOffset, + const ct::core::DiscreteArray& b, const double dt) { setZero(); + if (b.size() != K_ + 1) + throw std::runtime_error("b size not correct."); + // get LTI dynamics core::StateMatrix A; core::StateControlMatrix B; linearSystem.getDerivatives(A, B, x_traj.front(), u_traj.front(), 0); A_ = core::StateMatrixArray(K_, A); B_ = core::StateControlMatrixArray(K_, B); - b_ = core::DiscreteArray(K_ + 1, stateOffset); + b_ = b; // eval costs around the reference trajectory for (int i = 0; i < K_; i++) diff --git a/ct_optcon/include/ct/optcon/problem/LQOCProblem.hpp b/ct_optcon/include/ct/optcon/problem/LQOCProblem.hpp index df5407d33..ecfa757c3 100644 --- a/ct_optcon/include/ct/optcon/problem/LQOCProblem.hpp +++ b/ct_optcon/include/ct/optcon/problem/LQOCProblem.hpp @@ -204,7 +204,7 @@ class LQOCProblem const ct::core::DiscreteArray>& u_traj, ct::core::LinearSystem& linearSystem, ct::optcon::CostFunctionQuadratic& costFunction, - const typename MANIFOLD::Tangent& stateOffset, + const ct::core::DiscreteArray& stateOffset, const double dt); //! return a flag indicating whether this LQOC Problem is constrained or not @@ -225,7 +225,12 @@ class LQOCProblem //! LQ approximation of the pure state penalty, including terminal state penalty ct::core::DiscreteArray qv_; ct::core::StateMatrixArray Q_; - ct::core::StateMatrixArray Acal_; // TODO: temp + /** + * @brief The adjoint array for parallel transporting to the preceding tangent space + * Adj_x_[k] holds the adjoint used for parallel transporting vectors in the tangent space at k to the tangent + * space at [k-1]. + */ + ct::core::StateMatrixArray Adj_x_; //! LQ approximation of the pure control penalty ct::core::ControlVectorArray rv_; diff --git a/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver-impl.hpp b/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver-impl.hpp index 39591d47b..f6d3ca33d 100644 --- a/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver-impl.hpp +++ b/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver-impl.hpp @@ -32,15 +32,15 @@ void AugGNRiccatiSolver::solve() } template -void AugGNRiccatiSolver::solveSingleStage(int N) +void AugGNRiccatiSolver::solveSingleStage(int n) { - if (N == this->lqocProblem_->getNumberOfStages() - 1) + if (n == this->lqocProblem_->getNumberOfStages() - 1) initializeCostToGo(); - designController(N); + designController(n); - if (N > 0) - computeCostToGo(N); + if (n > 0) + computeCostToGo(n); } template @@ -63,7 +63,7 @@ void AugGNRiccatiSolver::computeStatesAndControls() this->u_sol_[k] = this->lv_[k] + this->L_[k] * this->x_sol_[k]; //! state update rule in diff coordinates - this->x_sol_[k + 1] = p.A_[k] * this->x_sol_[k] + p.B_[k] * (this->u_sol_[k]) + p.b_[k]; // TODO: transport + this->x_sol_[k + 1] = p.A_[k] * this->x_sol_[k] + p.B_[k] * (this->u_sol_[k]) + p.b_[k]; } } @@ -120,8 +120,8 @@ void AugGNRiccatiSolver::changeNumberOfStages(int N) sv_.resize(N + 1); S_.resize(N + 1); - sv_tilda_.resize(N + 1); - S_tilda_.resize(N + 1); + sv_t_.resize(N + 1); + S_t_.resize(N + 1); N_ = N; } @@ -139,8 +139,16 @@ void AugGNRiccatiSolver::initializeCostToGo() S_[N] = p.Q_[N]; sv_[N] = p.qv_[N]; - S_tilda_[N] = p.Acal_[N] * S_[N] * p.Acal_[N].transpose(); - sv_tilda_[N] = p.Acal_[N] * sv_[N]; + if (ct::core::is_euclidean::value) // euclidean case + { + S_t_[N] = S_[N]; + sv_t_[N] = sv_[N]; + } + else + { // manifold case requires parallel transport of value function + S_t_[N] = p.Adj_x_[N] * S_[N] * p.Adj_x_[N].transpose(); + sv_t_[N] = p.Adj_x_[N] * sv_[N]; + } } template @@ -149,20 +157,28 @@ void AugGNRiccatiSolver::computeCostToGo(size_t k) LQOCProblem_t& p = *this->lqocProblem_; S_[k] = p.Q_[k]; - S_[k].noalias() += p.A_[k].transpose() * S_tilda_[k + 1] * p.A_[k]; + S_[k].noalias() += p.A_[k].transpose() * S_t_[k + 1] * p.A_[k]; S_[k].noalias() -= this->L_[k].transpose() * Hi_[k] * this->L_[k]; S_[k] = 0.5 * (S_[k] + S_[k].transpose()).eval(); sv_[k] = p.qv_[k]; - sv_[k].noalias() += p.A_[k].transpose() * sv_tilda_[k + 1]; - sv_[k].noalias() += p.A_[k].transpose() * S_tilda_[k + 1] * p.b_[k]; + sv_[k].noalias() += p.A_[k].transpose() * sv_t_[k + 1]; + sv_[k].noalias() += p.A_[k].transpose() * S_t_[k + 1] * p.b_[k]; sv_[k].noalias() += this->L_[k].transpose() * Hi_[k] * this->lv_[k]; sv_[k].noalias() += this->L_[k].transpose() * gv_[k]; sv_[k].noalias() += G_[k].transpose() * this->lv_[k]; - S_tilda_[k] = p.Acal_[k] * S_[k] * p.Acal_[k].transpose(); - sv_tilda_[k] = p.Acal_[k] * sv_[k]; + if (ct::core::is_euclidean::value) // euclidean case + { + S_t_[k] = S_[k]; + sv_t_[k] = sv_[k]; + } + else + { // manifold case requires parallel transport of value function to previous stage + S_t_[k] = p.Adj_x_[k] * S_[k] * p.Adj_x_[k].transpose(); + sv_t_[k] = p.Adj_x_[k] * sv_[k]; + } } template @@ -171,16 +187,16 @@ void AugGNRiccatiSolver::designController(size_t k) LQOCProblem_t& p = *this->lqocProblem_; gv_[k] = p.rv_[k]; - gv_[k].noalias() += p.B_[k].transpose() * sv_tilda_[k + 1]; - //gv_[k].noalias() += p.B_[k].transpose() * S_tilda_[k + 1].template selfadjointView() * p.b_[k]; + gv_[k].noalias() += p.B_[k].transpose() * sv_t_[k + 1]; + gv_[k].noalias() += p.B_[k].transpose() * S_t_[k + 1].template selfadjointView() * p.b_[k]; - G_[k] = p.P_[k]; + G_[k] = p.P_[k]; // TODO: G_ could be left away for the first stage (efficiency) //G_[k].noalias() += B_[k].transpose() * S_[k+1] * A_[k]; - G_[k].noalias() += p.B_[k].transpose() * S_tilda_[k + 1].template selfadjointView() * p.A_[k]; + G_[k].noalias() += p.B_[k].transpose() * S_t_[k + 1].template selfadjointView() * p.A_[k]; H_[k] = p.R_[k]; //H_[k].noalias() += B_[k].transpose() * S_[k+1] * B_[k]; - H_[k].noalias() += p.B_[k].transpose() * S_tilda_[k + 1].template selfadjointView() * p.B_[k]; + H_[k].noalias() += p.B_[k].transpose() * S_t_[k + 1].template selfadjointView() * p.B_[k]; if (settings_.fixedHessianCorrection) { @@ -223,7 +239,7 @@ void AugGNRiccatiSolver::designController(size_t k) Hi_inverse_[k] = -Hi_[k].template selfadjointView().llt().solve(ControlMatrix::Identity()); - // calculate FB gain update + // calculate FB gain update // TODO: this could be left away for the first stage (efficiency) this->L_[k].noalias() = Hi_inverse_[k].template selfadjointView() * G_[k]; // calculate FF update diff --git a/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver.hpp b/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver.hpp index 846c251a5..2bf54c7ef 100644 --- a/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver.hpp +++ b/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver.hpp @@ -88,10 +88,12 @@ class AugGNRiccatiSolver : public LQOCSolver ControlMatrixArray Hi_inverse_; ControlMatrix H_corrFix_; + //! terms for quadratic value function ct::core::DiscreteArray sv_; StateMatrixArray S_; - ct::core::DiscreteArray sv_tilda_; - StateMatrixArray S_tilda_; + //! parallel-transported terms of value function + ct::core::DiscreteArray sv_t_; + StateMatrixArray S_t_; int N_; diff --git a/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp b/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp index 9f49c2d46..9a94d6a4b 100644 --- a/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp +++ b/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp @@ -63,7 +63,10 @@ void GNRiccatiSolver::computeStatesAndControls() this->u_sol_[k] = this->lv_[k] + this->L_[k] * this->x_sol_[k]; //! state update rule in diff coordinates - this->x_sol_[k + 1] = p.A_[k] * this->x_sol_[k] + p.B_[k] * (this->u_sol_[k]) + p.b_[k]; + StateMatrix A_orig = p.Adj_x_[k + 1] * p.A_[k]; // A "without trick for backwards pass" + StateControlMatrix B_orig = p.Adj_x_[k + 1] * p.B_[k]; // B "without trick for backwards pass" + typename MANIFOLD::Tangent b_orig = p.Adj_x_[k + 1] * p.b_[k]; // b "without trick for backwards pass" + this->x_sol_[k + 1] = A_orig * this->x_sol_[k] + B_orig * (this->u_sol_[k]) + b_orig; } } diff --git a/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver.hpp b/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver.hpp index 7caf75f45..6d635fb31 100644 --- a/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver.hpp +++ b/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver.hpp @@ -33,6 +33,7 @@ class GNRiccatiSolver : public LQOCSolver typedef typename Base::LQOCProblem_t LQOCProblem_t; typedef ct::core::StateMatrix StateMatrix; + typedef ct::core::StateControlMatrix StateControlMatrix; typedef ct::core::StateMatrixArray StateMatrixArray; typedef ct::core::ControlVector ControlVector; typedef ct::core::ControlMatrix ControlMatrix; diff --git a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp index 7efcd6535..83fdeb736 100644 --- a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp +++ b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp @@ -86,27 +86,37 @@ int main(int argc, char** argv) { std::cout << std::fixed; - const size_t N = 20*3; - const double dt = 0.125 / 3.0; + const bool use_single_shooting = false; // toggle between single and multiple shooting + + const size_t N = 25; + const double dt = 0.05; const ManifoldState_t x0 = manif::SO3(M_PI, 0, M_PI); ct::core::DiscreteArray x_traj(N + 1, x0); // init state trajectory, will be overwritten + ct::core::DiscreteArray b( + N + 1, ManifoldState_t::Tangent::Zero()); // defect traj, will be overwritten ct::core::DiscreteArray> u_traj(N); // init control traj for (size_t i = 0; i < N; i++) - u_traj[i] = ct::core::ControlVector::Ones() * dt; + u_traj[i] = ct::core::ControlVector::Random() * dt; + // choose a random initial state + // TODO: numerical trouble for more aggressive distributions, since the approximation of the value function becomes really bad? + for (size_t i = 1; i < N + 1; i++) + { + x_traj[i] = (ManifoldState_t::Random().log() * 0.2).exp(); + } // create instances of HPIPM and an unconstrained Gauss-Newton Riccati solver - std::shared_ptr> hpipmSolver( - new HPIPMInterface); std::shared_ptr> gnRiccatiSolver( + new GNRiccatiSolver); + std::shared_ptr> augGnRiccatiSolver( new AugGNRiccatiSolver); // store them, and identifying names, in a vectors std::vector>> lqocSolvers; lqocSolvers.push_back(gnRiccatiSolver); - lqocSolvers.push_back(hpipmSolver); - std::vector solverNames = {"Riccati", "HPIPM"}; + lqocSolvers.push_back(augGnRiccatiSolver); + std::vector solverNames = {"Riccati", "AugRiccati"}; // create linear-quadratic optimal control problem containers std::vector>> problems; @@ -139,8 +149,6 @@ int main(int argc, char** argv) std::shared_ptr> costFunction( new CostFunctionQuadraticSimple(Q, R, x_nominal, u_nom, x_final, Q_final)); - ManifoldState_t::Tangent b; - b.setZero(); // TODO why this? // integrate an initial state with the open-loop system to get initial trajectories ManifoldState_t x_curr; @@ -151,13 +159,16 @@ int main(int argc, char** argv) std::cout << std::setprecision(4) << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; for (size_t i = 0; i < N; i++) { - exampleSystem->computeControlledDynamics(x_curr, 0, u_traj[i], dx); - x_curr = x_curr + dx; - x_traj[i + 1] = x_curr; + exampleSystem->computeControlledDynamics(x_traj[i], 0, u_traj[i], dx); + x_curr = x_traj[i] + dx; + if (use_single_shooting) + x_traj[i + 1] = x_curr; + b[i] = dx - x_traj[i + 1].rminus(x_traj[i]); + std::cout << "b: " << b[i] << std::endl; std::cout << std::setprecision(4) << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; } - size_t nIter = 6; + size_t nIter = 13; for (size_t iter = 0; iter < nIter; iter++) { // initialize the optimal control problems for both solvers @@ -166,7 +177,7 @@ int main(int argc, char** argv) // HACKY corrections // TODO: move somewhere meaningful - for (size_t idx : {0}) // for riccati solver only + for (size_t idx : {0, 1}) { // intermediate stages cost transportation for (size_t i = 0; i < N; i++) @@ -174,7 +185,8 @@ int main(int argc, char** argv) auto e = x_nominal.rminus(x_traj[i]); // compute PT matrix w.r.t. current ref traj // TODO: clarifiy formulation of error in cost function auto e_adj = (e.exp()).adj(); - problems[idx]->Q_[i] = e_adj * problems[idx]->Q_[i] * e_adj.transpose(); + problems[idx]->Q_[i] = e_adj * problems[idx]->Q_[i] * + e_adj.transpose(); // TODO: sort out that thing with the cost function problems[idx]->qv_[i] = e_adj * problems[idx]->qv_[i]; } // terminal stage transportation @@ -182,24 +194,28 @@ int main(int argc, char** argv) auto e = x_final.rminus(x_traj[N]); // compute PT matrix w.r.t. current ref traj auto e_adj = (e.exp()).adj(); //std::cout << "cost adj" << std::endl << e_adj << std::endl; - problems[idx]->Q_.back() = /*e_adj * */ problems[idx]->Q_.back(); // * e_adj.transpose(); - problems[idx]->qv_.back() = /*e_adj * */ problems[idx]->qv_.back(); + problems[idx]->Q_.back() = /*e_adj * */ problems[idx]->Q_.back(); // * e_adj.transpose(); + problems[idx]->qv_.back() = /*e_adj * */ problems[idx]->qv_.back(); // dynamics transportation for (size_t i = 0; i < N; i++) { // std::cout << "dyn transport matrices" << std::endl; Eigen::Matrix3d Jl, Jr; - auto l = x_traj[i + 1].rminus(x_traj[i], Jl, Jr); // TODO: is it the right way round? + auto l = x_traj[i + 1].rminus(x_traj[i], Jl, Jr); auto l_adj = (l.exp()).adj(); //std::cout << "l_adj" << std::endl << l_adj << std::endl; //std::cout << std::endl; - // problems[idx]->A_[i] = l_adj.transpose() * problems[idx]->A_[i]; // todo: are those the right - // problems[idx]->B_[i] = l_adj.transpose() * problems[idx]->B_[i]; // TODO: are those the right - problems[idx]->Acal_[i + 1] = l_adj; + problems[idx]->Adj_x_[i + 1] = l_adj; // parallel transport matrix / adjoint from stage k+1 to stage k + + if (idx == 0) // make the corrections for the standard riccati solver + { + problems[idx]->A_[i] = l_adj.transpose() * problems[idx]->A_[i]; + problems[idx]->b_[i] = l_adj.transpose() * problems[idx]->b_[i]; + problems[idx]->B_[i] = l_adj.transpose() * problems[idx]->B_[i]; + } } - // set the problem pointers lqocSolvers[idx]->setProblem(problems[idx]); @@ -210,9 +226,9 @@ int main(int argc, char** argv) lqocSolvers[idx]->solve(); // postprocess data - lqocSolvers[idx]->computeStatesAndControls(); - lqocSolvers[idx]->computeFeedbackMatrices(); lqocSolvers[idx]->compute_lv(); + lqocSolvers[idx]->computeFeedbackMatrices(); + lqocSolvers[idx]->computeStatesAndControls(); } // retrieve solutions from both solvers @@ -220,12 +236,48 @@ int main(int argc, char** argv) auto uSol_riccati = lqocSolvers[0]->getSolutionControl(); ct::core::FeedbackArray KSol_riccati = lqocSolvers[0]->getSolutionFeedback(); ct::core::ControlVectorArray lv_sol_riccati = lqocSolvers[0]->get_lv(); - auto xSol_hpipm = lqocSolvers[1]->getSolutionState(); - auto uSol_hpipm = lqocSolvers[1]->getSolutionControl(); - ct::core::FeedbackArray KSol_hpipm = lqocSolvers[1]->getSolutionFeedback(); - ct::core::ControlVectorArray lv_sol_hpipm = lqocSolvers[1]->get_lv(); - std::cout << std::endl << std::endl; + auto xSol_aug_riccati = lqocSolvers[1]->getSolutionState(); + auto uSol_aug_riccati = lqocSolvers[1]->getSolutionControl(); + ct::core::FeedbackArray KSol_aug_riccati = lqocSolvers[1]->getSolutionFeedback(); + ct::core::ControlVectorArray lv_sol_aug_riccati = lqocSolvers[1]->get_lv(); + + // compare the quantities + for (size_t i = 0; i < lv_sol_riccati.size(); i++) + { + if (lv_sol_riccati[i].isApprox(lv_sol_aug_riccati[i], 1e-6) == false) + { + std::cout << lv_sol_riccati[i].transpose() << std::endl; + std::cout << lv_sol_aug_riccati[i].transpose() << std::endl; + throw std::runtime_error("lv solutions do not match"); + } + } + for (size_t i = 0; i < KSol_riccati.size(); i++) + { + if (KSol_riccati[i].isApprox(KSol_aug_riccati[i], 1e-6) == false) + throw std::runtime_error("K solutions do not match"); + } + + for (size_t i = 0; i < uSol_riccati.size(); i++) + { + if (uSol_riccati[i].isApprox(uSol_aug_riccati[i], 1e-6) == false) + { + std::cout << "for index " << i << std::endl; + std::cout << std::setprecision(10) << uSol_riccati[i].transpose() << std::endl; + std::cout << std::setprecision(10) << uSol_aug_riccati[i].transpose() << std::endl; + throw std::runtime_error("u solutions do not match"); + } + } + for (size_t i = 0; i < xSol_riccati.size(); i++) + { + if (xSol_riccati[i].isApprox(xSol_aug_riccati[i], 1e-6) == false) + { + std::cout << xSol_riccati[i].transpose() << std::endl; + std::cout << xSol_aug_riccati[i].transpose() << std::endl; + throw std::runtime_error("x solutions do not match"); + } + } + //std::cout << std::setprecision(4) << "dx solution from riccati solver and directly added state_traj, iter " // << iter << std::endl; //ct::core::DiscreteArray x_solver_direct(N + 1); @@ -251,20 +303,28 @@ int main(int argc, char** argv) ct::core::DiscreteArray x_traj_prev = x_traj; for (size_t i = 0; i < N; i++) { - ManifoldState_t::Tangent x_err = x_curr.rminus(x_traj_prev[i]); + ManifoldState_t::Tangent x_err = x_traj[i].rminus(x_traj_prev[i]); // TODO: some term is missing here; - const ct::core::ControlVector u = - u_traj[i] + lv_sol_riccati[i] + KSol_riccati[i] * (x_err /*- eucl. part here*/); - u_traj[i] = u; - - exampleSystem->computeControlledDynamics(x_curr, i * dt, u, dx); - Eigen::Quaterniond old_rot(x_curr.w(), x_curr.x(), x_curr.y(), x_curr.z()); - x_curr = x_curr + dx; - Eigen::Quaterniond new_rot(x_curr.w(), x_curr.x(), x_curr.y(), x_curr.z()); - std::cout << "m: " << x_curr << "\t tan: " << x_curr.log() + if (use_single_shooting) + u_traj[i] += lv_sol_riccati[i] + KSol_riccati[i] * (x_err /*- eucl. part here*/); + else + u_traj[i] += uSol_riccati[i]; + + exampleSystem->computeControlledDynamics(x_traj[i], i * dt, u_traj[i], dx); + Eigen::Quaterniond old_rot(x_traj[i].w(), x_traj[i].x(), x_traj[i].y(), x_traj[i].z()); + + if (use_single_shooting) + x_traj[i + 1] = x_traj[i] + dx; + else + x_traj[i + 1] = x_traj_prev[i + 1] + xSol_riccati[i + 1]; + + Eigen::Quaterniond new_rot(x_traj[i + 1].w(), x_traj[i + 1].x(), x_traj[i + 1].y(), x_traj[i + 1].z()); + std::cout << "m: " << x_traj[i + 1] << "\t tan: " << x_traj[i + 1].log() << "\t -- rot diff norm(): " << old_rot.angularDistance(new_rot) << std::endl; - x_traj[i + 1] = x_curr; + b[i] = dx - x_traj[i + 1].rminus(x_traj[i]); + + std::cout << "b: " << b[i] << std::endl; } std::cout << std::endl << std::endl; } // end iter From 1b1fb281ac71c7d8a368bbd7d28abfa2f48073e0 Mon Sep 17 00:00:00 2001 From: mgiftthaler Date: Thu, 30 Apr 2020 12:54:56 +0200 Subject: [PATCH 04/21] backup working example --- .../linearizer/DynamicsLinearizerNumDiff.h | 6 +- .../CostFunctionQuadraticSimple-impl.hpp | 20 +- .../CostFunctionQuadraticSimple.hpp | 2 + .../ct/optcon/problem/LQOCProblem-impl.hpp | 3 +- .../solver/lqp/AugGNRiccatiSolver-impl.hpp | 18 +- .../solver/lqp/GNRiccatiSolver-impl.hpp | 20 +- ct_optcon/test/CMakeLists.txt | 7 +- .../linear/KinematicManifoldLQOCTest.cpp | 364 +++++++++++ .../test/solver/linear/ManifoldLQOCTest.cpp | 616 ++++++++++++++---- 9 files changed, 908 insertions(+), 148 deletions(-) create mode 100644 ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp diff --git a/ct_core/include/ct/core/systems/linearizer/DynamicsLinearizerNumDiff.h b/ct_core/include/ct/core/systems/linearizer/DynamicsLinearizerNumDiff.h index 9662a2bf2..f9e530370 100644 --- a/ct_core/include/ct/core/systems/linearizer/DynamicsLinearizerNumDiff.h +++ b/ct_core/include/ct/core/systems/linearizer/DynamicsLinearizerNumDiff.h @@ -151,7 +151,8 @@ class DynamicsLinearizerNumDiff SCALAR dxp = mlog_ph - m_log.coeffs()(i); t_perturbed = m_log; - t_perturbed.coeffs()(i) = mlog_ph; + t_perturbed.set_coeff(i, mlog_ph); // TODO + //t_perturbed.coeffs()(i) = mlog_ph; // evaluate dynamics at perturbed state dynamics_fct_(retract_fct_(t_perturbed), t, u, res_plus); @@ -162,7 +163,8 @@ class DynamicsLinearizerNumDiff SCALAR dxm = m_log.coeffs()(i) - mlog_mh; t_perturbed = m_log; - t_perturbed.coeffs()(i) = mlog_mh; + t_perturbed.set_coeff(i, mlog_ph); + //t_perturbed.coeffs()(i) = mlog_ph; Tangent res_minus; dynamics_fct_(retract_fct_(t_perturbed), t, u, res_minus); diff --git a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp index 133868cab..2d516c791 100644 --- a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp +++ b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp @@ -19,6 +19,8 @@ CostFunctionQuadraticSimple::CostFunctionQuadraticSimple( Q_final_.setZero(); x_deviation_.setZero(); u_deviation_.setZero(); + J_curr_.setIdentity(); + J_ref_.setIdentity(); } template @@ -32,6 +34,8 @@ CostFunctionQuadraticSimple::CostFunctionQuadraticSimple( { x_deviation_.setZero(); u_deviation_.setZero(); + J_curr_.setIdentity(); + J_ref_.setIdentity(); } template @@ -42,6 +46,8 @@ CostFunctionQuadraticSimple::~CostFunctionQuadraticSimple template CostFunctionQuadraticSimple::CostFunctionQuadraticSimple(const CostFunctionQuadraticSimple& arg) : x_deviation_(arg.x_deviation_), + J_curr_(arg.J_curr_), + J_ref_(arg.J_ref_), x_nominal_(arg.x_nominal_), Q_(arg.Q_), u_deviation_(arg.u_deviation_), @@ -67,8 +73,10 @@ void CostFunctionQuadraticSimple::setCurrentStateAndContr this->u_ = u; this->t_ = t; - this->x_deviation_ = x - x_nominal_; // TODO: switch order and evluate jacs as well this->u_deviation_ = u - u_nominal_; + + //this->x_deviation_ = x - x_nominal_; + this->x_deviation_ = x_nominal_.rminus(this->x_, J_ref_, J_curr_); } template @@ -82,13 +90,13 @@ auto CostFunctionQuadraticSimple::evaluateIntermediate() template typename MANIFOLD::Tangent CostFunctionQuadraticSimple::stateDerivativeIntermediate() { - return Q_ * x_deviation_; + return Q_ * J_curr_.transpose() * x_deviation_; } template auto CostFunctionQuadraticSimple::stateSecondDerivativeIntermediate() -> state_matrix_t { - return Q_; + return J_curr_ * Q_ * J_curr_.transpose(); } template @@ -119,18 +127,20 @@ auto CostFunctionQuadraticSimple::evaluateTerminal() -> S template typename MANIFOLD::Tangent CostFunctionQuadraticSimple::stateDerivativeTerminal() { - Eigen::Matrix J_curr, J_ref; + Eigen::Matrix J_curr, J_ref; typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_, J_ref, J_curr); return Q_final_ * J_curr.transpose() * x_deviation_final; + //return - Q_final_ * x_deviation_final; //typename MANIFOLD::Tangent x_deviation_final = this->x_.rminus(x_final_, J_ref, J_curr); } template auto CostFunctionQuadraticSimple::stateSecondDerivativeTerminal() -> state_matrix_t { - Eigen::Matrix J_curr, J_ref; + Eigen::Matrix J_curr, J_ref; typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_, J_ref, J_curr); return J_curr * Q_final_ * J_curr.transpose(); + //return Q_final_ ; //typename MANIFOLD::Tangent x_deviation_final = this->x_.rminus(x_final_, J_ref, J_curr); //return Q_final_; } diff --git a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple.hpp b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple.hpp index d638cafb8..5d1278cd0 100644 --- a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple.hpp +++ b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple.hpp @@ -91,6 +91,8 @@ class CostFunctionQuadraticSimple : public CostFunctionQuadratic J_curr_; // TODO: rename + Eigen::Matrix J_ref_; // TODO: rename control_vector_t u_deviation_; control_vector_t u_nominal_; diff --git a/ct_optcon/include/ct/optcon/problem/LQOCProblem-impl.hpp b/ct_optcon/include/ct/optcon/problem/LQOCProblem-impl.hpp index 1178bfde2..c61b8f205 100644 --- a/ct_optcon/include/ct/optcon/problem/LQOCProblem-impl.hpp +++ b/ct_optcon/include/ct/optcon/problem/LQOCProblem-impl.hpp @@ -326,7 +326,8 @@ void LQOCProblem::setFromTimeInvariantLinearQuadraticProb core::StateMatrix A; core::StateControlMatrix B; linearSystem.getDerivatives(A, B, x_traj.front(), u_traj.front(), 0); - A_ = core::StateMatrixArray(K_, A); + + A_ = core::StateMatrixArray(K_, A); B_ = core::StateControlMatrixArray(K_, B); b_ = b; diff --git a/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver-impl.hpp b/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver-impl.hpp index f6d3ca33d..a0796490e 100644 --- a/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver-impl.hpp +++ b/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver-impl.hpp @@ -63,7 +63,9 @@ void AugGNRiccatiSolver::computeStatesAndControls() this->u_sol_[k] = this->lv_[k] + this->L_[k] * this->x_sol_[k]; //! state update rule in diff coordinates - this->x_sol_[k + 1] = p.A_[k] * this->x_sol_[k] + p.B_[k] * (this->u_sol_[k]) + p.b_[k]; + // Note that we need to transport the state update into the tagent space of k+1 + this->x_sol_[k + 1] = + p.Adj_x_[k + 1].transpose() * (p.A_[k] * this->x_sol_[k] + p.B_[k] * this->u_sol_[k] + p.b_[k]); } } @@ -157,17 +159,17 @@ void AugGNRiccatiSolver::computeCostToGo(size_t k) LQOCProblem_t& p = *this->lqocProblem_; S_[k] = p.Q_[k]; - S_[k].noalias() += p.A_[k].transpose() * S_t_[k + 1] * p.A_[k]; - S_[k].noalias() -= this->L_[k].transpose() * Hi_[k] * this->L_[k]; + S_[k]/*.noalias()*/ += p.A_[k].transpose() * S_t_[k + 1] * p.A_[k]; // TODO: bring back all the noalias() + S_[k]/*.noalias()*/ -= this->L_[k].transpose() * Hi_[k] * this->L_[k]; S_[k] = 0.5 * (S_[k] + S_[k].transpose()).eval(); sv_[k] = p.qv_[k]; - sv_[k].noalias() += p.A_[k].transpose() * sv_t_[k + 1]; - sv_[k].noalias() += p.A_[k].transpose() * S_t_[k + 1] * p.b_[k]; - sv_[k].noalias() += this->L_[k].transpose() * Hi_[k] * this->lv_[k]; - sv_[k].noalias() += this->L_[k].transpose() * gv_[k]; - sv_[k].noalias() += G_[k].transpose() * this->lv_[k]; + sv_[k]/*.noalias()*/ += p.A_[k].transpose() * sv_t_[k + 1]; + sv_[k]/*.noalias()*/ += p.A_[k].transpose() * S_t_[k + 1] * p.b_[k]; + sv_[k]/*.noalias()*/ += this->L_[k].transpose() * Hi_[k] * this->lv_[k]; + sv_[k]/*.noalias()*/ += this->L_[k].transpose() * gv_[k]; + sv_[k]/*.noalias()*/ += G_[k].transpose() * this->lv_[k]; if (ct::core::is_euclidean::value) // euclidean case { diff --git a/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp b/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp index 9a94d6a4b..ea9748b63 100644 --- a/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp +++ b/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp @@ -63,10 +63,12 @@ void GNRiccatiSolver::computeStatesAndControls() this->u_sol_[k] = this->lv_[k] + this->L_[k] * this->x_sol_[k]; //! state update rule in diff coordinates - StateMatrix A_orig = p.Adj_x_[k + 1] * p.A_[k]; // A "without trick for backwards pass" - StateControlMatrix B_orig = p.Adj_x_[k + 1] * p.B_[k]; // B "without trick for backwards pass" - typename MANIFOLD::Tangent b_orig = p.Adj_x_[k + 1] * p.b_[k]; // b "without trick for backwards pass" - this->x_sol_[k + 1] = A_orig * this->x_sol_[k] + B_orig * (this->u_sol_[k]) + b_orig; + StateMatrix A_orig = p.Adj_x_[k + 1] * p.A_[k]; // A "without trick for backward pass" + StateControlMatrix B_orig = p.Adj_x_[k + 1] * p.B_[k]; // B "without trick for backward pass" + typename MANIFOLD::Tangent b_orig = p.Adj_x_[k + 1] * p.b_[k]; // b "without trick for backward pass" + // Note that we need to transport the state update into the tagent space of k+1 + this->x_sol_[k + 1] = + p.Adj_x_[k + 1].transpose() * (A_orig * this->x_sol_[k] + B_orig * (this->u_sol_[k]) + b_orig); } } @@ -153,11 +155,11 @@ void GNRiccatiSolver::computeCostToGo(size_t k) S_[k] = 0.5 * (S_[k] + S_[k].transpose()).eval(); sv_[k] = p.qv_[k]; - sv_[k].noalias() += p.A_[k].transpose() * sv_[k + 1]; - sv_[k].noalias() += p.A_[k].transpose() * S_[k + 1] * p.b_[k]; - sv_[k].noalias() += this->L_[k].transpose() * Hi_[k] * this->lv_[k]; - sv_[k].noalias() += this->L_[k].transpose() * gv_[k]; - sv_[k].noalias() += G_[k].transpose() * this->lv_[k]; + sv_[k]/*.noalias()*/ += p.A_[k].transpose() * sv_[k + 1]; + sv_[k]/*.noalias()*/ += p.A_[k].transpose() * S_[k + 1] * p.b_[k]; + sv_[k]/*.noalias()*/ += this->L_[k].transpose() * Hi_[k] * this->lv_[k]; + sv_[k]/*.noalias()*/ += this->L_[k].transpose() * gv_[k]; + sv_[k]/*.noalias()*/ += G_[k].transpose() * this->lv_[k]; // TODO: bring back all the noalias() } template diff --git a/ct_optcon/test/CMakeLists.txt b/ct_optcon/test/CMakeLists.txt index 49c1c189a..1e54468b1 100644 --- a/ct_optcon/test/CMakeLists.txt +++ b/ct_optcon/test/CMakeLists.txt @@ -53,8 +53,13 @@ if(BUILD_TESTS) #package_add_test(dms_test_all_var dms/oscillator/oscDMSTestAllVariants.cpp) #package_add_test(system_interface_test system_interface/SystemInterfaceTest.cpp) + #add_executable(KinematicManifoldLQOCTest solver/linear/KinematicManifoldLQOCTest.cpp) + #target_link_libraries(KinematicManifoldLQOCTest ct_optcon) + #set_target_properties(KinematicManifoldLQOCTest PROPERTIES FOLDER test) + #list(APPEND UNIT_TEST_TARGETS KinematicManifoldLQOCTest) + add_executable(ManifoldLQOCTest solver/linear/ManifoldLQOCTest.cpp) - target_link_libraries(ManifoldLQOCTest ct_optcon) + target_link_libraries(ManifoldLQOCTest ct_optcon ct_core) set_target_properties(ManifoldLQOCTest PROPERTIES FOLDER test) list(APPEND UNIT_TEST_TARGETS ManifoldLQOCTest) diff --git a/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp b/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp new file mode 100644 index 000000000..227bfaaef --- /dev/null +++ b/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp @@ -0,0 +1,364 @@ + +#include +#include +#include + +//#include "matplotlibcpp.h" + +using namespace ct::core; +using namespace ct::optcon; + + +const bool verbose = true; + +using ManifoldState_t = ManifoldState; +const size_t state_dim = ManifoldState_t::TangentDim; +const size_t control_dim = 3; + +class DiscrSO3LTITestSystem final : public ct::core::ControlledSystem +{ +public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + + DiscrSO3LTITestSystem() {} + virtual void computeControlledDynamics(const ManifoldState_t& m, + const Time_t& n, + const ct::core::ControlVector& u, + ManifoldState_t::Tangent& dx) override + { + dx = u; + } + + virtual DiscrSO3LTITestSystem* clone() const override { return new DiscrSO3LTITestSystem(); } + /** + * @brief the log operator is defined as expressing the tangent vector w.r.t. m_ref_ + */ + virtual ManifoldState_t::Tangent lift(const ManifoldState_t& m) override + { + //throw std::runtime_error("not impl"); + return ManifoldState_t::Tangent::Zero(); // the system stays where it is (combination with A = identity()) + /*return m.rminus(m);*/ + } + // virtual ManifoldState_t retract(const ManifoldState_t::Tangent& t) override { + // throw std::runtime_error("not implemetned."); + // /*return m_ref_.rplus(t);*/ } +protected: + //ManifoldState_t m_ref_; +}; + + +// TODO: make this a unit test +void testParallelTransport() +{ + auto origin = manif::SE2(0, 0, 0); + Eigen::Matrix3d b; + b.setIdentity(); // some basis, assumed to be expressed in the manifolds over which we iterate + + for (double theta = M_PI / 2; theta <= 2 * M_PI; theta += M_PI / 2) + { + std::cout << "case theta = " << theta << std::endl; + auto m = manif::SE2(0, 0, theta); + Eigen::Matrix3d Jl, Jr; // jacobians + auto d_rminus = m.rminus(origin, Jl, Jr); + auto adj_rminus = d_rminus.exp().adj(); // the adjoint for transportation to the origin + + auto d_between = m.between(origin); + auto adj_between = d_rminus.exp().adj(); + + // ASSERT_TRUE(adj_rminus.isApprox(adj_between)); + + std::cout << adj_rminus * b << std::endl << std::endl; // express basis b in origin-manifold + std::cout << adj_between * b << std::endl << std::endl; // express basis b in origin-manifold + // std::cout << Jr * Jl * b << std::endl << std::endl; // TODO: how does the Jac relate to the adjoint here? + std::cout << std::endl; + } + + // auto m_pi_2 = manif::SE2(0, 0, M_PI / 2); + // auto m_pi = manif::SE2(0, 0, M_PI); + // auto m_mpi_2 = manif::SE2(0, 0, -M_PI / 2); + // + // + // std::cout << m_0.adj() * b << std::endl << std::endl; + // std::cout << m_pi_2.adj() * b << std::endl << std::endl; + // std::cout << m_pi.adj() * b << std::endl << std::endl; + // std::cout << m_mpi_2.adj() * b << std::endl << std::endl; +} + +int main(int argc, char** argv) +{ + std::cout << std::fixed; + + const bool use_single_shooting = true; // toggle between single and multiple shooting + + const size_t N = 250; + const double dt = 0.5; + + const ManifoldState_t x0 = manif::SO3(3.1, 0, 0); + ct::core::DiscreteArray x_traj(N + 1, x0); // init state trajectory, will be overwritten + ct::core::DiscreteArray b( + N + 1, ManifoldState_t::Tangent::Zero()); // defect traj, will be overwritten + ct::core::DiscreteArray> u_traj(N); // init control traj + for (size_t i = 0; i < N; i++) + u_traj[i] = ct::core::ControlVector::Random()* 0.01; + + // choose a random initial state + // TODO: numerical trouble for more aggressive distributions, since the approximation of the value function becomes really bad? + for (size_t i = 1; i < N + 1; i++) + { + x_traj[i] = ManifoldState_t::Random(); + } + + // create instances of HPIPM and an unconstrained Gauss-Newton Riccati solver + std::shared_ptr> gnRiccatiSolver( + new GNRiccatiSolver); + std::shared_ptr> augGnRiccatiSolver( + new AugGNRiccatiSolver); + + // store them, and identifying names, in a vectors + std::vector>> lqocSolvers; + lqocSolvers.push_back(gnRiccatiSolver); + lqocSolvers.push_back(augGnRiccatiSolver); + std::vector solverNames = {"Riccati", "AugRiccati"}; + + // create linear-quadratic optimal control problem containers + std::vector>> problems; + std::shared_ptr> lqocProblem1( + new LQOCProblem(N)); + std::shared_ptr> lqocProblem2( + new LQOCProblem(N)); + + problems.push_back(lqocProblem1); + problems.push_back(lqocProblem2); + + // create a discrete-time manifold system + std::shared_ptr> exampleSystem( + new DiscrSO3LTITestSystem()); + std::shared_ptr> linearizer( + new ct::core::SystemLinearizer(exampleSystem)); + + + // create a cost function + Eigen::Matrix Q, Q_final; + Eigen::Matrix R; + Q_final << 10000, 0, 0, 0, 10000, 0, 0, 0, 10000; + Q << 1, 0, 0, 0, 1, 0, 0, 0, 1; + //Q.setZero(); + R << 1, 0, 0, 0, 1, 0, 0, 0, 1; + ManifoldState_t x_final = manif::SO3(0, 0, 0); + std::cout << "desired final state: " << x_final << std::endl; + ManifoldState_t x_nominal = x0; + ct::core::ControlVector u_nom = ct::core::ControlVector::Zero(); + std::shared_ptr> costFunction( + new CostFunctionQuadraticSimple(Q, R, x_nominal, u_nom, x_final, Q_final)); + + + // integrate an initial state with the open-loop system to get initial trajectories + ManifoldState_t x_curr; + ManifoldState_t::Tangent dx; + x_curr = x0; + x_traj.front() = x0; + std::cout << "integrate an random initial state with the unstable system" << std::endl; + std::cout << std::setprecision(4) << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; + for (size_t i = 0; i < N; i++) + { + exampleSystem->computeControlledDynamics(x_traj[i], 0, u_traj[i], dx); + x_curr = x_traj[i] + dx; + if (use_single_shooting) + x_traj[i + 1] = x_curr; + b[i] = dx - x_traj[i + 1].rminus(x_traj[i]); + std::cout << "b: " << b[i] << std::endl; + std::cout << std::setprecision(4) << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; + } + + size_t nIter = 35; + for (size_t iter = 0; iter < nIter; iter++) + { + // initialize the optimal control problems for both solvers + problems[0]->setFromTimeInvariantLinearQuadraticProblem(x_traj, u_traj, *linearizer, *costFunction, b, dt); + problems[1]->setFromTimeInvariantLinearQuadraticProblem(x_traj, u_traj, *linearizer, *costFunction, b, dt); + + + // HACKY corrections // TODO: move somewhere meaningful + for (size_t idx : {0, 1}) + { + // intermediate stages cost transportation + for (size_t i = 0; i < N; i++) + { + auto e = x_nominal.rminus(x_traj[i]); + // compute PT matrix w.r.t. current ref traj // TODO: clarify formulation of error in cost function + auto e_adj = (e.exp()).adj(); + problems[idx]->Q_[i] = e_adj * problems[idx]->Q_[i] * + e_adj.transpose(); // TODO: sort out that thing with the cost function + problems[idx]->qv_[i] = e_adj * problems[idx]->qv_[i]; + } + // terminal stage transportation + // TODO: clarifiy formulation of error in cost function + auto e = x_final.rminus(x_traj[N]); // compute PT matrix w.r.t. current ref traj + auto e_adj = (e.exp()).adj(); + //std::cout << "cost adj" << std::endl << e_adj << std::endl; + problems[idx]->Q_.back() = e_adj * problems[idx]->Q_.back() * e_adj.transpose(); + problems[idx]->qv_.back() = e_adj * problems[idx]->qv_.back(); + + // dynamics transportation + for (size_t i = 0; i < N; i++) + { + // std::cout << "dyn transport matrices" << std::endl; + Eigen::Matrix3d Jl, Jr; + auto l = x_traj[i + 1].rminus(x_traj[i], Jl, Jr); + auto l_adj = (l.exp()).adj(); + //std::cout << "l_adj" << std::endl << l_adj << std::endl; + //std::cout << std::endl; + problems[idx]->Adj_x_[i + 1] = l_adj; // parallel transport matrix / adjoint from stage k+1 to stage k + + if (idx == 0) // make the corrections for the standard riccati solver + { + problems[idx]->A_[i] = l_adj.transpose() * problems[idx]->A_[i]; + problems[idx]->B_[i] = l_adj.transpose() * problems[idx]->B_[i]; + problems[idx]->b_[i] = l_adj.transpose() * problems[idx]->b_[i]; + } + + // now just a test // todo: remove later + //auto temp_l = x_traj[i + 1].between(x_traj[i]); + //auto temp_l_adj = temp_l.adj(); + //if (!(l_adj.isApprox(temp_l_adj.transpose(), 1e-5))) + //{ + // std::cout << std::setprecision(10) << "l_adj: " << std::endl << l_adj << std::endl; + // std::cout << std::setprecision(10) << "temp_l_adj: " << std::endl << temp_l_adj << std::endl; + // throw std::runtime_error("No- that does not work."); + //} + } + + // set the problem pointers + lqocSolvers[idx]->setProblem(problems[idx]); + + // allocate memory (if required) + lqocSolvers[idx]->initializeAndAllocate(); + + // solve the problems... + lqocSolvers[idx]->solve(); + + // postprocess data + lqocSolvers[idx]->compute_lv(); + lqocSolvers[idx]->computeFeedbackMatrices(); + lqocSolvers[idx]->computeStatesAndControls(); + } + + // retrieve solutions from both solvers + auto xSol_riccati = lqocSolvers[0]->getSolutionState(); + auto uSol_riccati = lqocSolvers[0]->getSolutionControl(); + ct::core::FeedbackArray KSol_riccati = lqocSolvers[0]->getSolutionFeedback(); + ct::core::ControlVectorArray lv_sol_riccati = lqocSolvers[0]->get_lv(); + + auto xSol_aug_riccati = lqocSolvers[1]->getSolutionState(); + auto uSol_aug_riccati = lqocSolvers[1]->getSolutionControl(); + ct::core::FeedbackArray KSol_aug_riccati = lqocSolvers[1]->getSolutionFeedback(); + ct::core::ControlVectorArray lv_sol_aug_riccati = lqocSolvers[1]->get_lv(); + + // compare the quantities + //for (size_t i = 0; i < lv_sol_riccati.size(); i++) + //{ + // if ((lv_sol_riccati[i] - lv_sol_aug_riccati[i]).array().abs().maxCoeff() > 1e-8) + // { + // std::cout << std::setprecision(10) << lv_sol_riccati[i].transpose() << std::endl; + // std::cout << std::setprecision(10) << lv_sol_aug_riccati[i].transpose() << std::endl; + // throw std::runtime_error("lv solutions do not match"); + // } + //} + //for (size_t i = 0; i < KSol_riccati.size(); i++) + //{ + // if (KSol_riccati[i].isApprox(KSol_aug_riccati[i], 1e-6) == false) + // throw std::runtime_error("K solutions do not match"); + //} +// + //for (size_t i = 0; i < uSol_riccati.size(); i++) + //{ + // if ((uSol_riccati[i] - uSol_aug_riccati[i]).array().abs().maxCoeff() > 1e-8) + // { + // std::cout << "for index " << i << std::endl; + // std::cout << std::setprecision(10) << uSol_riccati[i].transpose() << std::endl; + // std::cout << std::setprecision(10) << uSol_aug_riccati[i].transpose() << std::endl; + // throw std::runtime_error("u solutions do not match"); + // } + //} + //for (size_t i = 0; i < xSol_riccati.size(); i++) + //{ + // if ((xSol_riccati[i] - xSol_aug_riccati[i]).coeffs().array().abs().maxCoeff() > 1e-8) + // { + // std::cout << xSol_riccati[i].transpose() << std::endl; + // std::cout << xSol_aug_riccati[i].transpose() << std::endl; + // throw std::runtime_error("x solutions do not match"); + // } + //} + + //std::cout << std::setprecision(4) << "dx solution from riccati solver and directly added state_traj, iter " + // << iter << std::endl; + //ct::core::DiscreteArray x_solver_direct(N + 1); + //for (size_t j = 0; j < xSol_riccati.size(); j++) + //{ + // x_solver_direct[j] = x_traj[j] + xSol_riccati[j]; + // double angularDiff = 0; + // if (j > 0) + // { + // Eigen::Quaterniond old_rot(x_solver_direct[j - 1].w(), x_solver_direct[j - 1].x(), + // x_solver_direct[j - 1].y(), x_solver_direct[j - 1].z()); + // Eigen::Quaterniond new_rot( + // x_solver_direct[j].w(), x_solver_direct[j].x(), x_solver_direct[j].y(), x_solver_direct[j].z()); + // angularDiff = old_rot.angularDistance(new_rot); + // } + // std::cout << "m:" << x_solver_direct[j] << "\t dx:" << xSol_riccati[j].transpose() + // << "\t -- rot diff norm(): " << angularDiff << std::endl; + //} + + std::cout << std::setprecision(4) << "Forward integrated closed-loop solution, iter :" << iter << std::endl; + x_curr = x0; + std::cout << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; + ct::core::DiscreteArray x_traj_prev = x_traj; + double d_cum_sum = 0; + double dx_cum_sum = 0; + double cost_sum = 0; + for (size_t i = 0; i < N; i++) + { + dx.setZero(); + Eigen::Quaterniond old_rot(x_traj[i].w(), x_traj[i].x(), x_traj[i].y(), x_traj[i].z()); + + if (use_single_shooting) + { + // TODO: some term is missing here; + ManifoldState_t::Tangent x_err = x_traj[i].rminus(x_traj_prev[i]); + u_traj[i] += lv_sol_riccati[i] + KSol_riccati[i] * (x_err /*- eucl. part here*/); + exampleSystem->computeControlledDynamics(x_traj[i], i * dt, u_traj[i], dx); + x_traj[i + 1] = x_traj[i] + dx; + } + else // multiple shooting + { + u_traj[i] += uSol_riccati[i]; + x_traj[i + 1] += xSol_riccati[i + 1]; + exampleSystem->computeControlledDynamics(x_traj[i], i * dt, u_traj[i], dx); + } + + Eigen::Quaterniond new_rot(x_traj[i + 1].w(), x_traj[i + 1].x(), x_traj[i + 1].y(), x_traj[i + 1].z()); + //std::cout << "m: " << x_traj[i + 1] << "\t dx: " << xSol_riccati[i + 1] + // << "\t -- rot diff norm(): " << old_rot.angularDistance(new_rot) << std::endl; + + // compute defect + b[i] = dx - x_traj[i + 1].rminus(x_traj[i]); + + // compute update norms + d_cum_sum += b[i].coeffs().norm(); + dx_cum_sum += xSol_riccati[i + 1].coeffs().norm(); + //std::cout << "b: " << b[i] << std::endl; + // compute running cost + costFunction->setCurrentStateAndControl(x_traj[i], u_traj[i], i * dt); + cost_sum += costFunction->evaluateIntermediate(); + } + + // compute terminal cost + costFunction->setCurrentStateAndControl(x_traj.back(), u_traj.back(), N * dt); + cost_sum += costFunction->evaluateTerminal(); + + std::cout << "d_norm: \t " << d_cum_sum << "\t dx_norm: \t" << dx_cum_sum << "\t cost: " << cost_sum + << std::endl; + std::cout << std::endl << std::endl; + } // end iter + return 1; +} \ No newline at end of file diff --git a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp index 83fdeb736..e7dd2db10 100644 --- a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp +++ b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp @@ -3,107 +3,454 @@ #include #include + using namespace ct::core; using namespace ct::optcon; +const bool verbose = true; -const bool verbose = true; +template +class CompositeManifold; // forward declaratoin + +template +class CompositeManifoldTangent +{ +public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + static constexpr size_t DoF = POS_TANGENT::DoF + VEL_TANGENT::DoF; + using Scalar = double; + using Jacobian = Eigen::Matrix; + using OptJacobianRef = tl::optional>; + + CompositeManifoldTangent() : t_pos_(POS_TANGENT::Zero()), t_vel_(VEL_TANGENT::Zero()) { coeffs_.setZero(); } + CompositeManifoldTangent(const POS_TANGENT& p, const VEL_TANGENT& v) : t_pos_(p), t_vel_(v) + { + coeffs_ << t_pos_.coeffs(), t_vel_.coeffs(); + } + + template + CompositeManifoldTangent(const Eigen::MatrixBase<_EigenDerived>& v) + : t_pos_(v.template head()), t_vel_(v.template tail()), coeffs_(v) + { + // TODO a dimension assert would not hurt + } + + CompositeManifoldTangent(const CompositeManifoldTangent& resulting_t) + : t_pos_(resulting_t.t_pos_), t_vel_(resulting_t.t_vel_), coeffs_(resulting_t.coeffs_) + { + } + + static CompositeManifoldTangent Zero() + { + return CompositeManifoldTangent(POS_TANGENT::Zero(), VEL_TANGENT::Zero()); + } + static CompositeManifoldTangent Random() + { + return CompositeManifoldTangent(POS_TANGENT::Random(), VEL_TANGENT::Random()); + } + + void setRandom() + { + t_pos_.setRandom(); + t_vel_.setRandom(); + coeffs_ << t_pos_.coeffs(), t_vel_.coeffs(); + } + void setZero() + { + t_pos_.setZero(); + t_vel_.setZero(); + coeffs_.setZero(); + } + const Eigen::Matrix& coeffs() const { return coeffs_; } + void set_coeffs(const Eigen::Matrix& c) + { + coeffs_ = c; + t_pos_.coeffs() = c.template head(); + t_vel_.coeffs() = c.template tail(); + } + + void set_coeff(const int i, const Scalar val) + { + coeffs_(i) = val; + t_pos_.coeffs() = coeffs_.template head(); + t_vel_.coeffs() = coeffs_.template tail(); + } + + CompositeManifoldTangent operator-(const CompositeManifoldTangent& tb) const + { + CompositeManifoldTangent res; + res.set_pos(pos() - tb.pos()); + res.set_vel(vel() - tb.vel()); + return res; + } + + CompositeManifoldTangent operator+(const CompositeManifoldTangent& tb) const + { + CompositeManifoldTangent res; + res.set_pos(pos() + tb.pos()); + res.set_vel(vel() + tb.vel()); + return res; + } + CompositeManifoldTangent operator+=(const CompositeManifoldTangent& tb) + { + set_pos(pos() + tb.pos()); + set_vel(vel() + tb.vel()); + return *this; + } + + CompositeManifold exp(OptJacobianRef J_m_t = OptJacobianRef{}) const + { + //std::cout << "now calling exp..." << std::endl; + if (J_m_t) + { + throw std::runtime_error("J-m-resulting_t in tangent exp not impl yet."); + } + + CompositeManifold c( + this->t_pos_.exp(), this->t_pos_.exp().adj().transpose() * this->t_vel_.coeffs()); + return c; + } + + auto transpose() const { return coeffs().transpose(); } + const POS_TANGENT& pos() const { return t_pos_; } + void set_pos(const POS_TANGENT& p) + { + t_pos_ = p; + coeffs_.template head() = p.coeffs(); + } + void set_vel(const VEL_TANGENT& v) + { + t_vel_ = v; + coeffs_.template tail() = v.coeffs(); + } + const VEL_TANGENT& vel() const { return t_vel_; } + //Scalar& operator()(int idx) { return coeffs()(idx); } + const Scalar& operator()(int idx) const { return coeffs()(idx); } + //auto noalias() { return CompositeManifoldTangent(*this); /* coeffs().noalias();*/ } // TODO: sort this out +protected: + POS_TANGENT t_pos_; + VEL_TANGENT t_vel_; + Eigen::Matrix coeffs_; +}; + +template +_Stream& operator<<(_Stream& s, const CompositeManifoldTangent& resulting_t) +{ + s << resulting_t.coeffs().transpose(); + return s; +} + +template +auto operator*(const Eigen::MatrixBase<_EigenDerived>& J, const CompositeManifoldTangent& resulting_t) +{ + return J * resulting_t.coeffs(); +} + +template +auto operator*(const CompositeManifoldTangent& resulting_t, const double& s) +{ + return resulting_t.coeffs() * s; +} + +template +auto operator+(const Eigen::MatrixBase<_EigenDerived>& tb, const CompositeManifoldTangent& resulting_t) +{ + CompositeManifoldTangent res; + CompositeManifoldTangent rhs(tb); + res.set_pos(resulting_t.pos() + rhs.pos()); + res.set_vel(resulting_t.vel() + rhs.vel()); + return res; +} + + +template +class CompositeManifold +{ +public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + + using Tangent = CompositeManifoldTangent; + static constexpr size_t TangentDim = Tangent::DoF; + static constexpr size_t Dim = POS_MAN::Dim + VEL_MAN::Dim + 1; // todo: this is hacky + + using Scalar = double; + using Jacobian = Eigen::Matrix; + using OptJacobianRef = tl::optional>; + + // get ManifoldState templated on a different scalar from this expression + template + using RedefineScalar = CompositeManifold; + + CompositeManifold() : m_pos_(POS_MAN::Identity()), m_vel_(VEL_MAN::Identity()) {} + CompositeManifold(const POS_MAN& p, const VEL_MAN& v) : m_pos_(p), m_vel_(v) {} + CompositeManifold(const CompositeManifold& arg) : m_pos_(arg.m_pos_), m_vel_(arg.m_vel_) {} + static CompositeManifold NeutralElement() { return CompositeManifold::Identity(); } + static CompositeManifold Identity() { return CompositeManifold(POS_MAN::Identity(), VEL_MAN::Identity()); } + static CompositeManifold Random() { return CompositeManifold(POS_MAN::Random(), VEL_MAN::Random()); } + void setIdentity() + { + m_pos_.setIdentity(); + m_vel_.setIdentity(); + } + + const Eigen::Matrix coeffs() const // todo: return by value is bad + { + Eigen::Matrix c; + c << m_pos_.coeffs(), m_vel_.coeffs(); + return c; + } + + CompositeManifold operator+(const Tangent& resulting_t) const { return rplus(resulting_t); } + //CompositeManifold operator+=(const Tangent& resulting_t) + //{ + // *this = this->rplus(resulting_t); + // return *this; // todo test + //} + + /** + * @brief right plus for composite manifold + * NOTE: the operator also has to parallel transport velocities to the new tangent plane. + * Since for rplus, the rhs is expressed in the tangent space of the lhs, we choose: "first add, then transport" + */ + CompositeManifold rplus(const Tangent& resulting_t, OptJacobianRef Jl = {}, OptJacobianRef Jr = {}) const + { + typename POS_MAN::Jacobian pJl, pJr; + typename VEL_MAN::Jacobian vJl, vJr; + + VEL_MAN v_new_local = vel().rplus(resulting_t.vel(), vJl, vJr); + + VEL_MAN v_new_transported(resulting_t.pos().exp().adj().transpose() * v_new_local.coeffs()); + + CompositeManifold c; + c.vel() = v_new_transported; + //c.vel() = c.vel().rplus(resulting_t.vel(), vJl, vJr); + c.pos() = pos().rplus(resulting_t.pos(), pJl, pJr); // todo make jacobians optional + + if (Jl) + { + Jl->setZero(); + (*Jl).template topLeftCorner() = pJl; + (*Jl).template bottomRightCorner() = vJl; + } + + if (Jr) + { + Jr->setZero(); + (*Jr).template topLeftCorner() = pJr; + (*Jr).template bottomRightCorner() = + vJr; // TODO: what will the velocity jacobians become? + } -using ManifoldState_t = ManifoldState; + return c; + } + + + /** + * @brief right minus for composite manifold + * NOTE: the operator also has to express velocities in the rhs tangent plane. + * Since for rminus, the resulting difference is expressed in the tangent space of the rhs, we choose: "first transport, then substract" + */ + Tangent rminus(const CompositeManifold& rhs, OptJacobianRef Jl = {}, OptJacobianRef Jr = {}) const + { + typename POS_MAN::Jacobian pJl, pJr; + typename VEL_MAN::Jacobian vJl, vJr; + + Tangent resulting_t; + resulting_t.set_pos(pos().rminus(rhs.pos(), pJl, pJr)); // todo make jacobians optional + VEL_MAN v_lhs_transported(resulting_t.pos().exp().adj() * vel().coeffs()); + + VEL_TANGENT v_new_local = v_lhs_transported.rminus(rhs.vel(), vJl, vJr); + resulting_t.set_vel(v_new_local); + + if (Jl) + { + Jl->setIdentity(); + (*Jl).template topLeftCorner() = pJl; + (*Jl).template bottomRightCorner() = vJl; + } + + if (Jr) + { + Jr->setIdentity(); + (*Jr).template topLeftCorner() = pJr; + (*Jr).template bottomRightCorner() = vJr; + } + + return resulting_t; + } + + + Tangent log(OptJacobianRef J_t_m = {}) const + { + if (J_t_m) + throw std::runtime_error("J_t_m compuation not defined."); + + return rminus(CompositeManifold::Identity(), J_t_m); + //resulting_t.set_pos(m_pos_.log()); + //resulting_t.set_vel(m_vel_.log()); + //return resulting_t; + } + + /** + * @brief The question + * The fundamental question remains: what is the velocity adjoint? + * - multiple shooting: in the forward update rule, the computed velocities want to be transported to the next states manifold coordinate system + * - single shooting: seems to be much more stable when velocity part adjoint is identity -- is the value function more stable??? + */ + Jacobian adj() const + { + Jacobian J = Jacobian::Zero(); + J.template topLeftCorner() = m_pos_.adj(); + J.template bottomRightCorner() = + m_pos_.adj(); //.setIdentity(); // = m_pos_.adj(); // TODO: what exactly is it? + return J; + } + + POS_MAN& pos() { return m_pos_; } + const POS_MAN& pos() const { return m_pos_; } + VEL_MAN& vel() { return m_vel_; } + const VEL_MAN& vel() const { return m_vel_; } +protected: + POS_MAN m_pos_; + VEL_MAN m_vel_; +}; +template +_Stream& operator<<(_Stream& s, const CompositeManifold& m) +{ + s << m.coeffs().transpose(); + return s; +} + + +using ManifoldState_t = CompositeManifold; const size_t state_dim = ManifoldState_t::TangentDim; const size_t control_dim = 3; + class DiscrSO3LTITestSystem final : public ct::core::ControlledSystem { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW - DiscrSO3LTITestSystem() {} + DiscrSO3LTITestSystem(double dt) : dt_(dt) {} virtual void computeControlledDynamics(const ManifoldState_t& m, const Time_t& n, const ct::core::ControlVector& u, ManifoldState_t::Tangent& dx) override { - dx = u; + dx.setZero(); + dx.set_vel(dt_ * u); // velocity increment + dx.set_pos(dt_ * m.vel().coeffs()); // position increment } - virtual DiscrSO3LTITestSystem* clone() const override { return new DiscrSO3LTITestSystem(); } + virtual DiscrSO3LTITestSystem* clone() const override { return new DiscrSO3LTITestSystem(*this); } /** - * @brief the log operator is defined as expressing the tangent vector w.r.t. m_ref_ + * @brief the log operator is defined as expressing the tangent vector w.r.resulting_t. m_ref_ */ virtual ManifoldState_t::Tangent lift(const ManifoldState_t& m) override { - //throw std::runtime_error("not impl"); - return ManifoldState_t::Tangent::Zero(); // the system stays where it is (combination with A = identity()) - /*return m.rminus(m);*/ + ManifoldState_t::Tangent resulting_t = ManifoldState_t::Tangent::Zero(); + //resulting_t.set_pos( "zero..."); + resulting_t.set_vel(m.vel().log()); + return resulting_t; } - // virtual ManifoldState_t retract(const ManifoldState_t::Tangent& t) override { - // throw std::runtime_error("not implemetned."); - // /*return m_ref_.rplus(t);*/ } + + //virtual ManifoldState_t retract(const ManifoldState_t::Tangent& resulting_t) override { + // throw std::runtime_error(" retract not implemetned."); + // /*return m_ref_.rplus(resulting_t);*/ } protected: //ManifoldState_t m_ref_; + double dt_; }; // TODO: make this a unit test -void testParallelTransport() -{ - auto origin = manif::SE2(0, 0, 0); - Eigen::Matrix3d b; - b.setIdentity(); // some basis, assumed to be expressed in the manifolds over which we iterate - - for (double theta = M_PI / 2; theta <= 2 * M_PI; theta += M_PI / 2) - { - std::cout << "case theta = " << theta << std::endl; - auto m = manif::SE2(0, 0, theta); - Eigen::Matrix3d Jl, Jr; // jacobians - auto d_rminus = m.rminus(origin, Jl, Jr); - auto adj_rminus = d_rminus.exp().adj(); // the adjoint for transportation to the origin - - auto d_between = m.between(origin); - auto adj_between = d_rminus.exp().adj(); - - // ASSERT_TRUE(adj_rminus.isApprox(adj_between)); +//void testParallelTransport() +//{ +// auto origin = manif::SE2(0, 0, 0); +// Eigen::Matrix3d b; +// b.setIdentity(); // some basis, assumed to be expressed in the manifolds over which we iterate +// +// for (double theta = M_PI / 2; theta <= 2 * M_PI; theta += M_PI / 2) +// { +// std::cout << "case theta = " << theta << std::endl; +// auto m = manif::SE2(0, 0, theta); +// Eigen::Matrix3d Jl, Jr; // jacobians +// auto d_rminus = m.rminus(origin, Jl, Jr); +// auto adj_rminus = d_rminus.exp().adj(); // the adjoint for transportation to the origin +// +// auto d_between = m.between(origin); +// auto adj_between = d_rminus.exp().adj(); +// +// // ASSERT_TRUE(adj_rminus.isApprox(adj_between)); +// +// std::cout << adj_rminus * b << std::endl << std::endl; // express basis b in origin-manifold +// std::cout << adj_between * b << std::endl << std::endl; // express basis b in origin-manifold +// // std::cout << Jr * Jl * b << std::endl << std::endl; // TODO: how does the Jac relate to the adjoint here? +// std::cout << std::endl; +// } +// +// // auto m_pi_2 = manif::SE2(0, 0, M_PI / 2); +// // auto m_pi = manif::SE2(0, 0, M_PI); +// // auto m_mpi_2 = manif::SE2(0, 0, -M_PI / 2); +// // +// // +// // std::cout << m_0.adj() * b << std::endl << std::endl; +// // std::cout << m_pi_2.adj() * b << std::endl << std::endl; +// // std::cout << m_pi.adj() * b << std::endl << std::endl; +// // std::cout << m_mpi_2.adj() * b << std::endl << std::endl; +//} - std::cout << adj_rminus * b << std::endl << std::endl; // express basis b in origin-manifold - std::cout << adj_between * b << std::endl << std::endl; // express basis b in origin-manifold - // std::cout << Jr * Jl * b << std::endl << std::endl; // TODO: how does the Jac relate to the adjoint here? - std::cout << std::endl; - } - - // auto m_pi_2 = manif::SE2(0, 0, M_PI / 2); - // auto m_pi = manif::SE2(0, 0, M_PI); - // auto m_mpi_2 = manif::SE2(0, 0, -M_PI / 2); +int main(int argc, char** argv) +{ + //ManifoldState_t x_test; + //x_test.setIdentity(); + //Eigen::Vector3d vel_init(1.0, 0.0, 0.0); + //x_test.vel() = manif::R3d(vel_init); + ////x0.pos() = manif::SO3(0, 0, M_PI); + //std::cout << "m: " << x_test << std::endl; + //ManifoldState_t::Tangent t_test; + //t_test.set_coeff(2, M_PI / 2); + //t_test.set_coeff(3, 1); // + //std::cout << "resulting_t: " << t_test << std::endl; + //ManifoldState_t newX = x_test.rplus(t_test); + //std::cout << "newX: " << newX << std::endl; + //auto t_reverse = newX.rminus(x_test); + //std::cout << "t_reverse:" << t_reverse << std::endl; + //std::cout << "t_reverse.exp() " << t_reverse.exp() << std::endl; + //std::cout << "t_reverse.exp().log() " << t_reverse.exp().log() << std::endl; // - // std::cout << m_0.adj() * b << std::endl << std::endl; - // std::cout << m_pi_2.adj() * b << std::endl << std::endl; - // std::cout << m_pi.adj() * b << std::endl << std::endl; - // std::cout << m_mpi_2.adj() * b << std::endl << std::endl; -} + //std::cout << "parallel transport study: " << std::endl; + //auto ADJ = t_reverse.exp().adj(); + //std::cout << "adj: " << ADJ << std::endl; + //std::cout << "adj*t_test: " << ADJ * t_test << std::endl; + //exit(0); -int main(int argc, char** argv) -{ std::cout << std::fixed; - const bool use_single_shooting = false; // toggle between single and multiple shooting + const bool use_single_shooting = true; // toggle between single and multiple shooting - const size_t N = 25; - const double dt = 0.05; + const size_t N = 17; + const double dt = 0.1; - const ManifoldState_t x0 = manif::SO3(M_PI, 0, M_PI); + ManifoldState_t x0; + x0.setIdentity(); + x0.pos() = manif::SO3(M_PI/2, M_PI/2, 0.0); ct::core::DiscreteArray x_traj(N + 1, x0); // init state trajectory, will be overwritten ct::core::DiscreteArray b( N + 1, ManifoldState_t::Tangent::Zero()); // defect traj, will be overwritten ct::core::DiscreteArray> u_traj(N); // init control traj for (size_t i = 0; i < N; i++) - u_traj[i] = ct::core::ControlVector::Random() * dt; + { + u_traj[i] = ct::core::ControlVector::Random(); + } // choose a random initial state // TODO: numerical trouble for more aggressive distributions, since the approximation of the value function becomes really bad? for (size_t i = 1; i < N + 1; i++) { - x_traj[i] = (ManifoldState_t::Random().log() * 0.2).exp(); + x_traj[i] = ManifoldState_t::Random(); } // create instances of HPIPM and an unconstrained Gauss-Newton Riccati solver @@ -130,7 +477,7 @@ int main(int argc, char** argv) // create a discrete-time manifold system std::shared_ptr> exampleSystem( - new DiscrSO3LTITestSystem()); + new DiscrSO3LTITestSystem(dt)); std::shared_ptr> linearizer( new ct::core::SystemLinearizer(exampleSystem)); @@ -138,13 +485,16 @@ int main(int argc, char** argv) // create a cost function Eigen::Matrix Q, Q_final; Eigen::Matrix R; - Q_final << 10000, 0, 0, 0, 10000, 0, 0, 0, 10000; - //Q << 1, 0, 0, 0, 1, 0, 0, 0, 1; + Q_final.setZero(); + Q_final.diagonal() << 10000, 10000, 10000, 10000, 100000, 10000; Q.setZero(); - R << 1, 0, 0, 0, 1, 0, 0, 0, 1; - ManifoldState_t x_final = manif::SO3(0, 0, 0); + // Q.diagonal() << 3, 3, 3, 3, 3, 3; + R.setZero(); + R.diagonal() << 1, 1, 1; + ManifoldState_t x_final; + x_final.pos() = manif::SO3(0, 0, 0); std::cout << "desired final state: " << x_final << std::endl; - ManifoldState_t x_nominal = x0; + ManifoldState_t x_nominal = x_final; ct::core::ControlVector u_nom = ct::core::ControlVector::Zero(); std::shared_ptr> costFunction( new CostFunctionQuadraticSimple(Q, R, x_nominal, u_nom, x_final, Q_final)); @@ -156,19 +506,20 @@ int main(int argc, char** argv) x_curr = x0; x_traj.front() = x0; std::cout << "integrate an random initial state with the unstable system" << std::endl; - std::cout << std::setprecision(4) << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; + std::cout << std::setprecision(4) << "m: " << x_curr << "\resulting_t tan: " << x_curr.log() << std::endl; for (size_t i = 0; i < N; i++) { exampleSystem->computeControlledDynamics(x_traj[i], 0, u_traj[i], dx); - x_curr = x_traj[i] + dx; + std::cout << "dx" << dx << std::endl; + x_curr = x_traj[i].rplus(dx); if (use_single_shooting) x_traj[i + 1] = x_curr; b[i] = dx - x_traj[i + 1].rminus(x_traj[i]); std::cout << "b: " << b[i] << std::endl; - std::cout << std::setprecision(4) << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; + std::cout << std::setprecision(10) << "m: " << x_curr << "\resulting_t tan: " << x_curr.log() << std::endl; } - size_t nIter = 13; + size_t nIter = 25; for (size_t iter = 0; iter < nIter; iter++) { // initialize the optimal control problems for both solvers @@ -183,7 +534,7 @@ int main(int argc, char** argv) for (size_t i = 0; i < N; i++) { auto e = x_nominal.rminus(x_traj[i]); - // compute PT matrix w.r.t. current ref traj // TODO: clarifiy formulation of error in cost function + // compute PT matrix w.r.resulting_t. current ref traj // TODO: clarify formulation of error in cost function auto e_adj = (e.exp()).adj(); problems[idx]->Q_[i] = e_adj * problems[idx]->Q_[i] * e_adj.transpose(); // TODO: sort out that thing with the cost function @@ -191,21 +542,21 @@ int main(int argc, char** argv) } // terminal stage transportation // TODO: clarifiy formulation of error in cost function - auto e = x_final.rminus(x_traj[N]); // compute PT matrix w.r.t. current ref traj - auto e_adj = (e.exp()).adj(); + // auto e = x_final.rminus(x_traj[N]); // compute PT matrix w.r.resulting_t. current ref traj + // auto e_adj = (e.exp()).adj(); //std::cout << "cost adj" << std::endl << e_adj << std::endl; - problems[idx]->Q_.back() = /*e_adj * */ problems[idx]->Q_.back(); // * e_adj.transpose(); - problems[idx]->qv_.back() = /*e_adj * */ problems[idx]->qv_.back(); + //problems[idx]->Q_.back() = e_adj * problems[idx]->Q_.back() * e_adj.transpose(); + //problems[idx]->qv_.back() = e_adj * problems[idx]->qv_.back(); + //std::cout << "problems[idx]->Q_.back()" << std::endl << problems[idx]->Q_.back() << std::endl; + //std::cout << "problems[idx]->qv_.back()" << std::endl << problems[idx]->qv_.back() << std::endl; // dynamics transportation for (size_t i = 0; i < N; i++) { // std::cout << "dyn transport matrices" << std::endl; - Eigen::Matrix3d Jl, Jr; - auto l = x_traj[i + 1].rminus(x_traj[i], Jl, Jr); + //Eigen::Matrix3d Jl, Jr; + auto l = x_traj[i + 1].rminus(x_traj[i]); auto l_adj = (l.exp()).adj(); - //std::cout << "l_adj" << std::endl << l_adj << std::endl; - //std::cout << std::endl; problems[idx]->Adj_x_[i + 1] = l_adj; // parallel transport matrix / adjoint from stage k+1 to stage k if (idx == 0) // make the corrections for the standard riccati solver @@ -243,40 +594,40 @@ int main(int argc, char** argv) ct::core::ControlVectorArray lv_sol_aug_riccati = lqocSolvers[1]->get_lv(); // compare the quantities - for (size_t i = 0; i < lv_sol_riccati.size(); i++) - { - if (lv_sol_riccati[i].isApprox(lv_sol_aug_riccati[i], 1e-6) == false) - { - std::cout << lv_sol_riccati[i].transpose() << std::endl; - std::cout << lv_sol_aug_riccati[i].transpose() << std::endl; - throw std::runtime_error("lv solutions do not match"); - } - } - for (size_t i = 0; i < KSol_riccati.size(); i++) - { - if (KSol_riccati[i].isApprox(KSol_aug_riccati[i], 1e-6) == false) - throw std::runtime_error("K solutions do not match"); - } - - for (size_t i = 0; i < uSol_riccati.size(); i++) - { - if (uSol_riccati[i].isApprox(uSol_aug_riccati[i], 1e-6) == false) - { - std::cout << "for index " << i << std::endl; - std::cout << std::setprecision(10) << uSol_riccati[i].transpose() << std::endl; - std::cout << std::setprecision(10) << uSol_aug_riccati[i].transpose() << std::endl; - throw std::runtime_error("u solutions do not match"); - } - } - for (size_t i = 0; i < xSol_riccati.size(); i++) - { - if (xSol_riccati[i].isApprox(xSol_aug_riccati[i], 1e-6) == false) - { - std::cout << xSol_riccati[i].transpose() << std::endl; - std::cout << xSol_aug_riccati[i].transpose() << std::endl; - throw std::runtime_error("x solutions do not match"); - } - } + //for (size_t i = 0; i < lv_sol_riccati.size(); i++) + //{ + // if ((lv_sol_riccati[i] - lv_sol_aug_riccati[i]).array().abs().maxCoeff() > 1e-8) + // { + // std::cout << std::setprecision(10) << lv_sol_riccati[i].transpose() << std::endl; + // std::cout << std::setprecision(10) << lv_sol_aug_riccati[i].transpose() << std::endl; + // throw std::runtime_error("lv solutions do not match"); + // } + //} + //for (size_t i = 0; i < KSol_riccati.size(); i++) + //{ + // if (KSol_riccati[i].isApprox(KSol_aug_riccati[i], 1e-6) == false) + // throw std::runtime_error("K solutions do not match"); + //} + // + //for (size_t i = 0; i < uSol_riccati.size(); i++) + //{ + // if ((uSol_riccati[i] - uSol_aug_riccati[i]).array().abs().maxCoeff() > 1e-8) + // { + // std::cout << "for index " << i << std::endl; + // std::cout << std::setprecision(10) << uSol_riccati[i].transpose() << std::endl; + // std::cout << std::setprecision(10) << uSol_aug_riccati[i].transpose() << std::endl; + // throw std::runtime_error("u solutions do not match"); + // } + //} + //for (size_t i = 0; i < xSol_riccati.size(); i++) + //{ + // if ((xSol_riccati[i] - xSol_aug_riccati[i]).coeffs().array().abs().maxCoeff() > 1e-8) + // { + // std::cout << xSol_riccati[i].transpose() << std::endl; + // std::cout << xSol_aug_riccati[i].transpose() << std::endl; + // throw std::runtime_error("x solutions do not match"); + // } + //} //std::cout << std::setprecision(4) << "dx solution from riccati solver and directly added state_traj, iter " // << iter << std::endl; @@ -293,39 +644,60 @@ int main(int argc, char** argv) // x_solver_direct[j].w(), x_solver_direct[j].x(), x_solver_direct[j].y(), x_solver_direct[j].z()); // angularDiff = old_rot.angularDistance(new_rot); // } - // std::cout << "m:" << x_solver_direct[j] << "\t dx:" << xSol_riccati[j].transpose() - // << "\t -- rot diff norm(): " << angularDiff << std::endl; + // std::cout << "m:" << x_solver_direct[j] << "\resulting_t dx:" << xSol_riccati[j].transpose() + // << "\resulting_t -- rot diff norm(): " << angularDiff << std::endl; //} std::cout << std::setprecision(4) << "Forward integrated closed-loop solution, iter :" << iter << std::endl; x_curr = x0; std::cout << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; ct::core::DiscreteArray x_traj_prev = x_traj; + double d_cum_sum = 0; + double dx_cum_sum = 0; + double cost_sum = 0; for (size_t i = 0; i < N; i++) { - ManifoldState_t::Tangent x_err = x_traj[i].rminus(x_traj_prev[i]); - // TODO: some term is missing here; + dx.setZero(); + //Eigen::Quaterniond old_rot(x_traj[i].w(), x_traj[i].x(), x_traj[i].y(), x_traj[i].z()); + if (use_single_shooting) + { + // TODO: some term is missing here; + ManifoldState_t::Tangent x_err = x_traj[i].rminus(x_traj_prev[i]); u_traj[i] += lv_sol_riccati[i] + KSol_riccati[i] * (x_err /*- eucl. part here*/); - else + exampleSystem->computeControlledDynamics(x_traj[i], i * dt, u_traj[i], dx); + x_traj[i + 1] = x_traj[i] + dx; + } + else // multiple shooting + { u_traj[i] += uSol_riccati[i]; + x_traj[i + 1] = x_traj[i + 1].rplus(xSol_riccati[i + 1]); + exampleSystem->computeControlledDynamics(x_traj[i], i * dt, u_traj[i], dx); + } - exampleSystem->computeControlledDynamics(x_traj[i], i * dt, u_traj[i], dx); - Eigen::Quaterniond old_rot(x_traj[i].w(), x_traj[i].x(), x_traj[i].y(), x_traj[i].z()); - - if (use_single_shooting) - x_traj[i + 1] = x_traj[i] + dx; - else - x_traj[i + 1] = x_traj_prev[i + 1] + xSol_riccati[i + 1]; + //Eigen::Quaterniond new_rot(x_traj[i + 1].w(), x_traj[i + 1].x(), x_traj[i + 1].y(), x_traj[i + 1].z()); + //std::cout << "m: " << x_traj[i + 1] << "\resulting_t log: " << x_traj[i + 1].log() << " u: " << u_traj[i].transpose() + // << std::endl; - Eigen::Quaterniond new_rot(x_traj[i + 1].w(), x_traj[i + 1].x(), x_traj[i + 1].y(), x_traj[i + 1].z()); - std::cout << "m: " << x_traj[i + 1] << "\t tan: " << x_traj[i + 1].log() - << "\t -- rot diff norm(): " << old_rot.angularDistance(new_rot) << std::endl; + // compute defect + b[i] = dx - (x_traj[i + 1].rminus(x_traj[i])); - b[i] = dx - x_traj[i + 1].rminus(x_traj[i]); + // compute update norms + d_cum_sum += b[i].coeffs().norm(); + dx_cum_sum += xSol_riccati[i + 1].coeffs().norm(); + //std::cout << "b: " << b[i] << std::endl; - std::cout << "b: " << b[i] << std::endl; + // compute running cost + costFunction->setCurrentStateAndControl(x_traj[i], u_traj[i], i * dt); + cost_sum += costFunction->evaluateIntermediate(); } + + // compute terminal cost + costFunction->setCurrentStateAndControl(x_traj.back(), u_traj.back(), N * dt); + cost_sum += costFunction->evaluateTerminal(); + + std::cout << std::setprecision(10) << "d_norm: \t " << d_cum_sum << "\t dx_norm: \t" << dx_cum_sum + << " \t Jcost: " << cost_sum << std::endl; std::cout << std::endl << std::endl; } // end iter return 1; From 5779b6087317a21b3d77dcf8d0f2e3651d911496 Mon Sep 17 00:00:00 2001 From: mgiftthaler Date: Tue, 12 May 2020 20:41:06 +0200 Subject: [PATCH 05/21] completed working example of LQOC (multiple-shooting) on composite manifold S3xR3 --- .../CostFunctionQuadraticSimple-impl.hpp | 48 ++- .../CostFunctionQuadraticSimple.hpp | 4 +- .../ct/optcon/problem/LQOCProblem-impl.hpp | 2 +- .../solver/lqp/AugGNRiccatiSolver-impl.hpp | 311 ---------------- .../optcon/solver/lqp/AugGNRiccatiSolver.hpp | 113 ------ .../solver/lqp/GNRiccatiSolver-impl.hpp | 2 +- .../test/solver/linear/ManifoldLQOCTest.cpp | 350 ++++-------------- 7 files changed, 106 insertions(+), 724 deletions(-) delete mode 100644 ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver-impl.hpp delete mode 100644 ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver.hpp diff --git a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp index 2d516c791..0eb40671a 100644 --- a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp +++ b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp @@ -11,16 +11,16 @@ namespace optcon { template CostFunctionQuadraticSimple::CostFunctionQuadraticSimple() { + x_deviation_.setZero(); x_nominal_.setZero(); Q_.setZero(); + Jl_.setIdentity(); + Jr_.setIdentity(); u_nominal_.setZero(); R_.setZero(); x_final_.setZero(); Q_final_.setZero(); - x_deviation_.setZero(); u_deviation_.setZero(); - J_curr_.setIdentity(); - J_ref_.setIdentity(); } template @@ -34,8 +34,8 @@ CostFunctionQuadraticSimple::CostFunctionQuadraticSimple( { x_deviation_.setZero(); u_deviation_.setZero(); - J_curr_.setIdentity(); - J_ref_.setIdentity(); + Jl_.setIdentity(); + Jr_.setIdentity(); } template @@ -46,10 +46,10 @@ CostFunctionQuadraticSimple::~CostFunctionQuadraticSimple template CostFunctionQuadraticSimple::CostFunctionQuadraticSimple(const CostFunctionQuadraticSimple& arg) : x_deviation_(arg.x_deviation_), - J_curr_(arg.J_curr_), - J_ref_(arg.J_ref_), x_nominal_(arg.x_nominal_), Q_(arg.Q_), + Jl_(arg.Jl_), + Jr_(arg.Jr_), u_deviation_(arg.u_deviation_), u_nominal_(arg.u_nominal_), R_(arg.R_), @@ -74,15 +74,14 @@ void CostFunctionQuadraticSimple::setCurrentStateAndContr this->t_ = t; this->u_deviation_ = u - u_nominal_; - - //this->x_deviation_ = x - x_nominal_; - this->x_deviation_ = x_nominal_.rminus(this->x_, J_ref_, J_curr_); + this->x_deviation_ = x_nominal_.rminus(this->x_, Jl_, Jr_); } template auto CostFunctionQuadraticSimple::evaluateIntermediate() -> SCALAR { - SCALAR costQ = SCALAR(0.5) * (x_deviation_.transpose() * Q_ * x_deviation_)(0); + // TODO: including Jr_ might not be required here (identical result) + SCALAR costQ = SCALAR(0.5) * (x_deviation_.transpose() * Jr_ * Q_ * Jr_.transpose() * x_deviation_)(0); SCALAR costR = SCALAR(0.5) * (u_deviation_.transpose() * R_ * u_deviation_)(0); return costQ + costR; } @@ -90,13 +89,13 @@ auto CostFunctionQuadraticSimple::evaluateIntermediate() template typename MANIFOLD::Tangent CostFunctionQuadraticSimple::stateDerivativeIntermediate() { - return Q_ * J_curr_.transpose() * x_deviation_; + return Q_ * Jr_.transpose() * x_deviation_; } template auto CostFunctionQuadraticSimple::stateSecondDerivativeIntermediate() -> state_matrix_t { - return J_curr_ * Q_ * J_curr_.transpose(); + return Jr_ * Q_ * Jr_.transpose(); } template @@ -120,29 +119,26 @@ auto CostFunctionQuadraticSimple::stateControlDerivativeI template auto CostFunctionQuadraticSimple::evaluateTerminal() -> SCALAR { - typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_); - return SCALAR(0.5) * (x_deviation_final.transpose() * Q_final_ * x_deviation_final)(0); + // TODO: including Jr_ might not be required here (identical result) + Eigen::Matrix Jl, Jr; // TODO: ignore Jl cleanly + typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_, Jl, Jr); + return SCALAR(0.5) * (x_deviation_final.transpose() * Jr * Q_final_ * Jr.transpose() * x_deviation_final)(0); } template typename MANIFOLD::Tangent CostFunctionQuadraticSimple::stateDerivativeTerminal() { - Eigen::Matrix J_curr, J_ref; - typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_, J_ref, J_curr); - return Q_final_ * J_curr.transpose() * x_deviation_final; - //return - Q_final_ * x_deviation_final; - //typename MANIFOLD::Tangent x_deviation_final = this->x_.rminus(x_final_, J_ref, J_curr); + Eigen::Matrix Jl, Jr; // TODO: ignore Jl cleanly + typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_, Jl, Jr); + return Q_final_ * Jr.transpose() * x_deviation_final; } template auto CostFunctionQuadraticSimple::stateSecondDerivativeTerminal() -> state_matrix_t { - Eigen::Matrix J_curr, J_ref; - typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_, J_ref, J_curr); - return J_curr * Q_final_ * J_curr.transpose(); - //return Q_final_ ; - //typename MANIFOLD::Tangent x_deviation_final = this->x_.rminus(x_final_, J_ref, J_curr); - //return Q_final_; + Eigen::Matrix Jl, Jr; // TODO: ignore Jl cleanly + typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_, Jl, Jr); + return Jr * Q_final_ * Jr.transpose(); } template diff --git a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple.hpp b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple.hpp index 5d1278cd0..a912963b7 100644 --- a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple.hpp +++ b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple.hpp @@ -91,8 +91,8 @@ class CostFunctionQuadraticSimple : public CostFunctionQuadratic J_curr_; // TODO: rename - Eigen::Matrix J_ref_; // TODO: rename + Eigen::Matrix Jl_; // TODO: get rid of Jl cleanly + Eigen::Matrix Jr_; control_vector_t u_deviation_; control_vector_t u_nominal_; diff --git a/ct_optcon/include/ct/optcon/problem/LQOCProblem-impl.hpp b/ct_optcon/include/ct/optcon/problem/LQOCProblem-impl.hpp index c61b8f205..a9f832552 100644 --- a/ct_optcon/include/ct/optcon/problem/LQOCProblem-impl.hpp +++ b/ct_optcon/include/ct/optcon/problem/LQOCProblem-impl.hpp @@ -319,7 +319,7 @@ void LQOCProblem::setFromTimeInvariantLinearQuadraticProb { setZero(); - if (b.size() != K_ + 1) + if ((int)b.size() != K_ + 1) throw std::runtime_error("b size not correct."); // get LTI dynamics diff --git a/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver-impl.hpp b/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver-impl.hpp deleted file mode 100644 index a0796490e..000000000 --- a/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver-impl.hpp +++ /dev/null @@ -1,311 +0,0 @@ -/********************************************************************************************************************** -This file is part of the Control Toolbox (https://github.com/ethz-adrl/control-toolbox), copyright by ETH Zurich. -Licensed under the BSD-2 license (see LICENSE file in main directory) - **********************************************************************************************************************/ - -#pragma once - -namespace ct { -namespace optcon { - - -template -AugGNRiccatiSolver::AugGNRiccatiSolver(const std::shared_ptr& lqocProblem) - : LQOCSolver(lqocProblem), N_(-1) -{ - Eigen::initParallel(); - Eigen::setNbThreads(settings_.nThreadsEigen); -} - - -template -AugGNRiccatiSolver::AugGNRiccatiSolver(int N) -{ - changeNumberOfStages(N); -} - -template -void AugGNRiccatiSolver::solve() -{ - for (int i = this->lqocProblem_->getNumberOfStages() - 1; i >= 0; i--) - solveSingleStage(i); -} - -template -void AugGNRiccatiSolver::solveSingleStage(int n) -{ - if (n == this->lqocProblem_->getNumberOfStages() - 1) - initializeCostToGo(); - - designController(n); - - if (n > 0) - computeCostToGo(n); -} - -template -void AugGNRiccatiSolver::configure(const NLOptConSettings& settings) -{ - settings_ = settings; - H_corrFix_ = settings_.epsilon * ControlMatrix::Identity(); -} - -template -void AugGNRiccatiSolver::computeStatesAndControls() -{ - LQOCProblem_t& p = *this->lqocProblem_; - - this->x_sol_[0].setZero(); // should always be zero (fixed init state) - - for (int k = 0; k < this->lqocProblem_->getNumberOfStages(); k++) - { - //! control update rule in diff coordinates - this->u_sol_[k] = this->lv_[k] + this->L_[k] * this->x_sol_[k]; - - //! state update rule in diff coordinates - // Note that we need to transport the state update into the tagent space of k+1 - this->x_sol_[k + 1] = - p.Adj_x_[k + 1].transpose() * (p.A_[k] * this->x_sol_[k] + p.B_[k] * this->u_sol_[k] + p.b_[k]); - } -} - -template -void AugGNRiccatiSolver::computeFeedbackMatrices() -{ /*no action required, already computed in backward pass */ -} - -template -void AugGNRiccatiSolver::compute_lv() -{ /*no action required, already computed in backward pass*/ -} - -template -auto AugGNRiccatiSolver::getSmallestEigenvalue() -> SCALAR -{ - return smallestEigenvalue_; -} - -template -void AugGNRiccatiSolver::setProblemImpl(std::shared_ptr lqocProblem) -{ - if (lqocProblem->isConstrained()) - { - throw std::runtime_error( - "Selected wrong solver - AugGNRiccatiSolver cannot handle constrained problems. Use a different solver"); - } - - const int& N = lqocProblem->getNumberOfStages(); - changeNumberOfStages(N); -} - -template -void AugGNRiccatiSolver::changeNumberOfStages(int N) -{ - if (N <= 0) - return; - - if (N_ == N) - return; - - gv_.resize(N); - G_.resize(N); - - H_.resize(N); - Hi_.resize(N); - Hi_inverse_.resize(N); - - this->lv_.resize(N); - this->L_.resize(N); - - this->x_sol_.resize(N + 1); - this->u_sol_.resize(N); - - sv_.resize(N + 1); - S_.resize(N + 1); - sv_t_.resize(N + 1); - S_t_.resize(N + 1); - - N_ = N; -} - -template -void AugGNRiccatiSolver::initializeCostToGo() -{ - //! since intializeCostToGo is the first call, we initialize the smallestEigenvalue here. - smallestEigenvalue_ = std::numeric_limits::infinity(); - - // initialize quadratic approximation of cost to go - const int& N = this->lqocProblem_->getNumberOfStages(); - LQOCProblem_t& p = *this->lqocProblem_; - - S_[N] = p.Q_[N]; - sv_[N] = p.qv_[N]; - - if (ct::core::is_euclidean::value) // euclidean case - { - S_t_[N] = S_[N]; - sv_t_[N] = sv_[N]; - } - else - { // manifold case requires parallel transport of value function - S_t_[N] = p.Adj_x_[N] * S_[N] * p.Adj_x_[N].transpose(); - sv_t_[N] = p.Adj_x_[N] * sv_[N]; - } -} - -template -void AugGNRiccatiSolver::computeCostToGo(size_t k) -{ - LQOCProblem_t& p = *this->lqocProblem_; - - S_[k] = p.Q_[k]; - S_[k]/*.noalias()*/ += p.A_[k].transpose() * S_t_[k + 1] * p.A_[k]; // TODO: bring back all the noalias() - S_[k]/*.noalias()*/ -= this->L_[k].transpose() * Hi_[k] * this->L_[k]; - - S_[k] = 0.5 * (S_[k] + S_[k].transpose()).eval(); - - sv_[k] = p.qv_[k]; - sv_[k]/*.noalias()*/ += p.A_[k].transpose() * sv_t_[k + 1]; - sv_[k]/*.noalias()*/ += p.A_[k].transpose() * S_t_[k + 1] * p.b_[k]; - sv_[k]/*.noalias()*/ += this->L_[k].transpose() * Hi_[k] * this->lv_[k]; - sv_[k]/*.noalias()*/ += this->L_[k].transpose() * gv_[k]; - sv_[k]/*.noalias()*/ += G_[k].transpose() * this->lv_[k]; - - if (ct::core::is_euclidean::value) // euclidean case - { - S_t_[k] = S_[k]; - sv_t_[k] = sv_[k]; - } - else - { // manifold case requires parallel transport of value function to previous stage - S_t_[k] = p.Adj_x_[k] * S_[k] * p.Adj_x_[k].transpose(); - sv_t_[k] = p.Adj_x_[k] * sv_[k]; - } -} - -template -void AugGNRiccatiSolver::designController(size_t k) -{ - LQOCProblem_t& p = *this->lqocProblem_; - - gv_[k] = p.rv_[k]; - gv_[k].noalias() += p.B_[k].transpose() * sv_t_[k + 1]; - gv_[k].noalias() += p.B_[k].transpose() * S_t_[k + 1].template selfadjointView() * p.b_[k]; - - G_[k] = p.P_[k]; // TODO: G_ could be left away for the first stage (efficiency) - //G_[k].noalias() += B_[k].transpose() * S_[k+1] * A_[k]; - G_[k].noalias() += p.B_[k].transpose() * S_t_[k + 1].template selfadjointView() * p.A_[k]; - - H_[k] = p.R_[k]; - //H_[k].noalias() += B_[k].transpose() * S_[k+1] * B_[k]; - H_[k].noalias() += p.B_[k].transpose() * S_t_[k + 1].template selfadjointView() * p.B_[k]; - - if (settings_.fixedHessianCorrection) - { - if (settings_.epsilon > 1e-10) - Hi_[k] = H_[k] + settings_.epsilon * ControlMatrix::Identity(); - else - Hi_[k] = H_[k]; - - if (settings_.recordSmallestEigenvalue) - { - // compute eigenvalues with eigenvectors enabled - eigenvalueSolver_.compute(Hi_[k], Eigen::ComputeEigenvectors); - const ControlMatrix& V = eigenvalueSolver_.eigenvectors().real(); - const ControlVector& lambda = eigenvalueSolver_.eigenvalues(); - - smallestEigenvalue_ = std::min(smallestEigenvalue_, lambda.minCoeff()); - - // Corrected Eigenvalue Matrix - ControlMatrix D = ControlMatrix::Zero(); - // make D positive semi-definite (as described in IV. B.) - D.diagonal() = lambda.cwiseMax(settings_.epsilon); - - // reconstruct H - ControlMatrix Hi_regular = V * D * V.transpose(); - - // invert D - ControlMatrix D_inverse = ControlMatrix::Zero(); - // eigenvalue-wise inversion - D_inverse.diagonal() = -1.0 * D.diagonal().cwiseInverse(); - ControlMatrix Hi_inverse_regular = V * D_inverse * V.transpose(); - - if (!Hi_inverse_[k].isApprox(Hi_inverse_regular, 1e-4)) - { - std::cout << "warning, inverses not identical at " << k << std::endl; - std::cout << "Hi_inverse_fixed - Hi_inverse_regular: " << std::endl - << Hi_inverse_[k] - Hi_inverse_regular << std::endl - << std::endl; - } - } - - Hi_inverse_[k] = -Hi_[k].template selfadjointView().llt().solve(ControlMatrix::Identity()); - - // calculate FB gain update // TODO: this could be left away for the first stage (efficiency) - this->L_[k].noalias() = Hi_inverse_[k].template selfadjointView() * G_[k]; - - // calculate FF update - this->lv_[k].noalias() = Hi_inverse_[k].template selfadjointView() * gv_[k]; - } - else - { - // compute eigenvalues with eigenvectors enabled - eigenvalueSolver_.compute(H_[k], Eigen::ComputeEigenvectors); - const ControlMatrix& V = eigenvalueSolver_.eigenvectors().real(); - const ControlVector& lambda = eigenvalueSolver_.eigenvalues(); - - if (settings_.recordSmallestEigenvalue) - { - smallestEigenvalue_ = std::min(smallestEigenvalue_, lambda.minCoeff()); - } - - // Corrected Eigenvalue Matrix - ControlMatrix D = ControlMatrix::Zero(); - // make D positive semi-definite (as described in IV. B.) - D.diagonal() = lambda.cwiseMax(settings_.epsilon); - - // reconstruct H - Hi_[k].noalias() = V * D * V.transpose(); - - // invert D - ControlMatrix D_inverse = ControlMatrix::Zero(); - // eigenvalue-wise inversion - D_inverse.diagonal() = -1.0 * D.diagonal().cwiseInverse(); - Hi_inverse_[k].noalias() = V * D_inverse * V.transpose(); - - // calculate FB gain update - this->L_[k].noalias() = Hi_inverse_[k] * G_[k]; - - // calculate FF update - this->lv_[k].noalias() = Hi_inverse_[k] * gv_[k]; - } -} - -template -void AugGNRiccatiSolver::logToMatlab() -{ -#ifdef MATLAB_FULL_LOG - - matFile_.open("AugGNRiccatiSolver.mat"); - - matFile_.put("sv", sv_.toImplementation()); - matFile_.put("S", S_.toImplementation()); - matFile_.put("L", this->L_.toImplementation()); - matFile_.put("H", H_.toImplementation()); - matFile_.put("Hi_", Hi_.toImplementation()); - matFile_.put("Hi_inverse", Hi_inverse_.toImplementation()); - matFile_.put("G", G_.toImplementation()); - matFile_.put("gv", gv_.toImplementation()); - - matFile_.close(); -#endif -} - -template -void AugGNRiccatiSolver::initializeAndAllocate() -{ - // do nothing -} - -} // namespace optcon -} // namespace ct diff --git a/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver.hpp b/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver.hpp deleted file mode 100644 index 2bf54c7ef..000000000 --- a/ct_optcon/include/ct/optcon/solver/lqp/AugGNRiccatiSolver.hpp +++ /dev/null @@ -1,113 +0,0 @@ -/********************************************************************************************************************** -This file is part of the Control Toolbox (https://github.com/ethz-adrl/control-toolbox), copyright by ETH Zurich. -Licensed under the BSD-2 license (see LICENSE file in main directory) -**********************************************************************************************************************/ - -#pragma once - -#include "LQOCSolver.hpp" - -#ifdef MATLAB_FULL_LOG -#include -#endif - -namespace ct { -namespace optcon { - -/*! - * This class implements an general Riccati backward pass for solving an unconstrained - * linear-quadratic Optimal Control problem - */ -template -class AugGNRiccatiSolver : public LQOCSolver -{ -public: - EIGEN_MAKE_ALIGNED_OPERATOR_NEW - - static const int STATE_DIM = MANIFOLD::TangentDim; - static const int state_dim = STATE_DIM; - static const int control_dim = CONTROL_DIM; - - using Base = LQOCSolver; - using SCALAR = typename Base::SCALAR; - typedef typename Base::LQOCProblem_t LQOCProblem_t; - - typedef ct::core::StateMatrix StateMatrix; - typedef ct::core::StateMatrixArray StateMatrixArray; - typedef ct::core::ControlVector ControlVector; - typedef ct::core::ControlMatrix ControlMatrix; - typedef ct::core::ControlMatrixArray ControlMatrixArray; - typedef ct::core::StateControlMatrixArray StateControlMatrixArray; - typedef ct::core::FeedbackArray FeedbackArray; - typedef ct::core::ControlVectorArray ControlVectorArray; - - AugGNRiccatiSolver(const std::shared_ptr& lqocProblem = nullptr); - - AugGNRiccatiSolver(int N); - - virtual void solve() override; - - virtual void initializeAndAllocate() override; - - virtual void solveSingleStage(int N) override; - - virtual void configure(const NLOptConSettings& settings) override; - - virtual void computeStatesAndControls() override; - - virtual void computeFeedbackMatrices() override; - - virtual void compute_lv() override; - - virtual SCALAR getSmallestEigenvalue() override; - -protected: - /*! - * resize matrices - * @param lqocProblem - */ - virtual void setProblemImpl(std::shared_ptr lqocProblem) override; - - void changeNumberOfStages(int N); - - void initializeCostToGo(); - - void computeCostToGo(size_t k); - - void designController(size_t k); - - void logToMatlab(); - - NLOptConSettings settings_; - - ControlVectorArray gv_; - FeedbackArray G_; - - ControlMatrixArray H_; - ControlMatrixArray Hi_; - ControlMatrixArray Hi_inverse_; - ControlMatrix H_corrFix_; - - //! terms for quadratic value function - ct::core::DiscreteArray sv_; - StateMatrixArray S_; - //! parallel-transported terms of value function - ct::core::DiscreteArray sv_t_; - StateMatrixArray S_t_; - - int N_; - - SCALAR smallestEigenvalue_; - - //! Eigenvalue solver, used for inverting the Hessian and for regularization - Eigen::SelfAdjointEigenSolver> eigenvalueSolver_; - -//! if building with MATLAB support, include matfile -#ifdef MATLAB_FULL_LOG - matlab::MatFile matFile_; -#endif //MATLAB -}; - - -} // namespace optcon -} // namespace ct diff --git a/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp b/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp index ea9748b63..f6df146e5 100644 --- a/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp +++ b/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp @@ -63,7 +63,7 @@ void GNRiccatiSolver::computeStatesAndControls() this->u_sol_[k] = this->lv_[k] + this->L_[k] * this->x_sol_[k]; //! state update rule in diff coordinates - StateMatrix A_orig = p.Adj_x_[k + 1] * p.A_[k]; // A "without trick for backward pass" + StateMatrix A_orig = p.Adj_x_[k + 1] * p.A_[k]; // A "without trick for backward pass" //TODO: document this StateControlMatrix B_orig = p.Adj_x_[k + 1] * p.B_[k]; // B "without trick for backward pass" typename MANIFOLD::Tangent b_orig = p.Adj_x_[k + 1] * p.b_[k]; // b "without trick for backward pass" // Note that we need to transport the state update into the tagent space of k+1 diff --git a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp index e7dd2db10..4ecf41b5a 100644 --- a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp +++ b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp @@ -1,8 +1,5 @@ #include -#include -#include - using namespace ct::core; using namespace ct::optcon; @@ -10,14 +7,16 @@ const bool verbose = true; template -class CompositeManifold; // forward declaratoin +class CompositeManifold; // forward declaration, do not delete template class CompositeManifoldTangent { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW + static constexpr size_t DoF = POS_TANGENT::DoF + VEL_TANGENT::DoF; + using Scalar = double; using Jacobian = Eigen::Matrix; using OptJacobianRef = tl::optional>; @@ -35,8 +34,8 @@ class CompositeManifoldTangent // TODO a dimension assert would not hurt } - CompositeManifoldTangent(const CompositeManifoldTangent& resulting_t) - : t_pos_(resulting_t.t_pos_), t_vel_(resulting_t.t_vel_), coeffs_(resulting_t.coeffs_) + CompositeManifoldTangent(const CompositeManifoldTangent& arg) + : t_pos_(arg.t_pos_), t_vel_(arg.t_vel_), coeffs_(arg.coeffs_) { } @@ -100,10 +99,9 @@ class CompositeManifoldTangent CompositeManifold exp(OptJacobianRef J_m_t = OptJacobianRef{}) const { - //std::cout << "now calling exp..." << std::endl; if (J_m_t) { - throw std::runtime_error("J-m-resulting_t in tangent exp not impl yet."); + throw std::runtime_error("J-m-t in tangent exp not impl yet."); } CompositeManifold c( @@ -134,31 +132,31 @@ class CompositeManifoldTangent }; template -_Stream& operator<<(_Stream& s, const CompositeManifoldTangent& resulting_t) +_Stream& operator<<(_Stream& s, const CompositeManifoldTangent& t) { - s << resulting_t.coeffs().transpose(); + s << t.coeffs().transpose(); return s; } template -auto operator*(const Eigen::MatrixBase<_EigenDerived>& J, const CompositeManifoldTangent& resulting_t) +auto operator*(const Eigen::MatrixBase<_EigenDerived>& J, const CompositeManifoldTangent& t) { - return J * resulting_t.coeffs(); + return J * t.coeffs(); } template -auto operator*(const CompositeManifoldTangent& resulting_t, const double& s) +auto operator*(const CompositeManifoldTangent& t, const double& s) { - return resulting_t.coeffs() * s; + return t.coeffs() * s; } template -auto operator+(const Eigen::MatrixBase<_EigenDerived>& tb, const CompositeManifoldTangent& resulting_t) +auto operator+(const Eigen::MatrixBase<_EigenDerived>& tb, const CompositeManifoldTangent& t) { CompositeManifoldTangent res; CompositeManifoldTangent rhs(tb); - res.set_pos(resulting_t.pos() + rhs.pos()); - res.set_vel(resulting_t.vel() + rhs.vel()); + res.set_pos(t.pos() + rhs.pos()); + res.set_vel(t.vel() + rhs.vel()); return res; } @@ -171,7 +169,7 @@ class CompositeManifold using Tangent = CompositeManifoldTangent; static constexpr size_t TangentDim = Tangent::DoF; - static constexpr size_t Dim = POS_MAN::Dim + VEL_MAN::Dim + 1; // todo: this is hacky + static constexpr size_t Dim = POS_MAN::Dim + VEL_MAN::Dim + 1; // TODO: this is hacky using Scalar = double; using Jacobian = Eigen::Matrix; @@ -193,17 +191,17 @@ class CompositeManifold m_vel_.setIdentity(); } - const Eigen::Matrix coeffs() const // todo: return by value is bad + const Eigen::Matrix coeffs() const // TODO: return by value is bad { Eigen::Matrix c; c << m_pos_.coeffs(), m_vel_.coeffs(); return c; } - CompositeManifold operator+(const Tangent& resulting_t) const { return rplus(resulting_t); } - //CompositeManifold operator+=(const Tangent& resulting_t) + CompositeManifold operator+(const Tangent& t) const { return rplus(t); } + //CompositeManifold operator+=(const Tangent& t) //{ - // *this = this->rplus(resulting_t); + // *this = this->rplus(t); // return *this; // todo test //} @@ -212,19 +210,19 @@ class CompositeManifold * NOTE: the operator also has to parallel transport velocities to the new tangent plane. * Since for rplus, the rhs is expressed in the tangent space of the lhs, we choose: "first add, then transport" */ - CompositeManifold rplus(const Tangent& resulting_t, OptJacobianRef Jl = {}, OptJacobianRef Jr = {}) const + CompositeManifold rplus(const Tangent& t, OptJacobianRef Jl = {}, OptJacobianRef Jr = {}) const { typename POS_MAN::Jacobian pJl, pJr; typename VEL_MAN::Jacobian vJl, vJr; - VEL_MAN v_new_local = vel().rplus(resulting_t.vel(), vJl, vJr); + VEL_MAN v_new_local = vel().rplus(t.vel(), vJl, vJr); - VEL_MAN v_new_transported(resulting_t.pos().exp().adj().transpose() * v_new_local.coeffs()); + VEL_MAN v_new_transported(t.pos().exp().adj().transpose() * v_new_local.coeffs()); CompositeManifold c; c.vel() = v_new_transported; - //c.vel() = c.vel().rplus(resulting_t.vel(), vJl, vJr); - c.pos() = pos().rplus(resulting_t.pos(), pJl, pJr); // todo make jacobians optional + //c.vel() = c.vel().rplus(t.vel(), vJl, vJr); + c.pos() = pos().rplus(t.pos(), pJl, pJr); // todo make jacobians optional if (Jl) { @@ -255,12 +253,12 @@ class CompositeManifold typename POS_MAN::Jacobian pJl, pJr; typename VEL_MAN::Jacobian vJl, vJr; - Tangent resulting_t; - resulting_t.set_pos(pos().rminus(rhs.pos(), pJl, pJr)); // todo make jacobians optional - VEL_MAN v_lhs_transported(resulting_t.pos().exp().adj() * vel().coeffs()); + Tangent t; + t.set_pos(pos().rminus(rhs.pos(), pJl, pJr)); // todo make jacobians optional + VEL_MAN v_lhs_transported(t.pos().exp().adj() * vel().coeffs()); VEL_TANGENT v_new_local = v_lhs_transported.rminus(rhs.vel(), vJl, vJr); - resulting_t.set_vel(v_new_local); + t.set_vel(v_new_local); if (Jl) { @@ -276,7 +274,7 @@ class CompositeManifold (*Jr).template bottomRightCorner() = vJr; } - return resulting_t; + return t; } @@ -286,23 +284,13 @@ class CompositeManifold throw std::runtime_error("J_t_m compuation not defined."); return rminus(CompositeManifold::Identity(), J_t_m); - //resulting_t.set_pos(m_pos_.log()); - //resulting_t.set_vel(m_vel_.log()); - //return resulting_t; } - /** - * @brief The question - * The fundamental question remains: what is the velocity adjoint? - * - multiple shooting: in the forward update rule, the computed velocities want to be transported to the next states manifold coordinate system - * - single shooting: seems to be much more stable when velocity part adjoint is identity -- is the value function more stable??? - */ Jacobian adj() const { Jacobian J = Jacobian::Zero(); J.template topLeftCorner() = m_pos_.adj(); - J.template bottomRightCorner() = - m_pos_.adj(); //.setIdentity(); // = m_pos_.adj(); // TODO: what exactly is it? + J.template bottomRightCorner() = m_pos_.adj(); return J; } @@ -345,88 +333,23 @@ class DiscrSO3LTITestSystem final : public ct::core::ControlledSystem(0, 0, 0); -// Eigen::Matrix3d b; -// b.setIdentity(); // some basis, assumed to be expressed in the manifolds over which we iterate -// -// for (double theta = M_PI / 2; theta <= 2 * M_PI; theta += M_PI / 2) -// { -// std::cout << "case theta = " << theta << std::endl; -// auto m = manif::SE2(0, 0, theta); -// Eigen::Matrix3d Jl, Jr; // jacobians -// auto d_rminus = m.rminus(origin, Jl, Jr); -// auto adj_rminus = d_rminus.exp().adj(); // the adjoint for transportation to the origin -// -// auto d_between = m.between(origin); -// auto adj_between = d_rminus.exp().adj(); -// -// // ASSERT_TRUE(adj_rminus.isApprox(adj_between)); -// -// std::cout << adj_rminus * b << std::endl << std::endl; // express basis b in origin-manifold -// std::cout << adj_between * b << std::endl << std::endl; // express basis b in origin-manifold -// // std::cout << Jr * Jl * b << std::endl << std::endl; // TODO: how does the Jac relate to the adjoint here? -// std::cout << std::endl; -// } -// -// // auto m_pi_2 = manif::SE2(0, 0, M_PI / 2); -// // auto m_pi = manif::SE2(0, 0, M_PI); -// // auto m_mpi_2 = manif::SE2(0, 0, -M_PI / 2); -// // -// // -// // std::cout << m_0.adj() * b << std::endl << std::endl; -// // std::cout << m_pi_2.adj() * b << std::endl << std::endl; -// // std::cout << m_pi.adj() * b << std::endl << std::endl; -// // std::cout << m_mpi_2.adj() * b << std::endl << std::endl; -//} - int main(int argc, char** argv) { - //ManifoldState_t x_test; - //x_test.setIdentity(); - //Eigen::Vector3d vel_init(1.0, 0.0, 0.0); - //x_test.vel() = manif::R3d(vel_init); - ////x0.pos() = manif::SO3(0, 0, M_PI); - //std::cout << "m: " << x_test << std::endl; - //ManifoldState_t::Tangent t_test; - //t_test.set_coeff(2, M_PI / 2); - //t_test.set_coeff(3, 1); - // - //std::cout << "resulting_t: " << t_test << std::endl; - //ManifoldState_t newX = x_test.rplus(t_test); - //std::cout << "newX: " << newX << std::endl; - //auto t_reverse = newX.rminus(x_test); - //std::cout << "t_reverse:" << t_reverse << std::endl; - //std::cout << "t_reverse.exp() " << t_reverse.exp() << std::endl; - //std::cout << "t_reverse.exp().log() " << t_reverse.exp().log() << std::endl; - // - //std::cout << "parallel transport study: " << std::endl; - //auto ADJ = t_reverse.exp().adj(); - //std::cout << "adj: " << ADJ << std::endl; - //std::cout << "adj*t_test: " << ADJ * t_test << std::endl; - //exit(0); - std::cout << std::fixed; const bool use_single_shooting = true; // toggle between single and multiple shooting @@ -436,7 +359,7 @@ int main(int argc, char** argv) ManifoldState_t x0; x0.setIdentity(); - x0.pos() = manif::SO3(M_PI/2, M_PI/2, 0.0); + x0.pos() = manif::SO3(M_PI / 2, M_PI / 2, 0.0); ct::core::DiscreteArray x_traj(N + 1, x0); // init state trajectory, will be overwritten ct::core::DiscreteArray b( N + 1, ManifoldState_t::Tangent::Zero()); // defect traj, will be overwritten @@ -454,27 +377,12 @@ int main(int argc, char** argv) } // create instances of HPIPM and an unconstrained Gauss-Newton Riccati solver - std::shared_ptr> gnRiccatiSolver( - new GNRiccatiSolver); - std::shared_ptr> augGnRiccatiSolver( - new AugGNRiccatiSolver); - - // store them, and identifying names, in a vectors - std::vector>> lqocSolvers; - lqocSolvers.push_back(gnRiccatiSolver); - lqocSolvers.push_back(augGnRiccatiSolver); - std::vector solverNames = {"Riccati", "AugRiccati"}; + std::shared_ptr> solver(new GNRiccatiSolver); // create linear-quadratic optimal control problem containers - std::vector>> problems; - std::shared_ptr> lqocProblem1( - new LQOCProblem(N)); - std::shared_ptr> lqocProblem2( + std::shared_ptr> problem( new LQOCProblem(N)); - problems.push_back(lqocProblem1); - problems.push_back(lqocProblem2); - // create a discrete-time manifold system std::shared_ptr> exampleSystem( new DiscrSO3LTITestSystem(dt)); @@ -488,7 +396,7 @@ int main(int argc, char** argv) Q_final.setZero(); Q_final.diagonal() << 10000, 10000, 10000, 10000, 100000, 10000; Q.setZero(); - // Q.diagonal() << 3, 3, 3, 3, 3, 3; + Q.diagonal() << 3, 3, 3, 3, 3, 3; R.setZero(); R.diagonal() << 1, 1, 1; ManifoldState_t x_final; @@ -496,6 +404,8 @@ int main(int argc, char** argv) std::cout << "desired final state: " << x_final << std::endl; ManifoldState_t x_nominal = x_final; ct::core::ControlVector u_nom = ct::core::ControlVector::Zero(); + + // TODO: this currently only works with CostFunctionQuadratic simple (others not ported yet) std::shared_ptr> costFunction( new CostFunctionQuadraticSimple(Q, R, x_nominal, u_nom, x_final, Q_final)); @@ -506,151 +416,59 @@ int main(int argc, char** argv) x_curr = x0; x_traj.front() = x0; std::cout << "integrate an random initial state with the unstable system" << std::endl; - std::cout << std::setprecision(4) << "m: " << x_curr << "\resulting_t tan: " << x_curr.log() << std::endl; + //std::cout << std::setprecision(4) << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; for (size_t i = 0; i < N; i++) { exampleSystem->computeControlledDynamics(x_traj[i], 0, u_traj[i], dx); - std::cout << "dx" << dx << std::endl; + //std::cout << "dx" << dx << std::endl; x_curr = x_traj[i].rplus(dx); if (use_single_shooting) x_traj[i + 1] = x_curr; b[i] = dx - x_traj[i + 1].rminus(x_traj[i]); - std::cout << "b: " << b[i] << std::endl; - std::cout << std::setprecision(10) << "m: " << x_curr << "\resulting_t tan: " << x_curr.log() << std::endl; + //std::cout << "b: " << b[i] << std::endl; + //std::cout << std::setprecision(10) << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; } size_t nIter = 25; for (size_t iter = 0; iter < nIter; iter++) { - // initialize the optimal control problems for both solvers - problems[0]->setFromTimeInvariantLinearQuadraticProblem(x_traj, u_traj, *linearizer, *costFunction, b, dt); - problems[1]->setFromTimeInvariantLinearQuadraticProblem(x_traj, u_traj, *linearizer, *costFunction, b, dt); + // initialize the LQ optimal control problem + problem->setFromTimeInvariantLinearQuadraticProblem(x_traj, u_traj, *linearizer, *costFunction, b, dt); - - // HACKY corrections // TODO: move somewhere meaningful - for (size_t idx : {0, 1}) + // dynamics transportation for backwards riccati pass and forward solution candidate update + for (size_t i = 0; i < N; i++) { - // intermediate stages cost transportation - for (size_t i = 0; i < N; i++) - { - auto e = x_nominal.rminus(x_traj[i]); - // compute PT matrix w.r.resulting_t. current ref traj // TODO: clarify formulation of error in cost function - auto e_adj = (e.exp()).adj(); - problems[idx]->Q_[i] = e_adj * problems[idx]->Q_[i] * - e_adj.transpose(); // TODO: sort out that thing with the cost function - problems[idx]->qv_[i] = e_adj * problems[idx]->qv_[i]; - } - // terminal stage transportation - // TODO: clarifiy formulation of error in cost function - // auto e = x_final.rminus(x_traj[N]); // compute PT matrix w.r.resulting_t. current ref traj - // auto e_adj = (e.exp()).adj(); - //std::cout << "cost adj" << std::endl << e_adj << std::endl; - //problems[idx]->Q_.back() = e_adj * problems[idx]->Q_.back() * e_adj.transpose(); - //problems[idx]->qv_.back() = e_adj * problems[idx]->qv_.back(); - //std::cout << "problems[idx]->Q_.back()" << std::endl << problems[idx]->Q_.back() << std::endl; - //std::cout << "problems[idx]->qv_.back()" << std::endl << problems[idx]->qv_.back() << std::endl; - - // dynamics transportation - for (size_t i = 0; i < N; i++) - { - // std::cout << "dyn transport matrices" << std::endl; - //Eigen::Matrix3d Jl, Jr; - auto l = x_traj[i + 1].rminus(x_traj[i]); - auto l_adj = (l.exp()).adj(); - problems[idx]->Adj_x_[i + 1] = l_adj; // parallel transport matrix / adjoint from stage k+1 to stage k - - if (idx == 0) // make the corrections for the standard riccati solver - { - problems[idx]->A_[i] = l_adj.transpose() * problems[idx]->A_[i]; - problems[idx]->b_[i] = l_adj.transpose() * problems[idx]->b_[i]; - problems[idx]->B_[i] = l_adj.transpose() * problems[idx]->B_[i]; - } - } + auto l = x_traj[i + 1].rminus(x_traj[i]); + auto l_adj = (l.exp()).adj(); + problem->Adj_x_[i + 1] = l_adj; // parallel transport matrix / adjoint from stage k+1 to stage k + + // make the necessary overloads, which allows the standard riccati solver to backpropagate on manifolds + problem->A_[i] = l_adj.transpose() * problem->A_[i]; + problem->B_[i] = l_adj.transpose() * problem->B_[i]; + problem->b_[i] = l_adj.transpose() * problem->b_[i]; + } - // set the problem pointers - lqocSolvers[idx]->setProblem(problems[idx]); + // set the problem pointers + solver->setProblem(problem); - // allocate memory (if required) - lqocSolvers[idx]->initializeAndAllocate(); + // allocate memory (if required) + solver->initializeAndAllocate(); - // solve the problems... - lqocSolvers[idx]->solve(); + // solve the problem + solver->solve(); - // postprocess data - lqocSolvers[idx]->compute_lv(); - lqocSolvers[idx]->computeFeedbackMatrices(); - lqocSolvers[idx]->computeStatesAndControls(); - } + // postprocess data + solver->compute_lv(); + solver->computeFeedbackMatrices(); + solver->computeStatesAndControls(); + + // retrieve solutions from solver + auto xSol = solver->getSolutionState(); + auto uSol = solver->getSolutionControl(); + ct::core::FeedbackArray KSol = solver->getSolutionFeedback(); + ct::core::ControlVectorArray lv_sol = solver->get_lv(); - // retrieve solutions from both solvers - auto xSol_riccati = lqocSolvers[0]->getSolutionState(); - auto uSol_riccati = lqocSolvers[0]->getSolutionControl(); - ct::core::FeedbackArray KSol_riccati = lqocSolvers[0]->getSolutionFeedback(); - ct::core::ControlVectorArray lv_sol_riccati = lqocSolvers[0]->get_lv(); - - auto xSol_aug_riccati = lqocSolvers[1]->getSolutionState(); - auto uSol_aug_riccati = lqocSolvers[1]->getSolutionControl(); - ct::core::FeedbackArray KSol_aug_riccati = lqocSolvers[1]->getSolutionFeedback(); - ct::core::ControlVectorArray lv_sol_aug_riccati = lqocSolvers[1]->get_lv(); - - // compare the quantities - //for (size_t i = 0; i < lv_sol_riccati.size(); i++) - //{ - // if ((lv_sol_riccati[i] - lv_sol_aug_riccati[i]).array().abs().maxCoeff() > 1e-8) - // { - // std::cout << std::setprecision(10) << lv_sol_riccati[i].transpose() << std::endl; - // std::cout << std::setprecision(10) << lv_sol_aug_riccati[i].transpose() << std::endl; - // throw std::runtime_error("lv solutions do not match"); - // } - //} - //for (size_t i = 0; i < KSol_riccati.size(); i++) - //{ - // if (KSol_riccati[i].isApprox(KSol_aug_riccati[i], 1e-6) == false) - // throw std::runtime_error("K solutions do not match"); - //} - // - //for (size_t i = 0; i < uSol_riccati.size(); i++) - //{ - // if ((uSol_riccati[i] - uSol_aug_riccati[i]).array().abs().maxCoeff() > 1e-8) - // { - // std::cout << "for index " << i << std::endl; - // std::cout << std::setprecision(10) << uSol_riccati[i].transpose() << std::endl; - // std::cout << std::setprecision(10) << uSol_aug_riccati[i].transpose() << std::endl; - // throw std::runtime_error("u solutions do not match"); - // } - //} - //for (size_t i = 0; i < xSol_riccati.size(); i++) - //{ - // if ((xSol_riccati[i] - xSol_aug_riccati[i]).coeffs().array().abs().maxCoeff() > 1e-8) - // { - // std::cout << xSol_riccati[i].transpose() << std::endl; - // std::cout << xSol_aug_riccati[i].transpose() << std::endl; - // throw std::runtime_error("x solutions do not match"); - // } - //} - - //std::cout << std::setprecision(4) << "dx solution from riccati solver and directly added state_traj, iter " - // << iter << std::endl; - //ct::core::DiscreteArray x_solver_direct(N + 1); - //for (size_t j = 0; j < xSol_riccati.size(); j++) - //{ - // x_solver_direct[j] = x_traj[j] + xSol_riccati[j]; - // double angularDiff = 0; - // if (j > 0) - // { - // Eigen::Quaterniond old_rot(x_solver_direct[j - 1].w(), x_solver_direct[j - 1].x(), - // x_solver_direct[j - 1].y(), x_solver_direct[j - 1].z()); - // Eigen::Quaterniond new_rot( - // x_solver_direct[j].w(), x_solver_direct[j].x(), x_solver_direct[j].y(), x_solver_direct[j].z()); - // angularDiff = old_rot.angularDistance(new_rot); - // } - // std::cout << "m:" << x_solver_direct[j] << "\resulting_t dx:" << xSol_riccati[j].transpose() - // << "\resulting_t -- rot diff norm(): " << angularDiff << std::endl; - //} - - std::cout << std::setprecision(4) << "Forward integrated closed-loop solution, iter :" << iter << std::endl; x_curr = x0; - std::cout << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; ct::core::DiscreteArray x_traj_prev = x_traj; double d_cum_sum = 0; double dx_cum_sum = 0; @@ -658,36 +476,29 @@ int main(int argc, char** argv) for (size_t i = 0; i < N; i++) { dx.setZero(); - //Eigen::Quaterniond old_rot(x_traj[i].w(), x_traj[i].x(), x_traj[i].y(), x_traj[i].z()); if (use_single_shooting) { - // TODO: some term is missing here; ManifoldState_t::Tangent x_err = x_traj[i].rminus(x_traj_prev[i]); - u_traj[i] += lv_sol_riccati[i] + KSol_riccati[i] * (x_err /*- eucl. part here*/); + u_traj[i] += lv_sol[i] + KSol[i] * x_err; exampleSystem->computeControlledDynamics(x_traj[i], i * dt, u_traj[i], dx); x_traj[i + 1] = x_traj[i] + dx; } else // multiple shooting { - u_traj[i] += uSol_riccati[i]; - x_traj[i + 1] = x_traj[i + 1].rplus(xSol_riccati[i + 1]); + u_traj[i] += uSol[i]; + x_traj[i + 1] = x_traj[i + 1].rplus(xSol[i + 1]); exampleSystem->computeControlledDynamics(x_traj[i], i * dt, u_traj[i], dx); } - //Eigen::Quaterniond new_rot(x_traj[i + 1].w(), x_traj[i + 1].x(), x_traj[i + 1].y(), x_traj[i + 1].z()); - //std::cout << "m: " << x_traj[i + 1] << "\resulting_t log: " << x_traj[i + 1].log() << " u: " << u_traj[i].transpose() - // << std::endl; - // compute defect b[i] = dx - (x_traj[i + 1].rminus(x_traj[i])); // compute update norms d_cum_sum += b[i].coeffs().norm(); - dx_cum_sum += xSol_riccati[i + 1].coeffs().norm(); - //std::cout << "b: " << b[i] << std::endl; + dx_cum_sum += xSol[i + 1].coeffs().norm(); - // compute running cost + // compute intermediate cost costFunction->setCurrentStateAndControl(x_traj[i], u_traj[i], i * dt); cost_sum += costFunction->evaluateIntermediate(); } @@ -698,7 +509,6 @@ int main(int argc, char** argv) std::cout << std::setprecision(10) << "d_norm: \t " << d_cum_sum << "\t dx_norm: \t" << dx_cum_sum << " \t Jcost: " << cost_sum << std::endl; - std::cout << std::endl << std::endl; } // end iter return 1; } \ No newline at end of file From 7048b4c8d1ac203792ff634a5ec812256a4722ff Mon Sep 17 00:00:00 2001 From: acxz <17132214+acxz@users.noreply.github.com> Date: Mon, 18 May 2020 17:54:09 -0400 Subject: [PATCH 06/21] remove unneeded computation L^T * gv + L^T * Hi * lv evaluates to zero --- ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp b/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp index 74021f1ef..8f90f0c9e 100644 --- a/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp +++ b/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp @@ -157,8 +157,6 @@ void GNRiccatiSolver::computeCostToGo(size_t k) sv_[k] = p.qv_[k]; sv_[k].noalias() += p.A_[k].transpose() * sv_[k + 1]; sv_[k].noalias() += p.A_[k].transpose() * S_[k + 1] * p.b_[k]; - sv_[k].noalias() += this->L_[k].transpose() * Hi_[k] * this->lv_[k]; - sv_[k].noalias() += this->L_[k].transpose() * gv_[k]; sv_[k].noalias() += G_[k].transpose() * this->lv_[k]; } From f8d083d7ccb53cc5cdd445f9db7f732ce61b7c2c Mon Sep 17 00:00:00 2001 From: hmcm Date: Wed, 20 May 2020 14:33:32 +0200 Subject: [PATCH 07/21] fixed bug concerning hpipm_status display --- ct_optcon/include/ct/optcon/solver/lqp/HPIPMInterface-impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct_optcon/include/ct/optcon/solver/lqp/HPIPMInterface-impl.hpp b/ct_optcon/include/ct/optcon/solver/lqp/HPIPMInterface-impl.hpp index c1f018ed0..f0abb7988 100644 --- a/ct_optcon/include/ct/optcon/solver/lqp/HPIPMInterface-impl.hpp +++ b/ct_optcon/include/ct/optcon/solver/lqp/HPIPMInterface-impl.hpp @@ -198,7 +198,7 @@ void HPIPMInterface::solve() { printf("\n -> Solver failed! Minimum step length reached\n"); } - else if (hpipm_status_ == 2) + else if (hpipm_status_ == 3) { printf("\n -> Solver failed! NaN in computations\n"); } From 9b9cedd227449cfa26bd7e25dc61615b755f5c28 Mon Sep 17 00:00:00 2001 From: Marco Frigerio Date: Wed, 20 May 2020 15:38:27 +0200 Subject: [PATCH 08/21] Fix links to RobCoGen in the readme --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fe586f98c..c08222b81 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ The CT was designed with the following features in mind: - solve large scale optimal control problems in MPC fashion. - **Robot Modelling, Rigid Body Kinematics and Dynamics**: - - straight-forward interface to the state-of the art rigid body dynamics modelling tool RobCoGen. + - straight-forward interface to the state-of the art rigid body dynamics modelling tool [RobCoGen][robcogen-url]. - implementation of a basic nonlinear-programming inverse kinematics solver for fix-base robots. - **Automatic Differentiation**: @@ -196,9 +196,10 @@ The four different main modules are detailed in the following. - Operational Space Controllers - Basic soft **auto-differentiable contact model** for arbitrary frames (ct::rbd::EEContactModel) - **Actuator dynamics** (ct::rbd::ActuatorDynamics) - - Backend uses RobCoGen \cite frigerioCodeGen, + - Backend uses [RobCoGen][robcogen-url], a highly efficient Rigid Body Dynamics library +[robcogen-url]: https://robcogenteam.bitbucket.io/ ### ct_models From cd7312038f1d62a29dbbfd23808bab65c6e79b7d Mon Sep 17 00:00:00 2001 From: Romain Reignier Date: Fri, 22 May 2020 11:23:08 +0200 Subject: [PATCH 09/21] Downgrade minimum required CMake version to 3.5 (xenial) and use newer Python Cmake helpers for Cmake > 3.12 --- ct/CMakeLists.txt | 2 +- ct_core/CMakeLists.txt | 40 ++++++++++++++++++++++++++++------------ ct_doc/CMakeLists.txt | 2 +- ct_models/CMakeLists.txt | 2 +- ct_optcon/CMakeLists.txt | 2 +- ct_rbd/CMakeLists.txt | 2 +- 6 files changed, 33 insertions(+), 17 deletions(-) diff --git a/ct/CMakeLists.txt b/ct/CMakeLists.txt index 0df155c5c..8890c7609 100644 --- a/ct/CMakeLists.txt +++ b/ct/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.14.7) +cmake_minimum_required(VERSION 3.5) project(ct VERSION 3.0.2 LANGUAGES CXX) #Make sure metapackage does not fail when building documentation diff --git a/ct_core/CMakeLists.txt b/ct_core/CMakeLists.txt index 89840ce8f..00282ee0b 100755 --- a/ct_core/CMakeLists.txt +++ b/ct_core/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.14.7) +cmake_minimum_required (VERSION 3.5) include(${CMAKE_CURRENT_SOURCE_DIR}/../ct/cmake/compilerSettings.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/../ct/cmake/explicitTemplateHelpers.cmake) @@ -63,18 +63,30 @@ else() message(STATUS "COMPILING WITHOUT QWT") endif() -find_package(Python COMPONENTS Development NumPy QUIET) -if (Python_FOUND AND ${Python_VERSION_MAJOR} EQUAL 3) - message(STATUS "Found python " ${Python_VERSION}) - list(APPEND ct_core_COMPILE_DEFINITIONS PLOTTING_ENABLED) - set(PLOTTING_ENABLED ON) - message(STATUS "Python library path ... " ${Python_LIBRARIES}) - list(APPEND ct_core_LIBS ${Python_LIBRARIES}) +if(${CMAKE_VERSION} VERSION_LESS "3.12.0") + find_package(PythonLibs 3 QUIET) + if (PYTHONLIBS_FOUND) + message(STATUS "Found python 3") + list(APPEND ct_core_COMPILE_DEFINITIONS PLOTTING_ENABLED) + set(PLOTTING_ENABLED ON) + message(STATUS "Python library path ... " ${PYTHON_LIBRARY}) + list(APPEND ct_core_LIBS ${PYTHON_LIBRARY}) + else() + message(STATUS "Python 3 not found, plotting will not be available") + endif() else() - message(STATUS "Python 3 not found, plotting will not be available") + find_package(Python COMPONENTS Development Numpy QUIET) + if (Python_FOUND AND ${Python_VERSION_MAJOR} EQUAL 3) + message(STATUS "Found python " ${Python_VERSION}) + list(APPEND ct_core_COMPILE_DEFINITIONS PLOTTING_ENABLED) + set(PLOTTING_ENABLED ON) + message(STATUS "Python library path ... " ${Python_LIBRARIES}) + list(APPEND ct_core_LIBS ${Python_LIBRARIES}) + else() + message(STATUS "Python 3 not found, plotting will not be available") + endif() endif() - ## configure files required for code-generation set(CODEGEN_TEMPLATE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/templates") set(CODEGEN_OUTPUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/generated") @@ -87,8 +99,12 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/include/ct/core/templateDir.h.in ${CM ## define the directories to be included in all ct_core targets list(APPEND ct_core_target_include_dirs ${EIGEN3_INCLUDE_DIR}) -list(APPEND ct_core_target_include_dirs ${Python_INCLUDE_DIRS}) -list(APPEND ct_core_target_include_dirs ${Python_NumPy_INCLUDE_DIRS}) +if(${CMAKE_VERSION} VERSION_LESS "3.12.0") + list(APPEND ct_core_target_include_dirs ${PYTHON_INCLUDE_DIRS}) +else() + list(APPEND ct_core_target_include_dirs ${Python_INCLUDE_DIRS}) + list(APPEND ct_core_target_include_dirs ${Python_NumPy_INCLUDE_DIRS}) +endif() list(APPEND ct_core_target_include_dirs ${QWT_INCLUDE_DIR}) list(APPEND ct_core_target_include_dirs $) list(APPEND ct_core_target_include_dirs $) diff --git a/ct_doc/CMakeLists.txt b/ct_doc/CMakeLists.txt index e764aeaeb..3305b9fc4 100755 --- a/ct_doc/CMakeLists.txt +++ b/ct_doc/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.14.7) +cmake_minimum_required (VERSION 3.5) project (ct_doc VERSION 3.0.2 ) diff --git a/ct_models/CMakeLists.txt b/ct_models/CMakeLists.txt index 1ee7b3755..3667c6d80 100755 --- a/ct_models/CMakeLists.txt +++ b/ct_models/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.14.7) +cmake_minimum_required (VERSION 3.5) include(${CMAKE_CURRENT_SOURCE_DIR}/../ct/cmake/compilerSettings.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/../ct/cmake/explicitTemplateHelpers.cmake) diff --git a/ct_optcon/CMakeLists.txt b/ct_optcon/CMakeLists.txt index e53eab979..5ee87eb86 100644 --- a/ct_optcon/CMakeLists.txt +++ b/ct_optcon/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.14.7) +cmake_minimum_required (VERSION 3.5) include(${CMAKE_CURRENT_SOURCE_DIR}/../ct/cmake/compilerSettings.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/../ct/cmake/explicitTemplateHelpers.cmake) diff --git a/ct_rbd/CMakeLists.txt b/ct_rbd/CMakeLists.txt index 62173b8cb..f333359b0 100644 --- a/ct_rbd/CMakeLists.txt +++ b/ct_rbd/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.14.7) +cmake_minimum_required (VERSION 3.5) include(${CMAKE_CURRENT_SOURCE_DIR}/../ct/cmake/compilerSettings.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/../ct/cmake/explicitTemplateHelpers.cmake) From 600a62915a7383adf523102664652ff14f67100b Mon Sep 17 00:00:00 2001 From: Romain Reignier Date: Fri, 22 May 2020 11:23:45 +0200 Subject: [PATCH 10/21] Fix histogram plot crash The bins argument must be an integer --- ct_core/src/core/plot/plot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct_core/src/core/plot/plot.cpp b/ct_core/src/core/plot/plot.cpp index d972902fd..1a52d174d 100644 --- a/ct_core/src/core/plot/plot.cpp +++ b/ct_core/src/core/plot/plot.cpp @@ -238,7 +238,7 @@ bool hist(const Eigen::Ref& x, const double bins, const s PyList_SetItem(xlist, i, PyFloat_FromDouble(x(i))); } - PyObject* bins_py = PyFloat_FromDouble(bins); + PyObject* bins_py = PyLong_FromDouble(bins); PyObject* histtype_py = PyString_FromString(histtype.c_str()); // construct positional args From 6d6ea58269e13f29d727739ad1cc977e74f360f6 Mon Sep 17 00:00:00 2001 From: Markus Giftthaler Date: Sat, 23 May 2020 10:32:01 +0200 Subject: [PATCH 11/21] Update ct_core/CMakeLists.txt with suggested commits Co-authored-by: acxz <17132214+acxz@users.noreply.github.com> --- ct_core/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ct_core/CMakeLists.txt b/ct_core/CMakeLists.txt index 00282ee0b..80737cc0c 100755 --- a/ct_core/CMakeLists.txt +++ b/ct_core/CMakeLists.txt @@ -75,7 +75,7 @@ if(${CMAKE_VERSION} VERSION_LESS "3.12.0") message(STATUS "Python 3 not found, plotting will not be available") endif() else() - find_package(Python COMPONENTS Development Numpy QUIET) + find_package(Python COMPONENTS Development NumPy QUIET) if (Python_FOUND AND ${Python_VERSION_MAJOR} EQUAL 3) message(STATUS "Found python " ${Python_VERSION}) list(APPEND ct_core_COMPILE_DEFINITIONS PLOTTING_ENABLED) @@ -214,4 +214,3 @@ endif() ################# add_subdirectory(doc) - From f44104c59653fe9b4f0507356df0265dcac8046d Mon Sep 17 00:00:00 2001 From: acxz <17132214+acxz@users.noreply.github.com> Date: Tue, 2 Jun 2020 15:32:19 -0400 Subject: [PATCH 12/21] remove repeated header install line --- ct_optcon/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/ct_optcon/CMakeLists.txt b/ct_optcon/CMakeLists.txt index 5ee87eb86..9e9977565 100644 --- a/ct_optcon/CMakeLists.txt +++ b/ct_optcon/CMakeLists.txt @@ -183,8 +183,6 @@ endif() # for correct libraries locations across platforms include(GNUInstallDirs) -install(DIRECTORY include/ct/optcon DESTINATION include/ct) - ## copy the header files install(DIRECTORY include/ct/optcon DESTINATION include/ct) From c2fee0eb9e05718d56c68bc811ddbccc54138df1 Mon Sep 17 00:00:00 2001 From: Markus Giftthaler Date: Thu, 4 Jun 2020 13:58:04 +0200 Subject: [PATCH 13/21] fast forward with latest fixes. --- .../solver/lqp/GNRiccatiSolver-impl.hpp | 80 +++++++++---------- 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp b/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp index 24ef82b51..52b9686ff 100644 --- a/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp +++ b/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp @@ -9,31 +9,30 @@ namespace ct { namespace optcon { -template -GNRiccatiSolver::GNRiccatiSolver(const std::shared_ptr& lqocProblem) - : LQOCSolver(lqocProblem), N_(-1) +template +GNRiccatiSolver::GNRiccatiSolver(const std::shared_ptr& lqocProblem) + : LQOCSolver(lqocProblem), N_(-1) { Eigen::initParallel(); Eigen::setNbThreads(settings_.nThreadsEigen); } -template -GNRiccatiSolver::GNRiccatiSolver(int N) +template +GNRiccatiSolver::GNRiccatiSolver(int N) { changeNumberOfStages(N); } -template -void GNRiccatiSolver::solve() +template +void GNRiccatiSolver::solve() { for (int i = this->lqocProblem_->getNumberOfStages() - 1; i >= 0; i--) solveSingleStage(i); } - -template -void GNRiccatiSolver::solveSingleStage(int N) +template +void GNRiccatiSolver::solveSingleStage(int N) { if (N == this->lqocProblem_->getNumberOfStages() - 1) initializeCostToGo(); @@ -44,16 +43,15 @@ void GNRiccatiSolver::solveSingleStage(int N) computeCostToGo(N); } - -template -void GNRiccatiSolver::configure(const NLOptConSettings& settings) +template +void GNRiccatiSolver::configure(const NLOptConSettings& settings) { settings_ = settings; H_corrFix_ = settings_.epsilon * ControlMatrix::Identity(); } -template -void GNRiccatiSolver::computeStatesAndControls() +template +void GNRiccatiSolver::computeStatesAndControls() { LQOCProblem_t& p = *this->lqocProblem_; @@ -65,28 +63,33 @@ void GNRiccatiSolver::computeStatesAndControls() this->u_sol_[k] = this->lv_[k] + this->L_[k] * this->x_sol_[k]; //! state update rule in diff coordinates - this->x_sol_[k + 1] = p.A_[k] * this->x_sol_[k] + p.B_[k] * (this->u_sol_[k]) + p.b_[k]; + StateMatrix A_orig = p.Adj_x_[k + 1] * p.A_[k]; // A "without trick for backward pass" //TODO: document this + StateControlMatrix B_orig = p.Adj_x_[k + 1] * p.B_[k]; // B "without trick for backward pass" + typename MANIFOLD::Tangent b_orig = p.Adj_x_[k + 1] * p.b_[k]; // b "without trick for backward pass" + // Note that we need to transport the state update into the tagent space of k+1 + this->x_sol_[k + 1] = + p.Adj_x_[k + 1].transpose() * (A_orig * this->x_sol_[k] + B_orig * (this->u_sol_[k]) + b_orig); } } -template -void GNRiccatiSolver::computeFeedbackMatrices() +template +void GNRiccatiSolver::computeFeedbackMatrices() { /*no action required, already computed in backward pass */ } -template -void GNRiccatiSolver::compute_lv() +template +void GNRiccatiSolver::compute_lv() { /*no action required, already computed in backward pass*/ } -template -SCALAR GNRiccatiSolver::getSmallestEigenvalue() +template +auto GNRiccatiSolver::getSmallestEigenvalue() -> SCALAR { return smallestEigenvalue_; } -template -void GNRiccatiSolver::setProblemImpl(std::shared_ptr lqocProblem) +template +void GNRiccatiSolver::setProblemImpl(std::shared_ptr lqocProblem) { if (lqocProblem->isConstrained()) { @@ -98,9 +101,8 @@ void GNRiccatiSolver::setProblemImpl(std::shared changeNumberOfStages(N); } - -template -void GNRiccatiSolver::changeNumberOfStages(int N) +template +void GNRiccatiSolver::changeNumberOfStages(int N) { if (N <= 0) return; @@ -127,9 +129,8 @@ void GNRiccatiSolver::changeNumberOfStages(int N N_ = N; } - -template -void GNRiccatiSolver::initializeCostToGo() +template +void GNRiccatiSolver::initializeCostToGo() { //! since intializeCostToGo is the first call, we initialize the smallestEigenvalue here. smallestEigenvalue_ = std::numeric_limits::infinity(); @@ -142,9 +143,8 @@ void GNRiccatiSolver::initializeCostToGo() sv_[N] = p.qv_[N]; } - -template -void GNRiccatiSolver::computeCostToGo(size_t k) +template +void GNRiccatiSolver::computeCostToGo(size_t k) { LQOCProblem_t& p = *this->lqocProblem_; @@ -160,9 +160,8 @@ void GNRiccatiSolver::computeCostToGo(size_t k) sv_[k]/*.noalias()*/ += G_[k].transpose() * this->lv_[k]; // TODO: bring back all the noalias() } - -template -void GNRiccatiSolver::designController(size_t k) +template +void GNRiccatiSolver::designController(size_t k) { LQOCProblem_t& p = *this->lqocProblem_; @@ -259,9 +258,8 @@ void GNRiccatiSolver::designController(size_t k) } } - -template -void GNRiccatiSolver::logToMatlab() +template +void GNRiccatiSolver::logToMatlab() { #ifdef MATLAB_FULL_LOG @@ -280,8 +278,8 @@ void GNRiccatiSolver::logToMatlab() #endif } -template -void GNRiccatiSolver::initializeAndAllocate() +template +void GNRiccatiSolver::initializeAndAllocate() { // do nothing } From 4525468fe60f7e64d6d468b05e6ad428ff4d4626 Mon Sep 17 00:00:00 2001 From: mgiftthaler Date: Sat, 20 Jun 2020 09:17:40 +0200 Subject: [PATCH 14/21] non-working version --- .../core/systems/linearizer/DynamicsLinearizerNumDiff.h | 8 ++++---- ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ct_core/include/ct/core/systems/linearizer/DynamicsLinearizerNumDiff.h b/ct_core/include/ct/core/systems/linearizer/DynamicsLinearizerNumDiff.h index f9e530370..6ae6d1137 100644 --- a/ct_core/include/ct/core/systems/linearizer/DynamicsLinearizerNumDiff.h +++ b/ct_core/include/ct/core/systems/linearizer/DynamicsLinearizerNumDiff.h @@ -151,8 +151,8 @@ class DynamicsLinearizerNumDiff SCALAR dxp = mlog_ph - m_log.coeffs()(i); t_perturbed = m_log; - t_perturbed.set_coeff(i, mlog_ph); // TODO - //t_perturbed.coeffs()(i) = mlog_ph; + //t_perturbed.set_coeff(i, mlog_ph); // TODO remove + t_perturbed.coeffs()(i) = mlog_ph; // evaluate dynamics at perturbed state dynamics_fct_(retract_fct_(t_perturbed), t, u, res_plus); @@ -163,8 +163,8 @@ class DynamicsLinearizerNumDiff SCALAR dxm = m_log.coeffs()(i) - mlog_mh; t_perturbed = m_log; - t_perturbed.set_coeff(i, mlog_ph); - //t_perturbed.coeffs()(i) = mlog_ph; + //t_perturbed.set_coeff(i, mlog_ph); // TODO: remove + t_perturbed.coeffs()(i) = mlog_ph; Tangent res_minus; dynamics_fct_(retract_fct_(t_perturbed), t, u, res_minus); diff --git a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp index 4ecf41b5a..90d965445 100644 --- a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp +++ b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp @@ -60,7 +60,11 @@ class CompositeManifoldTangent t_vel_.setZero(); coeffs_.setZero(); } + + // TODO: Here is the problem... const Eigen::Matrix& coeffs() const { return coeffs_; } + + void set_coeffs(const Eigen::Matrix& c) { coeffs_ = c; From f8ba32152d408783a4d999d48534c7aa8ea2402a Mon Sep 17 00:00:00 2001 From: mgiftthaler Date: Sun, 21 Jun 2020 17:13:06 +0200 Subject: [PATCH 15/21] update with less coeff() work going on --- .../linearizer/DynamicsLinearizerNumDiff.h | 16 ++++---- .../test/solver/linear/ManifoldLQOCTest.cpp | 41 ++++++++----------- 2 files changed, 24 insertions(+), 33 deletions(-) diff --git a/ct_core/include/ct/core/systems/linearizer/DynamicsLinearizerNumDiff.h b/ct_core/include/ct/core/systems/linearizer/DynamicsLinearizerNumDiff.h index 6ae6d1137..9137cfb25 100644 --- a/ct_core/include/ct/core/systems/linearizer/DynamicsLinearizerNumDiff.h +++ b/ct_core/include/ct/core/systems/linearizer/DynamicsLinearizerNumDiff.h @@ -146,25 +146,23 @@ class DynamicsLinearizerNumDiff for (size_t i = 0; i < STATE_DIM; i++) { // inspired from http://en.wikipedia.org/wiki/Numerical_differentiation#Practical_considerations_using_floating_point_arithmetic - SCALAR h = eps_ * std::max(std::abs(m_log.coeffs()(i)), SCALAR(1.0)); - SCALAR mlog_ph = m_log.coeffs()(i) + h; - SCALAR dxp = mlog_ph - m_log.coeffs()(i); + SCALAR h = eps_ * std::max(std::abs(m_log(i)), SCALAR(1.0)); + SCALAR mlog_ph = m_log(i) + h; + SCALAR dxp = mlog_ph - m_log(i); t_perturbed = m_log; - //t_perturbed.set_coeff(i, mlog_ph); // TODO remove - t_perturbed.coeffs()(i) = mlog_ph; + t_perturbed(i) = mlog_ph; // evaluate dynamics at perturbed state dynamics_fct_(retract_fct_(t_perturbed), t, u, res_plus); if (doubleSidedDerivative_) { - SCALAR mlog_mh = m_log.coeffs()(i) - h; - SCALAR dxm = m_log.coeffs()(i) - mlog_mh; + SCALAR mlog_mh = m_log(i) - h; + SCALAR dxm = m_log(i) - mlog_mh; t_perturbed = m_log; - //t_perturbed.set_coeff(i, mlog_ph); // TODO: remove - t_perturbed.coeffs()(i) = mlog_ph; + t_perturbed(i) = mlog_ph; Tangent res_minus; dynamics_fct_(retract_fct_(t_perturbed), t, u, res_minus); diff --git a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp index 90d965445..a8f9c1423 100644 --- a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp +++ b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp @@ -21,21 +21,20 @@ class CompositeManifoldTangent using Jacobian = Eigen::Matrix; using OptJacobianRef = tl::optional>; - CompositeManifoldTangent() : t_pos_(POS_TANGENT::Zero()), t_vel_(VEL_TANGENT::Zero()) { coeffs_.setZero(); } + CompositeManifoldTangent() : t_pos_(POS_TANGENT::Zero()), t_vel_(VEL_TANGENT::Zero()) { } CompositeManifoldTangent(const POS_TANGENT& p, const VEL_TANGENT& v) : t_pos_(p), t_vel_(v) { - coeffs_ << t_pos_.coeffs(), t_vel_.coeffs(); } template CompositeManifoldTangent(const Eigen::MatrixBase<_EigenDerived>& v) - : t_pos_(v.template head()), t_vel_(v.template tail()), coeffs_(v) + : t_pos_(v.template head()), t_vel_(v.template tail()) { // TODO a dimension assert would not hurt } CompositeManifoldTangent(const CompositeManifoldTangent& arg) - : t_pos_(arg.t_pos_), t_vel_(arg.t_vel_), coeffs_(arg.coeffs_) + : t_pos_(arg.t_pos_), t_vel_(arg.t_vel_) { } @@ -52,31 +51,30 @@ class CompositeManifoldTangent { t_pos_.setRandom(); t_vel_.setRandom(); - coeffs_ << t_pos_.coeffs(), t_vel_.coeffs(); } void setZero() { t_pos_.setZero(); t_vel_.setZero(); - coeffs_.setZero(); } - - // TODO: Here is the problem... - const Eigen::Matrix& coeffs() const { return coeffs_; } - - void set_coeffs(const Eigen::Matrix& c) + // TODO: Here is the problem, trouble as soon as it gets passed by value. + const Eigen::Matrix& coeffs() const { - coeffs_ = c; - t_pos_.coeffs() = c.template head(); - t_vel_.coeffs() = c.template tail(); + Eigen::Matrix coeffs_temp; + coeffs_temp.template head<3>() = t_pos_.coeffs(); + coeffs_temp.template tail<3>() = t_vel_.coeffs(); + + const_cast&>(coeffs_) = coeffs_temp; + + return coeffs_; } - void set_coeff(const int i, const Scalar val) + + Scalar& operator()(const int k) { return k < POS_TANGENT::DoF ? t_pos_.coeffs()(k) : t_vel_.coeffs()(k - 3); } + const Scalar& operator()(const int k) const { - coeffs_(i) = val; - t_pos_.coeffs() = coeffs_.template head(); - t_vel_.coeffs() = coeffs_.template tail(); + return k < POS_TANGENT::DoF ? t_pos_.coeffs()(k) : t_vel_.coeffs()(k - 3); } CompositeManifoldTangent operator-(const CompositeManifoldTangent& tb) const @@ -118,21 +116,16 @@ class CompositeManifoldTangent void set_pos(const POS_TANGENT& p) { t_pos_ = p; - coeffs_.template head() = p.coeffs(); } void set_vel(const VEL_TANGENT& v) { t_vel_ = v; - coeffs_.template tail() = v.coeffs(); } const VEL_TANGENT& vel() const { return t_vel_; } - //Scalar& operator()(int idx) { return coeffs()(idx); } - const Scalar& operator()(int idx) const { return coeffs()(idx); } - //auto noalias() { return CompositeManifoldTangent(*this); /* coeffs().noalias();*/ } // TODO: sort this out protected: POS_TANGENT t_pos_; VEL_TANGENT t_vel_; - Eigen::Matrix coeffs_; + Eigen::Matrix coeffs_; // is now used only in one function to return a ref }; template From 88cfa2f50021635a978b6c26eca7d66d7dc2685d Mon Sep 17 00:00:00 2001 From: Markus Giftthaler Date: Thu, 25 Jun 2020 21:44:19 +0200 Subject: [PATCH 16/21] non-working hpipm test --- .../include/ct/optcon/problem/LQOCProblem.hpp | 2 + .../test/solver/linear/ManifoldLQOCTest.cpp | 75 +++++++++++-------- 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/ct_optcon/include/ct/optcon/problem/LQOCProblem.hpp b/ct_optcon/include/ct/optcon/problem/LQOCProblem.hpp index ecfa757c3..432aeeea1 100644 --- a/ct_optcon/include/ct/optcon/problem/LQOCProblem.hpp +++ b/ct_optcon/include/ct/optcon/problem/LQOCProblem.hpp @@ -89,6 +89,8 @@ class LQOCProblem ~LQOCProblem(); + LQOCProblem* clone() {return new LQOCProblem(*this);} + //! returns the number of discrete time steps in the LQOCP, including terminal stage int getNumberOfStages(); diff --git a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp index a8f9c1423..6a26e1e2a 100644 --- a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp +++ b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp @@ -21,10 +21,8 @@ class CompositeManifoldTangent using Jacobian = Eigen::Matrix; using OptJacobianRef = tl::optional>; - CompositeManifoldTangent() : t_pos_(POS_TANGENT::Zero()), t_vel_(VEL_TANGENT::Zero()) { } - CompositeManifoldTangent(const POS_TANGENT& p, const VEL_TANGENT& v) : t_pos_(p), t_vel_(v) - { - } + CompositeManifoldTangent() : t_pos_(POS_TANGENT::Zero()), t_vel_(VEL_TANGENT::Zero()) {} + CompositeManifoldTangent(const POS_TANGENT& p, const VEL_TANGENT& v) : t_pos_(p), t_vel_(v) {} template CompositeManifoldTangent(const Eigen::MatrixBase<_EigenDerived>& v) @@ -33,10 +31,7 @@ class CompositeManifoldTangent // TODO a dimension assert would not hurt } - CompositeManifoldTangent(const CompositeManifoldTangent& arg) - : t_pos_(arg.t_pos_), t_vel_(arg.t_vel_) - { - } + CompositeManifoldTangent(const CompositeManifoldTangent& arg) : t_pos_(arg.t_pos_), t_vel_(arg.t_vel_) {} static CompositeManifoldTangent Zero() { @@ -113,15 +108,10 @@ class CompositeManifoldTangent auto transpose() const { return coeffs().transpose(); } const POS_TANGENT& pos() const { return t_pos_; } - void set_pos(const POS_TANGENT& p) - { - t_pos_ = p; - } - void set_vel(const VEL_TANGENT& v) - { - t_vel_ = v; - } + void set_pos(const POS_TANGENT& p) { t_pos_ = p; } + void set_vel(const VEL_TANGENT& v) { t_vel_ = v; } const VEL_TANGENT& vel() const { return t_vel_; } + protected: POS_TANGENT t_pos_; VEL_TANGENT t_vel_; @@ -295,6 +285,7 @@ class CompositeManifold const POS_MAN& pos() const { return m_pos_; } VEL_MAN& vel() { return m_vel_; } const VEL_MAN& vel() const { return m_vel_; } + protected: POS_MAN m_pos_; VEL_MAN m_vel_; @@ -373,8 +364,11 @@ int main(int argc, char** argv) x_traj[i] = ManifoldState_t::Random(); } - // create instances of HPIPM and an unconstrained Gauss-Newton Riccati solver - std::shared_ptr> solver(new GNRiccatiSolver); + // create instances of HPIPM and an unconstrained Gauss-Newton Riccati gnSolver + std::shared_ptr> gnSolver( + new GNRiccatiSolver); + std::shared_ptr> hpipmSolver( + new HPIPMInterface); // create linear-quadratic optimal control problem containers std::shared_ptr> problem( @@ -439,31 +433,46 @@ int main(int argc, char** argv) auto l_adj = (l.exp()).adj(); problem->Adj_x_[i + 1] = l_adj; // parallel transport matrix / adjoint from stage k+1 to stage k - // make the necessary overloads, which allows the standard riccati solver to backpropagate on manifolds + // make the necessary overloads, which allows the standard riccati gnSolver to backpropagate on manifolds problem->A_[i] = l_adj.transpose() * problem->A_[i]; problem->B_[i] = l_adj.transpose() * problem->B_[i]; problem->b_[i] = l_adj.transpose() * problem->b_[i]; } // set the problem pointers - solver->setProblem(problem); + gnSolver->setProblem(problem); + + std::shared_ptr> hpipmProblem (problem->clone()); + hpipmSolver->setProblem(hpipmProblem); // allocate memory (if required) - solver->initializeAndAllocate(); + gnSolver->initializeAndAllocate(); + hpipmSolver->initializeAndAllocate(); // solve the problem - solver->solve(); + gnSolver->solve(); + hpipmSolver->solve(); // postprocess data - solver->compute_lv(); - solver->computeFeedbackMatrices(); - solver->computeStatesAndControls(); + gnSolver->compute_lv(); + gnSolver->computeFeedbackMatrices(); + gnSolver->computeStatesAndControls(); + hpipmSolver->compute_lv(); + hpipmSolver->computeFeedbackMatrices(); + hpipmSolver->computeStatesAndControls(); // retrieve solutions from solver - auto xSol = solver->getSolutionState(); - auto uSol = solver->getSolutionControl(); - ct::core::FeedbackArray KSol = solver->getSolutionFeedback(); - ct::core::ControlVectorArray lv_sol = solver->get_lv(); + auto xSol_gn = gnSolver->getSolutionState(); + auto uSol_gn = gnSolver->getSolutionControl(); + ct::core::FeedbackArray KSol_gn = gnSolver->getSolutionFeedback(); + ct::core::ControlVectorArray lv_sol_gn = gnSolver->get_lv(); + + auto xSol_hpipm = hpipmSolver->getSolutionState(); + auto uSol_hpipm = hpipmSolver->getSolutionControl(); + ct::core::FeedbackArray KSol_hpipm = hpipmSolver->getSolutionFeedback(); + ct::core::ControlVectorArray lv_sol_hpipm = hpipmSolver->get_lv(); + + // TODO: include a comparison here. x_curr = x0; ct::core::DiscreteArray x_traj_prev = x_traj; @@ -477,14 +486,14 @@ int main(int argc, char** argv) if (use_single_shooting) { ManifoldState_t::Tangent x_err = x_traj[i].rminus(x_traj_prev[i]); - u_traj[i] += lv_sol[i] + KSol[i] * x_err; + u_traj[i] += lv_sol_gn[i] + KSol_gn[i] * x_err; exampleSystem->computeControlledDynamics(x_traj[i], i * dt, u_traj[i], dx); x_traj[i + 1] = x_traj[i] + dx; } else // multiple shooting { - u_traj[i] += uSol[i]; - x_traj[i + 1] = x_traj[i + 1].rplus(xSol[i + 1]); + u_traj[i] += uSol_gn[i]; + x_traj[i + 1] = x_traj[i + 1].rplus(xSol_gn[i + 1]); exampleSystem->computeControlledDynamics(x_traj[i], i * dt, u_traj[i], dx); } @@ -493,7 +502,7 @@ int main(int argc, char** argv) // compute update norms d_cum_sum += b[i].coeffs().norm(); - dx_cum_sum += xSol[i + 1].coeffs().norm(); + dx_cum_sum += xSol_gn[i + 1].coeffs().norm(); // compute intermediate cost costFunction->setCurrentStateAndControl(x_traj[i], u_traj[i], i * dt); From d6572d37b1d2471cf059e35df82143f77abe5d6e Mon Sep 17 00:00:00 2001 From: Markus Giftthaler Date: Mon, 13 Jul 2020 08:18:51 +0200 Subject: [PATCH 17/21] Add impl for eigen-type to csv export. --- .../include/ct/core/common/EigenFileExport.h | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 ct_core/include/ct/core/common/EigenFileExport.h diff --git a/ct_core/include/ct/core/common/EigenFileExport.h b/ct_core/include/ct/core/common/EigenFileExport.h new file mode 100644 index 000000000..7d0c9aab2 --- /dev/null +++ b/ct_core/include/ct/core/common/EigenFileExport.h @@ -0,0 +1,132 @@ +/********************************************************************************************************************** +This file is part of the Control Toolbox (https://github.com/ethz-adrl/control-toolbox), copyright by ETH Zurich. +Licensed under the BSD-2 license (see LICENSE file in main directory) +**********************************************************************************************************************/ + +#include +#include +#include + +#include + +namespace ct::core { + +// +// @brief Save Eigen-types or vectors of Eigen-types to text file. +// Currently ordinary Eigen matrices and quaternion types are supported. +// +// Usage examples: +// // writing an eigen matrix +// Eigen::Matrix3d m = Eigen::Matrix3d::Random(); +// EigenFileExport::mat_to_file(EigenFileExport::OctaveFormat(), "/tmp/eigen_mat.csv", m, "test mat"); +// +// // writing a vector of eigen matrices +// std::vector> m_vec(3, Eigen::Matrix3d::Random()); +// EigenFileExport::mat_to_file(EigenFileExport::OctaveFormat(), "/tmp/eigen_mat_vec.csv", m_vec, "some title."); +// +// // writing a quaternion +// Eigen::Quaterniond q(Eigen::Quaterniond::Identity()); +// EigenFileExport::quat_to_file(EigenFileExport::OctaveFormat(), "/tmp/quat.csv", q, "test quat"); +// +// // writing a vector of quaternions +// std::vector> q_vec( +// 5, Eigen::Quaterniond::Identity()); +// EigenFileExport::quat_to_file(EigenFileExport::OctaveFormat(), "/tmp/quat_vec.csv", q_vec); +// +class EigenFileExport +{ +public: + // Create CSV formatting option, precision can be custimized if desired. + static const Eigen::IOFormat CSVFormat(int precision = Eigen::StreamPrecision) + { + return Eigen::IOFormat(precision, Eigen::DontAlignCols, ", ", "\n"); + } + // Create Octave formatting option, precision can be custimized if desired. + static const Eigen::IOFormat OctaveFormat(int precision = Eigen::StreamPrecision) + { + return Eigen::IOFormat(precision, 0, ", ", ";\n", "", "", "[", "]"); + } + // Create comma init formatting option, precision can be custimized if desired. + static const Eigen::IOFormat CommaInitFormat(int precision = Eigen::StreamPrecision) + { + return Eigen::IOFormat(precision, Eigen::DontAlignCols, ", ", ", ", "", "", " << ", ";"); + } + // Create a clean formatting option, precision can be custimized if desired. + static const Eigen::IOFormat CleanFormat(int precision = Eigen::StreamPrecision) + { + return Eigen::IOFormat(precision, 0, ", ", "\n", "[", "]"); + } + // create default full-info formatting option, precision can be custimized if desired. + static const Eigen::IOFormat HeavyFormat(int precision = Eigen::FullPrecision) + { + return Eigen::IOFormat(precision, 0, ", ", ";\n", "[", "]", "[", "]"); + } + + //! Save a single matrix to single text file. + template + static void mat_to_file(const Eigen::IOFormat fmt, + const std::string& filename, + const Eigen::MatrixBase& m, + const std::string& heading = "") + { + std::ofstream file(filename.c_str()); + if (!heading.empty()) + file << heading << "\n"; + file << m.format(fmt); + + file.close(); + } + + //! Save a single quaternion to a single text file. + template + static void quat_to_file(const Eigen::IOFormat fmt, + const std::string& filename, + const Eigen::QuaternionBase& q, + const std::string& heading = "") + { + std::ofstream file(filename.c_str()); + if (!heading.empty()) + file << heading << "\n"; + file << q.coeffs().transpose().format(fmt); + + file.close(); + } + + // Save a vector of matrices to a single text file. + template > + static void mat_to_file(const Eigen::IOFormat fmt, + const std::string& filename, + const std::vector& m_vec, + const std::string& heading = "") + { + std::ofstream file(filename.c_str()); + + if (!heading.empty()) + file << heading << "\n"; + + for (size_t i = 0; i < m_vec.size(); i++) + file << m_vec[i].format(fmt) << "\n"; + + file.close(); + } + + // Save a vector of quaternions to a single text file. + template >> + static void quat_to_file(const Eigen::IOFormat fmt, + const std::string& filename, + const std::vector, Alloc>& q_vec, + const std::string& heading = "") + { + std::ofstream file(filename.c_str()); + + if (!heading.empty()) + file << heading << "\n"; + + for (size_t i = 0; i < q_vec.size(); i++) + file << q_vec[i].coeffs().transpose().format(fmt) << "\n"; + + file.close(); + } +}; + +} // namespace ct::core From b1940a93022095f142924aaa1b91afbd05607923 Mon Sep 17 00:00:00 2001 From: Markus Giftthaler Date: Mon, 13 Jul 2020 16:24:02 +0200 Subject: [PATCH 18/21] backup --- ct_core/CMakeLists.txt | 2 +- ct_core/include/ct/core/Common | 1 + .../include/ct/core/types/EuclideanState.h | 19 +++ .../CostFunctionQuadraticSimple-impl.hpp | 2 +- ct_optcon/test/CMakeLists.txt | 10 +- .../test/solver/linear/HPIPMInterfaceTest.h | 10 +- .../linear/KinematicManifoldLQOCTest.cpp | 113 +++++++----------- ct_optcon/test/solver/linear/LQOCSolverTest.h | 4 +- .../test/solver/linear/ManifoldLQOCTest.cpp | 27 +++-- 9 files changed, 91 insertions(+), 97 deletions(-) diff --git a/ct_core/CMakeLists.txt b/ct_core/CMakeLists.txt index 993685ee4..139453ca3 100755 --- a/ct_core/CMakeLists.txt +++ b/ct_core/CMakeLists.txt @@ -157,7 +157,7 @@ target_link_libraries(ct_core INTERFACE dl # required for gcc compatibility ) - + ################## # BUILD EXAMPLES # ################## diff --git a/ct_core/include/ct/core/Common b/ct_core/include/ct/core/Common index 1d34208d7..017bc09b2 100644 --- a/ct_core/include/ct/core/Common +++ b/ct_core/include/ct/core/Common @@ -15,5 +15,6 @@ Licensed under the BSD-2 license (see LICENSE file in main directory) #include "common/linspace.h" #include "common/activations/Activations.h" #include "common/EigenIsNan.h" +#include "common/EigenFileExport.h" diff --git a/ct_core/include/ct/core/types/EuclideanState.h b/ct_core/include/ct/core/types/EuclideanState.h index 57b1ccd83..25eb4ceb6 100644 --- a/ct_core/include/ct/core/types/EuclideanState.h +++ b/ct_core/include/ct/core/types/EuclideanState.h @@ -6,6 +6,8 @@ Licensed under the BSD-2 license (see LICENSE file in main directory) #pragma once #include +#include "lt/optional.hpp" + namespace ct { namespace core { @@ -53,6 +55,23 @@ class EuclideanState : public Eigen::Matrix Tangent rminus(const EuclideanState& x) const { return *this - x; } EuclideanState rplus(const Tangent& x) const { return *this + x; } + + using OptJacobianRef = tl::optional>>; + Tangent rminus(const EuclideanState& m, OptJacobianRef J_t_ma, OptJacobianRef J_t_mb) const + { + const Tangent t = rminus(m); + + if (J_t_ma) + { + (*J_t_ma).setIdentity(); + } + if (J_t_mb) + { + (*J_t_mb).setIdentity(); + } + + return t; + } }; template diff --git a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp index 0eb40671a..aa7282bf6 100644 --- a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp +++ b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp @@ -137,7 +137,7 @@ template auto CostFunctionQuadraticSimple::stateSecondDerivativeTerminal() -> state_matrix_t { Eigen::Matrix Jl, Jr; // TODO: ignore Jl cleanly - typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_, Jl, Jr); + x_final_.rminus(this->x_, Jl, Jr); return Jr * Q_final_ * Jr.transpose(); } diff --git a/ct_optcon/test/CMakeLists.txt b/ct_optcon/test/CMakeLists.txt index 1e54468b1..0067cb119 100644 --- a/ct_optcon/test/CMakeLists.txt +++ b/ct_optcon/test/CMakeLists.txt @@ -53,10 +53,10 @@ if(BUILD_TESTS) #package_add_test(dms_test_all_var dms/oscillator/oscDMSTestAllVariants.cpp) #package_add_test(system_interface_test system_interface/SystemInterfaceTest.cpp) - #add_executable(KinematicManifoldLQOCTest solver/linear/KinematicManifoldLQOCTest.cpp) - #target_link_libraries(KinematicManifoldLQOCTest ct_optcon) - #set_target_properties(KinematicManifoldLQOCTest PROPERTIES FOLDER test) - #list(APPEND UNIT_TEST_TARGETS KinematicManifoldLQOCTest) + add_executable(KinematicManifoldLQOCTest solver/linear/KinematicManifoldLQOCTest.cpp) + target_link_libraries(KinematicManifoldLQOCTest ct_optcon) + set_target_properties(KinematicManifoldLQOCTest PROPERTIES FOLDER test) + list(APPEND UNIT_TEST_TARGETS KinematicManifoldLQOCTest) add_executable(ManifoldLQOCTest solver/linear/ManifoldLQOCTest.cpp) target_link_libraries(ManifoldLQOCTest ct_optcon ct_core) @@ -69,7 +69,7 @@ if(BUILD_TESTS) # add_executable(LQOCSolverTiming solver/linear/LQOCSolverTiming.cpp) # target_link_libraries(LQOCSolverTiming ct_optcon) - # package_add_test(LQOCSolverTest solver/linear/LQOCSolverTest.cpp) + #package_add_test(LQOCSolverTest solver/linear/LQOCSolverTest.cpp) # package_add_test(ConstrainedLQOCSolverTest solver/linear/ConstrainedLQOCSolverTest.cpp) # package_add_test(LinearSystemSolverComparison nloc/LinearSystemSolverComparison.cpp) # if(CPPADCG) diff --git a/ct_optcon/test/solver/linear/HPIPMInterfaceTest.h b/ct_optcon/test/solver/linear/HPIPMInterfaceTest.h index a98b4d5e3..e6e6d64f6 100644 --- a/ct_optcon/test/solver/linear/HPIPMInterfaceTest.h +++ b/ct_optcon/test/solver/linear/HPIPMInterfaceTest.h @@ -55,8 +55,10 @@ TEST(HPIPMInterfaceTest, compareSolvers) dt, system, ct::optcon::NLOptConSettings::APPROXIMATION::MATRIX_EXPONENTIAL); // initialize the linear quadratic optimal control problems - lqocProblem_hpipm->setFromTimeInvariantLinearQuadraticProblem(discretizedSystem, costFunction, stateOffset, dt); - lqocProblem_gnriccati->setFromTimeInvariantLinearQuadraticProblem(discretizedSystem, costFunction, stateOffset, dt); + lqocProblem_hpipm->setFromTimeInvariantLinearQuadraticProblem( + x0, u0, discretizedSystem, costFunction, stateOffset, dt); + lqocProblem_gnriccati->setFromTimeInvariantLinearQuadraticProblem( + x0, u0, discretizedSystem, costFunction, stateOffset, dt); // create hpipm solver instance, set and solve problem @@ -76,8 +78,8 @@ TEST(HPIPMInterfaceTest, compareSolvers) gnriccati.compute_lv(); // compute and retrieve solutions - ct::core::DiscreteArray x_sol_hpipm = hpipm.getSolutionState(); - ct::core::DiscreteArray x_sol_gnrccati = gnriccati.getSolutionState(); + ct::core::DiscreteArray x_sol_hpipm = hpipm.getSolutionState(); + ct::core::DiscreteArray x_sol_gnrccati = gnriccati.getSolutionState(); ct::core::ControlVectorArray u_sol_hpipm = hpipm.getSolutionControl(); ct::core::ControlVectorArray u_sol_gnrccati = gnriccati.getSolutionControl(); ct::core::FeedbackArray K_sol_hpipm = hpipm.getSolutionFeedback(); diff --git a/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp b/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp index 227bfaaef..5b4e1279e 100644 --- a/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp +++ b/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp @@ -1,14 +1,8 @@ #include -#include -#include - -//#include "matplotlibcpp.h" using namespace ct::core; using namespace ct::optcon; - - const bool verbose = true; using ManifoldState_t = ManifoldState; @@ -90,16 +84,16 @@ int main(int argc, char** argv) const bool use_single_shooting = true; // toggle between single and multiple shooting - const size_t N = 250; - const double dt = 0.5; + const size_t N = 17; + const double dt = 0.1; - const ManifoldState_t x0 = manif::SO3(3.1, 0, 0); + const ManifoldState_t x0 = manif::SO3(M_PI / 2, 0, 0); ct::core::DiscreteArray x_traj(N + 1, x0); // init state trajectory, will be overwritten ct::core::DiscreteArray b( N + 1, ManifoldState_t::Tangent::Zero()); // defect traj, will be overwritten ct::core::DiscreteArray> u_traj(N); // init control traj for (size_t i = 0; i < N; i++) - u_traj[i] = ct::core::ControlVector::Random()* 0.01; + u_traj[i] = ct::core::ControlVector::Random() * 0.01; // choose a random initial state // TODO: numerical trouble for more aggressive distributions, since the approximation of the value function becomes really bad? @@ -111,14 +105,14 @@ int main(int argc, char** argv) // create instances of HPIPM and an unconstrained Gauss-Newton Riccati solver std::shared_ptr> gnRiccatiSolver( new GNRiccatiSolver); - std::shared_ptr> augGnRiccatiSolver( - new AugGNRiccatiSolver); + // std::shared_ptr> hpipmSolver( + // new HPIPMInterface); // store them, and identifying names, in a vectors std::vector>> lqocSolvers; lqocSolvers.push_back(gnRiccatiSolver); - lqocSolvers.push_back(augGnRiccatiSolver); - std::vector solverNames = {"Riccati", "AugRiccati"}; + // lqocSolvers.push_back(hpipmSolver); + std::vector solverNames = {"Riccati", "hpipm"}; // create linear-quadratic optimal control problem containers std::vector>> problems; @@ -140,13 +134,15 @@ int main(int argc, char** argv) // create a cost function Eigen::Matrix Q, Q_final; Eigen::Matrix R; - Q_final << 10000, 0, 0, 0, 10000, 0, 0, 0, 10000; - Q << 1, 0, 0, 0, 1, 0, 0, 0, 1; - //Q.setZero(); - R << 1, 0, 0, 0, 1, 0, 0, 0, 1; + Q_final.setZero(); + Q_final.diagonal() << 10000, 10000, 10000; + Q.setZero(); + Q.diagonal() << 1, 1, 1; + R.setZero(); + R.diagonal() << 1, 1, 1; ManifoldState_t x_final = manif::SO3(0, 0, 0); std::cout << "desired final state: " << x_final << std::endl; - ManifoldState_t x_nominal = x0; + ManifoldState_t x_nominal = x_final; ct::core::ControlVector u_nom = ct::core::ControlVector::Zero(); std::shared_ptr> costFunction( new CostFunctionQuadraticSimple(Q, R, x_nominal, u_nom, x_final, Q_final)); @@ -170,7 +166,7 @@ int main(int argc, char** argv) std::cout << std::setprecision(4) << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; } - size_t nIter = 35; + size_t nIter = 25; for (size_t iter = 0; iter < nIter; iter++) { // initialize the optimal control problems for both solvers @@ -179,35 +175,14 @@ int main(int argc, char** argv) // HACKY corrections // TODO: move somewhere meaningful - for (size_t idx : {0, 1}) + for (size_t idx : {0}) { - // intermediate stages cost transportation - for (size_t i = 0; i < N; i++) - { - auto e = x_nominal.rminus(x_traj[i]); - // compute PT matrix w.r.t. current ref traj // TODO: clarify formulation of error in cost function - auto e_adj = (e.exp()).adj(); - problems[idx]->Q_[i] = e_adj * problems[idx]->Q_[i] * - e_adj.transpose(); // TODO: sort out that thing with the cost function - problems[idx]->qv_[i] = e_adj * problems[idx]->qv_[i]; - } - // terminal stage transportation - // TODO: clarifiy formulation of error in cost function - auto e = x_final.rminus(x_traj[N]); // compute PT matrix w.r.t. current ref traj - auto e_adj = (e.exp()).adj(); - //std::cout << "cost adj" << std::endl << e_adj << std::endl; - problems[idx]->Q_.back() = e_adj * problems[idx]->Q_.back() * e_adj.transpose(); - problems[idx]->qv_.back() = e_adj * problems[idx]->qv_.back(); - // dynamics transportation for (size_t i = 0; i < N; i++) { - // std::cout << "dyn transport matrices" << std::endl; - Eigen::Matrix3d Jl, Jr; - auto l = x_traj[i + 1].rminus(x_traj[i], Jl, Jr); + auto l = x_traj[i + 1].rminus(x_traj[i]); auto l_adj = (l.exp()).adj(); - //std::cout << "l_adj" << std::endl << l_adj << std::endl; - //std::cout << std::endl; + problems[idx]->Adj_x_[i + 1] = l_adj; // parallel transport matrix / adjoint from stage k+1 to stage k if (idx == 0) // make the corrections for the standard riccati solver @@ -216,16 +191,6 @@ int main(int argc, char** argv) problems[idx]->B_[i] = l_adj.transpose() * problems[idx]->B_[i]; problems[idx]->b_[i] = l_adj.transpose() * problems[idx]->b_[i]; } - - // now just a test // todo: remove later - //auto temp_l = x_traj[i + 1].between(x_traj[i]); - //auto temp_l_adj = temp_l.adj(); - //if (!(l_adj.isApprox(temp_l_adj.transpose(), 1e-5))) - //{ - // std::cout << std::setprecision(10) << "l_adj: " << std::endl << l_adj << std::endl; - // std::cout << std::setprecision(10) << "temp_l_adj: " << std::endl << temp_l_adj << std::endl; - // throw std::runtime_error("No- that does not work."); - //} } // set the problem pointers @@ -249,43 +214,43 @@ int main(int argc, char** argv) ct::core::FeedbackArray KSol_riccati = lqocSolvers[0]->getSolutionFeedback(); ct::core::ControlVectorArray lv_sol_riccati = lqocSolvers[0]->get_lv(); - auto xSol_aug_riccati = lqocSolvers[1]->getSolutionState(); - auto uSol_aug_riccati = lqocSolvers[1]->getSolutionControl(); - ct::core::FeedbackArray KSol_aug_riccati = lqocSolvers[1]->getSolutionFeedback(); - ct::core::ControlVectorArray lv_sol_aug_riccati = lqocSolvers[1]->get_lv(); + // auto xSol_hpipm = lqocSolvers[1]->getSolutionState(); + // auto uSol_hpipm = lqocSolvers[1]->getSolutionControl(); + // ct::core::FeedbackArray KSol_hpipm = lqocSolvers[1]->getSolutionFeedback(); + // ct::core::ControlVectorArray lv_sol_hpipm = lqocSolvers[1]->get_lv(); // compare the quantities //for (size_t i = 0; i < lv_sol_riccati.size(); i++) //{ - // if ((lv_sol_riccati[i] - lv_sol_aug_riccati[i]).array().abs().maxCoeff() > 1e-8) + // if ((lv_sol_riccati[i] - lv_sol_hpipm[i]).array().abs().maxCoeff() > 1e-8) // { // std::cout << std::setprecision(10) << lv_sol_riccati[i].transpose() << std::endl; - // std::cout << std::setprecision(10) << lv_sol_aug_riccati[i].transpose() << std::endl; + // std::cout << std::setprecision(10) << lv_sol_hpipm[i].transpose() << std::endl; // throw std::runtime_error("lv solutions do not match"); // } //} //for (size_t i = 0; i < KSol_riccati.size(); i++) //{ - // if (KSol_riccati[i].isApprox(KSol_aug_riccati[i], 1e-6) == false) + // if (KSol_riccati[i].isApprox(KSol_hpipm[i], 1e-6) == false) // throw std::runtime_error("K solutions do not match"); //} -// + // //for (size_t i = 0; i < uSol_riccati.size(); i++) //{ - // if ((uSol_riccati[i] - uSol_aug_riccati[i]).array().abs().maxCoeff() > 1e-8) + // if ((uSol_riccati[i] - uSol_hpipm[i]).array().abs().maxCoeff() > 1e-8) // { // std::cout << "for index " << i << std::endl; // std::cout << std::setprecision(10) << uSol_riccati[i].transpose() << std::endl; - // std::cout << std::setprecision(10) << uSol_aug_riccati[i].transpose() << std::endl; + // std::cout << std::setprecision(10) << uSol_hpipm[i].transpose() << std::endl; // throw std::runtime_error("u solutions do not match"); // } //} //for (size_t i = 0; i < xSol_riccati.size(); i++) //{ - // if ((xSol_riccati[i] - xSol_aug_riccati[i]).coeffs().array().abs().maxCoeff() > 1e-8) + // if ((xSol_riccati[i] - xSol_hpipm[i]).coeffs().array().abs().maxCoeff() > 1e-8) // { // std::cout << xSol_riccati[i].transpose() << std::endl; - // std::cout << xSol_aug_riccati[i].transpose() << std::endl; + // std::cout << xSol_hpipm[i].transpose() << std::endl; // throw std::runtime_error("x solutions do not match"); // } //} @@ -309,9 +274,9 @@ int main(int argc, char** argv) // << "\t -- rot diff norm(): " << angularDiff << std::endl; //} - std::cout << std::setprecision(4) << "Forward integrated closed-loop solution, iter :" << iter << std::endl; + //std::cout << std::setprecision(4) << "Forward integrated closed-loop solution, iter :" << iter << std::endl; x_curr = x0; - std::cout << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; + //std::cout << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; ct::core::DiscreteArray x_traj_prev = x_traj; double d_cum_sum = 0; double dx_cum_sum = 0; @@ -356,9 +321,15 @@ int main(int argc, char** argv) costFunction->setCurrentStateAndControl(x_traj.back(), u_traj.back(), N * dt); cost_sum += costFunction->evaluateTerminal(); - std::cout << "d_norm: \t " << d_cum_sum << "\t dx_norm: \t" << dx_cum_sum << "\t cost: " << cost_sum - << std::endl; - std::cout << std::endl << std::endl; + std::cout << std::setprecision(10) << "d_norm: \t " << d_cum_sum << "\t dx_norm: \t" << dx_cum_sum + << " \t Jcost: " << cost_sum << std::endl; } // end iter + + + // save the x-trajectory to file + std::vector rot_traj; + for(size_t i = 0; isetFromTimeInvariantLinearQuadraticProblem(discreteExampleSystem, *costFunction, b, dt); - problems[1]->setFromTimeInvariantLinearQuadraticProblem(discreteExampleSystem, *costFunction, b, dt); + problems[0]->setFromTimeInvariantLinearQuadraticProblem(x0, u0, discreteExampleSystem, *costFunction, b, dt); + problems[1]->setFromTimeInvariantLinearQuadraticProblem(x0, u0, discreteExampleSystem, *costFunction, b, dt); // set the problem pointers lqocSolvers[0]->setProblem(problems[0]); diff --git a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp index 6a26e1e2a..618146b35 100644 --- a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp +++ b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp @@ -106,6 +106,7 @@ class CompositeManifoldTangent return c; } + const Scalar* data() const { return coeffs().data(); } auto transpose() const { return coeffs().transpose(); } const POS_TANGENT& pos() const { return t_pos_; } void set_pos(const POS_TANGENT& p) { t_pos_ = p; } @@ -367,8 +368,8 @@ int main(int argc, char** argv) // create instances of HPIPM and an unconstrained Gauss-Newton Riccati gnSolver std::shared_ptr> gnSolver( new GNRiccatiSolver); - std::shared_ptr> hpipmSolver( - new HPIPMInterface); + // std::shared_ptr> hpipmSolver( + // new HPIPMInterface); // create linear-quadratic optimal control problem containers std::shared_ptr> problem( @@ -442,24 +443,24 @@ int main(int argc, char** argv) // set the problem pointers gnSolver->setProblem(problem); - std::shared_ptr> hpipmProblem (problem->clone()); - hpipmSolver->setProblem(hpipmProblem); + // std::shared_ptr> hpipmProblem (problem->clone()); + // hpipmSolver->setProblem(hpipmProblem); // allocate memory (if required) gnSolver->initializeAndAllocate(); - hpipmSolver->initializeAndAllocate(); + // hpipmSolver->initializeAndAllocate(); // solve the problem gnSolver->solve(); - hpipmSolver->solve(); + // hpipmSolver->solve(); // postprocess data gnSolver->compute_lv(); gnSolver->computeFeedbackMatrices(); gnSolver->computeStatesAndControls(); - hpipmSolver->compute_lv(); - hpipmSolver->computeFeedbackMatrices(); - hpipmSolver->computeStatesAndControls(); + // hpipmSolver->compute_lv(); + // hpipmSolver->computeFeedbackMatrices(); + // hpipmSolver->computeStatesAndControls(); // retrieve solutions from solver auto xSol_gn = gnSolver->getSolutionState(); @@ -467,10 +468,10 @@ int main(int argc, char** argv) ct::core::FeedbackArray KSol_gn = gnSolver->getSolutionFeedback(); ct::core::ControlVectorArray lv_sol_gn = gnSolver->get_lv(); - auto xSol_hpipm = hpipmSolver->getSolutionState(); - auto uSol_hpipm = hpipmSolver->getSolutionControl(); - ct::core::FeedbackArray KSol_hpipm = hpipmSolver->getSolutionFeedback(); - ct::core::ControlVectorArray lv_sol_hpipm = hpipmSolver->get_lv(); + // auto xSol_hpipm = hpipmSolver->getSolutionState(); + // auto uSol_hpipm = hpipmSolver->getSolutionControl(); + // ct::core::FeedbackArray KSol_hpipm = hpipmSolver->getSolutionFeedback(); + // ct::core::ControlVectorArray lv_sol_hpipm = hpipmSolver->get_lv(); // TODO: include a comparison here. From 39682a7cf581b983914e7128cef657d033119901 Mon Sep 17 00:00:00 2001 From: Markus Giftthaler Date: Mon, 13 Jul 2020 19:25:56 +0200 Subject: [PATCH 19/21] reverse backup --- .../CostFunctionQuadraticSimple-impl.hpp | 10 +++- .../linear/KinematicManifoldLQOCTest.cpp | 56 +++++++++++-------- 2 files changed, 41 insertions(+), 25 deletions(-) diff --git a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp index aa7282bf6..f40824a1a 100644 --- a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp +++ b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp @@ -122,7 +122,8 @@ auto CostFunctionQuadraticSimple::evaluateTerminal() -> S // TODO: including Jr_ might not be required here (identical result) Eigen::Matrix Jl, Jr; // TODO: ignore Jl cleanly typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_, Jl, Jr); - return SCALAR(0.5) * (x_deviation_final.transpose() * Jr * Q_final_ * Jr.transpose() * x_deviation_final)(0); + // return SCALAR(0.5) * (x_deviation_final.transpose() * Jr * Q_final_ * Jr.transpose() * x_deviation_final)(0); + return SCALAR(0.5) * (x_deviation_final.transpose() * Q_final_ * x_deviation_final)(0); } template @@ -130,7 +131,8 @@ typename MANIFOLD::Tangent CostFunctionQuadraticSimple::s { Eigen::Matrix Jl, Jr; // TODO: ignore Jl cleanly typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_, Jl, Jr); - return Q_final_ * Jr.transpose() * x_deviation_final; + return Q_final_ * x_deviation_final; + // return Q_final_ * Jr.transpose() * x_deviation_final; } template @@ -138,7 +140,9 @@ auto CostFunctionQuadraticSimple::stateSecondDerivativeTe { Eigen::Matrix Jl, Jr; // TODO: ignore Jl cleanly x_final_.rminus(this->x_, Jl, Jr); - return Jr * Q_final_ * Jr.transpose(); + + return Q_final_; + // return Jr * Q_final_ * Jr.transpose(); } template diff --git a/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp b/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp index 5b4e1279e..27e1ca2d2 100644 --- a/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp +++ b/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp @@ -5,16 +5,16 @@ using namespace ct::core; using namespace ct::optcon; const bool verbose = true; -using ManifoldState_t = ManifoldState; +using ManifoldState_t = ManifoldState; const size_t state_dim = ManifoldState_t::TangentDim; -const size_t control_dim = 3; +const size_t control_dim = 6; -class DiscrSO3LTITestSystem final : public ct::core::ControlledSystem +class DiscrSE3LTITestSystem final : public ct::core::ControlledSystem { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW - DiscrSO3LTITestSystem() {} + DiscrSE3LTITestSystem() {} virtual void computeControlledDynamics(const ManifoldState_t& m, const Time_t& n, const ct::core::ControlVector& u, @@ -23,7 +23,7 @@ class DiscrSO3LTITestSystem final : public ct::core::ControlledSystem(M_PI / 2, 0, 0); + const ManifoldState_t x0 = manif::SE3(0, 0, 0, 3.14, 0, 0); ct::core::DiscreteArray x_traj(N + 1, x0); // init state trajectory, will be overwritten ct::core::DiscreteArray b( N + 1, ManifoldState_t::Tangent::Zero()); // defect traj, will be overwritten ct::core::DiscreteArray> u_traj(N); // init control traj for (size_t i = 0; i < N; i++) - u_traj[i] = ct::core::ControlVector::Random() * 0.01; + u_traj[i] = ct::core::ControlVector::Random() * 0.0; // choose a random initial state // TODO: numerical trouble for more aggressive distributions, since the approximation of the value function becomes really bad? @@ -126,7 +126,7 @@ int main(int argc, char** argv) // create a discrete-time manifold system std::shared_ptr> exampleSystem( - new DiscrSO3LTITestSystem()); + new DiscrSE3LTITestSystem()); std::shared_ptr> linearizer( new ct::core::SystemLinearizer(exampleSystem)); @@ -135,12 +135,12 @@ int main(int argc, char** argv) Eigen::Matrix Q, Q_final; Eigen::Matrix R; Q_final.setZero(); - Q_final.diagonal() << 10000, 10000, 10000; + Q_final.diagonal() << 10000, 10000, 10000, 10000, 10000, 10000; Q.setZero(); - Q.diagonal() << 1, 1, 1; + //Q.diagonal() << 1, 1, 1, 1, 1, 1; R.setZero(); - R.diagonal() << 1, 1, 1; - ManifoldState_t x_final = manif::SO3(0, 0, 0); + R.diagonal() << 300, 300, 300, 300, 300, 300; + ManifoldState_t x_final = manif::SE3(1, 1, 1, 0, 0, 0); std::cout << "desired final state: " << x_final << std::endl; ManifoldState_t x_nominal = x_final; ct::core::ControlVector u_nom = ct::core::ControlVector::Zero(); @@ -153,8 +153,8 @@ int main(int argc, char** argv) ManifoldState_t::Tangent dx; x_curr = x0; x_traj.front() = x0; - std::cout << "integrate an random initial state with the unstable system" << std::endl; - std::cout << std::setprecision(4) << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; + // std::cout << "integrate an random initial state with the unstable system" << std::endl; + // std::cout << std::setprecision(4) << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; for (size_t i = 0; i < N; i++) { exampleSystem->computeControlledDynamics(x_traj[i], 0, u_traj[i], dx); @@ -162,11 +162,11 @@ int main(int argc, char** argv) if (use_single_shooting) x_traj[i + 1] = x_curr; b[i] = dx - x_traj[i + 1].rminus(x_traj[i]); - std::cout << "b: " << b[i] << std::endl; - std::cout << std::setprecision(4) << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; + // std::cout << "b: " << b[i] << std::endl; + // std::cout << std::setprecision(4) << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; } - size_t nIter = 25; + size_t nIter = 5; for (size_t iter = 0; iter < nIter; iter++) { // initialize the optimal control problems for both solvers @@ -193,6 +193,11 @@ int main(int argc, char** argv) } } + auto l = x_final.rminus(x_traj.back()); + auto l_adj = (l.exp()).adj(); + problems[idx]->Q_.back() = l_adj.transpose() * problems[idx]->Q_.back() * l_adj; + problems[idx]->qv_.back().coeffs() = - l_adj.transpose() * problems[idx]->qv_.back().coeffs(); + // set the problem pointers lqocSolvers[idx]->setProblem(problems[idx]); @@ -284,7 +289,8 @@ int main(int argc, char** argv) for (size_t i = 0; i < N; i++) { dx.setZero(); - Eigen::Quaterniond old_rot(x_traj[i].w(), x_traj[i].x(), x_traj[i].y(), x_traj[i].z()); + Eigen::Quaterniond old_rot( + x_traj[i].quat().w(), x_traj[i].quat().x(), x_traj[i].quat().y(), x_traj[i].quat().z()); if (use_single_shooting) { @@ -301,7 +307,8 @@ int main(int argc, char** argv) exampleSystem->computeControlledDynamics(x_traj[i], i * dt, u_traj[i], dx); } - Eigen::Quaterniond new_rot(x_traj[i + 1].w(), x_traj[i + 1].x(), x_traj[i + 1].y(), x_traj[i + 1].z()); + Eigen::Quaterniond new_rot( + x_traj[i + 1].quat().w(), x_traj[i + 1].quat().x(), x_traj[i + 1].quat().y(), x_traj[i + 1].quat().z()); //std::cout << "m: " << x_traj[i + 1] << "\t dx: " << xSol_riccati[i + 1] // << "\t -- rot diff norm(): " << old_rot.angularDistance(new_rot) << std::endl; @@ -328,8 +335,13 @@ int main(int argc, char** argv) // save the x-trajectory to file std::vector rot_traj; - for(size_t i = 0; i trans_traj; + for (size_t i = 0; i < x_traj.size(); i++) + { rot_traj.push_back(x_traj[i].rotation()); + trans_traj.push_back(x_traj[i].translation()); + } EigenFileExport::mat_to_file(EigenFileExport::CSVFormat(), "/tmp/rot_traj.csv", rot_traj); + EigenFileExport::mat_to_file(EigenFileExport::CSVFormat(), "/tmp/trans_traj.csv", trans_traj); return 1; } \ No newline at end of file From 94fb5a43b62f7410dd37544c18d8fd5c5ee85765 Mon Sep 17 00:00:00 2001 From: Markus Giftthaler Date: Tue, 14 Jul 2020 22:56:18 +0200 Subject: [PATCH 20/21] kinematic case not working with intermediate cost --- .../CostFunctionQuadraticSimple-impl.hpp | 39 +++++++++---------- .../CostFunctionQuadraticSimple.hpp | 3 +- .../solver/lqp/GNRiccatiSolver-impl.hpp | 8 ++-- .../linear/KinematicManifoldLQOCTest.cpp | 20 +++------- .../test/solver/linear/ManifoldLQOCTest.cpp | 5 ++- 5 files changed, 35 insertions(+), 40 deletions(-) diff --git a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp index f40824a1a..93c0e56a0 100644 --- a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp +++ b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp @@ -14,6 +14,7 @@ CostFunctionQuadraticSimple::CostFunctionQuadraticSimple( x_deviation_.setZero(); x_nominal_.setZero(); Q_.setZero(); + Adj_.setIdentity(); Jl_.setIdentity(); Jr_.setIdentity(); u_nominal_.setZero(); @@ -34,6 +35,7 @@ CostFunctionQuadraticSimple::CostFunctionQuadraticSimple( { x_deviation_.setZero(); u_deviation_.setZero(); + Adj_.setIdentity(); Jl_.setIdentity(); Jr_.setIdentity(); } @@ -48,6 +50,7 @@ CostFunctionQuadraticSimple::CostFunctionQuadraticSimple( : x_deviation_(arg.x_deviation_), x_nominal_(arg.x_nominal_), Q_(arg.Q_), + Adj_(arg.Adj_), Jl_(arg.Jl_), Jr_(arg.Jr_), u_deviation_(arg.u_deviation_), @@ -73,15 +76,16 @@ void CostFunctionQuadraticSimple::setCurrentStateAndContr this->u_ = u; this->t_ = t; - this->u_deviation_ = u - u_nominal_; - this->x_deviation_ = x_nominal_.rminus(this->x_, Jl_, Jr_); + u_deviation_ = u - u_nominal_; + x_deviation_ = x_nominal_.rminus(x, Jl_, Jr_); // ref frame located in x + + Adj_ = (x_deviation_.exp()).adj(); } template auto CostFunctionQuadraticSimple::evaluateIntermediate() -> SCALAR { - // TODO: including Jr_ might not be required here (identical result) - SCALAR costQ = SCALAR(0.5) * (x_deviation_.transpose() * Jr_ * Q_ * Jr_.transpose() * x_deviation_)(0); + SCALAR costQ = SCALAR(0.5) * (x_deviation_.transpose() * Adj_.transpose() * Q_ * Adj_ * x_deviation_)(0); SCALAR costR = SCALAR(0.5) * (u_deviation_.transpose() * R_ * u_deviation_)(0); return costQ + costR; } @@ -89,13 +93,13 @@ auto CostFunctionQuadraticSimple::evaluateIntermediate() template typename MANIFOLD::Tangent CostFunctionQuadraticSimple::stateDerivativeIntermediate() { - return Q_ * Jr_.transpose() * x_deviation_; + return -Q_ * Adj_.transpose() * x_deviation_; } template auto CostFunctionQuadraticSimple::stateSecondDerivativeIntermediate() -> state_matrix_t { - return Jr_ * Q_ * Jr_.transpose(); + return Adj_.transpose() * Q_ * Adj_; } template @@ -119,30 +123,25 @@ auto CostFunctionQuadraticSimple::stateControlDerivativeI template auto CostFunctionQuadraticSimple::evaluateTerminal() -> SCALAR { - // TODO: including Jr_ might not be required here (identical result) - Eigen::Matrix Jl, Jr; // TODO: ignore Jl cleanly - typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_, Jl, Jr); - // return SCALAR(0.5) * (x_deviation_final.transpose() * Jr * Q_final_ * Jr.transpose() * x_deviation_final)(0); - return SCALAR(0.5) * (x_deviation_final.transpose() * Q_final_ * x_deviation_final)(0); + typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_); + auto adj = (x_deviation_final.exp()).adj(); + return SCALAR(0.5) * (x_deviation_final.transpose() * adj.transpose() * Q_final_ * adj * x_deviation_final)(0); } template typename MANIFOLD::Tangent CostFunctionQuadraticSimple::stateDerivativeTerminal() { - Eigen::Matrix Jl, Jr; // TODO: ignore Jl cleanly - typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_, Jl, Jr); - return Q_final_ * x_deviation_final; - // return Q_final_ * Jr.transpose() * x_deviation_final; + typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_); // ref frame located in x_ + auto adj = (x_deviation_final.exp()).adj(); + return -Q_final_ * adj.transpose() * x_deviation_final; } template auto CostFunctionQuadraticSimple::stateSecondDerivativeTerminal() -> state_matrix_t { - Eigen::Matrix Jl, Jr; // TODO: ignore Jl cleanly - x_final_.rminus(this->x_, Jl, Jr); - - return Q_final_; - // return Jr * Q_final_ * Jr.transpose(); + typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_); + auto adj = (x_deviation_final.exp()).adj(); + return adj.transpose() * Q_final_ * adj; } template diff --git a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple.hpp b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple.hpp index a912963b7..d9e123e77 100644 --- a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple.hpp +++ b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple.hpp @@ -91,7 +91,8 @@ class CostFunctionQuadraticSimple : public CostFunctionQuadratic Jl_; // TODO: get rid of Jl cleanly + Eigen::Matrix Adj_; + Eigen::Matrix Jl_; Eigen::Matrix Jr_; control_vector_t u_deviation_; diff --git a/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp b/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp index 52b9686ff..e1faf9ebf 100644 --- a/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp +++ b/ct_optcon/include/ct/optcon/solver/lqp/GNRiccatiSolver-impl.hpp @@ -63,7 +63,7 @@ void GNRiccatiSolver::computeStatesAndControls() this->u_sol_[k] = this->lv_[k] + this->L_[k] * this->x_sol_[k]; //! state update rule in diff coordinates - StateMatrix A_orig = p.Adj_x_[k + 1] * p.A_[k]; // A "without trick for backward pass" //TODO: document this + StateMatrix A_orig = p.Adj_x_[k + 1] * p.A_[k]; // A "without trick for backward pass" //TODO: document this StateControlMatrix B_orig = p.Adj_x_[k + 1] * p.B_[k]; // B "without trick for backward pass" typename MANIFOLD::Tangent b_orig = p.Adj_x_[k + 1] * p.b_[k]; // b "without trick for backward pass" // Note that we need to transport the state update into the tagent space of k+1 @@ -155,9 +155,9 @@ void GNRiccatiSolver::computeCostToGo(size_t k) S_[k] = 0.5 * (S_[k] + S_[k].transpose()).eval(); sv_[k] = p.qv_[k]; - sv_[k]/*.noalias()*/ += p.A_[k].transpose() * sv_[k + 1]; - sv_[k]/*.noalias()*/ += p.A_[k].transpose() * S_[k + 1] * p.b_[k]; - sv_[k]/*.noalias()*/ += G_[k].transpose() * this->lv_[k]; // TODO: bring back all the noalias() + sv_[k] /*.noalias()*/ = sv_[k] + p.A_[k].transpose() * sv_[k + 1]; + sv_[k] /*.noalias()*/ = sv_[k] + p.A_[k].transpose() * S_[k + 1] * p.b_[k]; + sv_[k] /*.noalias()*/ = sv_[k] + G_[k].transpose() * this->lv_[k]; // TODO: bring back all the noalias() } template diff --git a/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp b/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp index 27e1ca2d2..fcd9a7322 100644 --- a/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp +++ b/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp @@ -135,11 +135,11 @@ int main(int argc, char** argv) Eigen::Matrix Q, Q_final; Eigen::Matrix R; Q_final.setZero(); - Q_final.diagonal() << 10000, 10000, 10000, 10000, 10000, 10000; + //Q_final.diagonal() << 10000, 10000, 10000, 10000, 10000, 10000; Q.setZero(); - //Q.diagonal() << 1, 1, 1, 1, 1, 1; + Q.diagonal() << 1, 1, 1, 1, 1, 1; R.setZero(); - R.diagonal() << 300, 300, 300, 300, 300, 300; + R.diagonal() << 1, 1, 1, 1, 1, 1; ManifoldState_t x_final = manif::SE3(1, 1, 1, 0, 0, 0); std::cout << "desired final state: " << x_final << std::endl; ManifoldState_t x_nominal = x_final; @@ -185,19 +185,11 @@ int main(int argc, char** argv) problems[idx]->Adj_x_[i + 1] = l_adj; // parallel transport matrix / adjoint from stage k+1 to stage k - if (idx == 0) // make the corrections for the standard riccati solver - { - problems[idx]->A_[i] = l_adj.transpose() * problems[idx]->A_[i]; - problems[idx]->B_[i] = l_adj.transpose() * problems[idx]->B_[i]; - problems[idx]->b_[i] = l_adj.transpose() * problems[idx]->b_[i]; - } + problems[idx]->A_[i] = l_adj.transpose() * problems[idx]->A_[i]; + problems[idx]->B_[i] = l_adj.transpose() * problems[idx]->B_[i]; + problems[idx]->b_[i] = l_adj.transpose() * problems[idx]->b_[i]; } - auto l = x_final.rminus(x_traj.back()); - auto l_adj = (l.exp()).adj(); - problems[idx]->Q_.back() = l_adj.transpose() * problems[idx]->Q_.back() * l_adj; - problems[idx]->qv_.back().coeffs() = - l_adj.transpose() * problems[idx]->qv_.back().coeffs(); - // set the problem pointers lqocSolvers[idx]->setProblem(problems[idx]); diff --git a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp index 618146b35..1857ef050 100644 --- a/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp +++ b/ct_optcon/test/solver/linear/ManifoldLQOCTest.cpp @@ -262,6 +262,7 @@ class CompositeManifold (*Jr).template bottomRightCorner() = vJr; } + // Das was ich hier als Jacobian verkaufe, was ist das eigentlich? return t; } @@ -269,7 +270,7 @@ class CompositeManifold Tangent log(OptJacobianRef J_t_m = {}) const { if (J_t_m) - throw std::runtime_error("J_t_m compuation not defined."); + throw std::runtime_error("J_t_m computation not defined."); return rminus(CompositeManifold::Identity(), J_t_m); } @@ -279,6 +280,8 @@ class CompositeManifold Jacobian J = Jacobian::Zero(); J.template topLeftCorner() = m_pos_.adj(); J.template bottomRightCorner() = m_pos_.adj(); + +// die adjoint hier ist eine reine rotation return J; } From 755d93a286d2a7f608f878f25a50839e135187c8 Mon Sep 17 00:00:00 2001 From: Markus Giftthaler Date: Sat, 19 Dec 2020 16:57:00 +0100 Subject: [PATCH 21/21] development backup holidays. --- .../CostFunctionQuadraticSimple-impl.hpp | 51 ++++++++--- ct_optcon/test/CMakeLists.txt | 8 +- .../linear/KinematicManifoldLQOCTest.cpp | 85 ++++++++++++++++--- 3 files changed, 113 insertions(+), 31 deletions(-) diff --git a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp index 93c0e56a0..1098d8469 100644 --- a/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp +++ b/ct_optcon/include/ct/optcon/costfunction/CostFunctionQuadraticSimple-impl.hpp @@ -77,15 +77,15 @@ void CostFunctionQuadraticSimple::setCurrentStateAndContr this->t_ = t; u_deviation_ = u - u_nominal_; - x_deviation_ = x_nominal_.rminus(x, Jl_, Jr_); // ref frame located in x + x_deviation_ = x.rminus(x_nominal_, Jl_, Jr_); // ref frame located in x - Adj_ = (x_deviation_.exp()).adj(); + Adj_ = x_deviation_.exp().adj(); } template auto CostFunctionQuadraticSimple::evaluateIntermediate() -> SCALAR { - SCALAR costQ = SCALAR(0.5) * (x_deviation_.transpose() * Adj_.transpose() * Q_ * Adj_ * x_deviation_)(0); + SCALAR costQ = SCALAR(0.5) * (x_deviation_.transpose() * Q_ * x_deviation_)(0); SCALAR costR = SCALAR(0.5) * (u_deviation_.transpose() * R_ * u_deviation_)(0); return costQ + costR; } @@ -93,13 +93,15 @@ auto CostFunctionQuadraticSimple::evaluateIntermediate() template typename MANIFOLD::Tangent CostFunctionQuadraticSimple::stateDerivativeIntermediate() { - return -Q_ * Adj_.transpose() * x_deviation_; + // return -Adj_.transpose() * Q_ * Adj_ * x_deviation_; + return Jl_.transpose() * Q_ * x_deviation_; } template auto CostFunctionQuadraticSimple::stateSecondDerivativeIntermediate() -> state_matrix_t { - return Adj_.transpose() * Q_ * Adj_; + //return Adj_.transpose() * Q_ * Adj_; + return Jl_.transpose() * Q_ * Jl_; } template @@ -123,25 +125,46 @@ auto CostFunctionQuadraticSimple::stateControlDerivativeI template auto CostFunctionQuadraticSimple::evaluateTerminal() -> SCALAR { - typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_); - auto adj = (x_deviation_final.exp()).adj(); - return SCALAR(0.5) * (x_deviation_final.transpose() * adj.transpose() * Q_final_ * adj * x_deviation_final)(0); + // Eigen::Matrix Jl, Jr; + // typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_, Jl, Jr); + // auto Adj = x_deviation_final.exp().adj(); + // //return SCALAR(0.5) * (x_deviation_final.transpose() * Jr * Q_final_ * Jr.transpose() * x_deviation_final)(0); + // return SCALAR(0.5) * (x_deviation_final.transpose() * Adj * Q_final_ * Adj.transpose() * x_deviation_final)(0); + + Eigen::Matrix Jl, Jr; + typename MANIFOLD::Tangent x_deviation_final = this->x_.rminus(x_final_, Jl, Jr); + //auto Adj = x_deviation_final.exp().adj(); + return SCALAR(0.5) * (x_deviation_final.transpose() * Q_final_ * x_deviation_final)(0); } template typename MANIFOLD::Tangent CostFunctionQuadraticSimple::stateDerivativeTerminal() { - typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_); // ref frame located in x_ - auto adj = (x_deviation_final.exp()).adj(); - return -Q_final_ * adj.transpose() * x_deviation_final; + // Eigen::Matrix Jl, Jr; + // typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_, Jl, Jr); + // auto Adj = x_deviation_final.exp().adj(); + // // return -Jr * Q_final_ * Jr.transpose() * x_deviation_final; + // return -Adj * Q_final_ * Adj.transpose() * x_deviation_final; + + Eigen::Matrix Jl, Jr; + typename MANIFOLD::Tangent x_deviation_final = this->x_.rminus(x_final_, Jl, Jr); + //auto Adj = x_deviation_final.exp().adj(); + return Jl.transpose() * Q_final_ * x_deviation_final; } template auto CostFunctionQuadraticSimple::stateSecondDerivativeTerminal() -> state_matrix_t { - typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_); - auto adj = (x_deviation_final.exp()).adj(); - return adj.transpose() * Q_final_ * adj; + // Eigen::Matrix Jl, Jr; + // typename MANIFOLD::Tangent x_deviation_final = x_final_.rminus(this->x_, Jl, Jr); + // auto Adj = x_deviation_final.exp().adj(); + // // return Jr * Q_final_ * Jr.transpose(); + // return Adj * Q_final_ * Adj.transpose(); + + Eigen::Matrix Jl, Jr; + typename MANIFOLD::Tangent x_deviation_final = this->x_.rminus(x_final_, Jl, Jr); + //auto Adj = x_deviation_final.exp().adj().inverse().transpose(); + return Jl.transpose() * Q_final_ * Jl; } template diff --git a/ct_optcon/test/CMakeLists.txt b/ct_optcon/test/CMakeLists.txt index 0067cb119..7418cf247 100644 --- a/ct_optcon/test/CMakeLists.txt +++ b/ct_optcon/test/CMakeLists.txt @@ -58,10 +58,10 @@ if(BUILD_TESTS) set_target_properties(KinematicManifoldLQOCTest PROPERTIES FOLDER test) list(APPEND UNIT_TEST_TARGETS KinematicManifoldLQOCTest) - add_executable(ManifoldLQOCTest solver/linear/ManifoldLQOCTest.cpp) - target_link_libraries(ManifoldLQOCTest ct_optcon ct_core) - set_target_properties(ManifoldLQOCTest PROPERTIES FOLDER test) - list(APPEND UNIT_TEST_TARGETS ManifoldLQOCTest) + #add_executable(ManifoldLQOCTest solver/linear/ManifoldLQOCTest.cpp) + #target_link_libraries(ManifoldLQOCTest ct_optcon ct_core) + #set_target_properties(ManifoldLQOCTest PROPERTIES FOLDER test) + #list(APPEND UNIT_TEST_TARGETS ManifoldLQOCTest) if(HPIPM) message(STATUS "ct_optcon: building unit tests requiring HPIPM") diff --git a/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp b/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp index fcd9a7322..bedf5d3fa 100644 --- a/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp +++ b/ct_optcon/test/solver/linear/KinematicManifoldLQOCTest.cpp @@ -40,6 +40,49 @@ class DiscrSE3LTITestSystem final : public ct::core::ControlledSystem Jl_tau, Jr_tau, Jl_zet, Jr_zet; + auto tau = Xf.rminus(X, Jl_tau, Jr_tau); + auto zet = X.rminus(Xf, Jl_zet, Jr_zet); + auto Adj_tau = tau.exp().adj(); + auto Adj_zet = zet.exp().adj(); + // std::cout << Adj_tau.transpose() * Adj_tau << std::endl; + auto tau_recon = -Adj_tau * zet; + auto tau_recon_J = Jr_tau * zet; + std::cout << "tau:" << tau << std::endl; + std::cout << "-Adj_tau * zet:" << std::endl << tau_recon.transpose() << std::endl; + std::cout << "Jr_tau * zet:" << std::endl << tau_recon_J.transpose() << std::endl; + std::cout << "-Jl_zet * zet:" << std::endl << (-Jl_zet * zet).transpose() << std::endl; + std::cout << "zet:" << zet << std::endl; + auto zet_recon = -Adj_zet * tau; + auto zet_recon_J = Jr_zet * tau; + std::cout << "-Adj_zet * tau:" << std::endl << zet_recon.transpose() << std::endl; + std::cout << "Jr_zet * tau:" << std::endl << zet_recon_J.transpose() << std::endl; + std::cout << "-Jl_tau * tau:" << std::endl << (-Jl_tau * tau).transpose() << std::endl; + // construct a random symmetric matrix Q + Eigen::Matrix Q, A; + A.setRandom(); + Q = A + A.transpose(); + // these costs all evaluate the same scalar value, so that means, for the second-order term cost eval we are fine! + // we note, however, that the hessian matrices themselves are not identical!!!! + std::cout << tau.transpose() * Q * tau << std::endl; + std::cout << zet.transpose() * Q * zet << std::endl; + std::cout << tau.transpose() * Adj_zet.transpose() * Q * Adj_zet * tau << std::endl; + std::cout << tau.transpose() * Jr_zet.transpose() * Q * Jr_zet * tau << std::endl; + std::cout << std::endl; + // .. but those matrices are not identical: + // std::cout << Adj_zet.transpose() * Q * Adj_zet << std::endl; + // std::cout << Jr_zet.transpose() * Q * Jr_zet << std::endl; + std::cout << std::endl; + } +} // TODO: make this a unit test void testParallelTransport() @@ -78,16 +121,17 @@ void testParallelTransport() // std::cout << m_mpi_2.adj() * b << std::endl << std::endl; } -int main(int argc, char** argv) + +void ocqpTest() { std::cout << std::fixed; const bool use_single_shooting = true; // toggle between single and multiple shooting - const size_t N = 150; - const double dt = 0.01; + const size_t N = 50; + const double dt = 0.1; - const ManifoldState_t x0 = manif::SE3(0, 0, 0, 3.14, 0, 0); + const ManifoldState_t x0 = manif::SE3(0, 0, 0, 3.0, 0, 0); ct::core::DiscreteArray x_traj(N + 1, x0); // init state trajectory, will be overwritten ct::core::DiscreteArray b( N + 1, ManifoldState_t::Tangent::Zero()); // defect traj, will be overwritten @@ -99,7 +143,7 @@ int main(int argc, char** argv) // TODO: numerical trouble for more aggressive distributions, since the approximation of the value function becomes really bad? for (size_t i = 1; i < N + 1; i++) { - x_traj[i] = ManifoldState_t::Random(); + x_traj[i] = x0; // ManifoldState_t::Random(); } // create instances of HPIPM and an unconstrained Gauss-Newton Riccati solver @@ -132,14 +176,19 @@ int main(int argc, char** argv) // create a cost function - Eigen::Matrix Q, Q_final; + Eigen::Matrix Q, Q_final, Q_temp; Eigen::Matrix R; + Q_temp.setRandom(); Q_final.setZero(); - //Q_final.diagonal() << 10000, 10000, 10000, 10000, 10000, 10000; Q.setZero(); + Q_final.diagonal() << 1000, 1000, 1000, 1000, 1000, 1000; + Q_final = Q_temp.transpose() * Q_final * Q_temp; + // std::cout << Q_final.eigenvalues() << std::endl; + // std::cout << Q_final << std::endl; Q.diagonal() << 1, 1, 1, 1, 1, 1; + Q = Q_temp.transpose() * Q * Q_temp; R.setZero(); - R.diagonal() << 1, 1, 1, 1, 1, 1; + R.diagonal() << 10, 10, 10, 10, 10, 10; ManifoldState_t x_final = manif::SE3(1, 1, 1, 0, 0, 0); std::cout << "desired final state: " << x_final << std::endl; ManifoldState_t x_nominal = x_final; @@ -166,7 +215,7 @@ int main(int argc, char** argv) // std::cout << std::setprecision(4) << "m: " << x_curr << "\t tan: " << x_curr.log() << std::endl; } - size_t nIter = 5; + size_t nIter = 20; for (size_t iter = 0; iter < nIter; iter++) { // initialize the optimal control problems for both solvers @@ -180,14 +229,17 @@ int main(int argc, char** argv) // dynamics transportation for (size_t i = 0; i < N; i++) { - auto l = x_traj[i + 1].rminus(x_traj[i]); + Eigen::Matrix Jl, Jr; + auto l = x_traj[i + 1].rminus(x_traj[i], Jl, Jr); auto l_adj = (l.exp()).adj(); + auto m = Jl; // best guess: Jl + problems[idx]->Adj_x_[i + 1] = l_adj; // parallel transport matrix / adjoint from stage k+1 to stage k - problems[idx]->A_[i] = l_adj.transpose() * problems[idx]->A_[i]; - problems[idx]->B_[i] = l_adj.transpose() * problems[idx]->B_[i]; - problems[idx]->b_[i] = l_adj.transpose() * problems[idx]->b_[i]; + problems[idx]->A_[i] = m.transpose() * problems[idx]->A_[i]; + problems[idx]->B_[i] = m.transpose() * problems[idx]->B_[i]; + problems[idx]->b_[i] = m.transpose() * problems[idx]->b_[i]; } // set the problem pointers @@ -335,5 +387,12 @@ int main(int argc, char** argv) } EigenFileExport::mat_to_file(EigenFileExport::CSVFormat(), "/tmp/rot_traj.csv", rot_traj); EigenFileExport::mat_to_file(EigenFileExport::CSVFormat(), "/tmp/trans_traj.csv", trans_traj); +} + + +int main(int argc, char** argv) +{ + //testCoordinateTransformCost(); + ocqpTest(); return 1; } \ No newline at end of file