Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port cartesian_impedance_controller to franka_ros2 example controllers #51

Open
wants to merge 12 commits into
base: humble
Choose a base branch
from

Conversation

sp-sophia-labs
Copy link

@sp-sophia-labs sp-sophia-labs commented Mar 19, 2024

Description:

This PR offers a high level port of Cartesian impedance example controller provided in Franka ROS1 and requests feedback.

ros humble run test

The port was basically based on :

  • ROS2 Franka example controllers for global architecture of controller and getting robot information (Robot State and Robot Model).
  • ROS1 Cartesian example controller example for the theoretical part (basically real-time update() method), all computation is pretty much ported with no changes.

Architecture:

Controller class is implemented based on ROS2 Control Controller Interface Class :

  • Member variables initialized in the constructor header.
  • init() method calls ControllerInterface::init() to initialize the lifecycle of the controller. Following this, a declaration of
    all parameters defining their default values are.
  • Get Robot State and Robot Model Interfaces in State_interface_configuration() :

`

const std::string k_robot_state_interface_name{"robot_state"};
const std::string k_robot_model_interface_name{"robot_model"};

for (const auto& franka_robot_model_name : franka_robot_model_->get_state_interface_names()) {
      state_interfaces_config.names.push_back(franka_robot_model_name);
 }

for (const auto& franka_robot_state_name : franka_robot_state_->get_state_interface_names()) {
      state_interfaces_config.names.push_back(franka_robot_state_name);
 }

`

  • Get Effort Command interface in Command_interface_configuration() :

`

for (int i = 1; i <= num_joints; i++) 
{
      command_interfaces_config.names.push_back(arm_id_ + "_joint" + std::to_string(i) + "/effort");     
 }

`

  • Real-time update() method is pretty much ported from ROS1 version (requires release build : DCMAKE_BUILD_TYPE=Release)

  • lifecycle management methods :

  1. on_configure() and on_deactivate() : Nothing special here, first to configures controller and second is called when controller is deactivated (stopped).

  2. on_activate() - called when controller is activated (started), I think this method is the equivalent functionality to the starting() method in ROS 1, since it is called when a controller is activated, and this is a suitable place to perform actions
    that need to occur just before the controller starts updating its state.

Notes :

  1. For Equilibrium pose, I first set it as initial state, then published through a separated node
  2. Compliance Parameters are set and updated through the code, Not dynamically configured yet.
  3. Controller Have been tested on Franka Emika research3 FR3.

Questions :

I need feedback/confirmation about those points if possible :

  1. Getting robot state
  2. Getting Initial robot state and Setting parameters on Activate() method

Thanks in advance !

@sp-sophia-labs
Copy link
Author

Friendly ping @BarisYazici

@BarisYazici
Copy link
Collaborator

Sorry for the delay! I will try to review in short the upcoming days

@BarisYazici
Copy link
Collaborator

BarisYazici commented Jul 1, 2024

Can you also create the bringup launch file that starts this controller?

@@ -0,0 +1,243 @@
#include "franka_example_controllers/cartesian_impedance_example_controller.hpp"
#include "franka_example_controllers/pseudo_inverse.hpp"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Header file is called pseudo_inversion.hpp

// Equilibrium pose subscription
sub_equilibrium_pose_ = get_node()->create_subscription<geometry_msgs::msg::PoseStamped>(
"equilibrium_pose", 20,
std::bind(&FSMImpedanceController::equilibriumPoseCallback, this, std::placeholders::_1));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is this &FSMImpedanceController coming from? Could you also provide the code for that?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was related to local class on our project, now equilibriumPoseCallback is memeber of &CartesianImpedanceExampleController

@sp-sophia-labs
Copy link
Author

@BarisYazici Thank you for your feedback! I have made the requested changes:

  • Updated equilibriumPoseCallback call.
  • Cleaned up leftover and header call.
  • Added launch for test.

Please let me know if there are any further adjustments needed or if there's anything else you'd like me to address

@sp-sophia-labs
Copy link
Author

@AndreasKuhner Thanks for your feedback! I have made the requested changes.

Please let me know if there are any further adjustments needed or if there's anything else you'd like me to address

@AndreasKuhner
Copy link
Member

Looks good from my side. I guess we would only need to run it on the robot for testing it :)

@BarisYazici
Copy link
Collaborator

Please add the src/cartesian_impedance_example_controller.cpp to the CMakeLists.txt under franka_example_controllers

add_library(
${PROJECT_NAME}
SHARED
src/gravity_compensation_example_controller.cpp
src/joint_impedance_example_controller.cpp
src/joint_velocity_example_controller.cpp
src/joint_position_example_controller.cpp
src/cartesian_velocity_example_controller.cpp
src/cartesian_pose_example_controller.cpp
src/joint_impedance_with_ik_example_controller.cpp
src/cartesian_elbow_example_controller.cpp
src/cartesian_orientation_example_controller.cpp
src/elbow_example_controller.cpp
src/model_example_controller.cpp
src/move_to_start_example_controller.cpp
src/motion_generator.cpp)

