Skip to content

Commit 08384a2

Browse files
[JTC] Accept larger number of joints than command_joints (#809)
1 parent 9063d8f commit 08384a2

File tree

11 files changed

+520
-73
lines changed

11 files changed

+520
-73
lines changed

joint_trajectory_controller/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ if(BUILD_TESTING)
7979
ament_target_dependencies(test_trajectory_controller ros2_control_test_assets)
8080
target_compile_definitions(joint_trajectory_controller PRIVATE _USE_MATH_DEFINES)
8181

82+
add_definitions(-DTEST_FILES_DIRECTORY="${CMAKE_CURRENT_SOURCE_DIR}/test")
8283
ament_add_gmock(test_load_joint_trajectory_controller
8384
test/test_load_joint_trajectory_controller.cpp
8485
)

joint_trajectory_controller/include/joint_trajectory_controller/joint_trajectory_controller.hpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ class JointTrajectoryController : public controller_interface::ControllerInterfa
100100

101101
// Degrees of freedom
102102
size_t dof_;
103+
size_t num_cmd_joints_;
104+
std::vector<size_t> map_cmd_to_joints_;
103105

104106
// Storing command joint names for interfaces
105107
std::vector<std::string> command_joint_names_;
@@ -267,9 +269,9 @@ class JointTrajectoryController : public controller_interface::ControllerInterfa
267269

268270
void init_hold_position_msg();
269271
void resize_joint_trajectory_point(
270-
trajectory_msgs::msg::JointTrajectoryPoint & point, size_t size);
272+
trajectory_msgs::msg::JointTrajectoryPoint & point, size_t size, double value = 0.0);
271273
void resize_joint_trajectory_point_command(
272-
trajectory_msgs::msg::JointTrajectoryPoint & point, size_t size);
274+
trajectory_msgs::msg::JointTrajectoryPoint & point, size_t size, double value = 0.0);
273275

274276
/**
275277
* @brief Assigns the values from a trajectory point interface to a joint interface.
@@ -283,9 +285,9 @@ class JointTrajectoryController : public controller_interface::ControllerInterfa
283285
void assign_interface_from_point(
284286
const T & joint_interface, const std::vector<double> & trajectory_point_interface)
285287
{
286-
for (size_t index = 0; index < dof_; ++index)
288+
for (size_t index = 0; index < num_cmd_joints_; ++index)
287289
{
288-
joint_interface[index].get().set_value(trajectory_point_interface[index]);
290+
joint_interface[index].get().set_value(trajectory_point_interface[map_cmd_to_joints_[index]]);
289291
}
290292
}
291293
};

joint_trajectory_controller/include/joint_trajectory_controller/trajectory.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ class Trajectory
169169
/**
170170
* \return The map between \p t1 indices (implicitly encoded in return vector indices) to \p t2
171171
* indices. If \p t1 is <tt>"{C, B}"</tt> and \p t2 is <tt>"{A, B, C, D}"</tt>, the associated
172-
* mapping vector is <tt>"{2, 1}"</tt>.
172+
* mapping vector is <tt>"{2, 1}"</tt>. return empty vector if \p t1 is not a subset of \p t2.
173173
*/
174174
template <class T>
175175
inline std::vector<size_t> mapping(const T & t1, const T & t2)

joint_trajectory_controller/src/joint_trajectory_controller.cpp

Lines changed: 89 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#include <chrono>
1818
#include <functional>
1919
#include <memory>
20-
20+
#include <numeric>
2121
#include <stdexcept>
2222
#include <string>
2323
#include <vector>
@@ -44,7 +44,7 @@
4444
namespace joint_trajectory_controller
4545
{
4646
JointTrajectoryController::JointTrajectoryController()
47-
: controller_interface::ControllerInterface(), dof_(0)
47+
: controller_interface::ControllerInterface(), dof_(0), num_cmd_joints_(0)
4848
{
4949
}
5050

@@ -105,16 +105,7 @@ JointTrajectoryController::command_interface_configuration() const
105105
{
106106
controller_interface::InterfaceConfiguration conf;
107107
conf.type = controller_interface::interface_configuration_type::INDIVIDUAL;
108-
if (dof_ == 0)
109-
{
110-
fprintf(
111-
stderr,
112-
"During ros2_control interface configuration, degrees of freedom is not valid;"
113-
" it should be positive. Actual DOF is %zu\n",
114-
dof_);
115-
std::exit(EXIT_FAILURE);
116-
}
117-
conf.names.reserve(dof_ * params_.command_interfaces.size());
108+
conf.names.reserve(num_cmd_joints_ * params_.command_interfaces.size());
118109
for (const auto & joint_name : command_joint_names_)
119110
{
120111
for (const auto & interface_type : params_.command_interfaces)
@@ -291,14 +282,17 @@ controller_interface::return_type JointTrajectoryController::update(
291282
if (use_closed_loop_pid_adapter_)
292283
{
293284
// Update PIDs
294-
for (auto i = 0ul; i < dof_; ++i)
285+
for (auto i = 0ul; i < num_cmd_joints_; ++i)
295286
{
296287
// If effort interface only, add desired effort as feed forward
297288
// If velocity interface, ignore desired effort
298-
tmp_command_[i] = (command_next_.velocities[i] * ff_velocity_scale_[i]) +
299-
(has_effort_command_interface_ ? command_next_.effort[i] : 0.0) +
300-
pids_[i]->compute_command(
301-
state_error_.positions[i], state_error_.velocities[i], period);
289+
size_t index_cmd_joint = map_cmd_to_joints_[i];
290+
tmp_command_[index_cmd_joint] =
291+
(command_next_.velocities[index_cmd_joint] * ff_velocity_scale_[i]) +
292+
(has_effort_command_interface_ ? command_next_.effort[index_cmd_joint] : 0.0) +
293+
pids_[i]->compute_command(
294+
state_error_.positions[index_cmd_joint], state_error_.velocities[index_cmd_joint],
295+
period);
302296
}
303297
}
304298

@@ -437,26 +431,38 @@ controller_interface::return_type JointTrajectoryController::update(
437431

438432
void JointTrajectoryController::read_state_from_state_interfaces(JointTrajectoryPoint & state)
439433
{
440-
auto assign_point_from_interface =
434+
auto assign_point_from_state_interface =
441435
[&](std::vector<double> & trajectory_point_interface, const auto & joint_interface)
442436
{
443437
for (size_t index = 0; index < dof_; ++index)
444438
{
445439
trajectory_point_interface[index] = joint_interface[index].get().get_value();
446440
}
447441
};
442+
auto assign_point_from_command_interface =
443+
[&](std::vector<double> & trajectory_point_interface, const auto & joint_interface)
444+
{
445+
std::fill(
446+
trajectory_point_interface.begin(), trajectory_point_interface.end(),
447+
std::numeric_limits<double>::quiet_NaN());
448+
for (size_t index = 0; index < num_cmd_joints_; ++index)
449+
{
450+
trajectory_point_interface[map_cmd_to_joints_[index]] =
451+
joint_interface[index].get().get_value();
452+
}
453+
};
448454

449455
// Assign values from the hardware
450456
// Position states always exist
451-
assign_point_from_interface(state.positions, joint_state_interface_[0]);
457+
assign_point_from_state_interface(state.positions, joint_state_interface_[0]);
452458
// velocity and acceleration states are optional
453459
if (has_velocity_state_interface_)
454460
{
455-
assign_point_from_interface(state.velocities, joint_state_interface_[1]);
461+
assign_point_from_state_interface(state.velocities, joint_state_interface_[1]);
456462
// Acceleration is used only in combination with velocity
457463
if (has_acceleration_state_interface_)
458464
{
459-
assign_point_from_interface(state.accelerations, joint_state_interface_[2]);
465+
assign_point_from_state_interface(state.accelerations, joint_state_interface_[2]);
460466
}
461467
else
462468
{
@@ -473,7 +479,7 @@ void JointTrajectoryController::read_state_from_state_interfaces(JointTrajectory
473479
// No state interface for now, use command interface
474480
if (has_effort_command_interface_)
475481
{
476-
assign_point_from_interface(state.effort, joint_command_interface_[3]);
482+
assign_point_from_command_interface(state.effort, joint_command_interface_[3]);
477483
}
478484
}
479485

@@ -484,9 +490,10 @@ bool JointTrajectoryController::read_state_from_command_interfaces(JointTrajecto
484490
auto assign_point_from_interface =
485491
[&](std::vector<double> & trajectory_point_interface, const auto & joint_interface)
486492
{
487-
for (size_t index = 0; index < dof_; ++index)
493+
for (size_t index = 0; index < num_cmd_joints_; ++index)
488494
{
489-
trajectory_point_interface[index] = joint_interface[index].get().get_value();
495+
trajectory_point_interface[map_cmd_to_joints_[index]] =
496+
joint_interface[index].get().get_value();
490497
}
491498
};
492499

@@ -568,9 +575,10 @@ bool JointTrajectoryController::read_commands_from_command_interfaces(
568575
auto assign_point_from_interface =
569576
[&](std::vector<double> & trajectory_point_interface, const auto & joint_interface)
570577
{
571-
for (size_t index = 0; index < dof_; ++index)
578+
for (size_t index = 0; index < num_cmd_joints_; ++index)
572579
{
573-
trajectory_point_interface[index] = joint_interface[index].get().get_value();
580+
trajectory_point_interface[map_cmd_to_joints_[index]] =
581+
joint_interface[index].get().get_value();
574582
}
575583
};
576584

@@ -710,12 +718,6 @@ controller_interface::CallbackReturn JointTrajectoryController::on_configure(
710718
return CallbackReturn::FAILURE;
711719
}
712720

713-
if (params_.joints.empty())
714-
{
715-
// TODO(destogl): is this correct? Can we really move-on if no joint names are not provided?
716-
RCLCPP_WARN(logger, "'joints' parameter is empty.");
717-
}
718-
719721
command_joint_names_ = params_.command_joints;
720722

721723
if (command_joint_names_.empty())
@@ -724,12 +726,40 @@ controller_interface::CallbackReturn JointTrajectoryController::on_configure(
724726
RCLCPP_INFO(
725727
logger, "No specific joint names are used for command interfaces. Using 'joints' parameter.");
726728
}
727-
else if (command_joint_names_.size() != params_.joints.size())
729+
num_cmd_joints_ = command_joint_names_.size();
730+
731+
if (num_cmd_joints_ > dof_)
728732
{
729733
RCLCPP_ERROR(
730-
logger, "'command_joints' parameter has to have the same size as 'joints' parameter.");
734+
logger, "The 'command_joints' parameter must not exceed the size of the 'joints' parameter.");
731735
return CallbackReturn::FAILURE;
732736
}
737+
else if (num_cmd_joints_ < dof_)
738+
{
739+
// create a map for the command joints
740+
map_cmd_to_joints_ = mapping(command_joint_names_, params_.joints);
741+
if (map_cmd_to_joints_.size() != num_cmd_joints_)
742+
{
743+
RCLCPP_ERROR(
744+
logger,
745+
"'command_joints' parameter must be a subset of 'joints' parameter, if their size is not "
746+
"equal.");
747+
return CallbackReturn::FAILURE;
748+
}
749+
for (size_t i = 0; i < command_joint_names_.size(); i++)
750+
{
751+
RCLCPP_DEBUG(
752+
logger, "Command joint %lu: '%s' maps to joint %lu: '%s'.", i,
753+
command_joint_names_[i].c_str(), map_cmd_to_joints_[i],
754+
params_.joints.at(map_cmd_to_joints_[i]).c_str());
755+
}
756+
}
757+
else
758+
{
759+
// create a map for the command joints, trivial if the size is the same
760+
map_cmd_to_joints_.resize(num_cmd_joints_);
761+
std::iota(map_cmd_to_joints_.begin(), map_cmd_to_joints_.end(), 0);
762+
}
733763

734764
if (params_.command_interfaces.empty())
735765
{
@@ -761,8 +791,8 @@ controller_interface::CallbackReturn JointTrajectoryController::on_configure(
761791

762792
if (use_closed_loop_pid_adapter_)
763793
{
764-
pids_.resize(dof_);
765-
ff_velocity_scale_.resize(dof_);
794+
pids_.resize(num_cmd_joints_);
795+
ff_velocity_scale_.resize(num_cmd_joints_);
766796

767797
update_pids();
768798
}
@@ -912,10 +942,12 @@ controller_interface::CallbackReturn JointTrajectoryController::on_configure(
912942
std::bind(&JointTrajectoryController::goal_accepted_callback, this, _1));
913943

914944
resize_joint_trajectory_point(state_current_, dof_);
915-
resize_joint_trajectory_point_command(command_current_, dof_);
945+
resize_joint_trajectory_point_command(
946+
command_current_, dof_, std::numeric_limits<double>::quiet_NaN());
916947
resize_joint_trajectory_point(state_desired_, dof_);
917948
resize_joint_trajectory_point(state_error_, dof_);
918-
resize_joint_trajectory_point(last_commanded_state_, dof_);
949+
resize_joint_trajectory_point(
950+
last_commanded_state_, dof_, std::numeric_limits<double>::quiet_NaN());
919951

920952
query_state_srv_ = get_node()->create_service<control_msgs::srv::QueryTrajectoryState>(
921953
std::string(get_node()->get_name()) + "/query_state",
@@ -955,8 +987,8 @@ controller_interface::CallbackReturn JointTrajectoryController::on_activate(
955987
command_interfaces_, command_joint_names_, interface, joint_command_interface_[index]))
956988
{
957989
RCLCPP_ERROR(
958-
logger, "Expected %zu '%s' command interfaces, got %zu.", dof_, interface.c_str(),
959-
joint_command_interface_[index].size());
990+
logger, "Expected %zu '%s' command interfaces, got %zu.", num_cmd_joints_,
991+
interface.c_str(), joint_command_interface_[index].size());
960992
return CallbackReturn::ERROR;
961993
}
962994
}
@@ -984,8 +1016,12 @@ controller_interface::CallbackReturn JointTrajectoryController::on_activate(
9841016
// running already)
9851017
trajectory_msgs::msg::JointTrajectoryPoint state;
9861018
resize_joint_trajectory_point(state, dof_);
1019+
// read from cmd joints only if all joints have command interface
1020+
// otherwise it leaves the entries of joints without command interface NaN.
1021+
// if no open_loop control, state_current_ is then used for `set_point_before_trajectory_msg` and
1022+
// future trajectory sampling will always give NaN for these joints
9871023
if (
988-
params_.set_last_command_interface_value_as_state_on_activation &&
1024+
params_.set_last_command_interface_value_as_state_on_activation && dof_ == num_cmd_joints_ &&
9891025
read_state_from_command_interfaces(state))
9901026
{
9911027
state_current_ = state;
@@ -1041,7 +1077,7 @@ controller_interface::CallbackReturn JointTrajectoryController::on_deactivate(
10411077
rt_active_goal_.writeFromNonRT(RealtimeGoalHandlePtr());
10421078
}
10431079

1044-
for (size_t index = 0; index < dof_; ++index)
1080+
for (size_t index = 0; index < num_cmd_joints_; ++index)
10451081
{
10461082
if (has_position_command_interface_)
10471083
{
@@ -1593,38 +1629,38 @@ bool JointTrajectoryController::contains_interface_type(
15931629
}
15941630

15951631
void JointTrajectoryController::resize_joint_trajectory_point(
1596-
trajectory_msgs::msg::JointTrajectoryPoint & point, size_t size)
1632+
trajectory_msgs::msg::JointTrajectoryPoint & point, size_t size, double value)
15971633
{
1598-
point.positions.resize(size, 0.0);
1634+
point.positions.resize(size, value);
15991635
if (has_velocity_state_interface_)
16001636
{
1601-
point.velocities.resize(size, 0.0);
1637+
point.velocities.resize(size, value);
16021638
}
16031639
if (has_acceleration_state_interface_)
16041640
{
1605-
point.accelerations.resize(size, 0.0);
1641+
point.accelerations.resize(size, value);
16061642
}
16071643
point.effort.resize(size, 0.0);
16081644
}
16091645

16101646
void JointTrajectoryController::resize_joint_trajectory_point_command(
1611-
trajectory_msgs::msg::JointTrajectoryPoint & point, size_t size)
1647+
trajectory_msgs::msg::JointTrajectoryPoint & point, size_t size, double value)
16121648
{
16131649
if (has_position_command_interface_)
16141650
{
1615-
point.positions.resize(size, 0.0);
1651+
point.positions.resize(size, value);
16161652
}
16171653
if (has_velocity_command_interface_)
16181654
{
1619-
point.velocities.resize(size, 0.0);
1655+
point.velocities.resize(size, value);
16201656
}
16211657
if (has_acceleration_command_interface_)
16221658
{
1623-
point.accelerations.resize(size, 0.0);
1659+
point.accelerations.resize(size, value);
16241660
}
16251661
if (has_effort_command_interface_)
16261662
{
1627-
point.effort.resize(size, 0.0);
1663+
point.effort.resize(size, value);
16281664
}
16291665
}
16301666

@@ -1635,9 +1671,9 @@ bool JointTrajectoryController::has_active_trajectory() const
16351671

16361672
void JointTrajectoryController::update_pids()
16371673
{
1638-
for (size_t i = 0; i < dof_; ++i)
1674+
for (size_t i = 0; i < num_cmd_joints_; ++i)
16391675
{
1640-
const auto & gains = params_.gains.joints_map.at(params_.joints[i]);
1676+
const auto & gains = params_.gains.joints_map.at(params_.joints.at(map_cmd_to_joints_[i]));
16411677
if (pids_[i])
16421678
{
16431679
// update PIDs with gains from ROS parameters

0 commit comments

Comments
 (0)