diff --git a/.gitignore b/.gitignore index b931091cf188bfe8175a6e2e0f7992f453d9ecad..914e43bf52d4a975464da295a54cab9d51b3f971 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,11 @@ .vscode/ +build/ +assets/data/*.asv +fr3_impedance_controller_real/build/ fr3_impedance_controller_sim/build/ fr3_ros1_mujoco_interface/build/ fr3_ps3_joy_publisher/build/ assets/data/matlab/*.asv assets/data/*.csv + # assets/data/*.bag diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..9d4025de473be6d24009f9b1855d44952d2e959c --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1,85 @@ +# @Kevin 01.2025 +# This builds common as a shared lib so it can be built as Nix package and +# linked to the other binaries. + +################################################################################ +## Set minimum required version of cmake, project name and compile options +################################################################################ +cmake_minimum_required(VERSION 3.10) +project(common) + +# Set C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +################################################################################ +## Find required CMake packages : pinocchio, casadi, catkin, ROS-related ... +################################################################################ +find_package(pinocchio REQUIRED) +find_package(casadi REQUIRED) +find_package(Eigen3 REQUIRED NO_MODULE) + +################################################################################ +## Find source and header files to create executable binary file +################################################################################ +file(GLOB_RECURSE SOURCES + controller/src/*.cpp + math/src/*.cpp + motion/src/*.cpp + robot/src/*.cpp + utilities/src/*.cpp +) + +include_directories( + controller/include + math/include + motion/include + robot/include + utilities/include +) + +################################################################################ +## Define executable, Add source files & Link libraries +################################################################################ +add_library(common SHARED ${SOURCES}) + +target_link_libraries(common + Eigen3::Eigen + pinocchio::pinocchio + casadi +) + +target_include_directories(common + PUBLIC + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/controller/include> + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/math/include> + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/motion/include> + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/robot/include> + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/utilities/include> + ) + +install(TARGETS common + EXPORT commonTargets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}) + +install(EXPORT commonTargets + FILE commonConfig.cmake + NAMESPACE common:: + DESTINATION lib/cmake/common) + +install(DIRECTORY controller/include/ + DESTINATION include) + +install(DIRECTORY math/include/ + DESTINATION include) + +install(DIRECTORY motion/include/ + DESTINATION include) + +install(DIRECTORY robot/include/ + DESTINATION include) + +install(DIRECTORY utilities/include/ + DESTINATION include) + diff --git a/common/default.nix b/common/default.nix new file mode 100644 index 0000000000000000000000000000000000000000..3c6701c4d3386c613b65e6ea14cae6c5dc0a1690 --- /dev/null +++ b/common/default.nix @@ -0,0 +1,31 @@ +# This defines a function which returns a packge, intended to be evaluated by callPackage +{ lib +, stdenv +, cmake +, eigen +, pinocchio +, casadi +}: + +stdenv.mkDerivation { + pname = "common"; + version = "0.0.0"; + src = ./.; + + # These are the build-time dependencies + nativeBuildInputs = [ + cmake + ]; + + # These dependencies are propagated, any package (or shell) which + # includes these will also include these deps + propagatedBuildInputs = [ + eigen + casadi + pinocchio + ]; + + meta = { + description = "Common files for the observer and impedance controller"; + }; +} diff --git a/flake.nix b/flake.nix index 54a0d8e6973d5e99e7c383414cea99c5956a8f6e..4ffcdd98fd4196dda84210f113b41c6ebd240e58 100644 --- a/flake.nix +++ b/flake.nix @@ -4,7 +4,6 @@ inputs = { nixpkgs_2411.url = "github:nixos/nixpkgs/nixos-24.11"; nixpkgs.follows = "noetic/nixpkgs"; - #pincas.url = "git+https://gitlab.cc-asp.fraunhofer.de/ipk_aut/bestpractices/nix.git?dir=pincas"; nix-ros-overlay.follows = "noetic/nix-ros-overlay"; noetic.url = "git+https://gitlab.cc-asp.fraunhofer.de/ipk_aut/bestpractices/nix.git?dir=ros1/noetic"; nixgl.url = "github:nix-community/nixGL"; @@ -16,7 +15,6 @@ overlays = [ inputs.nix-ros-overlay.overlays.default - #inputs.pincas.overlays.default inputs.nixgl.overlay ]; @@ -30,8 +28,6 @@ rospkgs = pkgs.rosPackages.noetic; - fr3-custom-msgs = rospkgs.callPackage ./fr3_custom_msgs {}; - rosPackages = with rospkgs; [ ros-core catkin # for builds, probably not yet needed @@ -58,16 +54,24 @@ urdf ]; + + common = pkgs_2411.callPackage ./common { }; + fr3-impedance-controller = rospkgs.callPackage ./fr3_impedance_controller_real { + inherit common; + }; + fr3-custom-msgs = rospkgs.callPackage ./fr3_custom_msgs {}; + + projectPackages = [ + common + fr3-impedance-controller + fr3-custom-msgs + ]; + simPackages = [ pkgs.mujoco pkgs.glfw pkgs.nixgl.nixGLIntel ]; - - customPackages = [ - fr3-custom-msgs - ]; - in { devShells.${system} = { @@ -75,7 +79,7 @@ default = pkgs.mkShell { packages = [ (rospkgs.buildEnv { - paths = rosPackages ++ [ + paths = rosPackages ++ projectPackages ++ [ #pkgs.python3Packages.pincas pkgs_2411.casadi pkgs_2411.pinocchio @@ -87,7 +91,7 @@ sim = pkgs.mkShell { packages = [ (rospkgs.buildEnv { - paths = rosPackages ++ simPackages ++ customPackages ++ [ + paths = rosPackages ++ simPackages ++ projectPackages ++ [ #pkgs.python3Packages.pincas pkgs_2411.casadi pkgs_2411.pinocchio diff --git a/fr3_impedance_controller_real/CMakeLists.txt b/fr3_impedance_controller_real/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..34e464adad3724270962fbf824cb520014d94e51 --- /dev/null +++ b/fr3_impedance_controller_real/CMakeLists.txt @@ -0,0 +1,144 @@ +cmake_minimum_required(VERSION 3.4) +project(fr3_impedance_controller_real) + +set(CMAKE_BUILD_TYPE Release) +# set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(catkin REQUIRED COMPONENTS + controller_interface + dynamic_reconfigure + eigen_conversions + franka_hw + franka_gripper + franka_example_controllers + geometry_msgs + hardware_interface + joint_limits_interface + tf + tf_conversions + message_generation + pluginlib + realtime_tools + roscpp + rospy + urdf + visualization_msgs + sensor_msgs +) + +find_package(Eigen3 REQUIRED) +find_package(Franka 0.9.0 QUIET) +if(NOT Franka_FOUND) + find_package(Franka 0.8.0 REQUIRED) +endif() + +#add_message_files(FILES +# JointTorqueComparison.msg +#) + +generate_messages() + +generate_dynamic_reconfigure_options( + cfg/compliance_param.cfg +) + +catkin_package( + INCLUDE_DIRS include + LIBRARIES franka_example_controllers + CATKIN_DEPENDS + controller_interface + dynamic_reconfigure + eigen_conversions + franka_hw + franka_gripper + franka_example_controllers + geometry_msgs + hardware_interface + joint_limits_interface + tf + tf_conversions + message_runtime + pluginlib + realtime_tools + roscpp + urdf + visualization_msgs + sensor_msgs + DEPENDS Franka +) + +add_library(${PROJECT_NAME} + src/fr3_impedance_controller_real.cpp +) + +add_dependencies(${PROJECT_NAME} + ${${PROJECT_NAME}_EXPORTED_TARGETS} + ${catkin_EXPORTED_TARGETS} + ${PROJECT_NAME}_generate_messages_cpp + ${PROJECT_NAME}_gencpp + ${PROJECT_NAME}_gencfg +) + +target_link_libraries(${PROJECT_NAME} PUBLIC + ${Franka_LIBRARIES} + ${catkin_LIBRARIES} +) + +target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC + ${Franka_INCLUDE_DIRS} + ${EIGEN3_INCLUDE_DIRS} + ${catkin_INCLUDE_DIRS} +) +target_include_directories(${PROJECT_NAME} PUBLIC + include + ${CMAKE_CURRENT_SOURCE_DIR}/../common/utilities/include + ${CMAKE_CURRENT_SOURCE_DIR}/../common/math/include +) + +## Installation +install(TARGETS ${PROJECT_NAME} + ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} + LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} + RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +) +install(DIRECTORY include/${PROJECT_NAME}/ + DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} +) +install(DIRECTORY launch + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} +) +install(DIRECTORY config + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} +) +install(FILES fr3_impedance_controller_real_plugin.xml + DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} +) + +## Tools +include(${CMAKE_CURRENT_LIST_DIR}/../cmake/ClangTools.cmake OPTIONAL + RESULT_VARIABLE CLANG_TOOLS +) +if(CLANG_TOOLS) + file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) + file(GLOB_RECURSE HEADERS + ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/../common/utilities/include/*.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/../common/math/include/*.hpp + ) + add_format_target(franka_example_controllers FILES ${SOURCES} ${HEADERS}) + add_tidy_target(franka_example_controllers + FILES ${SOURCES} + DEPENDS franka_example_controllers + ) +endif() + +include(${CMAKE_CURRENT_LIST_DIR}/../cmake/PepTools.cmake OPTIONAL + RESULT_VARIABLE PEP_TOOLS +) +if(PEP_TOOLS) + file(GLOB_RECURSE PYSOURCES ${CMAKE_CURRENT_SOURCE_DIR}/scripts/*.py) + add_pyformat_target(franka_control FILES ${PYSOURCES}) +endif() diff --git a/README.md b/fr3_impedance_controller_real/README.md similarity index 100% rename from README.md rename to fr3_impedance_controller_real/README.md diff --git a/fr3_impedance_controller_real/cfg/compliance_param.cfg b/fr3_impedance_controller_real/cfg/compliance_param.cfg new file mode 100644 index 0000000000000000000000000000000000000000..6d5bd8402f3736325f9001e9e00ffaad7b0679df --- /dev/null +++ b/fr3_impedance_controller_real/cfg/compliance_param.cfg @@ -0,0 +1,16 @@ +#!/usr/bin/env python +PACKAGE = "franka_example_controllers" + +from dynamic_reconfigure.parameter_generator_catkin import * + +gen = ParameterGenerator() + +gen.add("translational_stiffness", double_t, 0, "Cartesian translational stiffness", 200, 0, 400) +gen.add("translational_stiffness_x", double_t, 0, "Cartesian translational stiffness", 200, 0, 400) +gen.add("translational_stiffness_y", double_t, 0, "Cartesian translational stiffness", 200, 0, 400) +gen.add("translational_stiffness_z", double_t, 0, "Cartesian translational stiffness", 200, 0, 400) +gen.add("rotational_stiffness", double_t, 0, "Cartesian rotational stiffness", 10, 0, 30) +gen.add("nullspace_stiffness", double_t, 0, "Stiffness of the joint space nullspace controller (the desired configuration is the one at startup)", 0.5, 0, 100) + +exit(gen.generate(PACKAGE, "dynamic_compliance", "compliance_param")) + diff --git a/fr3_impedance_controller_real/config/franka_example_controllers.yaml b/fr3_impedance_controller_real/config/franka_example_controllers.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4cb8fa5665b64f40f8a8628dcae2b52fe9b3b169 --- /dev/null +++ b/fr3_impedance_controller_real/config/franka_example_controllers.yaml @@ -0,0 +1,179 @@ +joint_velocity_example_controller: + type: franka_example_controllers/JointVelocityExampleController + arm_id: $(arg arm_id) + joint_names: + - $(arg arm_id)_joint1 + - $(arg arm_id)_joint2 + - $(arg arm_id)_joint3 + - $(arg arm_id)_joint4 + - $(arg arm_id)_joint5 + - $(arg arm_id)_joint6 + - $(arg arm_id)_joint7 + +joint_position_example_controller: + type: franka_example_controllers/JointPositionExampleController + joint_names: + - $(arg arm_id)_joint1 + - $(arg arm_id)_joint2 + - $(arg arm_id)_joint3 + - $(arg arm_id)_joint4 + - $(arg arm_id)_joint5 + - $(arg arm_id)_joint6 + - $(arg arm_id)_joint7 + +cartesian_sinpose_controller: + type: franka_example_controllers/CartesianSinPoseExampleController + arm_id: $(arg arm_id) + +cartesian_pose_example_controller: + type: franka_example_controllers/CartesianPoseExampleController + arm_id: $(arg arm_id) + +elbow_example_controller: + type: franka_example_controllers/ElbowExampleController + arm_id: $(arg arm_id) + +cartesian_velocity_example_controller: + type: franka_example_controllers/CartesianVelocityExampleController + arm_id: $(arg arm_id) + +model_example_controller: + type: franka_example_controllers/ModelExampleController + arm_id: $(arg arm_id) + +force_example_controller: + type: franka_example_controllers/ForceExampleController + arm_id: $(arg arm_id) + joint_names: + - $(arg arm_id)_joint1 + - $(arg arm_id)_joint2 + - $(arg arm_id)_joint3 + - $(arg arm_id)_joint4 + - $(arg arm_id)_joint5 + - $(arg arm_id)_joint6 + - $(arg arm_id)_joint7 + +joint_impedance_example_controller: + type: franka_example_controllers/JointImpedanceExampleController + arm_id: $(arg arm_id) + joint_names: + - $(arg arm_id)_joint1 + - $(arg arm_id)_joint2 + - $(arg arm_id)_joint3 + - $(arg arm_id)_joint4 + - $(arg arm_id)_joint5 + - $(arg arm_id)_joint6 + - $(arg arm_id)_joint7 + k_gains: + - 600.0 + - 600.0 + - 600.0 + - 600.0 + - 250.0 + - 150.0 + - 50.0 + d_gains: + - 50.0 + - 50.0 + - 50.0 + - 20.0 + - 20.0 + - 20.0 + - 10.0 + radius: 0.1 + acceleration_time: 2.0 + vel_max: 0.15 + publish_rate: 10.0 + coriolis_factor: 1.0 + +cartesian_impedance_example_controller: + type: franka_example_controllers/CartesianImpedanceExampleController + arm_id: $(arg arm_id) + joint_names: + - $(arg arm_id)_joint1 + - $(arg arm_id)_joint2 + - $(arg arm_id)_joint3 + - $(arg arm_id)_joint4 + - $(arg arm_id)_joint5 + - $(arg arm_id)_joint6 + - $(arg arm_id)_joint7 + +dual_arm_cartesian_impedance_example_controller: + type: franka_example_controllers/DualArmCartesianImpedanceExampleController + right: + arm_id: panda_1 + joint_names: + - panda_1_joint1 + - panda_1_joint2 + - panda_1_joint3 + - panda_1_joint4 + - panda_1_joint5 + - panda_1_joint6 + - panda_1_joint7 + left: + arm_id: panda_2 + joint_names: + - panda_2_joint1 + - panda_2_joint2 + - panda_2_joint3 + - panda_2_joint4 + - panda_2_joint5 + - panda_2_joint6 + - panda_2_joint7 + +teleop_joint_pd_example_controller: + type: franka_example_controllers/TeleopJointPDExampleController + leader: + arm_id: leader + joint_names: + - leader_joint1 + - leader_joint2 + - leader_joint3 + - leader_joint4 + - leader_joint5 + - leader_joint6 + - leader_joint7 + # WARNING: Varying the following parameters may result in instability! + d_gains_lower: [1.0, 1.0, 1.0, 1.0, 0.33, 0.33, 0.17] + d_gains_upper: [35.0, 35.0, 35.0, 35.0, 10.0, 10.0, 8.0] + dq_max_lower: [1.3, 1.3, 1.3, 1.3, 1.4, 1.4, 1.4] # [rad/s] + dq_max_upper: [1.9, 1.9, 1.9, 1.9, 2.2, 2.2, 2.2] # [rad/s] + contact_force_threshold: 4.0 # [N] + + follower: + arm_id: follower + joint_names: + - follower_joint1 + - follower_joint2 + - follower_joint3 + - follower_joint4 + - follower_joint5 + - follower_joint6 + - follower_joint7 + # WARNING: Varying the following parameters may result in instability! + p_gains: [900.0, 900.0, 900.0, 900.0, 375.0, 225.0, 100.0] + d_gains: [45.0, 45.0, 45.0, 45.0, 15.0, 15.0, 10.0] + drift_comp_gains: [4.3, 4.3, 4.3, 4.3, 4.3, 4.3, 4.3] + dq_max_lower: [0.8, 0.8, 0.8, 0.8, 2.5, 2.5, 2.5] # [rad/s] + dq_max_upper: [2.0, 2.0, 2.0, 2.0, 2.5, 2.5, 2.5] # [rad/s] + ddq_max_lower: [0.8, 0.8, 0.8, 0.8, 10.0, 10.0, 10.0] # [rad/s^2] + ddq_max_upper: [6.0, 6.0, 6.0, 6.0, 15.0, 20.0, 20.0] # [rad/s^2] + contact_force_threshold: 5.0 # [N] + +spacenav_teleop_twist_controller: + type: franka_example_controllers/SpacenavTeleopTwistController + arm_id: $(arg arm_id) + linear_gain: $(arg linear_gain) + angular_gain: $(arg angular_gain) + +cartesian_pose_controller: + type: franka_example_controllers/CartesianPoseController + arm_id: $(arg arm_id) + +cartesian_traj_controller: + type: franka_example_controllers/CartesianTrajController + arm_id: $(arg arm_id) + +cartesian_traj_twist_correct_controller: + type: franka_example_controllers/CartesianTrajTwistCorrectController + arm_id: $(arg arm_id) diff --git a/fr3_impedance_controller_real/default.nix b/fr3_impedance_controller_real/default.nix new file mode 100644 index 0000000000000000000000000000000000000000..8c7ab949d7951526a050262d6bb70bba529b0e1d --- /dev/null +++ b/fr3_impedance_controller_real/default.nix @@ -0,0 +1,72 @@ +# This defines a function which returns a packge, intended to be evaluated by callPackage +{ lib +, buildRosPackage +, catkin +, controller-interface +, dynamic-reconfigure +, pluginlib +, realtime-tools +, eigen +, eigen-conversions +, franka-control +, franka-description +, franka-gripper +, franka-hw +, franka-example-controllers +, libfranka +, hardware-interface +, joint-limits-interface +, message-generation +, message-runtime +, geometry-msgs +, visualization-msgs +, roscpp +, rospy +, tf +, tf-conversions +, urdf + +, common # shared library from the project +}: + +buildRosPackage { + pname = "fr3-impedance-controller"; + version = "0.0.0"; + src = ./.; + + buildType = "catkin"; + + buildInputs = [ catkin eigen message-generation ]; + propagatedBuildInputs = [ + controller-interface + dynamic-reconfigure + eigen-conversions + franka-control + franka-description + franka-gripper + franka-hw + libfranka + franka-example-controllers + geometry-msgs + hardware-interface + joint-limits-interface + + message-runtime + pluginlib + realtime-tools + roscpp rospy + tf + tf-conversions + urdf + visualization-msgs + common + ]; + + nativeBuildInputs = [ catkin ]; + + + meta = { + description = "Impedance controller to run on the Franka Emika FR3"; + }; +} + diff --git a/fr3_impedance_controller_real/fr3_impedance_controller_real_plugin.xml b/fr3_impedance_controller_real/fr3_impedance_controller_real_plugin.xml new file mode 100644 index 0000000000000000000000000000000000000000..1bec1f5d6871804f5fca75b9f2cf7b1d414278dd --- /dev/null +++ b/fr3_impedance_controller_real/fr3_impedance_controller_real_plugin.xml @@ -0,0 +1,84 @@ +<library path="lib/libfranka_example_controllers"> + <class name="franka_example_controllers/JointVelocityExampleController" type="franka_example_controllers::JointVelocityExampleController" base_class_type="controller_interface::ControllerBase"> + <description> + A controller that executes a short motion based on joint velocities to demonstrate correct usage + </description> + </class> + <class name="franka_example_controllers/JointPositionExampleController" type="franka_example_controllers::JointPositionExampleController" base_class_type="controller_interface::ControllerBase"> + <description> + A controller that executes a short motion based on joint positions to demonstrate correct usage + </description> + </class> + <class name="franka_example_controllers/CartesianPoseExampleController" type="franka_example_controllers::CartesianPoseExampleController" base_class_type="controller_interface::ControllerBase"> + <description> + A controller that executes a short motion based on cartesian poses to demonstrate correct usage + </description> + </class> + <!-- ADD here --> + <class name="franka_example_controllers/CartesianSinPoseExampleController" type="franka_example_controllers::CartesianSinPoseExampleController" base_class_type="controller_interface::ControllerBase"> + <description> + A controller that executes a short motion based on cartesian poses to demonstrate correct usage + </description> + </class> + <!-- --> + <class name="franka_example_controllers/CartesianVelocityExampleController" type="franka_example_controllers::CartesianVelocityExampleController" base_class_type="controller_interface::ControllerBase"> + <description> + A controller that executes a short motion based on cartesian velocities to demonstrate correct usage + </description> + </class> + <class name="franka_example_controllers/ElbowExampleController" type="franka_example_controllers::ElbowExampleController" base_class_type="controller_interface::ControllerBase"> + <description> + A controller that executes a short motion based on cartesian poses and elbow to demonstrate correct usage + </description> + </class> + <class name="franka_example_controllers/ModelExampleController" type="franka_example_controllers::ModelExampleController" base_class_type="controller_interface::ControllerBase"> + <description> + A controller that evaluates and prints the dynamic model of Franka + </description> + </class> + <class name="franka_example_controllers/JointImpedanceExampleController" type="franka_example_controllers::JointImpedanceExampleController" base_class_type="controller_interface::ControllerBase"> + <description> + A controller that tracks a cartesian path with a joint impedance controller that compensates coriolis torques. The torque commands are compared to measured torques in Console outputs. + </description> + </class> + <class name="franka_example_controllers/CartesianImpedanceExampleController" type="franka_example_controllers::CartesianImpedanceExampleController" base_class_type="controller_interface::ControllerBase"> + <description> + A controller that renders a spring damper system in cartesian space. Compliance parameters and the equilibrium pose can be modified online with dynamic reconfigure and an interactive marker respectively. + </description> + </class> + <class name="franka_example_controllers/ForceExampleController" type="franka_example_controllers::ForceExampleController" base_class_type="controller_interface::ControllerBase"> + <description> + A PI controller that applies a force corresponding to a user-provided desired mass in the z axis. The desired mass value can be modified online with dynamic reconfigure. + </description> + </class> + <class name="franka_example_controllers/DualArmCartesianImpedanceExampleController" type="franka_example_controllers::DualArmCartesianImpedanceExampleController" base_class_type="controller_interface::ControllerBase"> + <description> + A controller that renders Cartesian impedance based tracking of separate target poses for 2 panda arms. + </description> + </class> + <class name="franka_example_controllers/TeleopJointPDExampleController" type="franka_example_controllers::TeleopJointPDExampleController" base_class_type="controller_interface::ControllerBase"> + <description> + A PD controller that tracks position and velocity of the leader arm and applies it to the follower arm. It also applies force-feedback to the leader arm. + </description> + </class> + <class name="franka_example_controllers/SpacenavTeleopTwistController" type="franka_example_controllers::SpacenavTeleopTwistController" base_class_type="controller_interface::ControllerBase"> + <description> + A Controller that takes Twist Input from the 6DoF spacenav mouse and sends these to the robot. + </description> + </class> + <class name="franka_example_controllers/CartesianPoseController" type="franka_example_controllers::CartesianPoseController" base_class_type="controller_interface::ControllerBase"> + <description> + A Controller that takes single cartesian pose (currently only position, no orientation). + </description> + </class> + <class name="franka_example_controllers/CartesianTrajController" type="franka_example_controllers::CartesianTrajController" base_class_type="controller_interface::ControllerBase"> + <description> + A Controller that takes PoseArray (pos traj). + </description> + </class> + <class name="franka_example_controllers/CartesianTrajTwistCorrectController" type="franka_example_controllers::CartesianTrajTwistCorrectController" base_class_type="controller_interface::ControllerBase"> + <description> + A Controller that takes PoseArray (pos traj) and can correct the unsmoothness of the traj with the spacenav twist commands. + </description> + </class> +</library> diff --git a/fr3_impedance_controller_real/include/fr3_impedance_controller_real/fr3_impedance_controller_real.hpp b/fr3_impedance_controller_real/include/fr3_impedance_controller_real/fr3_impedance_controller_real.hpp new file mode 100644 index 0000000000000000000000000000000000000000..bfe738d9b80449658c32b9823d5dfe913f019099 --- /dev/null +++ b/fr3_impedance_controller_real/include/fr3_impedance_controller_real/fr3_impedance_controller_real.hpp @@ -0,0 +1,116 @@ +// Copyright (c) 2017 Franka Emika GmbH +// Use of this source code is governed by the Apache-2.0 license, see LICENSE +#pragma once + +#include <memory> +#include <mutex> +#include <string> +#include <vector> + +#include <controller_interface/multi_interface_controller.h> +#include <dynamic_reconfigure/server.h> +#include <geometry_msgs/PoseStamped.h> +#include <hardware_interface/joint_command_interface.h> +#include <hardware_interface/robot_hw.h> +#include <ros/node_handle.h> +#include <ros/time.h> +#include <Eigen/Dense> + +#include <geometry_msgs/Twist.h> // for spacenave teleop +#include <sensor_msgs/Joy.h> // for joystick command +#include "eigen_types.hpp" // for custom Eigen types +#include "orientation_utilities.hpp" // for orientation computation + +#include <franka_example_controllers/compliance_paramConfig.h> +#include <franka_hw/franka_model_interface.h> +#include <franka_hw/franka_state_interface.h> + +namespace franka_example_controllers +{ +class CartesianImpedanceExampleController : + public controller_interface::MultiInterfaceController<franka_hw::FrankaModelInterface, + hardware_interface::EffortJointInterface, + franka_hw::FrankaStateInterface> +{ +public: + bool init(hardware_interface::RobotHW * robot_hw, ros::NodeHandle & node_handle) override; + void starting(const ros::Time &) override; + // void stopping(const ros::Time&) override; + void update(const ros::Time &, const ros::Duration & period) override; + +private: + // Saturation + Eigen::Matrix<double, 7, 1> saturateTorqueRate( + const Eigen::Matrix<double, 7, 1> & tau_d_calculated, + const Eigen::Matrix<double, 7, 1> & tau_J_d); // NOLINT (readability-identifier-naming) + + std::unique_ptr<franka_hw::FrankaStateHandle> state_handle_; + std::unique_ptr<franka_hw::FrankaModelHandle> model_handle_; + std::vector<hardware_interface::JointHandle> joint_handles_; + + double filter_params_{ 0.005 }; + double nullspace_stiffness_{ 20.0 }; + double nullspace_stiffness_target_{ 20.0 }; + const double delta_tau_max_{ 1.0 }; + + const std::vector<double> fric_phi1{ + 0.54615, 0.87224, 0.64068, 1.2794, 0.83904, 0.30301, 0.56489 + }; + const std::vector<double> fric_phi2{ 5.1181, 9.0657, 10.136, 5.590, 8.3469, 17.133, 10.336 }; + // const std::vector<double> fric_phi3{0.039533, 0.025882, -0.04607, 0.036194, 0.026226, + // -0.021047, 0.0035526}; + const std::vector<double> fric_phi3{ + 0., 0., 0., 0., 0., 0., 0., + }; + + Eigen::Matrix<double, 6, 6> cartesian_stiffness_; + Eigen::Matrix<double, 6, 6> cartesian_stiffness_target_; + Eigen::Matrix<double, 6, 6> cartesian_damping_; + Eigen::Matrix<double, 6, 6> cartesian_damping_target_; + Eigen::Matrix<double, 7, 1> q_d_nullspace_; + Eigen::Vector3d position_d_; + Eigen::Quaterniond orientation_d_; + std::mutex position_and_orientation_d_target_mutex_; + Eigen::Vector3d position_d_target_; + Eigen::Quaterniond orientation_d_target_; + + // Dynamic reconfigure + std::unique_ptr<dynamic_reconfigure::Server<franka_example_controllers::compliance_paramConfig>> + dynamic_server_compliance_param_; + ros::NodeHandle dynamic_reconfigure_compliance_param_node_; + void complianceParamCallback(franka_example_controllers::compliance_paramConfig & config, + uint32_t level); + + // Equilibrium pose subscriber + ros::Subscriber sub_equilibrium_pose_; + void equilibriumPoseCallback(const geometry_msgs::PoseStampedConstPtr & msg); + + /* Target EE Pose command */ + // bool b_is_marker_mode_ = false; + // ros::Subscriber sub_interactive_marker_; + // std::mutex target_ee_pose_mutex_; + // Vec3<double> ee_pos_world_des_target_; + // Quat<double> ee_quat_world_des_target_; + + /* spacenav target twist subscriber */ + ros::Subscriber sub_target_twist_; + void subscribe_target_twist(const geometry_msgs::TwistConstPtr & msg); + // Vec6<double> ee_vel_world_target_; + Vec6<double> ee_vel_scale_; + Vec6<double> ee_vel_world_des_, ee_vel_world_des_prev_; + Vec6<double> ee_acc_world_des_, ee_acc_world_des_prev_; + Vec3<double> ee_pos_world_des_; + Quat<double> ee_quat_world_des_; + Vec3<double> ee_rpy_world_des_, ee_rpy_world_des_prev_; + Vec3<double> ee_rpy_world_mes_, ee_rpy_world_mes_prev_; + + Vec6<double> fr3_vel_limit_; + Vec6<double> fr3_acc_limit_; + Vec6<double> fr3_jrk_limit_; + + ros::Subscriber sub_joy_; + void subscribe_joy_command(const sensor_msgs::JoyConstPtr & msg); + std::array<int, 2> btn_cmd_; +}; + +} // namespace franka_example_controllers diff --git a/fr3_impedance_controller_real/launch/cartesian_impedance_example_controller.launch b/fr3_impedance_controller_real/launch/cartesian_impedance_example_controller.launch new file mode 100644 index 0000000000000000000000000000000000000000..682f04cfcd180840857522b592d624f7b1c2eae4 --- /dev/null +++ b/fr3_impedance_controller_real/launch/cartesian_impedance_example_controller.launch @@ -0,0 +1,20 @@ +<?xml version="1.0" ?> +<launch> + <arg name="robot" default="panda" doc="choose your robot. Possible values: [panda, fr3]"/> + <arg name="arm_id" default="$(arg robot)" /> + + <arg name="linear_gain" default="1.0" /> + <arg name="angular_gain" default="1.0" /> + + <include file="$(find franka_control)/launch/franka_control.launch" pass_all_args="true"/> + <include file="$(find spacenav_node)/launch/classic.launch" pass_all_args="true"/> + + <rosparam command="load" file="$(find franka_example_controllers)/config/franka_example_controllers.yaml" subst_value="true" /> + <node name="controller_spawner" pkg="controller_manager" type="spawner" respawn="false" output="screen" args="cartesian_impedance_example_controller"/> + <node pkg="rviz" type="rviz" output="screen" name="rviz" args="-d $(find franka_example_controllers)/launch/rviz/franka_description_with_marker.rviz"/> + <node name="interactive_marker" pkg="franka_example_controllers" type="interactive_marker.py" required="true" output="screen"> + <param name="link_name" value="$(arg arm_id)_link0" /> + <remap from="equilibrium_pose" to="/cartesian_impedance_example_controller/equilibrium_pose" /> + </node> + <node name="rqt_reconfigure" pkg="rqt_reconfigure" type="rqt_reconfigure" required="false" /> +</launch> diff --git a/fr3_impedance_controller_real/launch/rviz/franka_description_with_marker.rviz b/fr3_impedance_controller_real/launch/rviz/franka_description_with_marker.rviz new file mode 100644 index 0000000000000000000000000000000000000000..931fd1cb7a451ef3d2e437be10e3d56bdfcb3055 --- /dev/null +++ b/fr3_impedance_controller_real/launch/rviz/franka_description_with_marker.rviz @@ -0,0 +1,279 @@ +Panels: + - Class: rviz/Displays + Help Height: 78 + Name: Displays + Property Tree Widget: + Expanded: + - /Global Options1 + - /Status1 + - /Marker1 + - /Marker1/Status1 + - /Pose1 + Splitter Ratio: 0.5 + Tree Height: 862 + - Class: rviz/Selection + Name: Selection + - Class: rviz/Tool Properties + Expanded: + - /2D Pose Estimate1 + - /2D Nav Goal1 + - /Publish Point1 + Name: Tool Properties + Splitter Ratio: 0.5886790156364441 + - Class: rviz/Views + Expanded: + - /Current View1 + Name: Views + Splitter Ratio: 0.5 + - Class: rviz/Time + Name: Time + SyncMode: 0 + SyncSource: "" +Preferences: + PromptSaveOnExit: true +Toolbars: + toolButtonStyle: 2 +Visualization Manager: + Class: "" + Displays: + - Alpha: 0.5 + Cell Size: 1 + Class: rviz/Grid + Color: 160; 160; 164 + Enabled: true + Line Style: + Line Width: 0.029999999329447746 + Value: Lines + Name: Grid + Normal Cell Count: 0 + Offset: + X: 0 + Y: 0 + Z: 0 + Plane: XY + Plane Cell Count: 10 + Reference Frame: <Fixed Frame> + Value: true + - Alpha: 1 + Class: rviz/RobotModel + Collision Enabled: false + Enabled: true + Links: + All Links Enabled: true + Expand Joint Details: false + Expand Link Details: false + Expand Tree: false + Link Tree Style: Links in Alphabetic Order + panda_hand: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_hand_sc: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_hand_tcp: + Alpha: 1 + Show Axes: false + Show Trail: false + panda_leftfinger: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_link0: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_link0_sc: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_link1: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_link1_sc: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_link2: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_link2_sc: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_link3: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_link3_sc: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_link4: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_link4_sc: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_link5: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_link5_sc: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_link6: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_link6_sc: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_link7: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_link7_sc: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + panda_link8: + Alpha: 1 + Show Axes: false + Show Trail: false + panda_rightfinger: + Alpha: 1 + Show Axes: false + Show Trail: false + Value: true + Name: RobotModel + Robot Description: robot_description + TF Prefix: "" + Update Interval: 0 + Value: true + Visual Enabled: true + - Class: rviz/InteractiveMarkers + Enable Transparency: true + Enabled: false + Name: InteractiveMarkers + Show Axes: false + Show Descriptions: true + Show Visual Aids: false + Update Topic: /equilibrium_pose_marker/update + Value: false + - Class: rviz/Marker + Enabled: true + Marker Topic: /mpc_trajectory + Name: Marker + Namespaces: + "": true + Queue Size: 100 + Value: true + - Alpha: 1 + Axes Length: 0.10000000149011612 + Axes Radius: 0.05000000074505806 + Class: rviz/Pose + Color: 255; 25; 0 + Enabled: true + Head Length: 0.30000001192092896 + Head Radius: 0.10000000149011612 + Name: Pose + Queue Size: 10 + Shaft Length: 1 + Shaft Radius: 0.05000000074505806 + Shape: Axes + Topic: /cartesian_impedance_example_controller/equilibrium_pose + Unreliable: false + Value: true + Enabled: true + Global Options: + Background Color: 48; 48; 48 + Default Light: true + Fixed Frame: panda_link0 + Frame Rate: 30 + Name: root + Tools: + - Class: rviz/Interact + Hide Inactive Objects: true + - Class: rviz/MoveCamera + - Class: rviz/Select + - Class: rviz/FocusCamera + - Class: rviz/Measure + - Class: rviz/SetInitialPose + Theta std deviation: 0.2617993950843811 + Topic: /initialpose + X std deviation: 0.5 + Y std deviation: 0.5 + - Class: rviz/SetGoal + Topic: /move_base_simple/goal + - Class: rviz/PublishPoint + Single click: true + Topic: /clicked_point + Value: true + Views: + Current: + Class: rviz/Orbit + Distance: 1.8979061841964722 + Enable Stereo Rendering: + Stereo Eye Separation: 0.05999999865889549 + Stereo Focal Distance: 1 + Swap Stereo Eyes: false + Value: false + Field of View: 0.7853981852531433 + Focal Point: + X: 0 + Y: 0 + Z: 0 + Focal Shape Fixed Size: true + Focal Shape Size: 0.05000000074505806 + Invert Z Axis: false + Name: Current View + Near Clip Distance: 0.009999999776482582 + Pitch: 0.17539797723293304 + Target Frame: <Fixed Frame> + Yaw: 0.0903986394405365 + Saved: ~ +Window Geometry: + Displays: + collapsed: false + Height: 1163 + Hide Left Dock: false + Hide Right Dock: false + QMainWindow State: 000000ff00000000fd00000004000000000000016a000003e9fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000003e9000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f000003e9fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d000003e9000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e100000197000000030000078000000042fc0100000002fb0000000800540069006d0065010000000000000780000003bc00fffffffb0000000800540069006d00650100000000000004500000000000000000000004fb000003e900000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 + Selection: + collapsed: false + Time: + collapsed: false + Tool Properties: + collapsed: false + Views: + collapsed: false + Width: 1920 + X: 2560 + Y: 0 diff --git a/fr3_impedance_controller_real/package.xml b/fr3_impedance_controller_real/package.xml new file mode 100644 index 0000000000000000000000000000000000000000..297f2003c5617cd900dc32956aec47fee2b122c8 --- /dev/null +++ b/fr3_impedance_controller_real/package.xml @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<package format="2"> + <name>fr3_impedance_controller_real</name> + <version>0.0.1</version> + <description>Impedance controller for the Franka Emika FR3</description> + <maintainer email="todo@todo.com"></maintainer> + <license>Apache 2.0</license> + + + <buildtool_depend>catkin</buildtool_depend> + + <build_depend>message_generation</build_depend> + <build_depend>eigen</build_depend> + + <build_export_depend>message_runtime</build_export_depend> + + <depend>controller_interface</depend> + <depend>dynamic_reconfigure</depend> + <depend>eigen_conversions</depend> + <depend>franka_hw</depend> + <depend>franka_gripper</depend> + <depend>franka_example_controllers</depend> + <depend>geometry_msgs</depend> + <depend>hardware_interface</depend> + <depend>joint_limits_interface</depend> + <depend>tf</depend> + <depend>tf_conversions</depend> + <depend>libfranka</depend> + <depend>pluginlib</depend> + <depend>realtime_tools</depend> + <depend>roscpp</depend> + <depend>urdf</depend> + <depend>sensor_msgs</depend> + <depend>visualization_msgs</depend> + + <exec_depend>franka_control</exec_depend> + <exec_depend>franka_description</exec_depend> + <exec_depend>message_runtime</exec_depend> + <exec_depend>rospy</exec_depend> + + <export> + <controller_interface plugin="${prefix}/fr3_impedance_controller_real_plugin.xml" /> + </export> +</package> diff --git a/fr3_impedance_controller_real/src/fr3_impedance_controller_real.cpp b/fr3_impedance_controller_real/src/fr3_impedance_controller_real.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e7798abde6b77b30ad58cf259a7af0f5293b60d6 --- /dev/null +++ b/fr3_impedance_controller_real/src/fr3_impedance_controller_real.cpp @@ -0,0 +1,363 @@ +// Copyright (c) 2017 Franka Emika GmbH +// Use of this source code is governed by the Apache-2.0 license, see LICENSE +#include <franka_example_controllers/cartesian_impedance_example_controller.h> + +#include <cmath> +#include <iomanip> +#include <memory> + +#include <controller_interface/controller_base.h> +#include <franka/robot_state.h> +#include <pluginlib/class_list_macros.h> +#include <ros/ros.h> + +#include <franka_example_controllers/pseudo_inversion.h> + +namespace franka_example_controllers +{ +bool CartesianImpedanceExampleController::init(hardware_interface::RobotHW * robot_hw, + ros::NodeHandle & node_handle) +{ + std::vector<double> cartesian_stiffness_vector; + std::vector<double> cartesian_damping_vector; + + /* Subscriber declaration */ + sub_equilibrium_pose_ = node_handle.subscribe( + "equilibrium_pose", 20, &CartesianImpedanceExampleController::equilibriumPoseCallback, this, + ros::TransportHints().reliable().tcpNoDelay()); + + sub_target_twist_ = node_handle.subscribe<geometry_msgs::Twist>( + "/spacenav/twist", 1, &CartesianImpedanceExampleController::subscribe_target_twist, this); + + sub_joy_ = node_handle.subscribe<sensor_msgs::Joy>( + "/spacenave/joy", 1, &CartesianImpedanceExampleController::subscribe_joy_command, this); + + /* Initialize limits */ + fr3_vel_limit_ << 3.0, 3.0, 3.0, 2.5, 2.5, 2.5; // [m/s, rad/s] + fr3_acc_limit_ << 9.0, 9.0, 9.0, 17.0, 17.0, 17.0; // [m/s^2, rad/s^2] + fr3_jrk_limit_ << 4500.0, 4500.0, 4500.0, 8500.0, 8500.0, 8500.0; // [m/s^3, rad/s^3] + + std::string arm_id; + if (!node_handle.getParam("arm_id", arm_id)) + { + ROS_ERROR_STREAM("CartesianImpedanceExampleController: Could not read parameter arm_id"); + return false; + } + std::vector<std::string> joint_names; + if (!node_handle.getParam("joint_names", joint_names) || joint_names.size() != 7) + { + ROS_ERROR( + "CartesianImpedanceExampleController: Invalid or no joint_names parameters provided, " + "aborting controller init!"); + return false; + } + + auto * model_interface = robot_hw->get<franka_hw::FrankaModelInterface>(); + if (model_interface == nullptr) + { + ROS_ERROR_STREAM( + "CartesianImpedanceExampleController: Error getting model interface from hardware"); + return false; + } + try + { + model_handle_ = + std::make_unique<franka_hw::FrankaModelHandle>(model_interface->getHandle(arm_id + "_model")); + } + catch (hardware_interface::HardwareInterfaceException & ex) + { + ROS_ERROR_STREAM( + "CartesianImpedanceExampleController: Exception getting model handle from interface: " + << ex.what()); + return false; + } + + auto * state_interface = robot_hw->get<franka_hw::FrankaStateInterface>(); + if (state_interface == nullptr) + { + ROS_ERROR_STREAM( + "CartesianImpedanceExampleController: Error getting state interface from hardware"); + return false; + } + try + { + state_handle_ = + std::make_unique<franka_hw::FrankaStateHandle>(state_interface->getHandle(arm_id + "_robot")); + } + catch (hardware_interface::HardwareInterfaceException & ex) + { + ROS_ERROR_STREAM( + "CartesianImpedanceExampleController: Exception getting state handle from interface: " + << ex.what()); + return false; + } + + auto * effort_joint_interface = robot_hw->get<hardware_interface::EffortJointInterface>(); + if (effort_joint_interface == nullptr) + { + ROS_ERROR_STREAM( + "CartesianImpedanceExampleController: Error getting effort joint interface from hardware"); + return false; + } + for (size_t i = 0; i < 7; ++i) + { + try + { + joint_handles_.push_back(effort_joint_interface->getHandle(joint_names[i])); + } + catch (const hardware_interface::HardwareInterfaceException & ex) + { + ROS_ERROR_STREAM( + "CartesianImpedanceExampleController: Exception getting joint handles: " << ex.what()); + return false; + } + } + + dynamic_reconfigure_compliance_param_node_ = + ros::NodeHandle(node_handle.getNamespace() + "/dynamic_reconfigure_compliance_param_node"); + + dynamic_server_compliance_param_ = std::make_unique< + dynamic_reconfigure::Server<franka_example_controllers::compliance_paramConfig>>( + + dynamic_reconfigure_compliance_param_node_); + dynamic_server_compliance_param_->setCallback( + boost::bind(&CartesianImpedanceExampleController::complianceParamCallback, this, _1, _2)); + + /* Initialize states */ + ee_vel_scale_ << 0.1, 0.1, 0.1, 0.1, 0.1, 0.1; + ee_vel_world_des_.setZero(); + ee_vel_world_des_prev_.setZero(); + ee_acc_world_des_.setZero(); + ee_acc_world_des_prev_.setZero(); + ee_pos_world_des_.setZero(); + ee_quat_world_des_.coeffs() << 0.0, 0.0, 0.0, 1.0; + ee_rpy_world_des_.setZero(); + ee_rpy_world_des_prev_.setZero(); + ee_rpy_world_mes_.setZero(); + ee_rpy_world_mes_prev_.setZero(); + + position_d_.setZero(); + orientation_d_.coeffs() << 0.0, 0.0, 0.0, 1.0; + position_d_target_.setZero(); + orientation_d_target_.coeffs() << 0.0, 0.0, 0.0, 1.0; + + cartesian_stiffness_.setZero(); + cartesian_damping_.setZero(); + + return true; +} + +void CartesianImpedanceExampleController::starting(const ros::Time & /*time*/) +{ + // compute initial velocity with jacobian and set x_attractor and q_d_nullspace + // to initial configuration + franka::RobotState initial_state = state_handle_->getRobotState(); + // get jacobian + std::array<double, 42> jacobian_array = + model_handle_->getZeroJacobian(franka::Frame::kEndEffector); + // convert to eigen + Eigen::Map<Eigen::Matrix<double, 7, 1>> q_initial(initial_state.q.data()); + Eigen::Affine3d initial_transform(Eigen::Matrix4d::Map(initial_state.O_T_EE.data())); + + // set equilibrium point to current state + position_d_ = initial_transform.translation(); + orientation_d_ = Eigen::Quaterniond(initial_transform.rotation()); + position_d_target_ = initial_transform.translation(); + orientation_d_target_ = Eigen::Quaterniond(initial_transform.rotation()); + + ee_pos_world_des_ = initial_transform.translation(); + ee_quat_world_des_ = Eigen::Quaterniond(initial_transform.rotation()); + + // set nullspace equilibrium configuration to initial q + q_d_nullspace_ = q_initial; +} + +void CartesianImpedanceExampleController::update(const ros::Time & /*time*/, + const ros::Duration & period) +{ + /* get target end-effector's twist */ + // std::cout << std::fixed; + // std::cout << std::setprecision(5); + // std::cout << "Desired EE twist:\t" << ee_vel_world_des_.transpose() << std::endl; + + // TODO: Add safety for FR3 end-effector's vel, acc, jerk limits + + Vec3<double> v_des = ee_vel_world_des_.head(3); + Vec3<double> w_des = ee_vel_world_des_.tail(3); + ee_pos_world_des_ += v_des * period.toSec(); + ee_quat_world_des_ = orient_utils::getNewQuat(ee_quat_world_des_, w_des, period.toSec()); + ee_rpy_world_des_ = orient_utils::quaternionToRPY(ee_quat_world_des_, ee_rpy_world_des_prev_); + + // TODO: Add conversion of Quat2RPY to make debugging easier + + // get state variables + franka::RobotState robot_state = state_handle_->getRobotState(); + std::array<double, 7> coriolis_array = model_handle_->getCoriolis(); + std::array<double, 42> jacobian_array = + model_handle_->getZeroJacobian(franka::Frame::kEndEffector); + + // convert to Eigen + Eigen::Map<Eigen::Matrix<double, 7, 1>> coriolis(coriolis_array.data()); + Eigen::Map<Eigen::Matrix<double, 6, 7>> jacobian(jacobian_array.data()); + Eigen::Map<Eigen::Matrix<double, 7, 1>> q(robot_state.q.data()); + Eigen::Map<Eigen::Matrix<double, 7, 1>> dq(robot_state.dq.data()); + Eigen::Map<Eigen::Matrix<double, 7, 1>> tau_J_d( // NOLINT (readability-identifier-naming) + robot_state.tau_J_d.data()); + Eigen::Affine3d transform(Eigen::Matrix4d::Map(robot_state.O_T_EE.data())); + Eigen::Vector3d position(transform.translation()); + Eigen::Quaterniond orientation(transform.rotation()); + ee_rpy_world_mes_ = orient_utils::quaternionToRPY(orientation, ee_rpy_world_mes_prev_); + // std::cout << "Desired EE pos [m]:\t" << ee_pos_world_des_.transpose() << std::endl; + // std::cout << "Measured EE pos [m]:\t" << position.transpose() << std::endl << std::endl; + // std::cout << "Desired EE RPY [rad]:\t" << ee_rpy_world_des_.transpose() << std::endl; + // std::cout << "Measured EE RPY [rad]:\t" << ee_rpy_world_mes_.transpose() << std::endl << + // std::endl; + + // compute error to desired pose + // position error + Eigen::Matrix<double, 6, 1> error; + error.head(3) << position - ee_pos_world_des_; + + // orientation error + if (ee_quat_world_des_.coeffs().dot(orientation.coeffs()) < 0.0) + { + orientation.coeffs() << -orientation.coeffs(); + } + // "difference" quaternion + Eigen::Quaterniond error_quaternion(orientation.inverse() * ee_quat_world_des_); + error.tail(3) << error_quaternion.x(), error_quaternion.y(), error_quaternion.z(); + // Transform to base frame + error.tail(3) << -transform.rotation() * error.tail(3); + + // compute control + // allocate variables + Eigen::VectorXd tau_task(7), tau_nullspace(7), tau_d(7), tau_fr(7); + + tau_fr.setZero(); + + // pseudoinverse for nullspace handling + // kinematic pseuoinverse + Eigen::MatrixXd jacobian_transpose_pinv; + pseudoInverse(jacobian.transpose(), jacobian_transpose_pinv); + + // Cartesian PD control with damping ratio = 1 + tau_task << jacobian.transpose() * + (-cartesian_stiffness_ * error - cartesian_damping_ * (jacobian * dq)); + // nullspace PD control with damping ratio = 1 + tau_nullspace << (Eigen::MatrixXd::Identity(7, 7) - + jacobian.transpose() * jacobian_transpose_pinv) * + (nullspace_stiffness_ * (q_d_nullspace_ - q) - + (2.0 * sqrt(nullspace_stiffness_)) * dq); + + // calculate friction torque + for (size_t i = 0; i < 7; i++) + { + tau_fr(i) = fric_phi1[i] / (1 + exp(-fric_phi2[i] * (dq(i) + fric_phi3[i]))) - + fric_phi1[i] / (1 + exp(-fric_phi2[i] * fric_phi3[i])); + } + + // Desired torque + tau_d << tau_task + tau_nullspace + coriolis + tau_fr; + + // Saturate torque rate to avoid discontinuities + tau_d << saturateTorqueRate(tau_d, tau_J_d); + // std::cout << "Desired torque:\t" << tau_d.transpose() << std::endl; + for (size_t i = 0; i < 7; ++i) + { + joint_handles_[i].setCommand(tau_d(i)); + } + + /* update previous states */ + ee_vel_world_des_prev_ = ee_vel_world_des_; + ee_acc_world_des_prev_ = ee_acc_world_des_; + ee_rpy_world_des_prev_ = ee_rpy_world_des_; + ee_rpy_world_mes_prev_ = ee_rpy_world_mes_; + + // update parameters changed online either through dynamic reconfigure or through the interactive + // target by filtering + cartesian_stiffness_ = + filter_params_ * cartesian_stiffness_target_ + (1.0 - filter_params_) * cartesian_stiffness_; + cartesian_damping_ = + filter_params_ * cartesian_damping_target_ + (1.0 - filter_params_) * cartesian_damping_; + nullspace_stiffness_ = + filter_params_ * nullspace_stiffness_target_ + (1.0 - filter_params_) * nullspace_stiffness_; + std::lock_guard<std::mutex> position_d_target_mutex_lock( + position_and_orientation_d_target_mutex_); + position_d_ = filter_params_ * position_d_target_ + (1.0 - filter_params_) * position_d_; + orientation_d_ = orientation_d_.slerp(filter_params_, orientation_d_target_); +} + +Eigen::Matrix<double, 7, 1> CartesianImpedanceExampleController::saturateTorqueRate( + const Eigen::Matrix<double, 7, 1> & tau_d_calculated, const Eigen::Matrix<double, 7, 1> & tau_J_d) +{ // NOLINT (readability-identifier-naming) + Eigen::Matrix<double, 7, 1> tau_d_saturated{}; + for (size_t i = 0; i < 7; i++) + { + double difference = tau_d_calculated[i] - tau_J_d[i]; + tau_d_saturated[i] = + tau_J_d[i] + std::max(std::min(difference, delta_tau_max_), -delta_tau_max_); + } + return tau_d_saturated; +} + +void CartesianImpedanceExampleController::complianceParamCallback( + franka_example_controllers::compliance_paramConfig & config, uint32_t /*level*/) +{ + cartesian_stiffness_target_.setIdentity(); + + // 26.4.23 @Kevin, change so can set stiffness independent per DOF + // cartesian_stiffness_target_.topLeftCorner(3, 3) + // << config.translational_stiffness * Eigen::Matrix3d::Identity(); + cartesian_stiffness_target_(0, 0) = config.translational_stiffness_x; + cartesian_stiffness_target_(1, 1) = config.translational_stiffness_y; + cartesian_stiffness_target_(2, 2) = config.translational_stiffness_z; + + cartesian_stiffness_target_.bottomRightCorner(3, 3) + << config.rotational_stiffness * Eigen::Matrix3d::Identity(); + cartesian_damping_target_.setIdentity(); + // Damping ratio = 1 + for (size_t i = 0; i < 6; i++) + { + cartesian_damping_target_(i, i) = 2.0 * sqrt(cartesian_stiffness_target_(i, i)); + } + nullspace_stiffness_target_ = config.nullspace_stiffness; +} + +void CartesianImpedanceExampleController::equilibriumPoseCallback( + const geometry_msgs::PoseStampedConstPtr & msg) +{ + std::lock_guard<std::mutex> position_d_target_mutex_lock( + position_and_orientation_d_target_mutex_); + position_d_target_ << msg->pose.position.x, msg->pose.position.y, msg->pose.position.z; + Eigen::Quaterniond last_orientation_d_target(orientation_d_target_); + orientation_d_target_.coeffs() << msg->pose.orientation.x, msg->pose.orientation.y, + msg->pose.orientation.z, msg->pose.orientation.w; + if (last_orientation_d_target.coeffs().dot(orientation_d_target_.coeffs()) < 0.0) + { + orientation_d_target_.coeffs() << -orientation_d_target_.coeffs(); + } +} + +void CartesianImpedanceExampleController::subscribe_target_twist( + const geometry_msgs::TwistConstPtr & msg) +{ + // std::cout << msg->linear.x << ", " << msg->linear.y << ", " << msg->linear.z << std::endl; + ee_vel_world_des_[0] = ee_vel_scale_[0] * msg->linear.x; + ee_vel_world_des_[1] = ee_vel_scale_[1] * msg->linear.y; + ee_vel_world_des_[2] = ee_vel_scale_[2] * msg->linear.z; + ee_vel_world_des_[3] = ee_vel_scale_[3] * msg->angular.x; + ee_vel_world_des_[4] = ee_vel_scale_[4] * msg->angular.y; + ee_vel_world_des_[5] = ee_vel_scale_[5] * msg->angular.z; +} + +void CartesianImpedanceExampleController::subscribe_joy_command( + const sensor_msgs::JoyConstPtr & msg) +{ + btn_cmd_[0] = msg->buttons[0]; + btn_cmd_[1] = msg->buttons[1]; +} + +} // namespace franka_example_controllers + +PLUGINLIB_EXPORT_CLASS(franka_example_controllers::CartesianImpedanceExampleController, + controller_interface::ControllerBase)