diff --git a/src/deepcpp/controller.cpp b/src/deepcpp/controller.cpp index 3d2e768..6056ef2 100644 --- a/src/deepcpp/controller.cpp +++ b/src/deepcpp/controller.cpp @@ -137,18 +137,22 @@ void Controller::clear() y_ini.clear(); } -std::vector Controller::apply(const std::vector &target) +std::vector Controller::apply(const std::vector &target, const std::vector &offset) { if (!is_initialized()) return {}; check_dimensions(target, "target", target_size, output_dims); + if (!offset.empty()) + check_dimensions(offset, "offset", target_size, input_dims); + // Flatten VectorXd target_ = concat(target); + VectorXd offset_ = offset.empty() ? VectorXd::Zero(target_size * output_dims) : concat(offset); auto x = concat(u_ini.get(), y_ini.get()); - auto w = M_u.transpose() * Q * (target_ - M_x * x); + auto w = M_u.transpose() * Q * (target_ - M_x * x) + R * offset_; auto u_star = G.ldlt().solve(w).eval(); if (input_constrain_fkt != nullptr) @@ -163,7 +167,10 @@ std::vector Controller::apply(const std::vector &target) return split(u_star, target_size); } -std::vector Controller::apply(const VectorXd &target) +std::vector Controller::apply(const VectorXd &target, const VectorXd &offset) { - return apply(std::vector{target}); + return apply( + std::vector{target}, + offset.size() == 0 ? std::vector{} : std::vector{offset} + ); } diff --git a/src/deepcpp/controller.h b/src/deepcpp/controller.h index 623890b..1e26bbc 100644 --- a/src/deepcpp/controller.h +++ b/src/deepcpp/controller.h @@ -74,6 +74,6 @@ class Controller bool is_initialized() const; void update(VectorXd u, VectorXd y); void clear(); - std::vector apply(const std::vector &); - std::vector apply(const VectorXd &); + std::vector apply(const std::vector &target, const std::vector &offset = {}); + std::vector apply(const VectorXd &target, const VectorXd &offset = {}); }; diff --git a/tests/deepcpp/test_controller.cpp b/tests/deepcpp/test_controller.cpp index ca8e3c6..92bc4cb 100644 --- a/tests/deepcpp/test_controller.cpp +++ b/tests/deepcpp/test_controller.cpp @@ -76,12 +76,17 @@ void warm_up_controller(Controller& controller, DiscreteLTI& system, const Vecto // Control the system for a given number of time steps. // Returns the output of the system after the last time step. -VectorXd control_system(Controller& controller, DiscreteLTI& system, const std::vector& target, int time_steps) +VectorXd control_system( + Controller& controller, + DiscreteLTI& system, + const std::vector& target, + int time_steps, + const std::vector& offset = {}) { VectorXd u, y; for (int i = 0; i < time_steps; ++i) { - u = controller.apply(target).front(); + u = controller.apply(target, offset).front(); y = system.apply(u); controller.update(u, y); } @@ -119,6 +124,14 @@ TEST_F(Test_1D_in_1D_out_LTI, Constrained) expect_near(y, target[0], 1e-5); } +TEST_F(Test_1D_in_1D_out_LTI, Offset) +{ + Controller controller{u_d, y_d, T_ini, target.size(), /*Q*/1.0, /*R*/0.001}; + warm_up_controller(controller, system, Vector(1)); + VectorXd y = control_system(controller, system, target, T_ini, {Vector(10), Vector(10)}); + expect_near(y, target[0], 0.02); +} + class Test_2D_in_3D_out_LTI : public ::testing::Test { @@ -150,3 +163,11 @@ TEST_F(Test_2D_in_3D_out_LTI, Constrained) VectorXd y = control_system(controller, system, target, 2 * T_ini); expect_near(y, target[0], 0.05); } + +TEST_F(Test_2D_in_3D_out_LTI, Offset) +{ + Controller controller{u_d, y_d, T_ini, target.size(), /*Q*/1.0, /*R*/0.001}; + warm_up_controller(controller, system, Vector(1, 1)); + VectorXd y = control_system(controller, system, target, T_ini, {Vector(1, 1)}); + expect_near(y, target[0], 0.05); +}