Skip to content

Commit 1fd8bc3

Browse files
livanov93bmagyar
andauthored
[JointTrajectoryController] Add velocity-only command option for JTC with closed loop controller (#239)
* Add velocity pid support. * Remove incorrect init test for only velocity command interface. * Add clarification comments for pid aux variables. Adapt update loop. * Change dt for pid to appropriate measure. * Improve partial commands for velocity-only mode. * Extend tests to use velocity-only mode. * Increase timeout for velocity-only mode parametrized tests. * add is_same_sign for better refactor * refactor boolean logic * set velocity to 0.0 on deactivate Co-authored-by: Bence Magyar <[email protected]>
1 parent 6c3f743 commit 1fd8bc3

File tree

5 files changed

+338
-167
lines changed

5 files changed

+338
-167
lines changed

joint_trajectory_controller/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ find_package(ament_cmake REQUIRED)
1414
find_package(angles REQUIRED)
1515
find_package(controller_interface REQUIRED)
1616
find_package(control_msgs REQUIRED)
17+
find_package(control_toolbox REQUIRED)
1718
find_package(hardware_interface REQUIRED)
1819
find_package(pluginlib REQUIRED)
1920
find_package(rclcpp REQUIRED)
@@ -31,6 +32,7 @@ ament_target_dependencies(joint_trajectory_controller
3132
builtin_interfaces
3233
controller_interface
3334
control_msgs
35+
control_toolbox
3436
hardware_interface
3537
pluginlib
3638
rclcpp
@@ -65,7 +67,7 @@ if(BUILD_TESTING)
6567
ament_add_gtest(test_trajectory_controller
6668
test/test_trajectory_controller.cpp
6769
ENV config_file=${CMAKE_CURRENT_SOURCE_DIR}/test/config/test_joint_trajectory_controller.yaml)
68-
set_tests_properties(test_trajectory_controller PROPERTIES TIMEOUT 180)
70+
set_tests_properties(test_trajectory_controller PROPERTIES TIMEOUT 220)
6971
target_include_directories(test_trajectory_controller PRIVATE include)
7072
target_link_libraries(test_trajectory_controller
7173
joint_trajectory_controller

joint_trajectory_controller/include/joint_trajectory_controller/joint_trajectory_controller.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#include "control_msgs/action/follow_joint_trajectory.hpp"
2626
#include "control_msgs/msg/joint_trajectory_controller_state.hpp"
27+
#include "control_toolbox/pid.hpp"
2728
#include "controller_interface/controller_interface.hpp"
2829
#include "hardware_interface/types/hardware_interface_type_values.hpp"
2930
#include "joint_trajectory_controller/tolerances.hpp"
@@ -134,6 +135,7 @@ class JointTrajectoryController : public controller_interface::ControllerInterfa
134135
InterfaceReferences<hardware_interface::LoanedCommandInterface> joint_command_interface_;
135136
InterfaceReferences<hardware_interface::LoanedStateInterface> joint_state_interface_;
136137

138+
bool has_position_state_interface_ = false;
137139
bool has_velocity_state_interface_ = false;
138140
bool has_acceleration_state_interface_ = false;
139141
bool has_position_command_interface_ = false;
@@ -145,6 +147,12 @@ class JointTrajectoryController : public controller_interface::ControllerInterfa
145147
// There should be PID-approach used as in ROS1:
146148
// https://github.com/ros-controls/ros_controllers/blob/noetic-devel/joint_trajectory_controller/include/joint_trajectory_controller/hardware_interface_adapter.h#L283
147149
bool use_closed_loop_pid_adapter = false;
150+
using PidPtr = std::shared_ptr<control_toolbox::Pid>;
151+
std::vector<PidPtr> pids_;
152+
/// Feed-forward velocity weight factor when calculating closed loop pid adapter's command
153+
std::vector<double> ff_velocity_scale_;
154+
/// reserved storage for result of the command when closed loop pid adapter is used
155+
std::vector<double> tmp_command_;
148156

149157
// TODO(karsten1987): eventually activate and deactivate subscriber directly when its supported
150158
bool subscriber_is_active_ = false;

joint_trajectory_controller/src/joint_trajectory_controller.cpp

Lines changed: 81 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ JointTrajectoryController::state_interface_configuration() const
107107
}
108108

109109
controller_interface::return_type JointTrajectoryController::update(
110-
const rclcpp::Time & /*time*/, const rclcpp::Duration & /*period*/)
110+
const rclcpp::Time & /*time*/, const rclcpp::Duration & period)
111111
{
112112
if (get_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE)
113113
{
@@ -197,7 +197,23 @@ controller_interface::return_type JointTrajectoryController::update(
197197
}
198198
if (has_velocity_command_interface_)
199199
{
200-
assign_interface_from_point(joint_command_interface_[1], state_desired.velocities);
200+
if (!use_closed_loop_pid_adapter)
201+
{
202+
assign_interface_from_point(joint_command_interface_[1], state_desired.velocities);
203+
}
204+
else
205+
{
206+
// Update PIDs
207+
for (auto i = 0ul; i < joint_num; ++i)
208+
{
209+
tmp_command_[i] = (state_desired.velocities[i] * ff_velocity_scale_[i]) +
210+
pids_[i]->computeCommand(
211+
state_desired.positions[i] - state_current.positions[i],
212+
state_desired.velocities[i] - state_current.velocities[i],
213+
(uint64_t)period.nanoseconds());
214+
}
215+
assign_interface_from_point(joint_command_interface_[1], tmp_command_);
216+
}
201217
}
202218
if (has_acceleration_command_interface_)
203219
{
@@ -483,29 +499,19 @@ CallbackReturn JointTrajectoryController::on_configure(const rclcpp_lifecycle::S
483499
}
484500
}
485501

486-
if (contains_interface_type(command_interface_types_, hardware_interface::HW_IF_POSITION))
487-
{
488-
has_position_command_interface_ = true;
489-
}
490-
if (contains_interface_type(command_interface_types_, hardware_interface::HW_IF_VELOCITY))
491-
{
492-
has_velocity_command_interface_ = true;
493-
}
494-
if (contains_interface_type(command_interface_types_, hardware_interface::HW_IF_ACCELERATION))
495-
{
496-
has_acceleration_command_interface_ = true;
497-
}
502+
has_position_command_interface_ =
503+
contains_interface_type(command_interface_types_, hardware_interface::HW_IF_POSITION);
504+
has_velocity_command_interface_ =
505+
contains_interface_type(command_interface_types_, hardware_interface::HW_IF_VELOCITY);
506+
has_acceleration_command_interface_ =
507+
contains_interface_type(command_interface_types_, hardware_interface::HW_IF_ACCELERATION);
498508

499509
if (has_velocity_command_interface_)
500510
{
501511
// if there is only velocity then use also PID adapter
502512
if (command_interface_types_.size() == 1)
503513
{
504514
use_closed_loop_pid_adapter = true;
505-
// TODO(anyone): remove this when implemented
506-
RCLCPP_ERROR(logger, "using 'velocity' command interface alone is not yet implemented yet.");
507-
return CallbackReturn::FAILURE;
508-
// if velocity interface can be used without position if multiple defined
509515
}
510516
else if (!has_position_command_interface_)
511517
{
@@ -526,6 +532,28 @@ CallbackReturn JointTrajectoryController::on_configure(const rclcpp_lifecycle::S
526532
return CallbackReturn::FAILURE;
527533
}
528534

535+
// TODO(livanov93): change when other option is implemented
536+
if (has_velocity_command_interface_ && use_closed_loop_pid_adapter)
537+
{
538+
size_t num_joints = joint_names_.size();
539+
pids_.resize(num_joints);
540+
ff_velocity_scale_.resize(num_joints);
541+
tmp_command_.resize(num_joints, 0.0);
542+
543+
// Init PID gains from ROS parameter server
544+
for (size_t i = 0; i < pids_.size(); ++i)
545+
{
546+
const std::string prefix = "gains." + joint_names_[i];
547+
const auto k_p = auto_declare<double>(prefix + ".p", 0.0);
548+
const auto k_i = auto_declare<double>(prefix + ".i", 0.0);
549+
const auto k_d = auto_declare<double>(prefix + ".d", 0.0);
550+
const auto i_clamp = auto_declare<double>(prefix + ".i_clamp", 0.0);
551+
ff_velocity_scale_[i] = auto_declare<double>("ff_velocity_scale/" + joint_names_[i], 0.0);
552+
// Initialize PID
553+
pids_[i] = std::make_shared<control_toolbox::Pid>(k_p, k_i, k_d, i_clamp, -i_clamp);
554+
}
555+
}
556+
529557
// Read always state interfaces from the parameter because they can be used
530558
// independently from the controller's type.
531559
// Specialized, child controllers should set its default value.
@@ -558,14 +586,12 @@ CallbackReturn JointTrajectoryController::on_configure(const rclcpp_lifecycle::S
558586
}
559587
}
560588

561-
if (contains_interface_type(state_interface_types_, hardware_interface::HW_IF_VELOCITY))
562-
{
563-
has_velocity_state_interface_ = true;
564-
}
565-
if (contains_interface_type(state_interface_types_, hardware_interface::HW_IF_ACCELERATION))
566-
{
567-
has_acceleration_state_interface_ = true;
568-
}
589+
has_position_state_interface_ =
590+
contains_interface_type(state_interface_types_, hardware_interface::HW_IF_POSITION);
591+
has_velocity_state_interface_ =
592+
contains_interface_type(state_interface_types_, hardware_interface::HW_IF_VELOCITY);
593+
has_acceleration_state_interface_ =
594+
contains_interface_type(state_interface_types_, hardware_interface::HW_IF_ACCELERATION);
569595

570596
if (has_velocity_state_interface_)
571597
{
@@ -774,8 +800,16 @@ CallbackReturn JointTrajectoryController::on_deactivate(const rclcpp_lifecycle::
774800
// TODO(anyone): How to halt when using effort commands?
775801
for (size_t index = 0; index < joint_names_.size(); ++index)
776802
{
777-
joint_command_interface_[0][index].get().set_value(
778-
joint_command_interface_[0][index].get().get_value());
803+
if (has_position_command_interface_)
804+
{
805+
joint_command_interface_[0][index].get().set_value(
806+
joint_command_interface_[0][index].get().get_value());
807+
}
808+
809+
if (has_velocity_command_interface_)
810+
{
811+
joint_command_interface_[1][index].get().set_value(0.0);
812+
}
779813
}
780814

781815
for (size_t index = 0; index < allowed_interface_types_.size(); ++index)
@@ -820,6 +854,12 @@ bool JointTrajectoryController::reset()
820854
traj_home_point_ptr_.reset();
821855
traj_msg_home_ptr_.reset();
822856

857+
// reset pids
858+
for (const auto & pid : pids_)
859+
{
860+
pid->reset();
861+
}
862+
823863
return true;
824864
}
825865

@@ -965,7 +1005,19 @@ void JointTrajectoryController::fill_partial_goal(
9651005
for (auto & it : trajectory_msg->points)
9661006
{
9671007
// Assume hold position with 0 velocity and acceleration for missing joints
968-
it.positions.push_back(joint_command_interface_[0][index].get().get_value());
1008+
if (!it.positions.empty())
1009+
{
1010+
if (has_position_command_interface_)
1011+
{
1012+
// copy last command if cmd interface exists
1013+
it.positions.push_back(joint_command_interface_[0][index].get().get_value());
1014+
}
1015+
else if (has_position_state_interface_)
1016+
{
1017+
// copy current state if state interface exists
1018+
it.positions.push_back(joint_state_interface_[0][index].get().get_value());
1019+
}
1020+
}
9691021
if (!it.velocities.empty())
9701022
{
9711023
it.velocities.push_back(0.0);

0 commit comments

Comments
 (0)