@BarisYazici
Copy link
Collaborator

Add the arm_id parameter similar to the other launch file examples and pass parameter this to the franka.launch.py


// pseudoinverse for nullspace handling
Eigen::MatrixXd jacobian_transpose_pinv;
pseudoInverse(jacobian.transpose(), jacobian_transpose_pinv);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

based on the definition on PseudoInverse from you:

inline Eigen::MatrixXd pseudoInverse(const Eigen::MatrixXd& M_, bool damped = true) {
  double lambda_ = damped ? 0.2 : 0.0;

  Eigen::JacobiSVD<Eigen::MatrixXd> svd(M_, Eigen::ComputeFullU | Eigen::ComputeFullV);
  Eigen::JacobiSVD<Eigen::MatrixXd>::SingularValuesType sing_vals_ = svd.singularValues();
  Eigen::MatrixXd S_ = M_;  // copying the dimensions of M_, its content is not needed.
  S_.setZero();

  for (int i = 0; i < sing_vals_.size(); i++)
    S_(i, i) = (sing_vals_(i)) / (sing_vals_(i) * sing_vals_(i) + lambda_ * lambda_);

  return svd.matrixV() * S_.transpose() * svd.matrixU().transpose();
}

second parameter should be bool damped. And the returned value should be the inverted matrix.

@sp-sophia-labs
Copy link
Author

Thanks for the review @BarisYazici. Updated

@AndreasKuhner
Copy link
Member

I think Baris tried to run it on the robot and it didn't work - did you do tests on the robot?

@BarisYazici
Copy link
Collaborator

I think Baris tried to run it on the robot and it didn't work - did you do tests on the robot?

Yes after fixing all the issues in the PR, it still didn't run on the robot. Did you do any tests on the robot?

@sp-sophia-labs
Copy link
Author

sp-sophia-labs commented Aug 14, 2024

Yes, tested on FR3 and i could access the robot interface, send commands and the robot successfully converged to equilibrium pose (set it manually). I'll provide terminal outputs once i get access to the robot.
@BarisYazici can you provide which part doesn't work, so i can help

@sp-sophia-labs
Copy link
Author

Here's my output :

  • Successful connection with gripper
  • Successful connection with FrankaHardwareInterface
  • Controller loaded and configured correctly

outputHWfr3

Here's a short demo : Robot starts in random pose and converges to equilibrium pose relative to starting point of fr3_link8 (published manually through /equilibrium_pose topic)
IMG_5002online-video-cutter com-ezgif com-optimize

@BarisYazici
Copy link
Collaborator

BarisYazici commented Aug 19, 2024

Yes, tested on FR3 and i could access the robot interface, send commands and the robot successfully converged to equilibrium pose (set it manually). I'll provide terminal outputs once i get access to the robot.
@BarisYazici can you provide which part doesn't work, so i can help

I wrote this because the code you started the PR with was not compiling and the source code was not in the CMakelists.txt. I invested some time to fix the issues with the code and got it in a correctly compiling state. After I tested couple of time with the robot and it was throwing some communication violations. I didn't try to debug further than that. If you say you tried it on the hardware, I will give it another try. But please put all the files necessary to run the example in this PR. You mention a node to publish equilibrium pose. Could you also put it in this PR?

I noticed in the image of the terminal output your example controller name is force_impedance_control. Is it the same setup you are using in the tests? Please make sure it's exactly the same.

tau_d << tau_task + tau_nullspace + coriolis;

// saturate the commanded torque to joint limits
tau_d << saturateTorqueRate(tau_d, tau_j_d);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo with tau_j_d -> tau_J_d

}

controller_interface::return_type CartesianImpedanceExampleController::update(
const rclcpp::Time& time,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment time and period out to make linter happy

Suggested change
const rclcpp::Time& time,
controller_interface::return_type CartesianImpedanceExampleController::update(
const rclcpp::Time& /*time*/,
const rclcpp::Duration& /*period*/) {

@@ -47,6 +47,10 @@ controller_manager:
franka_robot_state_broadcaster:
type: franka_robot_state_broadcaster/FrankaRobotStateBroadcaster

cartesian_impedance_example_controller:
type: franka_example_controllers::CartesianImpedanceExampleController
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
type: franka_example_controllers::CartesianImpedanceExampleController
type: franka_example_controllers/CartesianImpedanceExampleController


for (int i = 0; i < num_joints; i++) {
command_interfaces_[i].set_value(tau_d[i]);
std::cout << tau_d[i] << std::endl;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove std::cout

@AllanXuRan
Copy link

There is an error occurs because franka_example_controllers references a non-existent
source file equilibrium_pose_publisher.cpp in its CMakeLists.txt. As a result,
CMake fails with:

  Cannot find source file: src/equilibrium_pose_publisher.cpp
  No SOURCES given to target: equilibrium_pose_publisher

How to fix this?

@AndreasKuhner
Copy link
Member

To my knowledge, there is no equilibrium_pose_publisher.cpp. So, not sure where this would come from. Maybe it was part of the old implementation?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants