Skip to content

Chained Controller with Parallel Gripper Action Controller #1750

Open
@zacharyyamaoka

Description

@zacharyyamaoka

Hi ros2 team,

Just making a note here in case someone runs into the same problem.

Out of the Box Parallel Gripper Action Controller doesn't seem to account for prefixes on chained controllers properly:

this works:

  eef_controller:
      ros__parameters:
        action_monitor_rate: 25.0
        joint: eef_joint_1 
        state_interfaces:
          - position
          - velocity

This doesn't work:

  chained_eef_controller:
      ros__parameters:
        action_monitor_rate: 25.0
        joint: moteus_eef_controller/eef_joint_1 
        state_interfaces:
          - position
          - velocity

My solution for now is to properly slice the joint names...

inline std::string get_full_prefix(const std::string & full_name)
{
  auto pos = full_name.rfind('/');
  if (pos == std::string::npos || pos == 0)
  {
    return "";  // No slash or nothing before it
  }
  return full_name.substr(0, pos);
}

inline std::string get_suffix(const std::string & full_name)
{
  auto pos = full_name.rfind('/');
  if (pos == std::string::npos || pos == full_name.length() - 1)
  {
    return full_name;  // No slash, or slash at end
  }
  return full_name.substr(pos + 1);
}
controller_interface::CallbackReturn GripperActionController::on_activate(
  const rclcpp_lifecycle::State &)
{

// Log all available command interfaces
  RCLCPP_INFO(get_node()->get_logger(), "Available command interfaces:");
  for (const auto & interface : command_interfaces_)
  {
    RCLCPP_INFO(
      get_node()->get_logger(),
      "- prefix: '%s', interface: '%s', full name: '%s', full prefix: '%s', suffix: '%s'",
      interface.get_prefix_name().c_str(),
      interface.get_interface_name().c_str(),
      interface.get_name().c_str(),
      get_full_prefix(interface.get_name()).c_str(),
      get_suffix(interface.get_name()).c_str());
  }

  auto command_interface_it = std::find_if(
    command_interfaces_.begin(), command_interfaces_.end(),
    [](const hardware_interface::LoanedCommandInterface & command_interface)
      { return get_suffix(command_interface.get_name()) == hardware_interface::HW_IF_POSITION; });
    // { return command_interface.get_interface_name() == hardware_interface::HW_IF_POSITION; });


  if (command_interface_it == command_interfaces_.end())
  {
    RCLCPP_ERROR(
      get_node()->get_logger(), "Expected 1 %s command interface",
      hardware_interface::HW_IF_POSITION);
    return controller_interface::CallbackReturn::ERROR;
  }
  if (get_full_prefix(command_interface_it->get_name()) != params_.joint)
  // if (command_interface_it->get_prefix_name() != params_.joint)
  {
    RCLCPP_ERROR(
      get_node()->get_logger(), "Command interface is different than joint name `%s` != `%s`",
      get_full_prefix(command_interface_it->get_name()).c_str(), params_.joint.c_str());
    return controller_interface::CallbackReturn::ERROR;
  }
  const auto position_state_interface_it = std::find_if(
    state_interfaces_.begin(), state_interfaces_.end(),
    [](const hardware_interface::LoanedStateInterface & state_interface)
      { return get_suffix(state_interface.get_name()) == hardware_interface::HW_IF_POSITION; });
    // { return state_interface.get_interface_name() == hardware_interface::HW_IF_POSITION; });


  if (position_state_interface_it == state_interfaces_.end())
  {
    RCLCPP_ERROR(get_node()->get_logger(), "Expected 1 position state interface");
    return controller_interface::CallbackReturn::ERROR;
  }
  if (get_full_prefix(position_state_interface_it->get_name()) != params_.joint)
  // if (position_state_interface_it->get_prefix_name() != params_.joint)
  {
    RCLCPP_ERROR(
      get_node()->get_logger(),
      "Position state interface is different than joint name `%s` != `%s`",
      get_full_prefix(position_state_interface_it->get_name()).c_str(), params_.joint.c_str());
    return controller_interface::CallbackReturn::ERROR;
  }

  const auto velocity_state_interface_it = std::find_if(
    state_interfaces_.begin(), state_interfaces_.end(),
    [](const hardware_interface::LoanedStateInterface & state_interface)
      { return get_suffix(state_interface.get_name()) == hardware_interface::HW_IF_VELOCITY; });
      // { return state_interface.get_interface_name() == hardware_interface::HW_IF_VELOCITY; });

  if (velocity_state_interface_it == state_interfaces_.end())
  {
    RCLCPP_ERROR(get_node()->get_logger(), "Expected 1 velocity state interface");
    return controller_interface::CallbackReturn::ERROR;
  }
  if (get_full_prefix(velocity_state_interface_it->get_name()) != params_.joint)
  // if (velocity_state_interface_it->get_prefix_name() != params_.joint)
  {
    RCLCPP_ERROR(
      get_node()->get_logger(),
      "Velocity command interface is different than joint name `%s` != `%s`",
      get_full_prefix(velocity_state_interface_it->get_name()).c_str(), params_.joint.c_str());
    return controller_interface::CallbackReturn::ERROR;
  }

  joint_command_interface_ = *command_interface_it;
  joint_position_state_interface_ = *position_state_interface_it;
  joint_velocity_state_interface_ = *velocity_state_interface_it;

  for (auto & interface : command_interfaces_)
  {
    if (interface.get_interface_name() == "set_gripper_max_effort")
    {
      effort_interface_ = interface;
    }
    else if (interface.get_interface_name() == "set_gripper_max_velocity")
    {
      speed_interface_ = interface;
    }
  }

JTC doesn't seem to have this issue as it doesn't some logic in here that I think takes care of it... note sure though!

    if (!controller_interface::get_ordered_interfaces(
          command_interfaces_, command_joint_names_, interface, joint_command_interface_[index]))
    {
bool get_ordered_interfaces(
  std::vector<T> & unordered_interfaces, const std::vector<std::string> & ordered_names,
  const std::string & interface_type, std::vector<std::reference_wrapper<T>> & ordered_interfaces)
{
  ordered_interfaces.reserve(ordered_names.size());
  for (const auto & name : ordered_names)
  {
    for (auto & interface : unordered_interfaces)
    {
      if (!interface_type.empty())
      {
        // check case where:
        // (<joint> == <joint> AND <interface> == <interface>) OR <joint>/<interface> == 'full name'
        if (
          ((name == interface.get_prefix_name()) &&
           (interface_type == interface.get_interface_name())) ||
          ((name + "/" + interface_type) == interface.get_name()))
        {
          ordered_interfaces.push_back(std::ref(interface));
        }
      }
      else
      {
        if (name == interface.get_name())
        {
          ordered_interfaces.push_back(std::ref(interface));
        }
      }
    }
  }

  return ordered_names.size() == ordered_interfaces.size();
}

another challenge I am looking into right now is using Moveit with a chained JTC, as move it sends trajectories with raw joint names joint_1, which fail the trajectory_validation in JTC as it expects names with chained_controller_1/joint_1

(PS. Sorry for not pushing a patch right now. I know I also have few other open issues I need to merge changes for... 😅, I am working solo, and I am very bad with this CI/CD stuff so makes it more difficult)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions