@@ -673,6 +673,141 @@ TEST_F(PidControllerTest, test_update_chained_feedforward_off_with_gain)
673673 ASSERT_EQ (controller_->command_interfaces_ [0 ].get_value (), expected_command_value);
674674}
675675
676+ /* *
677+ * @brief Test if retention of the integral state is deactivated
678+ *
679+ */
680+
681+ TEST_F (PidControllerTest, test_save_i_term_off)
682+ {
683+ SetUpController (" test_save_i_term_off" );
684+ rclcpp::executors::MultiThreadedExecutor executor;
685+ executor.add_node (controller_->get_node ()->get_node_base_interface ());
686+ executor.add_node (service_caller_node_->get_node_base_interface ());
687+
688+ ASSERT_EQ (controller_->on_configure (rclcpp_lifecycle::State ()), NODE_SUCCESS);
689+ controller_->set_chained_mode (false );
690+ for (const auto & dof_name : dof_names_)
691+ {
692+ ASSERT_FALSE (controller_->params_ .gains .dof_names_map [dof_name].save_i_term );
693+ }
694+ ASSERT_EQ (controller_->on_activate (rclcpp_lifecycle::State ()), NODE_SUCCESS);
695+ ASSERT_FALSE (controller_->is_in_chained_mode ());
696+
697+ std::shared_ptr<ControllerCommandMsg> msg = std::make_shared<ControllerCommandMsg>();
698+ msg->dof_names = dof_names_;
699+ msg->values .resize (dof_names_.size (), 0.0 );
700+ for (size_t i = 0 ; i < dof_command_values_.size (); ++i)
701+ {
702+ msg->values [i] = dof_command_values_[i];
703+ }
704+ msg->values_dot .resize (dof_names_.size (), std::numeric_limits<double >::quiet_NaN ());
705+ controller_->input_ref_ .writeFromNonRT (msg);
706+
707+ ASSERT_EQ (
708+ controller_->update (rclcpp::Time (0 ), rclcpp::Duration::from_seconds (0.01 )),
709+ controller_interface::return_type::OK);
710+
711+ // check the command value
712+ // error = ref - state = 100.001, error_dot = error/ds = 10000.1,
713+ // p_term = 100.001 * 1, i_term = 1.00001 * 2 = 2.00002, d_term = error/ds = 10000.1 * 3
714+ // feedforward OFF -> cmd = p_term + i_term + d_term = 30102.3
715+ auto expected_command_value = 30102.30102 ;
716+
717+ double actual_value = std::round (controller_->command_interfaces_ [0 ].get_value () * 1e5 ) / 1e5 ;
718+ EXPECT_NEAR (actual_value, expected_command_value, 1e-5 );
719+
720+ // deactivate the controller and set command=state
721+ ASSERT_EQ (controller_->on_deactivate (rclcpp_lifecycle::State ()), NODE_SUCCESS);
722+
723+ msg = std::make_shared<ControllerCommandMsg>();
724+ msg->dof_names = dof_names_;
725+ msg->values .resize (dof_names_.size (), 0.0 );
726+ for (size_t i = 0 ; i < dof_command_values_.size (); ++i)
727+ {
728+ msg->values [i] = dof_state_values_[i];
729+ }
730+ msg->values_dot .resize (dof_names_.size (), std::numeric_limits<double >::quiet_NaN ());
731+ controller_->input_ref_ .writeFromNonRT (msg);
732+
733+ // reactivate the controller, the integral term should NOT be saved
734+ ASSERT_EQ (controller_->on_activate (rclcpp_lifecycle::State ()), NODE_SUCCESS);
735+
736+ ASSERT_EQ (
737+ controller_->update (rclcpp::Time (0 ), rclcpp::Duration::from_seconds (0.01 )),
738+ controller_interface::return_type::OK);
739+ actual_value = std::round (controller_->command_interfaces_ [0 ].get_value () * 1e5 ) / 1e5 ;
740+ EXPECT_NEAR (actual_value, 0.0 , 1e-5 );
741+ }
742+
743+ /* *
744+ * @brief Test if retention of the integral state is working
745+ *
746+ */
747+
748+ TEST_F (PidControllerTest, test_save_i_term_on)
749+ {
750+ SetUpController (" test_save_i_term_on" );
751+ rclcpp::executors::MultiThreadedExecutor executor;
752+ executor.add_node (controller_->get_node ()->get_node_base_interface ());
753+ executor.add_node (service_caller_node_->get_node_base_interface ());
754+
755+ ASSERT_EQ (controller_->on_configure (rclcpp_lifecycle::State ()), NODE_SUCCESS);
756+ for (const auto & dof_name : dof_names_)
757+ {
758+ ASSERT_TRUE (controller_->params_ .gains .dof_names_map [dof_name].save_i_term );
759+ }
760+ controller_->set_chained_mode (false );
761+ ASSERT_EQ (controller_->on_activate (rclcpp_lifecycle::State ()), NODE_SUCCESS);
762+ ASSERT_FALSE (controller_->is_in_chained_mode ());
763+
764+ std::shared_ptr<ControllerCommandMsg> msg = std::make_shared<ControllerCommandMsg>();
765+ msg->dof_names = dof_names_;
766+ msg->values .resize (dof_names_.size (), 0.0 );
767+ for (size_t i = 0 ; i < dof_command_values_.size (); ++i)
768+ {
769+ msg->values [i] = dof_command_values_[i];
770+ }
771+ msg->values_dot .resize (dof_names_.size (), std::numeric_limits<double >::quiet_NaN ());
772+ controller_->input_ref_ .writeFromNonRT (msg);
773+
774+ ASSERT_EQ (
775+ controller_->update (rclcpp::Time (0 ), rclcpp::Duration::from_seconds (0.01 )),
776+ controller_interface::return_type::OK);
777+
778+ // check the command value
779+ // error = ref - state = 100.001, error_dot = error/ds = 10000.1,
780+ // p_term = 100.001 * 1, i_term = 1.00001 * 2 = 2.00002, d_term = error/ds = 10000.1 * 3
781+ // feedforward OFF -> cmd = p_term + i_term + d_term = 30102.3
782+ auto expected_command_value = 30102.30102 ;
783+
784+ double actual_value = std::round (controller_->command_interfaces_ [0 ].get_value () * 1e5 ) / 1e5 ;
785+ EXPECT_NEAR (actual_value, expected_command_value, 1e-5 );
786+
787+ // deactivate the controller and set command=state
788+ ASSERT_EQ (controller_->on_deactivate (rclcpp_lifecycle::State ()), NODE_SUCCESS);
789+
790+ msg = std::make_shared<ControllerCommandMsg>();
791+ msg->dof_names = dof_names_;
792+ msg->values .resize (dof_names_.size (), 0.0 );
793+ for (size_t i = 0 ; i < dof_command_values_.size (); ++i)
794+ {
795+ msg->values [i] = dof_state_values_[i];
796+ }
797+ msg->values_dot .resize (dof_names_.size (), std::numeric_limits<double >::quiet_NaN ());
798+ controller_->input_ref_ .writeFromNonRT (msg);
799+
800+ // reactivate the controller, the integral term should be saved
801+ ASSERT_EQ (controller_->on_activate (rclcpp_lifecycle::State ()), NODE_SUCCESS);
802+
803+ ASSERT_EQ (
804+ controller_->update (rclcpp::Time (0 ), rclcpp::Duration::from_seconds (0.01 )),
805+ controller_interface::return_type::OK);
806+ expected_command_value = 2.00002 ; // i_term from above
807+ actual_value = std::round (controller_->command_interfaces_ [0 ].get_value () * 1e5 ) / 1e5 ;
808+ EXPECT_NEAR (actual_value, expected_command_value, 1e-5 );
809+ }
810+
676811int main (int argc, char ** argv)
677812{
678813 ::testing::InitGoogleTest (&argc, argv);
0 commit comments