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