Skip to content

Commit 97c9431

Browse files
Ace314159destogl
andauthored
[JTC] Implement effort-only command interface (ros-controls#225)
* Fix trajectory tolerance parameters * Implement effort command interface for JTC * Use auto_declare for pid params * Set effort to 0 on deactivate Co-authored-by: Denis Štogl <destogl@users.noreply.github.com>
1 parent 98eef77 commit 97c9431

File tree

4 files changed

+150
-53
lines changed

4 files changed

+150
-53
lines changed

‎joint_trajectory_controller/include/joint_trajectory_controller/joint_trajectory_controller.hpp‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,9 @@ class JointTrajectoryController : public controller_interface::ControllerInterfa
141141
bool has_position_command_interface_ = false;
142142
bool has_velocity_command_interface_ = false;
143143
bool has_acceleration_command_interface_ = false;
144+
bool has_effort_command_interface_ = false;
144145

145146
/// If true, a velocity feedforward term plus corrective PID term is used
146-
// TODO(anyone): This flag is not used for now
147-
// There should be PID-approach used as in ROS1:
148-
// https://github.com/ros-controls/ros_controllers/blob/noetic-devel/joint_trajectory_controller/include/joint_trajectory_controller/hardware_interface_adapter.h#L283
149147
bool use_closed_loop_pid_adapter = false;
150148
using PidPtr = std::shared_ptr<control_toolbox::Pid>;
151149
std::vector<PidPtr> pids_;

‎joint_trajectory_controller/src/joint_trajectory_controller.cpp‎

Lines changed: 80 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -189,39 +189,48 @@ controller_interface::return_type JointTrajectoryController::update(
189189
const bool before_last_point = end_segment_itr != (*traj_point_active_ptr_)->end();
190190

191191
// set values for next hardware write()
192+
if (use_closed_loop_pid_adapter)
193+
{
194+
// Update PIDs
195+
for (auto i = 0ul; i < joint_num; ++i)
196+
{
197+
tmp_command_[i] = (state_desired.velocities[i] * ff_velocity_scale_[i]) +
198+
pids_[i]->computeCommand(
199+
state_desired.positions[i] - state_current.positions[i],
200+
state_desired.velocities[i] - state_current.velocities[i],
201+
(uint64_t)period.nanoseconds());
202+
}
203+
}
192204
if (has_position_command_interface_)
193205
{
194206
assign_interface_from_point(joint_command_interface_[0], state_desired.positions);
195207
}
196208
if (has_velocity_command_interface_)
197209
{
198-
if (!use_closed_loop_pid_adapter)
210+
if (use_closed_loop_pid_adapter)
199211
{
200-
assign_interface_from_point(joint_command_interface_[1], state_desired.velocities);
212+
assign_interface_from_point(joint_command_interface_[1], tmp_command_);
201213
}
202214
else
203215
{
204-
// Update PIDs
205-
for (auto i = 0ul; i < joint_num; ++i)
206-
{
207-
tmp_command_[i] = (state_desired.velocities[i] * ff_velocity_scale_[i]) +
208-
pids_[i]->computeCommand(
209-
state_desired.positions[i] - state_current.positions[i],
210-
state_desired.velocities[i] - state_current.velocities[i],
211-
(uint64_t)period.nanoseconds());
212-
}
213-
assign_interface_from_point(joint_command_interface_[1], tmp_command_);
216+
assign_interface_from_point(joint_command_interface_[1], state_desired.velocities);
214217
}
215218
}
216219
if (has_acceleration_command_interface_)
217220
{
218221
assign_interface_from_point(joint_command_interface_[2], state_desired.accelerations);
219222
}
220-
// TODO(anyone): Add here "if using_closed_loop_hw_interface_adapter" (see ROS1) - #171
221-
// if (check_if_interface_type_exist(
222-
// command_interface_types_, hardware_interface::HW_IF_EFFORT)) {
223-
// assign_interface_from_point(joint_command_interface_[3], state_desired.effort);
224-
// }
223+
if (has_effort_command_interface_)
224+
{
225+
if (use_closed_loop_pid_adapter)
226+
{
227+
assign_interface_from_point(joint_command_interface_[3], tmp_command_);
228+
}
229+
else
230+
{
231+
assign_interface_from_point(joint_command_interface_[3], state_desired.effort);
232+
}
233+
}
225234

226235
for (size_t index = 0; index < joint_num; ++index)
227236
{
@@ -477,32 +486,14 @@ CallbackReturn JointTrajectoryController::on_configure(const rclcpp_lifecycle::S
477486
// 2. velocity
478487
// 2. position [velocity, [acceleration]]
479488

480-
// effort can't be combined with other interfaces
481-
if (contains_interface_type(command_interface_types_, hardware_interface::HW_IF_EFFORT))
482-
{
483-
if (command_interface_types_.size() == 1)
484-
{
485-
// TODO(anyone): This flag is not used for now
486-
// There should be PID-approach used as in ROS1:
487-
// https://github.com/ros-controls/ros_controllers/blob/noetic-devel/joint_trajectory_controller/include/joint_trajectory_controller/hardware_interface_adapter.h#L283
488-
use_closed_loop_pid_adapter = true;
489-
// TODO(anyone): remove the next two lines when implemented
490-
RCLCPP_ERROR(logger, "using 'effort' command interface alone is not yet implemented yet.");
491-
return CallbackReturn::FAILURE;
492-
}
493-
else
494-
{
495-
RCLCPP_ERROR(logger, "'effort' command interface has to be used alone.");
496-
return CallbackReturn::FAILURE;
497-
}
498-
}
499-
500489
has_position_command_interface_ =
501490
contains_interface_type(command_interface_types_, hardware_interface::HW_IF_POSITION);
502491
has_velocity_command_interface_ =
503492
contains_interface_type(command_interface_types_, hardware_interface::HW_IF_VELOCITY);
504493
has_acceleration_command_interface_ =
505494
contains_interface_type(command_interface_types_, hardware_interface::HW_IF_ACCELERATION);
495+
has_effort_command_interface_ =
496+
contains_interface_type(command_interface_types_, hardware_interface::HW_IF_EFFORT);
506497

507498
if (has_velocity_command_interface_)
508499
{
@@ -530,8 +521,21 @@ CallbackReturn JointTrajectoryController::on_configure(const rclcpp_lifecycle::S
530521
return CallbackReturn::FAILURE;
531522
}
532523

533-
// TODO(livanov93): change when other option is implemented
534-
if (has_velocity_command_interface_ && use_closed_loop_pid_adapter)
524+
// effort can't be combined with other interfaces
525+
if (has_effort_command_interface_)
526+
{
527+
if (command_interface_types_.size() == 1)
528+
{
529+
use_closed_loop_pid_adapter = true;
530+
}
531+
else
532+
{
533+
RCLCPP_ERROR(logger, "'effort' command interface has to be used alone.");
534+
return CallbackReturn::FAILURE;
535+
}
536+
}
537+
538+
if (use_closed_loop_pid_adapter)
535539
{
536540
size_t num_joints = joint_names_.size();
537541
pids_.resize(num_joints);
@@ -593,7 +597,7 @@ CallbackReturn JointTrajectoryController::on_configure(const rclcpp_lifecycle::S
593597

594598
if (has_velocity_state_interface_)
595599
{
596-
if (!contains_interface_type(state_interface_types_, hardware_interface::HW_IF_POSITION))
600+
if (!has_position_state_interface_)
597601
{
598602
RCLCPP_ERROR(
599603
logger,
@@ -602,13 +606,33 @@ CallbackReturn JointTrajectoryController::on_configure(const rclcpp_lifecycle::S
602606
return CallbackReturn::FAILURE;
603607
}
604608
}
605-
else if (has_acceleration_state_interface_)
609+
else
606610
{
607-
RCLCPP_ERROR(
608-
logger,
609-
"'acceleration' state interface cannot be used if 'position' and 'velocity' "
610-
"interfaces are not present.");
611-
return CallbackReturn::FAILURE;
611+
if (has_acceleration_state_interface_)
612+
{
613+
RCLCPP_ERROR(
614+
logger,
615+
"'acceleration' state interface cannot be used if 'position' and 'velocity' "
616+
"interfaces are not present.");
617+
return CallbackReturn::FAILURE;
618+
}
619+
if (has_velocity_command_interface_ && command_interface_types_.size() == 1)
620+
{
621+
RCLCPP_ERROR(
622+
logger,
623+
"'velocity' command interface can only be used alone if 'velocity' and "
624+
"'position' state interfaces are present");
625+
return CallbackReturn::FAILURE;
626+
}
627+
// effort is always used alone so no need for size check
628+
if (has_effort_command_interface_)
629+
{
630+
RCLCPP_ERROR(
631+
logger,
632+
"'effort' command interface can only be used alone if 'velocity' and "
633+
"'position' state interfaces are present");
634+
return CallbackReturn::FAILURE;
635+
}
612636
}
613637

614638
auto get_interface_list = [](const std::vector<std::string> & interface_types) {
@@ -808,6 +832,11 @@ CallbackReturn JointTrajectoryController::on_deactivate(const rclcpp_lifecycle::
808832
{
809833
joint_command_interface_[1][index].get().set_value(0.0);
810834
}
835+
836+
if (has_effort_command_interface_)
837+
{
838+
joint_command_interface_[3][index].get().set_value(0.0);
839+
}
811840
}
812841

813842
for (size_t index = 0; index < allowed_interface_types_.size(); ++index)
@@ -845,6 +874,11 @@ bool JointTrajectoryController::reset()
845874
subscriber_is_active_ = false;
846875
joint_command_subscriber_.reset();
847876

877+
for (const auto & pid : pids_)
878+
{
879+
pid->reset();
880+
}
881+
848882
// iterator has no default value
849883
// prev_traj_point_ptr_;
850884
traj_point_active_ptr_ = nullptr;

‎joint_trajectory_controller/test/test_trajectory_controller.cpp‎

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,13 @@ TEST_P(TrajectoryControllerTestParameterized, correct_initialization_using_param
323323
EXPECT_LE(0.0, joint_vel_[2]);
324324
}
325325

326+
if (traj_controller_->has_effort_command_interface())
327+
{
328+
EXPECT_LE(0.0, joint_eff_[0]);
329+
EXPECT_LE(0.0, joint_eff_[1]);
330+
EXPECT_LE(0.0, joint_eff_[2]);
331+
}
332+
326333
// cleanup
327334
state = traj_controller_->get_node()->cleanup();
328335

@@ -469,6 +476,14 @@ TEST_P(TrajectoryControllerTestParameterized, test_jumbled_joint_order)
469476
traj_msg.points[0].velocities[2] = -0.1;
470477
}
471478

479+
if (traj_controller_->has_effort_command_interface())
480+
{
481+
traj_msg.points[0].effort.resize(3);
482+
traj_msg.points[0].effort[0] = -0.1;
483+
traj_msg.points[0].effort[1] = -0.1;
484+
traj_msg.points[0].effort[2] = -0.1;
485+
}
486+
472487
trajectory_publisher_->publish(traj_msg);
473488
}
474489

@@ -491,6 +506,13 @@ TEST_P(TrajectoryControllerTestParameterized, test_jumbled_joint_order)
491506
EXPECT_GT(0.0, joint_vel_[1]);
492507
EXPECT_GT(0.0, joint_vel_[2]);
493508
}
509+
510+
if (traj_controller_->has_effort_command_interface())
511+
{
512+
EXPECT_GT(0.0, joint_eff_[0]);
513+
EXPECT_GT(0.0, joint_eff_[1]);
514+
EXPECT_GT(0.0, joint_eff_[2]);
515+
}
494516
}
495517

496518
/**
@@ -552,6 +574,18 @@ TEST_P(TrajectoryControllerTestParameterized, test_partial_joint_list)
552574
EXPECT_NEAR(0.0, joint_vel_[2], threshold)
553575
<< "Joint 3 velocity should be 0.0 since it's not in the goal";
554576
}
577+
578+
if (
579+
std::find(command_interface_types_.begin(), command_interface_types_.end(), "effort") !=
580+
command_interface_types_.end())
581+
{
582+
// estimate the sign of the velocity
583+
// joint rotates forward
584+
EXPECT_TRUE(is_same_sign(traj_msg.points[0].positions[0] - initial_joint2_cmd, joint_eff_[0]));
585+
EXPECT_TRUE(is_same_sign(traj_msg.points[0].positions[1] - initial_joint1_cmd, joint_eff_[1]));
586+
EXPECT_NEAR(0.0, joint_eff_[2], threshold)
587+
<< "Joint 3 effort should be 0.0 since it's not in the goal";
588+
}
555589
// TODO(anyone): add here ckecks for acceleration commands
556590

557591
executor.cancel();
@@ -1180,6 +1214,16 @@ INSTANTIATE_TEST_SUITE_P(
11801214
std::vector<std::string>({"velocity"}),
11811215
std::vector<std::string>({"position", "velocity", "acceleration"}))));
11821216

1217+
// only effort controller
1218+
INSTANTIATE_TEST_CASE_P(
1219+
OnlyEffortTrajectoryControllers, TrajectoryControllerTestParameterized,
1220+
::testing::Values(
1221+
std::make_tuple(
1222+
std::vector<std::string>({"effort"}), std::vector<std::string>({"position", "velocity"})),
1223+
std::make_tuple(
1224+
std::vector<std::string>({"effort"}),
1225+
std::vector<std::string>({"position", "velocity", "acceleration"}))));
1226+
11831227
TEST_F(TrajectoryControllerTest, incorrect_initialization_using_interface_parameters)
11841228
{
11851229
auto set_parameter_and_check_result = [&]() {
@@ -1199,10 +1243,6 @@ TEST_F(TrajectoryControllerTest, incorrect_initialization_using_interface_parame
11991243
command_interface_types_ = {"bad_name"};
12001244
set_parameter_and_check_result();
12011245

1202-
// command interfaces: effort not yet implemented
1203-
command_interface_types_ = {"effort"};
1204-
set_parameter_and_check_result();
1205-
12061246
// command interfaces: effort has to be only
12071247
command_interface_types_ = {"effort", "position"};
12081248
set_parameter_and_check_result();
@@ -1236,4 +1276,18 @@ TEST_F(TrajectoryControllerTest, incorrect_initialization_using_interface_parame
12361276
// state interfaces: acceleration without position and velocity
12371277
state_interface_types_ = {"acceleration"};
12381278
set_parameter_and_check_result();
1279+
1280+
// velocity-only command interface: position - velocity not present
1281+
command_interface_types_ = {"velocity"};
1282+
state_interface_types_ = {"position"};
1283+
set_parameter_and_check_result();
1284+
state_interface_types_ = {"velocity"};
1285+
set_parameter_and_check_result();
1286+
1287+
// effort-only command interface: position - velocity not present
1288+
command_interface_types_ = {"effort"};
1289+
state_interface_types_ = {"position"};
1290+
set_parameter_and_check_result();
1291+
state_interface_types_ = {"velocity"};
1292+
set_parameter_and_check_result();
12391293
}

‎joint_trajectory_controller/test/test_trajectory_controller_utils.hpp‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const std::vector<double> INITIAL_POS_JOINTS = {
3636
INITIAL_POS_JOINT1, INITIAL_POS_JOINT2, INITIAL_POS_JOINT3};
3737
const std::vector<double> INITIAL_VEL_JOINTS = {0.0, 0.0, 0.0};
3838
const std::vector<double> INITIAL_ACC_JOINTS = {0.0, 0.0, 0.0};
39+
const std::vector<double> INITIAL_EFF_JOINTS = {0.0, 0.0, 0.0};
3940
} // namespace
4041

4142
namespace test_trajectory_controllers
@@ -98,6 +99,8 @@ class TestableJointTrajectoryController
9899

99100
bool has_velocity_command_interface() { return has_velocity_command_interface_; }
100101

102+
bool has_effort_command_interface() { return has_effort_command_interface_; }
103+
101104
rclcpp::WaitSet joint_cmd_sub_wait_set_;
102105
};
103106

@@ -117,6 +120,7 @@ class TrajectoryControllerTest : public ::testing::Test
117120
joint_state_vel_.resize(joint_names_.size(), 0.0);
118121
joint_acc_.resize(joint_names_.size(), 0.0);
119122
joint_state_acc_.resize(joint_names_.size(), 0.0);
123+
joint_eff_.resize(joint_names_.size(), 0.0);
120124
// Default interface values - they will be overwritten by parameterized tests
121125
command_interface_types_ = {"position"};
122126
state_interface_types_ = {"position", "velocity"};
@@ -199,6 +203,7 @@ class TrajectoryControllerTest : public ::testing::Test
199203
pos_cmd_interfaces_.reserve(joint_names_.size());
200204
vel_cmd_interfaces_.reserve(joint_names_.size());
201205
acc_cmd_interfaces_.reserve(joint_names_.size());
206+
eff_cmd_interfaces_.reserve(joint_names_.size());
202207
pos_state_interfaces_.reserve(joint_names_.size());
203208
vel_state_interfaces_.reserve(joint_names_.size());
204209
acc_state_interfaces_.reserve(joint_names_.size());
@@ -210,6 +215,8 @@ class TrajectoryControllerTest : public ::testing::Test
210215
joint_names_[i], hardware_interface::HW_IF_VELOCITY, &joint_vel_[i]));
211216
acc_cmd_interfaces_.emplace_back(hardware_interface::CommandInterface(
212217
joint_names_[i], hardware_interface::HW_IF_ACCELERATION, &joint_acc_[i]));
218+
eff_cmd_interfaces_.emplace_back(hardware_interface::CommandInterface(
219+
joint_names_[i], hardware_interface::HW_IF_EFFORT, &joint_eff_[i]));
213220

214221
pos_state_interfaces_.emplace_back(hardware_interface::StateInterface(
215222
joint_names_[i], hardware_interface::HW_IF_POSITION,
@@ -228,6 +235,8 @@ class TrajectoryControllerTest : public ::testing::Test
228235
cmd_interfaces.back().set_value(INITIAL_VEL_JOINTS[i]);
229236
cmd_interfaces.emplace_back(acc_cmd_interfaces_.back());
230237
cmd_interfaces.back().set_value(INITIAL_ACC_JOINTS[i]);
238+
cmd_interfaces.emplace_back(eff_cmd_interfaces_.back());
239+
cmd_interfaces.back().set_value(INITIAL_EFF_JOINTS[i]);
231240
joint_state_pos_[i] = INITIAL_POS_JOINTS[i];
232241
joint_state_vel_[i] = INITIAL_VEL_JOINTS[i];
233242
joint_state_acc_[i] = INITIAL_ACC_JOINTS[i];
@@ -399,12 +408,14 @@ class TrajectoryControllerTest : public ::testing::Test
399408
std::vector<double> joint_pos_;
400409
std::vector<double> joint_vel_;
401410
std::vector<double> joint_acc_;
411+
std::vector<double> joint_eff_;
402412
std::vector<double> joint_state_pos_;
403413
std::vector<double> joint_state_vel_;
404414
std::vector<double> joint_state_acc_;
405415
std::vector<hardware_interface::CommandInterface> pos_cmd_interfaces_;
406416
std::vector<hardware_interface::CommandInterface> vel_cmd_interfaces_;
407417
std::vector<hardware_interface::CommandInterface> acc_cmd_interfaces_;
418+
std::vector<hardware_interface::CommandInterface> eff_cmd_interfaces_;
408419
std::vector<hardware_interface::StateInterface> pos_state_interfaces_;
409420
std::vector<hardware_interface::StateInterface> vel_state_interfaces_;
410421
std::vector<hardware_interface::StateInterface> acc_state_interfaces_;

0 commit comments

Comments
 (0)