From 7acec08749b0d1a61cb492cf61878653944c7f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Ga=C3=9Fner?= Date: Tue, 8 Apr 2025 13:39:06 +0200 Subject: [PATCH] Add ros2 support for franka fr3 --- .gitignore | 5 + README.md | 24 +- imgs/fr3_gello_calib_pose.jpeg | Bin 0 -> 186017 bytes ros2/.clang-format | 4 + ros2/.clang-tidy | 120 +++++++++ ros2/.devcontainer/Dockerfile | 91 +++++++ ros2/.devcontainer/devcontainer.json | 41 +++ ros2/.devcontainer/docker-compose.yml | 15 ++ ros2/.gitignore | 1 + ros2/README.md | 153 +++++++++++ ros2/pyproject.toml | 3 + .../franka_fr3_arm_controllers/CMakeLists.txt | 133 ++++++++++ ros2/src/franka_fr3_arm_controllers/LICENSE | 202 +++++++++++++++ ros2/src/franka_fr3_arm_controllers/NOTICE | 14 + .../config/controllers.yaml | 32 +++ .../franka_fr3_arm_controllers.xml | 9 + .../joint_impedance_controller.hpp | 70 +++++ .../motion_generator.hpp | 73 ++++++ .../launch/franka.launch.py | 182 +++++++++++++ .../franka_fr3_arm_controllers.launch.py | 85 ++++++ .../franka_fr3_arm_controllers/package.xml | 36 +++ .../src/joint_impedance_controller.cpp | 242 ++++++++++++++++++ .../src/motion_generator.cpp | 127 +++++++++ .../mocks/mock_joint_state_publisher.hpp | 48 ++++ .../include/mocks/mock_parameter_server.hpp | 29 +++ .../test_joint_impedance_controller.hpp | 81 ++++++ .../test/invalid_configuration_test.cpp | 44 ++++ .../test/setup_test.cpp | 119 +++++++++ .../test/test_joint_impedance_controller.cpp | 113 ++++++++ ros2/src/franka_gello_state_publisher/LICENSE | 22 ++ .../config/gello_config.yaml | 7 + .../franka_gello_state_publisher/__init__.py | 0 .../franka_gello_state_publisher/driver.py | 0 .../gello_publisher.py | 132 ++++++++++ .../launch/main.launch.py | 22 ++ .../franka_gello_state_publisher/package.xml | 24 ++ .../resource/franka_gello_state_publisher | 0 .../franka_gello_state_publisher/setup.cfg | 4 + .../src/franka_gello_state_publisher/setup.py | 26 ++ .../test/test_copyright.py | 25 ++ .../test/test_flake8.py | 33 +++ .../test/test_pep257.py | 25 ++ ros2/src/franka_gripper_manager/LICENSE | 202 +++++++++++++++ ros2/src/franka_gripper_manager/NOTICE | 15 ++ .../config/robotiq_controllers.yaml | 20 ++ .../franka_gripper_manager/__init__.py | 0 .../franka_gripper_client.py | 151 +++++++++++ .../robotiq_gripper_client.py | 58 +++++ .../launch/franka_gripper_client.launch.py | 15 ++ ...obotiq_gripper_controller_client.launch.py | 174 +++++++++++++ ros2/src/franka_gripper_manager/package.xml | 20 ++ .../resource/franka_gripper_manager | 0 ros2/src/franka_gripper_manager/setup.cfg | 4 + ros2/src/franka_gripper_manager/setup.py | 31 +++ .../test/test_copyright.py | 25 ++ .../test/test_flake8.py | 25 ++ .../test/test_pep257.py | 23 ++ .../urdf/robotiq_2f_85_gripper.urdf.xacro | 15 ++ 58 files changed, 3189 insertions(+), 5 deletions(-) create mode 100644 imgs/fr3_gello_calib_pose.jpeg create mode 100644 ros2/.clang-format create mode 100644 ros2/.clang-tidy create mode 100644 ros2/.devcontainer/Dockerfile create mode 100644 ros2/.devcontainer/devcontainer.json create mode 100644 ros2/.devcontainer/docker-compose.yml create mode 100644 ros2/.gitignore create mode 100644 ros2/README.md create mode 100644 ros2/pyproject.toml create mode 100644 ros2/src/franka_fr3_arm_controllers/CMakeLists.txt create mode 100644 ros2/src/franka_fr3_arm_controllers/LICENSE create mode 100644 ros2/src/franka_fr3_arm_controllers/NOTICE create mode 100644 ros2/src/franka_fr3_arm_controllers/config/controllers.yaml create mode 100644 ros2/src/franka_fr3_arm_controllers/franka_fr3_arm_controllers.xml create mode 100644 ros2/src/franka_fr3_arm_controllers/include/franka_fr3_arm_controllers/joint_impedance_controller.hpp create mode 100644 ros2/src/franka_fr3_arm_controllers/include/franka_fr3_arm_controllers/motion_generator.hpp create mode 100644 ros2/src/franka_fr3_arm_controllers/launch/franka.launch.py create mode 100644 ros2/src/franka_fr3_arm_controllers/launch/franka_fr3_arm_controllers.launch.py create mode 100644 ros2/src/franka_fr3_arm_controllers/package.xml create mode 100644 ros2/src/franka_fr3_arm_controllers/src/joint_impedance_controller.cpp create mode 100644 ros2/src/franka_fr3_arm_controllers/src/motion_generator.cpp create mode 100644 ros2/src/franka_fr3_arm_controllers/test/include/mocks/mock_joint_state_publisher.hpp create mode 100644 ros2/src/franka_fr3_arm_controllers/test/include/mocks/mock_parameter_server.hpp create mode 100644 ros2/src/franka_fr3_arm_controllers/test/include/test_joint_impedance_controller.hpp create mode 100644 ros2/src/franka_fr3_arm_controllers/test/invalid_configuration_test.cpp create mode 100644 ros2/src/franka_fr3_arm_controllers/test/setup_test.cpp create mode 100644 ros2/src/franka_fr3_arm_controllers/test/test_joint_impedance_controller.cpp create mode 100644 ros2/src/franka_gello_state_publisher/LICENSE create mode 100644 ros2/src/franka_gello_state_publisher/config/gello_config.yaml create mode 100644 ros2/src/franka_gello_state_publisher/franka_gello_state_publisher/__init__.py create mode 100644 ros2/src/franka_gello_state_publisher/franka_gello_state_publisher/driver.py create mode 100644 ros2/src/franka_gello_state_publisher/franka_gello_state_publisher/gello_publisher.py create mode 100755 ros2/src/franka_gello_state_publisher/launch/main.launch.py create mode 100644 ros2/src/franka_gello_state_publisher/package.xml create mode 100644 ros2/src/franka_gello_state_publisher/resource/franka_gello_state_publisher create mode 100644 ros2/src/franka_gello_state_publisher/setup.cfg create mode 100644 ros2/src/franka_gello_state_publisher/setup.py create mode 100644 ros2/src/franka_gello_state_publisher/test/test_copyright.py create mode 100644 ros2/src/franka_gello_state_publisher/test/test_flake8.py create mode 100644 ros2/src/franka_gello_state_publisher/test/test_pep257.py create mode 100644 ros2/src/franka_gripper_manager/LICENSE create mode 100644 ros2/src/franka_gripper_manager/NOTICE create mode 100644 ros2/src/franka_gripper_manager/config/robotiq_controllers.yaml create mode 100644 ros2/src/franka_gripper_manager/franka_gripper_manager/__init__.py create mode 100644 ros2/src/franka_gripper_manager/franka_gripper_manager/franka_gripper_client.py create mode 100644 ros2/src/franka_gripper_manager/franka_gripper_manager/robotiq_gripper_client.py create mode 100644 ros2/src/franka_gripper_manager/launch/franka_gripper_client.launch.py create mode 100644 ros2/src/franka_gripper_manager/launch/robotiq_gripper_controller_client.launch.py create mode 100644 ros2/src/franka_gripper_manager/package.xml create mode 100644 ros2/src/franka_gripper_manager/resource/franka_gripper_manager create mode 100644 ros2/src/franka_gripper_manager/setup.cfg create mode 100644 ros2/src/franka_gripper_manager/setup.py create mode 100644 ros2/src/franka_gripper_manager/test/test_copyright.py create mode 100644 ros2/src/franka_gripper_manager/test/test_flake8.py create mode 100644 ros2/src/franka_gripper_manager/test/test_pep257.py create mode 100644 ros2/src/franka_gripper_manager/urdf/robotiq_2f_85_gripper.urdf.xacro diff --git a/.gitignore b/.gitignore index 1b141e3..7103453 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,8 @@ outputs/* # vim *.swp MUJOCO_LOG.TXT + +#ros +ros2/build/ +ros2/install/ +ros2/log/ \ No newline at end of file diff --git a/README.md b/README.md index 0a4081b..3d3f1d1 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,10 @@ We have provided an entry point into the docker container python scripts/launch.py ``` +## Use with ROS 2 + +> **Note:** GELLO also supports ROS 2 humble for the Franka FR3 robot. For more details, see the [ROS 2-specific README](ros2/README.md) located in the `ros2` directory. + # GELLO configuration setup (PLEASE READ) Now that you have downloaded the code, there is some additional preparation work to properly configure the Dynamixels and GELLO. These instructions will guide you on how to update the motor ids of the Dynamixels and then how to extract the joint offsets to configure your GELLO. @@ -56,13 +60,14 @@ Dynamixels have a symmetric 4 hole pattern which means there the joint offset is The `GelloAgent` class accepts a `DynamixelRobotConfig` (found in `gello/agents/gello_agent.py`). The Dynamixel config specifies the parameters you need to find to operate your GELLO. Look at the documentation for more details. We have created a simple script to automatically detect the joint offset: -* set GELLO into a known configuration, where you know what the corresponding joint angles should be. For example, we set out GELLO in this configuration, where we know the desired ground truth joints. (0, -90, 90, -90, -90, 0) +* set GELLO into a known configuration, where you know what the corresponding joint angles should be. For example, we set our GELLO for the UR and Franka FR3 in this configuration, where we know the desired ground truth joints (0, -90, 90, -90, -90, 0), or (0, 0, 0, -90, 0, 90 , 0) respectively.

- - + + +

-* run +* For the UR run ``` python scripts/gello_get_offset.py \ --start-joints 0 -1.57 1.57 -1.57 -1.57 0 \ # in radians @@ -70,13 +75,22 @@ python scripts/gello_get_offset.py \ --port /dev/serial/by-id/usb-FTDI_USB__-__Serial_Converter_FT7WBG6 # replace values with your own ``` +* For the Franka FR3 run +``` +python scripts/gello_get_offset.py \ + --start-joints 0 0 0 -1.57 0 1.57 0 \ # in radians + --joint-signs 1 1 1 1 1 -1 1 \ + --port /dev/serial/by-id/usb-FTDI_USB__-__Serial_Converter_FT7WBG6 +# replace values with your own +``` * Use the known starting joints for `start-joints`. -* Use the `joint-signs` for your own robot (see below). +* Depending on the mechanical setup of your GELLO, the joint signs can flip, so you need to specify them for each axis. * Use your serial port for `port`. You can find the port id of your U2D2 Dynamixel device by running `ls /dev/serial/by-id` and looking for the path that starts with `usb-FTDI_USB__-__Serial_Converter` (on Ubuntu). On Mac, look in /dev/ and the device that starts with `cu.usbserial` `joint-signs` for each robot type: * UR: `1 1 -1 1 1 1` * Panda: `1 -1 1 1 1 -1 1` +* FR3: `1 1 1 1 1 -1 1` * xArm: `1 1 1 1 1 1 1` The script prints out a list of joint offsets. Go to `gello/agents/gello_agent.py` and add a DynamixelRobotConfig to the PORT_CONFIG_MAP. You are now ready to run your GELLO! diff --git a/imgs/fr3_gello_calib_pose.jpeg b/imgs/fr3_gello_calib_pose.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..68439de84f8147fb649f42f1890c5fa46c067a99 GIT binary patch literal 186017 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 >wzwwwCMq9oEVW|AFW}bD) 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℞m`gv9JMVz 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 fW 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
  • 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 ~/.bashrc &&\ + echo "source /opt/ros/humble/franka/setup.sh" >> ~/.profile' + + +# Install robotiq gripper +ARG ROS2_ROBOTIQ_GRIPPER_COMMIT_HASH="2ff85455d4b9f973c4b0bab1ce95fb09367f0d26" +RUN /bin/bash -c "source /opt/ros/humble/setup.bash && \ + mkdir tmp_build && \ + cd tmp_build && \ + git clone https://github.com/PickNikRobotics/ros2_robotiq_gripper.git && \ + cd ros2_robotiq_gripper && git checkout ${ROS2_ROBOTIQ_GRIPPER_COMMIT_HASH} && cd .. && \ + sed -i 's/kGripperMaxSpeed = 0.150;/kGripperMaxSpeed = 1.0;/g' ros2_robotiq_gripper/robotiq_driver/src/hardware_interface.cpp && \ + vcs import . --input ros2_robotiq_gripper/ros2_robotiq_gripper.humble.repos && \ + colcon build --install-base /opt/ros/humble/franka --cmake-args -DCMAKE_BUILD_TYPE=Release && \ + cd .. && \ + rm -rf tmp_build && \ + rm -rf /var/lib/apt/lists/*" \ No newline at end of file diff --git a/ros2/.devcontainer/devcontainer.json b/ros2/.devcontainer/devcontainer.json new file mode 100644 index 0000000..0f64954 --- /dev/null +++ b/ros2/.devcontainer/devcontainer.json @@ -0,0 +1,41 @@ +{ + "name": "gello-ros2", + "dockerComposeFile": "docker-compose.yml", + "service": "gello-ros2", + "overrideCommand": true, + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "charliermarsh.ruff", + "ms-vscode.cpptools-extension-pack", + "ms-iot.vscode-ros", + "smilerobotics.urdf", + "redhat.vscode-xml", + "tamasfe.even-better-toml", + "timonwong.shellcheck", + "yzhang.markdown-all-in-one", + "GitHub.copilot-chat", + "xaver.clang-format" + ], + "settings": { + "files.associations": { + "*.rviz": "yaml", + "*.srdf": "xml", + "*.urdf": "xml", + "*.xacro": "xml" + }, + "terminal.integrated.defaultProfile.linux": "bash", + "terminal.integrated.profiles.linux": { + "bash": { + "path": "/bin/bash" + } + }, + "terminal.integrated.cwd": "/workspace" + } + } + }, + "workspaceFolder": "/workspace/", + "shutdownAction": "stopCompose", + "postCreateCommand": "echo \"source /workspace/install/setup.bash \">> ~/.bashrc" +} diff --git a/ros2/.devcontainer/docker-compose.yml b/ros2/.devcontainer/docker-compose.yml new file mode 100644 index 0000000..b62c934 --- /dev/null +++ b/ros2/.devcontainer/docker-compose.yml @@ -0,0 +1,15 @@ +services: + gello-ros2: + build: . + privileged: true + restart: no + network_mode: host + volumes: + - ../:/workspace + - ../../gello/dynamixel/driver.py:/workspace/src/franka_gello_state_publisher/franka_gello_state_publisher/driver.py + - /dev/serial/by-id:/dev/serial/by-id + # GELLO Calibration Files + - ../../scripts/gello_get_offset.py:/workspace/gello_get_offset.py + - ../../gello/dynamixel:/workspace/gello/dynamixel + # Python flake8 config + - ../../.flake8:/workspace/.flake8 \ No newline at end of file diff --git a/ros2/.gitignore b/ros2/.gitignore new file mode 100644 index 0000000..dbe9c82 --- /dev/null +++ b/ros2/.gitignore @@ -0,0 +1 @@ +.vscode/ \ No newline at end of file diff --git a/ros2/README.md b/ros2/README.md new file mode 100644 index 0000000..214cbb5 --- /dev/null +++ b/ros2/README.md @@ -0,0 +1,153 @@ +# GELLO ROS 2 humble integration + +This folder contains all required ROS 2 packages for using GELLO. + +## Packages + +### 1. `franka_fr3_arm_controllers` +This package provides a Joint Impedance controller for the Franka FR3. It subscribes to the GELLO joint states and sends torque commands to the robot. + +#### Key Features: +- Implements a `JointImpedanceController` for controlling the robot's torques. +- Subscribes to `/gello/joint_states` topic for the GELLO joint states. + +#### Launch Files: +- **`franka.launch.py`**: Launches the Franka robot ros interfaces. +- **`franka_fr3_arm_controllers.launch.py`**: Launches the Joint Impedance controller. + +### 2. `franka_gello_state_publisher` +This package provides a ROS 2 node that reads input from the GELLO and publishes it as `sensor_msgs/msg/JointState` messages. + +#### Key Features: +- Publishes GELLO state to the `/gello/joint_states` topic. + +#### Launch Files: +- **`main.launch.py`**: Launches the GELLO publisher node. + +### 3. `franka_gripper_manager` +This package provides a ROS 2 node for managing the gripper connected to the Franka robot. Supported grippers are either the `Franka Hand` or the `Robotiq 2F-85`. It allows sending commands to control the gripper's width and perform homing actions. + +#### Key Features: +- Subscribes to `/gripper_client/target_gripper_width_percent` for gripper width commands. +- Supports homing and move actions for the gripper. + +#### Launch Files: +- **`franka_gripper_client.launch.py`**: Launches the gripper manager node for the `Franka Hand`. +- **`robotiq_gripper_controller_client.launch.py`**: Launches the gripper manager node for the `Robotiq 2F-85`. + +## VS-Code Dev-Container + +We recommend working inside the provided VS-Code Dev-Container for a seamless development experience. Dev-Containers allow you to use a consistent environment with all necessary dependencies pre-installed. For more information, refer to the [VS-Code Dev-Containers documentation](https://code.visualstudio.com/docs/devcontainers/containers). + +If you choose not to use the Dev-Container, you will need to manually install the dependencies listed in the `Dockerfile` located in the `.devcontainer` folder. + +## Build and Test + +### Building the Project + +To build the project, use the following `colcon` command with CMake arguments, required for clang-tidy: + +```bash +colcon build --cmake-args -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCHECK_TIDY=ON +``` + +### Testing the Project + +The packages come with a set of tests, which can be executed using the following command: + +```bash +colcon test +``` + +## Getting Started + +### 1. **Run the GELLO Publisher** +#### Step 1: Determine your GELLO USB ID + +To proceed, you need to know the USB ID of your GELLO device. This can be determined by running: + +```bash +ls /dev/serial/by-id +``` + +Example output: + +```bash +usb-FTDI_USB__-__Serial_Converter_FT7WBG6 +``` + +In this case, the `GELLO_USB_ID` would be `/dev/serial/by-id/usb-FTDI_USB__-__Serial_Converter_FT7WBG6`. + +#### Step 2: Configure your GELLO + +If not done already, follow the instructions of the `Create the GELLO configuration and determining joint ID's` section in the main README.md. Use the provided script to configure the GELLO for Franka FR3: + +```bash +python3 gello_get_offset.py \ +--start-joints 0 0 0 -1.57 0 1.57 0 \ +--joint-signs 1 1 1 1 1 -1 1 \ +--port /dev/serial/by-id/ +``` + +To apply your configuration: +- Update the `/workspace/src/franka_gello_state_publisher/config/gello_config.yaml` file. +- Rebuild the project to ensure the updated configuration is applied: + +```bash +colcon build +``` + +#### Step 3: Launch the GELLO publisher: +Launch the GELLO publisher node with the appropriate `com_port` parameter: + +```bash +ros2 launch franka_gello_state_publisher main.launch.py com_port:=/dev/serial/by-id/ +``` + +### 2. **Launch the Joint Impedance Controller** + + Launch the controller to send torque commands to the Franka robot: + ```bash + ros2 launch franka_fr3_arm_controllers franka_fr3_arm_controllers.launch.py robot_ip:= load_gripper:= + ``` + + - `robot_ip:` Replace `` with the IP address of your Franka robot. + - `load_gripper`: A boolean parameter (true or false) that specifies whether to load the Franka Hand: + - Set load_gripper:=true if you are using the Franka Hand. + - Set load_gripper:=false if you are not using the Franka Hand. + +### 3. **Launch the Gripper Manager** + + To control the gripper, use the appropriate launch file based on the gripper type: + + - **For the Franka Hand**: + ```bash + ros2 launch franka_gripper_manager franka_gripper_client.launch.py + ``` + + - **For the Robotiq 2F-85**: + ```bash + ros2 launch franka_gripper_manager robotiq_gripper_controller_client.launch.py com_port:= + ``` + + The `ROBOTIQ_USB_ID` can be determined by `ls /dev/serial/by-id`. + + +## Troubleshooting + +### SerialException(msg.errno, "could not open port {}: {}".format(self._port, msg)) + +The open com port could not be opened. Possible reasons are: +- Wrongly specified, the full path is required, such as: `/dev/serial/by-id/usb-FTDI_***` +- The device was plugged in after the docker container started, re-open the container + +### libfranka: Incompatible library version (server version: X, library version: Y). + +The libfranka version and robot system version are not compatible. More information can be found [here](https://frankaemika.github.io/docs/compatibility.html). +Fix this by correcting the `LIBFRANKA_VERSION=0.15.0` in the [Dockerfile](./.devcontainer/Dockerfile) and update the `FRANKA_ROS2_VERSION` and `FRANKA_DESCRIPTION_VERSION` accordingly. + + +## Acknowledgements +The source code for the Robotiq gripper control is based on +[ros2_robotiq_gripper](https://github.com/PickNikRobotics/ros2_robotiq_gripper.git), licensed under the BSD 3-Clause license. + diff --git a/ros2/pyproject.toml b/ros2/pyproject.toml new file mode 100644 index 0000000..dbfa66c --- /dev/null +++ b/ros2/pyproject.toml @@ -0,0 +1,3 @@ +[tool.black] +line-length = 99 +skip-string-normalization = false \ No newline at end of file diff --git a/ros2/src/franka_fr3_arm_controllers/CMakeLists.txt b/ros2/src/franka_fr3_arm_controllers/CMakeLists.txt new file mode 100644 index 0000000..b58a430 --- /dev/null +++ b/ros2/src/franka_fr3_arm_controllers/CMakeLists.txt @@ -0,0 +1,133 @@ +cmake_minimum_required(VERSION 3.5) +project(franka_fr3_arm_controllers) + +# Default to C++17 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) +endif() + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build type (default is Debug)" FORCE) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + add_compile_options(--coverage -O0 -g) + add_link_options(--coverage) + endif() +endif() + +option(CHECK_TIDY "Adds clang-tidy tests" OFF) + +# find dependencies +find_package(ament_cmake REQUIRED) +find_package(rclcpp REQUIRED) +find_package(controller_interface REQUIRED) +find_package(pluginlib REQUIRED) +find_package(rclcpp_lifecycle REQUIRED) +find_package(hardware_interface REQUIRED) +find_package(franka_msgs REQUIRED) +find_package(Eigen3 REQUIRED) +find_package(franka_semantic_components REQUIRED) +find_package(moveit_core REQUIRED) +find_package(moveit_msgs REQUIRED) + +add_library( + ${PROJECT_NAME} + SHARED + src/joint_impedance_controller.cpp + src/motion_generator.cpp) +target_include_directories( + ${PROJECT_NAME} + PUBLIC + include + ${EIGEN3_INCLUDE_DIRS} +) +ament_target_dependencies( + ${PROJECT_NAME} + controller_interface + hardware_interface + pluginlib + rclcpp + rclcpp_lifecycle + franka_semantic_components + moveit_core + moveit_msgs +) + +pluginlib_export_plugin_description_file( + controller_interface franka_fr3_arm_controllers.xml) + +install( + TARGETS + ${PROJECT_NAME} + RUNTIME DESTINATION bin + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +install( + DIRECTORY include/ + DESTINATION include +) + +install( + DIRECTORY config launch + DESTINATION share/${PROJECT_NAME} +) + +ament_export_include_directories( + include +) +ament_export_libraries( + ${PROJECT_NAME} +) +ament_export_dependencies( + controller_interface + pluginlib + rclcpp + rclcpp_lifecycle + hardware_interface + moveit_core +) + +if(BUILD_TESTING) + find_package(ament_cmake_gtest REQUIRED) + find_package(ament_cmake_clang_format REQUIRED) + find_package(ament_cmake_copyright REQUIRED) + find_package(ament_cmake_cppcheck REQUIRED) + find_package(ament_cmake_lint_cmake REQUIRED) + find_package(ament_cmake_xmllint REQUIRED) + + set(CPP_DIRECTORIES src include test) + + ament_cppcheck(${CPP_DIRECTORIES}) + ament_lint_cmake(CMakeLists.txt) + ament_xmllint() + ament_clang_format(CONFIG_FILE /workspace/.clang-format ${CPP_DIRECTORIES}) + ament_copyright(${CPP_DIRECTORIES} package.xml) + +ament_add_gtest( + test_joint_impedance_controller + test/test_joint_impedance_controller.cpp + test/invalid_configuration_test.cpp + test/setup_test.cpp +) + if(TARGET test_joint_impedance_controller) + target_link_libraries(test_joint_impedance_controller ${PROJECT_NAME}) + endif() +endif() + +if(CHECK_TIDY) + find_package(ament_cmake_clang_tidy REQUIRED) + set(ament_cmake_clang_tidy_CONFIG_FILE /workspace/.clang-tidy) + ament_clang_tidy(${CMAKE_BINARY_DIR}) +endif() + + +ament_package() \ No newline at end of file diff --git a/ros2/src/franka_fr3_arm_controllers/LICENSE b/ros2/src/franka_fr3_arm_controllers/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/ros2/src/franka_fr3_arm_controllers/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ros2/src/franka_fr3_arm_controllers/NOTICE b/ros2/src/franka_fr3_arm_controllers/NOTICE new file mode 100644 index 0000000..6ffb8cf --- /dev/null +++ b/ros2/src/franka_fr3_arm_controllers/NOTICE @@ -0,0 +1,14 @@ +franka_fr3_arm_controllers +Copyright 2025 Franka Robotics GmbH + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/ros2/src/franka_fr3_arm_controllers/config/controllers.yaml b/ros2/src/franka_fr3_arm_controllers/config/controllers.yaml new file mode 100644 index 0000000..08382d0 --- /dev/null +++ b/ros2/src/franka_fr3_arm_controllers/config/controllers.yaml @@ -0,0 +1,32 @@ +controller_manager: + ros__parameters: + update_rate: 1000 # Hz + + joint_impedance_controller: + type: franka_fr3_arm_controllers/JointImpedanceController + + joint_state_broadcaster: + type: joint_state_broadcaster/JointStateBroadcaster + + franka_robot_state_broadcaster: + type: franka_robot_state_broadcaster/FrankaRobotStateBroadcaster + +joint_impedance_controller: + ros__parameters: + k_alpha: 0.99 + k_gains: + - 240.0 + - 240.0 + - 240.0 + - 240.0 + - 100.0 + - 60.0 + - 20.0 + d_gains: + - 20.0 + - 20.0 + - 20.0 + - 10.0 + - 10.0 + - 10.0 + - 5.0 \ No newline at end of file diff --git a/ros2/src/franka_fr3_arm_controllers/franka_fr3_arm_controllers.xml b/ros2/src/franka_fr3_arm_controllers/franka_fr3_arm_controllers.xml new file mode 100644 index 0000000..baef264 --- /dev/null +++ b/ros2/src/franka_fr3_arm_controllers/franka_fr3_arm_controllers.xml @@ -0,0 +1,9 @@ + + + + The JointImpedanceController single arm controller for gello + + + \ No newline at end of file diff --git a/ros2/src/franka_fr3_arm_controllers/include/franka_fr3_arm_controllers/joint_impedance_controller.hpp b/ros2/src/franka_fr3_arm_controllers/include/franka_fr3_arm_controllers/joint_impedance_controller.hpp new file mode 100644 index 0000000..56bd705 --- /dev/null +++ b/ros2/src/franka_fr3_arm_controllers/include/franka_fr3_arm_controllers/joint_impedance_controller.hpp @@ -0,0 +1,70 @@ +// Copyright (c) 2025 Franka Robotics GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include +#include +#include +#include "franka_fr3_arm_controllers/motion_generator.hpp" + +using CallbackReturn = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn; + +namespace franka_fr3_arm_controllers { + +/** + * Controller to move the robot to a desired joint position. + */ +class JointImpedanceController : public controller_interface::ControllerInterface { + public: + using Vector7d = Eigen::Matrix; + [[nodiscard]] controller_interface::InterfaceConfiguration command_interface_configuration() + const override; + [[nodiscard]] controller_interface::InterfaceConfiguration state_interface_configuration() + const override; + controller_interface::return_type update(const rclcpp::Time& time, + const rclcpp::Duration& period) override; + CallbackReturn on_init() override; + CallbackReturn on_configure(const rclcpp_lifecycle::State& previous_state) override; + CallbackReturn on_activate(const rclcpp_lifecycle::State& previous_state) override; + + private: + std::string arm_id_; + std::string robot_description_; + const int num_joints = 7; + Vector7d q_; + Vector7d dq_; + Vector7d dq_filtered_; + Vector7d k_gains_; + Vector7d d_gains_; + double k_alpha_; + bool move_to_start_position_finished_{false}; + rclcpp::Time start_time_; + std::unique_ptr motion_generator_; + rclcpp::Subscription::SharedPtr joint_state_subscriber_ = nullptr; + bool gello_position_values_valid_ = false; + std::array gello_position_values_{0, 0, 0, 0, 0, 0, 0}; + rclcpp::Time last_joint_state_time_; + + Vector7d calculateTauDGains_(const Vector7d& q_goal); + bool validateGains_(const std::vector& gains, const std::string& gains_name); + void initializeMotionGenerator_(); + void updateJointStates_(); + void validateGelloPositions_(const sensor_msgs::msg::JointState& msg); + void jointStateCallback_(const sensor_msgs::msg::JointState msg); +}; + +} // namespace franka_fr3_arm_controllers diff --git a/ros2/src/franka_fr3_arm_controllers/include/franka_fr3_arm_controllers/motion_generator.hpp b/ros2/src/franka_fr3_arm_controllers/include/franka_fr3_arm_controllers/motion_generator.hpp new file mode 100644 index 0000000..34b3519 --- /dev/null +++ b/ros2/src/franka_fr3_arm_controllers/include/franka_fr3_arm_controllers/motion_generator.hpp @@ -0,0 +1,73 @@ +// Copyright (c) 2023 Franka Robotics GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include + +#include +#include +/** + * An example showing how to generate a joint pose motion to a goal position. Adapted from: + * Wisama Khalil and Etienne Dombre. 2002. Modeling, Identification and Control of Robots + * (Kogan Page Science Paper edition). + */ +class MotionGenerator { + public: + using Vector7d = Eigen::Matrix; + /** + * Creates a new MotionGenerator instance for a target q. + * + * @param[in] speed_factor General speed factor in range (0, 1]. + * @param[in] q_start Start joint positions. + * @param[in] q_goal Target joint positions. + */ + MotionGenerator(double speed_factor, const Vector7d& q_start, const Vector7d& q_goal); + + /** + * Sends joint position calculations + * + * @param[in] robot_state Current state of the robot. + * @param[in] trajectory_time Amount of time, that has passed since the start of the trajectory. + * + * @return Joint positions to use inside a control loop and a boolean indicating whether the + * motion is finished. + */ + std::pair getDesiredJointPositions(const rclcpp::Duration& trajectory_time); + + private: + using Vector7i = Eigen::Matrix; + + bool calculateDesiredValues(double time, Vector7d* delta_q_d) const; + void calculateSynchronizedValues(); + + static constexpr double kDeltaQMotionFinished = 1e-6; + static const int kJoints = 7; + + Vector7d q_start_; + Vector7d delta_q_; + + Vector7d dq_max_sync_; + Vector7d t_1_sync_; + Vector7d t_2_sync_; + Vector7d t_f_sync_; + Vector7d q_1_; + + double time_ = 0.0; + + Vector7d dq_max_ = (Vector7d() << 2.0, 2.0, 2.0, 2.0, 2.5, 2.5, 2.5).finished(); // in m/s + Vector7d ddq_max_start_ = (Vector7d() << 5, 5, 5, 5, 5, 5, 5).finished(); // in m/s^2 + Vector7d ddq_max_goal_ = (Vector7d() << 5, 5, 5, 5, 5, 5, 5).finished(); // in m/s^2 +}; diff --git a/ros2/src/franka_fr3_arm_controllers/launch/franka.launch.py b/ros2/src/franka_fr3_arm_controllers/launch/franka.launch.py new file mode 100644 index 0000000..fa5cd32 --- /dev/null +++ b/ros2/src/franka_fr3_arm_controllers/launch/franka.launch.py @@ -0,0 +1,182 @@ +# Copyright (c) 2024 Franka Robotics GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os + +from ament_index_python.packages import get_package_share_directory +from launch import LaunchContext, LaunchDescription +from launch.actions import ( + DeclareLaunchArgument, + IncludeLaunchDescription, + OpaqueFunction, + Shutdown +) +from launch.conditions import IfCondition, UnlessCondition +from launch.launch_description_sources import PythonLaunchDescriptionSource +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution +from launch_ros.actions import Node +from launch_ros.substitutions import FindPackageShare + +import xacro + + +def robot_description_dependent_nodes_spawner( + context: LaunchContext, + robot_ip, + arm_id, + use_fake_hardware, + fake_sensor_commands, + load_gripper): + + robot_ip_str = context.perform_substitution(robot_ip) + arm_id_str = context.perform_substitution(arm_id) + use_fake_hardware_str = context.perform_substitution(use_fake_hardware) + fake_sensor_commands_str = context.perform_substitution( + fake_sensor_commands) + load_gripper_str = context.perform_substitution(load_gripper) + + franka_xacro_filepath = os.path.join(get_package_share_directory( + 'franka_description'), 'robots', arm_id_str, arm_id_str+'.urdf.xacro') + robot_description = xacro.process_file(franka_xacro_filepath, + mappings={ + 'ros2_control': 'true', + 'arm_id': arm_id_str, + 'robot_ip': robot_ip_str, + 'hand': load_gripper_str, + 'use_fake_hardware': use_fake_hardware_str, + 'fake_sensor_commands': fake_sensor_commands_str, + }).toprettyxml(indent=' ') + + franka_controllers = PathJoinSubstitution( + [FindPackageShare('franka_fr3_arm_controllers'), 'config', 'controllers.yaml']) + + return [ + Node( + package='robot_state_publisher', + executable='robot_state_publisher', + name='robot_state_publisher', + output='screen', + parameters=[{'robot_description': robot_description}], + ), + Node( + package='controller_manager', + executable='ros2_control_node', + parameters=[franka_controllers, + {'robot_description': robot_description}, + {'arm_id': arm_id}, + {'load_gripper': load_gripper}, + ], + remappings=[('joint_states', 'franka/joint_states')], + output={ + 'stdout': 'screen', + 'stderr': 'screen', + }, + on_exit=Shutdown(), + )] + + +def generate_launch_description(): + arm_id_parameter_name = 'arm_id' + robot_ip_parameter_name = 'robot_ip' + load_gripper_parameter_name = 'load_gripper' + use_fake_hardware_parameter_name = 'use_fake_hardware' + fake_sensor_commands_parameter_name = 'fake_sensor_commands' + use_rviz_parameter_name = 'use_rviz' + + arm_id = LaunchConfiguration(arm_id_parameter_name) + robot_ip = LaunchConfiguration(robot_ip_parameter_name) + load_gripper = LaunchConfiguration(load_gripper_parameter_name) + use_fake_hardware = LaunchConfiguration(use_fake_hardware_parameter_name) + fake_sensor_commands = LaunchConfiguration( + fake_sensor_commands_parameter_name) + use_rviz = LaunchConfiguration(use_rviz_parameter_name) + + rviz_file = os.path.join(get_package_share_directory('franka_description'), 'rviz', + 'visualize_franka.rviz') + + robot_description_dependent_nodes_spawner_opaque_function = OpaqueFunction( + function=robot_description_dependent_nodes_spawner, + args=[ + robot_ip, + arm_id, + use_fake_hardware, + fake_sensor_commands, + load_gripper]) + + launch_description = LaunchDescription([ + DeclareLaunchArgument( + robot_ip_parameter_name, + description='Hostname or IP address of the robot.'), + DeclareLaunchArgument( + arm_id_parameter_name, + description='ID of the type of arm used. Supported values: fer, fr3, fp3'), + DeclareLaunchArgument( + use_rviz_parameter_name, + default_value='false', + description='Visualize the robot in Rviz'), + DeclareLaunchArgument( + use_fake_hardware_parameter_name, + default_value='false', + description='Use fake hardware'), + DeclareLaunchArgument( + fake_sensor_commands_parameter_name, + default_value='false', + description='Fake sensor commands. Only valid when "{}" is true'.format( + use_fake_hardware_parameter_name)), + DeclareLaunchArgument( + load_gripper_parameter_name, + default_value='true', + description='Use Franka Gripper as an end-effector, otherwise, the robot is loaded ' + 'without an end-effector.'), + Node( + package='joint_state_publisher', + executable='joint_state_publisher', + name='joint_state_publisher', + parameters=[ + {'source_list': ['franka/joint_states', 'franka_gripper/joint_states'], + 'rate': 30}], + ), + robot_description_dependent_nodes_spawner_opaque_function, + Node( + package='controller_manager', + executable='spawner', + arguments=['joint_state_broadcaster'], + output='screen', + ), + Node( + package='controller_manager', + executable='spawner', + arguments=['franka_robot_state_broadcaster'], + parameters=[{'arm_id': arm_id}], + output='screen', + condition=UnlessCondition(use_fake_hardware), + ), + IncludeLaunchDescription( + PythonLaunchDescriptionSource([PathJoinSubstitution( + [FindPackageShare('franka_gripper'), 'launch', 'gripper.launch.py'])]), + launch_arguments={robot_ip_parameter_name: robot_ip, + use_fake_hardware_parameter_name: use_fake_hardware}.items(), + condition=IfCondition(load_gripper) + ), + Node(package='rviz2', + executable='rviz2', + name='rviz2', + arguments=['--display-config', rviz_file], + condition=IfCondition(use_rviz) + ) + + ]) + + return launch_description diff --git a/ros2/src/franka_fr3_arm_controllers/launch/franka_fr3_arm_controllers.launch.py b/ros2/src/franka_fr3_arm_controllers/launch/franka_fr3_arm_controllers.launch.py new file mode 100644 index 0000000..0d05d0f --- /dev/null +++ b/ros2/src/franka_fr3_arm_controllers/launch/franka_fr3_arm_controllers.launch.py @@ -0,0 +1,85 @@ +# Copyright (c) 2024 Franka Robotics GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution +from launch_ros.actions import Node +from launch_ros.substitutions import FindPackageShare + + +def generate_launch_description(): + robot_ip_parameter_name = 'robot_ip' + arm_id_parameter_name = 'arm_id' + load_gripper_parameter_name = 'load_gripper' + use_fake_hardware_parameter_name = 'use_fake_hardware' + fake_sensor_commands_parameter_name = 'fake_sensor_commands' + use_rviz_parameter_name = 'use_rviz' + + robot_ip = LaunchConfiguration(robot_ip_parameter_name) + arm_id = LaunchConfiguration(arm_id_parameter_name) + load_gripper = LaunchConfiguration(load_gripper_parameter_name) + use_fake_hardware = LaunchConfiguration(use_fake_hardware_parameter_name) + fake_sensor_commands = LaunchConfiguration( + fake_sensor_commands_parameter_name) + use_rviz = LaunchConfiguration(use_rviz_parameter_name) + + return LaunchDescription([ + DeclareLaunchArgument( + robot_ip_parameter_name, + description='Hostname or IP address of the robot.'), + DeclareLaunchArgument( + arm_id_parameter_name, + default_value='fr3', + description='ID of the type of arm used. Supported values: fer, fr3, fp3'), + DeclareLaunchArgument( + use_rviz_parameter_name, + default_value='false', + description='Visualize the robot in Rviz'), + DeclareLaunchArgument( + use_fake_hardware_parameter_name, + default_value='false', + description='Use fake hardware'), + DeclareLaunchArgument( + fake_sensor_commands_parameter_name, + default_value='false', + description="Fake sensor commands. Only valid when '{}' is true".format( + use_fake_hardware_parameter_name)), + DeclareLaunchArgument( + load_gripper_parameter_name, + default_value='false', + description='Use Franka Gripper as an end-effector, otherwise, the robot is loaded ' + 'without an end-effector.'), + + IncludeLaunchDescription( + PythonLaunchDescriptionSource([PathJoinSubstitution( + [FindPackageShare('franka_fr3_arm_controllers'), 'launch', 'franka.launch.py'])]), + launch_arguments={robot_ip_parameter_name: robot_ip, + arm_id_parameter_name: arm_id, + load_gripper_parameter_name: load_gripper, + use_fake_hardware_parameter_name: use_fake_hardware, + fake_sensor_commands_parameter_name: fake_sensor_commands, + use_rviz_parameter_name: use_rviz + }.items(), + ), + + Node( + package='controller_manager', + executable='spawner', + arguments=['joint_impedance_controller'], + output='screen', + ), + ]) \ No newline at end of file diff --git a/ros2/src/franka_fr3_arm_controllers/package.xml b/ros2/src/franka_fr3_arm_controllers/package.xml new file mode 100644 index 0000000..960d30e --- /dev/null +++ b/ros2/src/franka_fr3_arm_controllers/package.xml @@ -0,0 +1,36 @@ + + + + franka_fr3_arm_controllers + 0.1.0 + Single arm controller for gello + Franka Robotics GmbH + Apache 2.0 + + ament_cmake + + rclcpp + controller_interface + pluginlib + rclcpp_lifecycle + franka_msgs + franka_semantic_components + + + ament_cmake_clang_format + ament_cmake_copyright + ament_cmake_cppcheck + ament_cmake_flake8 + ament_cmake_lint_cmake + ament_cmake_pep257 + ament_cmake_xmllint + ament_cmake_gmock + ament_cmake_clang_tidy + controller_manager + ros2_control_test_assets + + + + ament_cmake + + \ No newline at end of file diff --git a/ros2/src/franka_fr3_arm_controllers/src/joint_impedance_controller.cpp b/ros2/src/franka_fr3_arm_controllers/src/joint_impedance_controller.cpp new file mode 100644 index 0000000..8e14cd7 --- /dev/null +++ b/ros2/src/franka_fr3_arm_controllers/src/joint_impedance_controller.cpp @@ -0,0 +1,242 @@ +// Copyright (c) 2025 Franka Robotics GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include +#include +#include + +using std::placeholders::_1; + +namespace franka_fr3_arm_controllers { + +controller_interface::InterfaceConfiguration +JointImpedanceController::command_interface_configuration() const { + controller_interface::InterfaceConfiguration config; + config.type = controller_interface::interface_configuration_type::INDIVIDUAL; + + for (int i = 1; i <= num_joints; ++i) { + config.names.push_back(arm_id_ + "_joint" + std::to_string(i) + "/effort"); + } + return config; +} + +controller_interface::InterfaceConfiguration +JointImpedanceController::state_interface_configuration() const { + controller_interface::InterfaceConfiguration config; + config.type = controller_interface::interface_configuration_type::INDIVIDUAL; + for (int i = 1; i <= num_joints; ++i) { + config.names.push_back(arm_id_ + "_joint" + std::to_string(i) + "/position"); + config.names.push_back(arm_id_ + "_joint" + std::to_string(i) + "/velocity"); + } + return config; +} + +controller_interface::return_type JointImpedanceController::update( + const rclcpp::Time& /*time*/, + const rclcpp::Duration& /*period*/) { + updateJointStates_(); + Vector7d q_goal; + Vector7d tau_d_calculated; + + if (!move_to_start_position_finished_) { + auto trajectory_time = this->get_node()->now() - start_time_; + auto motion_generator_output = motion_generator_->getDesiredJointPositions(trajectory_time); + move_to_start_position_finished_ = motion_generator_output.second; + + q_goal = motion_generator_output.first; + } + + if (move_to_start_position_finished_) { + if (!gello_position_values_valid_) { + RCLCPP_FATAL(get_node()->get_logger(), "Timeout: No valid joint states received from Gello"); + rclcpp::shutdown(); // Exit the node permanently + } + for (int i = 0; i < num_joints; ++i) { + q_goal(i) = gello_position_values_[i]; + } + } + + tau_d_calculated = calculateTauDGains_(q_goal); + + for (int i = 0; i < num_joints; ++i) { + command_interfaces_[i].set_value(tau_d_calculated(i)); + } + + return controller_interface::return_type::OK; +} + +void JointImpedanceController::jointStateCallback_(const sensor_msgs::msg::JointState msg) { + if (last_joint_state_time_.seconds() == 0.0) { + return; + } + + if (msg.position.size() < gello_position_values_.size()) { + RCLCPP_WARN(get_node()->get_logger(), + "Received joint state size is smaller than expected size."); + return; + } + std::copy(msg.position.begin(), msg.position.begin() + gello_position_values_.size(), + gello_position_values_.begin()); + + validateGelloPositions_(msg); + last_joint_state_time_ = msg.header.stamp; +} + +CallbackReturn JointImpedanceController::on_init() { + try { + auto_declare("arm_id", ""); + auto_declare>("k_gains", {}); + auto_declare>("d_gains", {}); + } catch (const std::exception& e) { + fprintf(stderr, "Exception thrown during init stage with message: %s \n", e.what()); + return CallbackReturn::ERROR; + } + return CallbackReturn::SUCCESS; +} + +CallbackReturn JointImpedanceController::on_configure( + const rclcpp_lifecycle::State& /*previous_state*/) { + arm_id_ = get_node()->get_parameter("arm_id").as_string(); + auto k_gains = get_node()->get_parameter("k_gains").as_double_array(); + auto d_gains = get_node()->get_parameter("d_gains").as_double_array(); + auto k_alpha = get_node()->get_parameter("k_alpha").as_double(); + + if (!validateGains_(k_gains, "k_gains") || !validateGains_(d_gains, "d_gains")) { + return CallbackReturn::FAILURE; + } + + for (int i = 0; i < num_joints; ++i) { + d_gains_(i) = d_gains.at(i); + k_gains_(i) = k_gains.at(i); + } + + if (k_alpha < 0.0 || k_alpha > 1.0) { + RCLCPP_FATAL(get_node()->get_logger(), "k_alpha should be in the range [0, 1]"); + return CallbackReturn::FAILURE; + } + + k_alpha_ = k_alpha; + + dq_filtered_.setZero(); + + auto parameters_client = + std::make_shared(get_node(), "/robot_state_publisher"); + parameters_client->wait_for_service(); + + auto future = parameters_client->get_parameters({"robot_description"}); + auto result = future.get(); + if (!result.empty()) { + robot_description_ = result[0].value_to_string(); + } else { + RCLCPP_ERROR(get_node()->get_logger(), "Failed to get robot_description parameter."); + } + + joint_state_subscriber_ = get_node()->create_subscription( + "/gello/joint_states", 1, + [this](const sensor_msgs::msg::JointState& msg) { jointStateCallback_(msg); }); + + return CallbackReturn::SUCCESS; +} + +CallbackReturn JointImpedanceController::on_activate( + const rclcpp_lifecycle::State& /*previous_state*/) { + initializeMotionGenerator_(); + + dq_filtered_.setZero(); + start_time_ = this->get_node()->now(); + + return CallbackReturn::SUCCESS; +} + +auto JointImpedanceController::calculateTauDGains_(const Vector7d& q_goal) -> Vector7d { + dq_filtered_ = (1 - k_alpha_) * dq_filtered_ + k_alpha_ * dq_; + Vector7d tau_d_calculated; + tau_d_calculated = k_gains_.cwiseProduct(q_goal - q_) + d_gains_.cwiseProduct(-dq_filtered_); + + return tau_d_calculated; +} + +bool JointImpedanceController::validateGains_(const std::vector& gains, + const std::string& gains_name) { + if (gains.empty()) { + RCLCPP_FATAL(get_node()->get_logger(), "%s parameter not set", gains_name.c_str()); + return false; + } + + if (gains.size() != static_cast(num_joints)) { + RCLCPP_FATAL(get_node()->get_logger(), "%s should be of size %d but is of size %ld", + gains_name.c_str(), num_joints, gains.size()); + return false; + } + + return true; +} + +void JointImpedanceController::validateGelloPositions_(const sensor_msgs::msg::JointState& msg) { + const double max_time_diff = 0.5; + auto current_time = get_node()->now(); + auto time_since_last_joint_state = (current_time - last_joint_state_time_).seconds(); + auto time_since_msg_stamp = (current_time - msg.header.stamp).seconds(); + gello_position_values_valid_ = + (time_since_last_joint_state < max_time_diff && time_since_msg_stamp < max_time_diff); + if (!gello_position_values_valid_) { + RCLCPP_WARN(get_node()->get_logger(), + "Gello position values are not valid. Time since last joint state: %f // Time " + "since message stamp: %f", + time_since_last_joint_state, time_since_msg_stamp); + } +} + +void JointImpedanceController::updateJointStates_() { + for (auto i = 0; i < num_joints; ++i) { + const auto& position_interface = state_interfaces_.at(2 * i); + const auto& velocity_interface = state_interfaces_.at(2 * i + 1); + + assert(position_interface.get_interface_name() == "position"); + assert(velocity_interface.get_interface_name() == "velocity"); + + q_(i) = position_interface.get_value(); + dq_(i) = velocity_interface.get_value(); + } +} + +void JointImpedanceController::initializeMotionGenerator_() { + last_joint_state_time_ = get_node()->now(); + while (!gello_position_values_valid_) { + RCLCPP_WARN(get_node()->get_logger(), "Waiting for valid joint states..."); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + last_joint_state_time_ = get_node()->now(); + } + + Vector7d q_goal; + updateJointStates_(); + for (int i = 0; i < num_joints; ++i) { + q_goal(i) = gello_position_values_[i]; + } + RCLCPP_INFO(get_node()->get_logger(), "q_goal of motion generator: [%f, %f, %f, %f, %f, %f, %f]", + q_goal(0), q_goal(1), q_goal(2), q_goal(3), q_goal(4), q_goal(5), q_goal(6)); + + const double motion_generator_speed_factor = 0.2; + motion_generator_ = std::make_unique(motion_generator_speed_factor, q_, q_goal); +} + +} // namespace franka_fr3_arm_controllers +#include "pluginlib/class_list_macros.hpp" +// NOLINTNEXTLINE +PLUGINLIB_EXPORT_CLASS(franka_fr3_arm_controllers::JointImpedanceController, + controller_interface::ControllerInterface) diff --git a/ros2/src/franka_fr3_arm_controllers/src/motion_generator.cpp b/ros2/src/franka_fr3_arm_controllers/src/motion_generator.cpp new file mode 100644 index 0000000..0b8686a --- /dev/null +++ b/ros2/src/franka_fr3_arm_controllers/src/motion_generator.cpp @@ -0,0 +1,127 @@ +// Copyright (c) 2023 Franka Robotics GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include +#include +#include + +#include + +MotionGenerator::MotionGenerator(double speed_factor, + const Vector7d& q_start, + const Vector7d& q_goal) + : q_start_(q_start) { + assert(speed_factor > 0); + assert(speed_factor <= 1); + delta_q_ = q_goal - q_start; + dq_max_ *= speed_factor; + ddq_max_start_ *= speed_factor; + ddq_max_goal_ *= speed_factor; + calculateSynchronizedValues(); +} + +bool MotionGenerator::calculateDesiredValues(double time, Vector7d* delta_q_d) const { + Vector7i sign_delta_q; + sign_delta_q << delta_q_.cwiseSign().cast(); + Vector7d t_d = t_2_sync_ - t_1_sync_; + Vector7d delta_t_2_sync = t_f_sync_ - t_2_sync_; + std::array joint_motion_finished{}; + + for (auto i = 0; i < kJoints; i++) { + if (std::abs(delta_q_[i]) < kDeltaQMotionFinished) { + (*delta_q_d)[i] = 0; + joint_motion_finished.at(i) = true; + } else { + if (time < t_1_sync_[i]) { + (*delta_q_d)[i] = -1.0 / std::pow(t_1_sync_[i], 3.0) * dq_max_sync_[i] * sign_delta_q[i] * + (0.5 * time - t_1_sync_[i]) * std::pow(time, 3.0); + } else if (time >= t_1_sync_[i] && time < t_2_sync_[i]) { + (*delta_q_d)[i] = q_1_[i] + (time - t_1_sync_[i]) * dq_max_sync_[i] * sign_delta_q[i]; + } else if (time >= t_2_sync_[i] && time < t_f_sync_[i]) { + (*delta_q_d)[i] = + delta_q_[i] + + 0.5 * + (1.0 / std::pow(delta_t_2_sync[i], 3.0) * + (time - t_1_sync_[i] - 2.0 * delta_t_2_sync[i] - t_d[i]) * + std::pow((time - t_1_sync_[i] - t_d[i]), 3.0) + + (2.0 * time - 2.0 * t_1_sync_[i] - delta_t_2_sync[i] - 2.0 * t_d[i])) * + dq_max_sync_[i] * sign_delta_q[i]; + } else { + (*delta_q_d)[i] = delta_q_[i]; + joint_motion_finished.at(i) = true; + } + } + } + return std::all_of(joint_motion_finished.cbegin(), joint_motion_finished.cend(), + [](bool each_joint_finished) { return each_joint_finished; }); +} + +void MotionGenerator::calculateSynchronizedValues() { + Vector7d dq_max_reach(dq_max_); + Vector7d t_f = Vector7d::Zero(); + Vector7d delta_t_2 = Vector7d::Zero(); + Vector7d t_1 = Vector7d::Zero(); + Vector7d delta_t_2_sync = Vector7d::Zero(); + Vector7i sign_delta_q; + sign_delta_q << delta_q_.cwiseSign().cast(); + + for (auto i = 0; i < kJoints; i++) { + if (std::abs(delta_q_[i]) > kDeltaQMotionFinished) { + if (std::abs(delta_q_[i]) < (3.0 / 4.0 * (std::pow(dq_max_[i], 2.0) / ddq_max_start_[i]) + + 3.0 / 4.0 * (std::pow(dq_max_[i], 2.0) / ddq_max_goal_[i]))) { + dq_max_reach[i] = std::sqrt(4.0 / 3.0 * delta_q_[i] * sign_delta_q[i] * + (ddq_max_start_[i] * ddq_max_goal_[i]) / + (ddq_max_start_[i] + ddq_max_goal_[i])); + } + t_1[i] = 1.5 * dq_max_reach[i] / ddq_max_start_[i]; + delta_t_2[i] = 1.5 * dq_max_reach[i] / ddq_max_goal_[i]; + t_f[i] = t_1[i] / 2.0 + delta_t_2[i] / 2.0 + std::abs(delta_q_[i]) / dq_max_reach[i]; + } + } + double max_t_f = t_f.maxCoeff(); + for (auto i = 0; i < kJoints; i++) { + if (std::abs(delta_q_[i]) > kDeltaQMotionFinished) { + double param_a = 1.5 / 2.0 * (ddq_max_goal_[i] + ddq_max_start_[i]); + double param_b = -1.0 * max_t_f * ddq_max_goal_[i] * ddq_max_start_[i]; + double param_c = std::abs(delta_q_[i]) * ddq_max_goal_[i] * ddq_max_start_[i]; + double delta = param_b * param_b - 4.0 * param_a * param_c; + if (delta < 0.0) { + delta = 0.0; + } + dq_max_sync_[i] = (-1.0 * param_b - std::sqrt(delta)) / (2.0 * param_a); + t_1_sync_[i] = 1.5 * dq_max_sync_[i] / ddq_max_start_[i]; + delta_t_2_sync[i] = 1.5 * dq_max_sync_[i] / ddq_max_goal_[i]; + t_f_sync_[i] = + (t_1_sync_)[i] / 2.0 + delta_t_2_sync[i] / 2.0 + std::abs(delta_q_[i] / dq_max_sync_[i]); + t_2_sync_[i] = (t_f_sync_)[i] - delta_t_2_sync[i]; + q_1_[i] = (dq_max_sync_)[i] * sign_delta_q[i] * (0.5 * (t_1_sync_)[i]); + } + } +} + +std::pair MotionGenerator::getDesiredJointPositions( + const rclcpp::Duration& trajectory_time) { + time_ = trajectory_time.seconds(); + + Vector7d delta_q_d; + bool motion_finished = calculateDesiredValues(time_, &delta_q_d); + + std::array joint_positions{}; + Eigen::VectorXd::Map(joint_positions.data(), kJoints) = (q_start_ + delta_q_d); + return std::make_pair(q_start_ + delta_q_d, motion_finished); +} diff --git a/ros2/src/franka_fr3_arm_controllers/test/include/mocks/mock_joint_state_publisher.hpp b/ros2/src/franka_fr3_arm_controllers/test/include/mocks/mock_joint_state_publisher.hpp new file mode 100644 index 0000000..53df89f --- /dev/null +++ b/ros2/src/franka_fr3_arm_controllers/test/include/mocks/mock_joint_state_publisher.hpp @@ -0,0 +1,48 @@ +// Copyright (c) 2025 Franka Robotics GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include + +/** + * @class MockGelloJointStatePublisher + * @brief A mock joint state publisher node replacing the gello publisher for testing purposes. + * + */ +class MockGelloJointStatePublisher : public rclcpp::Node { + public: + MockGelloJointStatePublisher() : Node("joint_state_publisher_node") { + publisher_ = this->create_publisher("/gello/joint_states", 3); + timer_ = + this->create_wall_timer(std::chrono::milliseconds(100), + std::bind(&MockGelloJointStatePublisher::publishJointState_, this)); + } + + private: + void publishJointState_() { + auto message = sensor_msgs::msg::JointState(); + message.header.stamp = this->now(); + message.name = {"joint1", "joint2", "joint3", "joint4", "joint5", "joint6", "joint7", "joint8"}; + message.position = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; + message.velocity = {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1}; + message.effort = {0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01}; + + publisher_->publish(message); + } + + rclcpp::Publisher::SharedPtr publisher_; + rclcpp::TimerBase::SharedPtr timer_; +}; \ No newline at end of file diff --git a/ros2/src/franka_fr3_arm_controllers/test/include/mocks/mock_parameter_server.hpp b/ros2/src/franka_fr3_arm_controllers/test/include/mocks/mock_parameter_server.hpp new file mode 100644 index 0000000..f8d5256 --- /dev/null +++ b/ros2/src/franka_fr3_arm_controllers/test/include/mocks/mock_parameter_server.hpp @@ -0,0 +1,29 @@ +// Copyright (c) 2025 Franka Robotics GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include + +/** + * @class MockRobotParameterServer + * @brief A mock parameter server node replacing the robot parameter server for testing purposes. + * + */ +class MockRobotParameterServer : public rclcpp::Node { + public: + MockRobotParameterServer() : Node("robot_state_publisher") { + this->declare_parameter("robot_description", std::string("...")); + } +}; \ No newline at end of file diff --git a/ros2/src/franka_fr3_arm_controllers/test/include/test_joint_impedance_controller.hpp b/ros2/src/franka_fr3_arm_controllers/test/include/test_joint_impedance_controller.hpp new file mode 100644 index 0000000..5123f0a --- /dev/null +++ b/ros2/src/franka_fr3_arm_controllers/test/include/test_joint_impedance_controller.hpp @@ -0,0 +1,81 @@ +// Copyright (c) 2025 Franka Robotics GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include +#include "franka_fr3_arm_controllers/joint_impedance_controller.hpp" +#include "hardware_interface/types/hardware_interface_type_values.hpp" +#include "mocks/mock_joint_state_publisher.hpp" +#include "mocks/mock_parameter_server.hpp" + +using hardware_interface::CommandInterface; +using hardware_interface::HW_IF_EFFORT; +using hardware_interface::HW_IF_POSITION; +using hardware_interface::HW_IF_VELOCITY; +using hardware_interface::LoanedCommandInterface; +using hardware_interface::LoanedStateInterface; +using hardware_interface::StateInterface; + +/** + * @brief Test fixture for the JointImpedanceController + * + */ +class JointImpedanceControllerTest : public ::testing::Test { + public: + JointImpedanceControllerTest(); + + protected: + void SetUp() override; + void TearDown() override; + void startExecutorThread(); + void stopExecutorThread(); + rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn startController(); + void setRobotPosition(const std::vector& positions); + void setValidControllerParameters(); + + std::shared_ptr controller_; + std::shared_ptr mock_parameter_server_; + std::shared_ptr mock_joint_state_publisher_; + std::shared_ptr executor_; + std::thread executor_thread_; + std::condition_variable cv_executor_thread_; + std::mutex cv_mut_executor_thread_; + bool done_; + + /** + * @brief Current state of the "robot" + */ + static constexpr size_t num_joints = 7; + static constexpr std::array kInitialJointCommands = {1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0}; + static constexpr std::array kInitialPositionStates = {1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0}; + static constexpr std::array kInitialVelocityStates = {1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0}; + + const std::array joint_names_ = { + "fr3_joint1", "fr3_joint2", "fr3_joint3", "fr3_joint4", + "fr3_joint5", "fr3_joint6", "fr3_joint7"}; + + std::vector joint_commands_{kInitialJointCommands.begin(), kInitialJointCommands.end()}; + std::vector position_states_{kInitialPositionStates.begin(), + kInitialPositionStates.end()}; + std::vector velocity_states_{kInitialVelocityStates.begin(), + kInitialVelocityStates.end()}; + std::vector command_interfaces_; + std::vector state_interfaces_; +}; \ No newline at end of file diff --git a/ros2/src/franka_fr3_arm_controllers/test/invalid_configuration_test.cpp b/ros2/src/franka_fr3_arm_controllers/test/invalid_configuration_test.cpp new file mode 100644 index 0000000..58dd17d --- /dev/null +++ b/ros2/src/franka_fr3_arm_controllers/test/invalid_configuration_test.cpp @@ -0,0 +1,44 @@ +// Copyright (c) 2025 Franka Robotics GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "include/test_joint_impedance_controller.hpp" + +/** + * @brief Test fixture for the JointImpedanceController + */ +class InvalidConfigurationTest : public JointImpedanceControllerTest, + public ::testing::WithParamInterface {}; + +TEST_P(InvalidConfigurationTest, TestInvalidConfiguration) { + auto parameter_invalid_configuration = GetParam(); + controller_->get_node()->set_parameters({parameter_invalid_configuration}); + controller_->on_init(); + rclcpp_lifecycle::State state; + + EXPECT_EQ(controller_->on_configure(state), CallbackReturn::FAILURE); +} + +INSTANTIATE_TEST_SUITE_P( + InvalidConfigurations, + InvalidConfigurationTest, + ::testing::Values(rclcpp::Parameter("k_gains", + std::vector{1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0}), // exceeding number of joints + rclcpp::Parameter("d_gains", + std::vector{1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0}), // exceeding number of joints + rclcpp::Parameter("k_gains", std::vector{}), // empty + rclcpp::Parameter("d_gains", std::vector{}), // empty + rclcpp::Parameter("k_alpha", double(-1.0)), // out of range (<0) + rclcpp::Parameter("k_alpha", double(2.0)))); // out of range (>1) diff --git a/ros2/src/franka_fr3_arm_controllers/test/setup_test.cpp b/ros2/src/franka_fr3_arm_controllers/test/setup_test.cpp new file mode 100644 index 0000000..9f8d100 --- /dev/null +++ b/ros2/src/franka_fr3_arm_controllers/test/setup_test.cpp @@ -0,0 +1,119 @@ +// Copyright (c) 2025 Franka Robotics GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "include/test_joint_impedance_controller.hpp" + +TEST_F(JointImpedanceControllerTest, TestInitialization) { + EXPECT_EQ(controller_->on_init(), CallbackReturn::SUCCESS); +} + +TEST_F(JointImpedanceControllerTest, TestValidConfiguration) { + controller_->on_init(); + rclcpp_lifecycle::State state; + + EXPECT_EQ(controller_->on_configure(state), CallbackReturn::SUCCESS); +} + +TEST_F(JointImpedanceControllerTest, TestActivate) { + EXPECT_EQ(startController(), CallbackReturn::SUCCESS); +} + +TEST_F(JointImpedanceControllerTest, TestUpdateMotionGeneratorOnly) { + static const std::vector kInitialRobotPosition = {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1}; + static constexpr double kExpectedValue = -0.05; + static constexpr double kTolerance = 1e-6; + + setRobotPosition(kInitialRobotPosition); + startController(); + + rclcpp::Time time; + rclcpp::Duration period = rclcpp::Duration::from_seconds(1.0); + EXPECT_EQ(controller_->update(time, period), controller_interface::return_type::OK); + + for (size_t i = 0; i < joint_commands_.size(); ++i) { + EXPECT_NEAR(joint_commands_[i], kExpectedValue, kTolerance); + } +} + +TEST_F(JointImpedanceControllerTest, TestUpdateMotionGeneratorAndGelloPositionValues) { + static const std::vector kInitialRobotPosition = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; + static constexpr double kExpectedValue = -0.05; + static constexpr double kTolerance = 1e-6; + + setRobotPosition(kInitialRobotPosition); + startController(); + + rclcpp::Time time; + rclcpp::Duration period = rclcpp::Duration::from_seconds(1.0); + EXPECT_EQ(controller_->update(time, period), controller_interface::return_type::OK); + + for (size_t i = 0; i < joint_commands_.size(); ++i) { + EXPECT_NEAR(joint_commands_[i], kExpectedValue, kTolerance); + } +} + +TEST_F(JointImpedanceControllerTest, TestUpdateGelloPositionValuesOnly) { + static const std::vector kInitialRobotPosition = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; + static constexpr double kExpectedValue = -0.075; + static constexpr double kTolerance = 1e-6; + + setRobotPosition(kInitialRobotPosition); + startController(); + + rclcpp::Time time; + rclcpp::Duration period = rclcpp::Duration::from_seconds(1.0); + EXPECT_EQ(controller_->update(time, period), controller_interface::return_type::OK); + period = rclcpp::Duration::from_seconds(1.1); + EXPECT_EQ(controller_->update(time, period), controller_interface::return_type::OK); + + for (size_t i = 0; i < joint_commands_.size(); ++i) { + EXPECT_NEAR(joint_commands_[i], kExpectedValue, kTolerance); + } +} + +TEST_F(JointImpedanceControllerTest, TestUpdateInvalidGelloPositionValues) { + GTEST_SKIP() << "Skipping this test because yet not implemented"; + + /** + * Description: Modify mock_joint_state_publisher.hpp (Fake Gello), to be reconfigured to publish + * invalid joint states (invalid / old timestamp). + */ +} + +TEST_F(JointImpedanceControllerTest, TestCommandInterfaceConfiguration) { + controller_->on_init(); + rclcpp_lifecycle::State state; + controller_->on_configure(state); + + auto config = controller_->command_interface_configuration(); + EXPECT_EQ(config.type, controller_interface::interface_configuration_type::INDIVIDUAL); + ASSERT_EQ(config.names.size(), command_interfaces_.size()); + for (size_t i = 0; i < command_interfaces_.size(); i++) { + EXPECT_EQ(config.names[i], joint_names_[i] + "/" + HW_IF_EFFORT); + } +} + +TEST_F(JointImpedanceControllerTest, TestStateInterfaceConfiguration) { + controller_->on_init(); + rclcpp_lifecycle::State state; + controller_->on_configure(state); + + auto config = controller_->state_interface_configuration(); + EXPECT_EQ(config.type, controller_interface::interface_configuration_type::INDIVIDUAL); + ASSERT_EQ(config.names.size(), state_interfaces_.size()); + for (size_t i = 0; i < joint_names_.size(); ++i) { + EXPECT_EQ(config.names[2 * i], joint_names_[i] + "/" + HW_IF_POSITION); + EXPECT_EQ(config.names[2 * i + 1], joint_names_[i] + "/" + HW_IF_VELOCITY); + } +} \ No newline at end of file diff --git a/ros2/src/franka_fr3_arm_controllers/test/test_joint_impedance_controller.cpp b/ros2/src/franka_fr3_arm_controllers/test/test_joint_impedance_controller.cpp new file mode 100644 index 0000000..cf233f0 --- /dev/null +++ b/ros2/src/franka_fr3_arm_controllers/test/test_joint_impedance_controller.cpp @@ -0,0 +1,113 @@ +// Copyright (c) 2025 Franka Robotics GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "include/test_joint_impedance_controller.hpp" + +JointImpedanceControllerTest::JointImpedanceControllerTest() : done_(false) { + for (size_t i = 0; i < joint_names_.size(); ++i) { + command_interfaces_.emplace_back(joint_names_[i], HW_IF_EFFORT, &joint_commands_[i]); + state_interfaces_.emplace_back(joint_names_[i], HW_IF_POSITION, &position_states_[i]); + state_interfaces_.emplace_back(joint_names_[i], HW_IF_VELOCITY, &velocity_states_[i]); + } +} + +void JointImpedanceControllerTest::SetUp() { + rclcpp::init(0, nullptr); + executor_ = std::make_shared(); + + controller_ = std::make_shared(); + controller_->init("single_arm_controller_test"); + + mock_parameter_server_ = std::make_shared(); + mock_joint_state_publisher_ = std::make_shared(); + + executor_->add_node(controller_->get_node()->get_node_base_interface()); + executor_->add_node(mock_parameter_server_); + executor_->add_node(mock_joint_state_publisher_); + + std::vector loaned_command_interfaces; + loaned_command_interfaces.reserve(command_interfaces_.size()); + for (auto& command_interface : command_interfaces_) { + loaned_command_interfaces.emplace_back(command_interface); + } + + std::vector loaned_state_interfaces; + loaned_state_interfaces.reserve(state_interfaces_.size()); + for (auto& state_interface : state_interfaces_) { + loaned_state_interfaces.emplace_back(state_interface); + } + + controller_->assign_interfaces(std::move(loaned_command_interfaces), + std::move(loaned_state_interfaces)); + + setValidControllerParameters(); + + startExecutorThread(); +} + +void JointImpedanceControllerTest::TearDown() { + stopExecutorThread(); + + executor_->remove_node(controller_->get_node()->get_node_base_interface()); + executor_->remove_node(mock_joint_state_publisher_); + executor_->remove_node(mock_parameter_server_); + + rclcpp::shutdown(); +} + +void JointImpedanceControllerTest::startExecutorThread() { + done_ = false; + executor_thread_ = std::thread([this]() { + std::unique_lock lock(cv_mut_executor_thread_); + while (!done_) { + executor_->spin_some(); + cv_executor_thread_.wait_for(lock, std::chrono::milliseconds(10)); + } + }); +} + +void JointImpedanceControllerTest::stopExecutorThread() { + { + std::lock_guard lock(cv_mut_executor_thread_); + done_ = true; + } + cv_executor_thread_.notify_all(); + if (executor_thread_.joinable()) { + executor_thread_.join(); + } +} + +rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn +JointImpedanceControllerTest::startController() { + controller_->on_init(); + rclcpp_lifecycle::State state; + controller_->on_configure(state); + return controller_->on_activate(state); +} + +void JointImpedanceControllerTest::setRobotPosition(const std::vector& positions) { + for (size_t i = 0; i < positions.size(); ++i) { + position_states_[i] = positions[i]; + } +} + +void JointImpedanceControllerTest::setValidControllerParameters() { + static const std::string kArmId = "fr3"; + static const std::vector kKGains = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; + static const std::vector kDGains = {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1}; + static constexpr double kKAlpha = 0.5; + + controller_->get_node()->set_parameters( + {{"arm_id", kArmId}, {"k_gains", kKGains}, {"d_gains", kDGains}, {"k_alpha", kKAlpha}}); +} diff --git a/ros2/src/franka_gello_state_publisher/LICENSE b/ros2/src/franka_gello_state_publisher/LICENSE new file mode 100644 index 0000000..54aac19 --- /dev/null +++ b/ros2/src/franka_gello_state_publisher/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Original Copyright (c) 2023 Philipp Wu +Modified Copyright (c) 2025 Franka Robotics GmbH + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/ros2/src/franka_gello_state_publisher/config/gello_config.yaml b/ros2/src/franka_gello_state_publisher/config/gello_config.yaml new file mode 100644 index 0000000..71e413b --- /dev/null +++ b/ros2/src/franka_gello_state_publisher/config/gello_config.yaml @@ -0,0 +1,7 @@ +usb-FTDI_USB__-__Serial_Converter_FT94EVW4-if00-port0: + side: "sample-config" + num_joints: 7 + joint_signs: [1, 1, 1, 1, 1, -1, 1] + gripper: true + best_offsets: [6.283, 4.712, 3.142, 3.142, 3.142, 3.142, 4.712] + gripper_range_rad: [2.77856, 3.50931] \ No newline at end of file diff --git a/ros2/src/franka_gello_state_publisher/franka_gello_state_publisher/__init__.py b/ros2/src/franka_gello_state_publisher/franka_gello_state_publisher/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ros2/src/franka_gello_state_publisher/franka_gello_state_publisher/driver.py b/ros2/src/franka_gello_state_publisher/franka_gello_state_publisher/driver.py new file mode 100644 index 0000000..e69de29 diff --git a/ros2/src/franka_gello_state_publisher/franka_gello_state_publisher/gello_publisher.py b/ros2/src/franka_gello_state_publisher/franka_gello_state_publisher/gello_publisher.py new file mode 100644 index 0000000..6c7d9f3 --- /dev/null +++ b/ros2/src/franka_gello_state_publisher/franka_gello_state_publisher/gello_publisher.py @@ -0,0 +1,132 @@ +import os +import glob +from typing import Tuple +import rclpy +from rclpy.node import Node +import numpy as np + +try: + from franka_gello_state_publisher.driver import DynamixelDriver +except ImportError: + from driver import DynamixelDriver +from sensor_msgs.msg import JointState +from std_msgs.msg import Float32 +import yaml +from ament_index_python.packages import get_package_share_directory + + +class GelloPublisher(Node): + def __init__(self): + super().__init__("gello_publisher") + + default_com_port = self.determine_default_com_port() + self.declare_parameter("com_port", default_com_port) + self.com_port = self.get_parameter("com_port").get_parameter_value().string_value + self.port = self.com_port.split("/")[-1] + """The port that GELLO is connected to.""" + + config_path = os.path.join( + get_package_share_directory("franka_gello_state_publisher"), + "config", + "gello_config.yaml", + ) + self.get_values_from_config(config_path) + + self.robot_joint_publisher = self.create_publisher(JointState, "/gello/joint_states", 10) + self.gripper_joint_publisher = self.create_publisher( + Float32, "/gripper_client/target_gripper_width_percent", 10 + ) + + self.timer = self.create_timer(1 / 25, self.publish_joint_jog) + + self.joint_names = [ + "fr3_joint1", + "fr3_joint2", + "fr3_joint3", + "fr3_joint4", + "fr3_joint5", + "fr3_joint6", + "fr3_joint7", + ] + + def determine_default_com_port(self) -> str: + matches = glob.glob("/dev/serial/by-id/usb-FTDI_USB__-__Serial_Converter*") + if matches: + self.get_logger().info(f"Auto-detected com_ports: {matches}") + return matches[0] + else: + self.get_logger().warn("No com_ports detected. Please specify the com_port manually.") + return "INVALID_COM_PORT" + + def get_values_from_config(self, config_file: str): + with open(config_file, "r") as file: + config = yaml.safe_load(file) + + self.num_robot_joints: int = config[self.port]["num_joints"] + """The number of joints in the robot.""" + + self.joint_signs: Tuple[float, ...] = config[self.port]["joint_signs"] + """Depending on how the motor is mounted on the Gello, its rotation direction can be reversed.""" + + self.gripper: bool = config[self.port]["gripper"] + """Whether or not the gripper is attached.""" + + joint_ids = list(range(1, self.num_joints + 1)) + self.driver = DynamixelDriver(joint_ids, port=self.com_port, baudrate=57600) + """The driver for the Dynamixel motors.""" + + self.best_offsets = np.array(config[self.port]["best_offsets"]) + """The best offsets for the joints.""" + + self.gripper_range_rad: Tuple[float, float] = config[self.port]["gripper_range_rad"] + """The range of the gripper in radians.""" + + self.__post_init__() + + def __post_init__(self): + assert len(self.joint_signs) == self.num_robot_joints + for idx, j in enumerate(self.joint_signs): + assert j == -1 or j == 1, f"Joint idx: {idx} should be -1 or 1, but got {j}." + + @property + def num_joints(self) -> int: + extra_joints = 1 if self.gripper else 0 + return self.num_robot_joints + extra_joints + + def publish_joint_jog(self): + current_joints = self.driver.get_joints() + current_robot_joints = current_joints[: self.num_robot_joints] + current_joints_corrected = (current_robot_joints - self.best_offsets) * self.joint_signs + + robot_joint_states = JointState() + robot_joint_states.header.stamp = self.get_clock().now().to_msg() + robot_joint_states.name = self.joint_names + robot_joint_states.header.frame_id = "fr3_link0" + robot_joint_states.position = [float(joint) for joint in current_joints_corrected] + + gripper_joint_states = Float32() + if self.gripper: + gripper_position = current_joints[-1] + gripper_joint_states.data = self.gripper_readout_to_percent(gripper_position) + else: + gripper_joint_states.position = 0.0 + self.robot_joint_publisher.publish(robot_joint_states) + self.gripper_joint_publisher.publish(gripper_joint_states) + + def gripper_readout_to_percent(self, gripper_position: float) -> float: + gripper_percent = (gripper_position - self.gripper_range_rad[0]) / ( + self.gripper_range_rad[1] - self.gripper_range_rad[0] + ) + return max(0.0, min(1.0, gripper_percent)) + + +def main(args=None): + rclpy.init(args=args) + gello_publisher = GelloPublisher() + rclpy.spin(gello_publisher) + gello_publisher.destroy_node() + rclpy.shutdown() + + +if __name__ == "__main__": + main() diff --git a/ros2/src/franka_gello_state_publisher/launch/main.launch.py b/ros2/src/franka_gello_state_publisher/launch/main.launch.py new file mode 100755 index 0000000..9d6c8d2 --- /dev/null +++ b/ros2/src/franka_gello_state_publisher/launch/main.launch.py @@ -0,0 +1,22 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch_ros.actions import Node +from launch.substitutions import LaunchConfiguration + + +def generate_launch_description(): + args = [] + args.append( + DeclareLaunchArgument(name="com_port", default_value=None, description="Default COM port") + ) + nodes = [ + Node( + package="franka_gello_state_publisher", + executable="gello_publisher", + name="gello_publisher", + output="screen", + parameters=[{"com_port": LaunchConfiguration("com_port")}], + ), + ] + + return LaunchDescription(args + nodes) diff --git a/ros2/src/franka_gello_state_publisher/package.xml b/ros2/src/franka_gello_state_publisher/package.xml new file mode 100644 index 0000000..43cdf8a --- /dev/null +++ b/ros2/src/franka_gello_state_publisher/package.xml @@ -0,0 +1,24 @@ + + + + franka_gello_state_publisher + 0.1.0 + Publisher of joint states of gello + Franka Robotics GmbH + MIT + + rclpy + std_msgs + sensor_msgs + control_msgs + geometry_msgs + + ament_copyright + ament_flake8 + ament_pep257 + python3-pytest + + + ament_python + + diff --git a/ros2/src/franka_gello_state_publisher/resource/franka_gello_state_publisher b/ros2/src/franka_gello_state_publisher/resource/franka_gello_state_publisher new file mode 100644 index 0000000..e69de29 diff --git a/ros2/src/franka_gello_state_publisher/setup.cfg b/ros2/src/franka_gello_state_publisher/setup.cfg new file mode 100644 index 0000000..0b65838 --- /dev/null +++ b/ros2/src/franka_gello_state_publisher/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script_dir=$base/lib/franka_gello_state_publisher +[install] +install_scripts=$base/lib/franka_gello_state_publisher diff --git a/ros2/src/franka_gello_state_publisher/setup.py b/ros2/src/franka_gello_state_publisher/setup.py new file mode 100644 index 0000000..ae7f096 --- /dev/null +++ b/ros2/src/franka_gello_state_publisher/setup.py @@ -0,0 +1,26 @@ +from setuptools import find_packages, setup +import glob + +package_name = "franka_gello_state_publisher" + +setup( + name=package_name, + version="0.1.0", + packages=find_packages(exclude=["test"]), + data_files=[ + ("share/ament_index/resource_index/packages", ["resource/" + package_name]), + ("share/" + package_name, ["package.xml"]), + ("share/" + package_name + "/config", glob.glob("config/*.yaml")), + ("share/" + package_name + "/launch", ["launch/main.launch.py"]), + ], + install_requires=["setuptools", "dynamixel_sdk"], + zip_safe=True, + maintainer="Franka Robotics GmbH", + maintainer_email="support@franka.de", + description="Publishes the state of the GELLO teleoperation device.", + license="MIT", + tests_require=["pytest"], + entry_points={ + "console_scripts": ["gello_publisher = franka_gello_state_publisher.gello_publisher:main"], + }, +) diff --git a/ros2/src/franka_gello_state_publisher/test/test_copyright.py b/ros2/src/franka_gello_state_publisher/test/test_copyright.py new file mode 100644 index 0000000..95f0381 --- /dev/null +++ b/ros2/src/franka_gello_state_publisher/test/test_copyright.py @@ -0,0 +1,25 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_copyright.main import main +import pytest + + +# Remove the `skip` decorator once the source file(s) have a copyright header +@pytest.mark.skip(reason="No copyright header has been placed in the generated source file.") +@pytest.mark.copyright +@pytest.mark.linter +def test_copyright(): + rc = main(argv=[".", "test"]) + assert rc == 0, "Found errors" diff --git a/ros2/src/franka_gello_state_publisher/test/test_flake8.py b/ros2/src/franka_gello_state_publisher/test/test_flake8.py new file mode 100644 index 0000000..75c3f19 --- /dev/null +++ b/ros2/src/franka_gello_state_publisher/test/test_flake8.py @@ -0,0 +1,33 @@ +# Copyright 2017 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_flake8.main import main_with_errors +import pytest +from pathlib import Path + + +@pytest.mark.flake8 +@pytest.mark.linter +def test_flake8(): + config_path = Path(__file__).resolve().parents[3] / ".flake8" + excluded_file = Path(__file__).resolve().parents[1] / "franka_gello_state_publisher/driver.py" + print(excluded_file) + rc, errors = main_with_errors( + argv=["--config", str(config_path), "--exclude", str(excluded_file)] + ) + assert rc == 0, "Found %d code style errors / warnings:\n" % len(errors) + "\n".join(errors) + + +if __name__ == "__main__": + test_flake8() diff --git a/ros2/src/franka_gello_state_publisher/test/test_pep257.py b/ros2/src/franka_gello_state_publisher/test/test_pep257.py new file mode 100644 index 0000000..647b3c3 --- /dev/null +++ b/ros2/src/franka_gello_state_publisher/test/test_pep257.py @@ -0,0 +1,25 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_pep257.main import main +import pytest +from pathlib import Path + + +@pytest.mark.linter +@pytest.mark.pep257 +def test_pep257(): + excluded_file = Path(__file__).resolve().parents[1] / "franka_gello_state_publisher/driver.py" + rc = main(argv=[".", "test", "--exclude", str(excluded_file)]) + assert rc == 0, "Found code style errors / warnings" diff --git a/ros2/src/franka_gripper_manager/LICENSE b/ros2/src/franka_gripper_manager/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/ros2/src/franka_gripper_manager/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ros2/src/franka_gripper_manager/NOTICE b/ros2/src/franka_gripper_manager/NOTICE new file mode 100644 index 0000000..8fb983f --- /dev/null +++ b/ros2/src/franka_gripper_manager/NOTICE @@ -0,0 +1,15 @@ +franka_gripper_manager + +Copyright 2025 Franka Robotics GmbH + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/ros2/src/franka_gripper_manager/config/robotiq_controllers.yaml b/ros2/src/franka_gripper_manager/config/robotiq_controllers.yaml new file mode 100644 index 0000000..23edc91 --- /dev/null +++ b/ros2/src/franka_gripper_manager/config/robotiq_controllers.yaml @@ -0,0 +1,20 @@ +robotiq/controller_manager: + ros__parameters: + update_rate: 500 # Hz + joint_state_broadcaster: + type: joint_state_broadcaster/JointStateBroadcaster + robotiq_gripper_controller: + type: position_controllers/GripperActionController + robotiq_activation_controller: + type: robotiq_controllers/RobotiqActivationController + +robotiq/robotiq_gripper_controller: + ros__parameters: + default: true + joint: robotiq_85_left_knuckle_joint + use_effort_interface: true + use_speed_interface: true + +robotiq/robotiq_activation_controller: + ros__parameters: + default: true diff --git a/ros2/src/franka_gripper_manager/franka_gripper_manager/__init__.py b/ros2/src/franka_gripper_manager/franka_gripper_manager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ros2/src/franka_gripper_manager/franka_gripper_manager/franka_gripper_client.py b/ros2/src/franka_gripper_manager/franka_gripper_manager/franka_gripper_client.py new file mode 100644 index 0000000..498e771 --- /dev/null +++ b/ros2/src/franka_gripper_manager/franka_gripper_manager/franka_gripper_client.py @@ -0,0 +1,151 @@ +import rclpy +import time +from rclpy.node import Node +from franka_msgs.action import Move +from franka_msgs.action import Homing +from sensor_msgs.msg import JointState +from rclpy.action import ActionClient +from std_msgs.msg import Float32 + +DEFAULT_MOVE_ACTION_TOPIC = "/fr3_gripper/move" +DEFAULT_HOMING_ACTION_TOPIC = "/fr3_gripper/homing" +DEFAULT_JOINT_STATES_TOPIC = "/fr3_gripper/joint_states" +DEFAULT_GRIPPER_COMMAND_TOPIC = "/gripper_client/target_gripper_width_percent" + + +class GripperClient(Node): + def __init__(self): + super().__init__("gripper_client") + + self.declare_parameter("move_action_topic", DEFAULT_MOVE_ACTION_TOPIC) + self.declare_parameter("homing_action_topic", DEFAULT_HOMING_ACTION_TOPIC) + self.declare_parameter("gripper_command_topic", DEFAULT_GRIPPER_COMMAND_TOPIC) + self.declare_parameter("joint_states_topic", DEFAULT_JOINT_STATES_TOPIC) + + move_action_topic = ( + self.get_parameter("move_action_topic").get_parameter_value().string_value + ) + homing_action_topic = ( + self.get_parameter("homing_action_topic").get_parameter_value().string_value + ) + gripper_command_topic = ( + self.get_parameter("gripper_command_topic").get_parameter_value().string_value + ) + joint_states_topic = ( + self.get_parameter("joint_states_topic").get_parameter_value().string_value + ) + + self._ACTION_SERVER_TIMEOUT = 10.0 + self._MIN_GRIPPER_WIDTH_PERCENT = 0.0 + self._MAX_GRIPPER_WIDTH_PERCENT = 1.0 + self._gripper_command_transmitted = True + self._max_width = 0.0 + self._last_gripper_command = self._max_width * self._MAX_GRIPPER_WIDTH_PERCENT + + self.get_logger().info("Initializing gripper client...") + self._home_gripper(homing_action_topic) + self._get_max_gripper_width(joint_states_topic) + + self.get_logger().info("Subscribing to gripper commands...") + self._gripper_command_subscription = self.create_subscription( + Float32, gripper_command_topic, self._gripper_command_callback, 10 + ) + self._action_client = ActionClient(self, Move, move_action_topic) + + self.get_logger().info("Waiting for gripper move action server...") + if not self._action_client.wait_for_server(timeout_sec=self._ACTION_SERVER_TIMEOUT): + raise RuntimeError( + f"Move action server not available after {self._ACTION_SERVER_TIMEOUT} seconds!" + ) + + self.get_logger().info("Gripper client initialized!") + + def _home_gripper(self, homing_action_topic: str) -> None: + self.get_logger().info("Starting gripper homing...") + homing_client = ActionClient(self, Homing, homing_action_topic) + + self.get_logger().info(f"Waiting for homing action server {homing_action_topic}...") + if not homing_client.wait_for_server(timeout_sec=self._ACTION_SERVER_TIMEOUT): + raise RuntimeError( + f"Homing action server not available after {self._ACTION_SERVER_TIMEOUT} seconds!" + ) + + self.get_logger().info("Homing action server found!") + goal_msg = Homing.Goal() + future = homing_client.send_goal_async(goal_msg) + rclpy.spin_until_future_complete(self, future) + + goal_handle = future.result() + if not goal_handle.accepted: + raise RuntimeError("Homing action rejected!") + + result_future = goal_handle.get_result_async() + rclpy.spin_until_future_complete(self, result_future) + result = result_future.result() + time.sleep(2) + + if result.result.success: + self.get_logger().info("Gripper homing successful!") + else: + raise RuntimeError("Gripper homing failed!") + + def _get_max_gripper_width(self, joint_states_topic: str) -> None: + self.get_logger().info("Readout maximum gripper width...") + future = rclpy.task.Future() + + def joint_state_callback(msg): + _INDEX_FINGER_LEFT = 0 + self._max_width = 2 * msg.position[_INDEX_FINGER_LEFT] + self.get_logger().info(f"Maximum gripper width determined: {self._max_width}") + future.set_result(True) + + self.get_logger().info(f"Subscribing to {joint_states_topic}...") + gripper_subscription = self.create_subscription( + JointState, joint_states_topic, joint_state_callback, 10 + ) + + self.get_logger().info(f"Waiting for {joint_states_topic}...") + rclpy.spin_until_future_complete(self, future) + + self.get_logger().info(f"Unsubscribing from {joint_states_topic}") + self.destroy_subscription(gripper_subscription) + + def _gripper_command_callback(self, msg: Float32) -> None: + new_open_width_percent = msg.data + new_open_width = self._max_width * new_open_width_percent + if self._gripper_command_transmitted and new_open_width != self._last_gripper_command: + self._send_gripper_command(new_open_width) + self._last_gripper_command = new_open_width + self._gripper_command_transmitted = False + + def _send_gripper_command(self, gripper_position: float) -> None: + goal_msg = Move.Goal() + goal_msg.width = gripper_position + goal_msg.speed = 1.0 + self._future = self._action_client.send_goal_async(goal_msg) + self._future.add_done_callback(self._gripper_response_callback) + + def _gripper_response_callback(self, future: rclpy.task.Future) -> None: + goal_handle = future.result() + + if not goal_handle.accepted: + raise RuntimeError(f"Goal rejected with status: {goal_handle.status}") + + self._get_result_future = goal_handle.get_result_async() + self._get_result_future.add_done_callback(self._get_result_callback) + + def _get_result_callback(self, future: rclpy.task.Future) -> None: + result = future.result().result + self.get_logger().info("Result: {0}".format(result)) + self._gripper_command_transmitted = True + + +def main(args=None): + rclpy.init(args=args) + gripper_client = GripperClient() + rclpy.spin(gripper_client) + rclpy.shutdown() + + +if __name__ == "__main__": + main() diff --git a/ros2/src/franka_gripper_manager/franka_gripper_manager/robotiq_gripper_client.py b/ros2/src/franka_gripper_manager/franka_gripper_manager/robotiq_gripper_client.py new file mode 100644 index 0000000..0121a26 --- /dev/null +++ b/ros2/src/franka_gripper_manager/franka_gripper_manager/robotiq_gripper_client.py @@ -0,0 +1,58 @@ +import rclpy +from rclpy.node import Node +from control_msgs.action import GripperCommand +from rclpy.action import ActionClient +from std_msgs.msg import Float32 + +DEFAULT_GRIPPER_COMMAND_TOPIC = "/gripper_client/target_gripper_width_percent" +DEFAULT_MOVE_ACTION_TOPIC = "/robotiq/robotiq_gripper_controller/gripper_cmd" + + +class RobotiqGripperClient(Node): + def __init__(self): + super().__init__("robotiq_gripper_client") + self.get_logger().info("Starting Robotiq Gripper Client") + self.gripper_state_callback = self.create_subscription( + Float32, DEFAULT_GRIPPER_COMMAND_TOPIC, self.gripper_state_callback, 10 + ) + self.action_client = ActionClient(self, GripperCommand, DEFAULT_MOVE_ACTION_TOPIC) + self.action_client.wait_for_server() + self.last_width = -1.0 + self.get_logger().info("Gripper action server is up and running") + + def gripper_state_callback(self, msg): + gripper_target_position = msg.data + if abs(gripper_target_position - self.last_width) < 0.02: + return + self.send_gripper_command(gripper_target_position) + + def send_gripper_command(self, gripper_position): + self.last_width = gripper_position + goal_msg = GripperCommand.Goal() + goal_msg.command.position = 1 - gripper_position # Convert to 0-1 range + goal_msg.command.max_effort = 1.0 + self.future = self.action_client.send_goal_async(goal_msg) + self.future.add_done_callback(self.gripper_response_callback) + + def gripper_response_callback(self, future): + goal_handle = future.result() + if not goal_handle.accepted: + raise RuntimeError(f"Goal rejected with status: {goal_handle.status}") + + self._get_result_future = goal_handle.get_result_async() + self._get_result_future.add_done_callback(self.get_result_callback) + + def get_result_callback(self, future): + result = future.result().result + self.get_logger().info("Result: {0}".format(result)) + + +def main(args=None): + rclpy.init(args=args) + gripper_client = RobotiqGripperClient() + rclpy.spin(gripper_client) + rclpy.shutdown() + + +if __name__ == "__main__": + main() diff --git a/ros2/src/franka_gripper_manager/launch/franka_gripper_client.launch.py b/ros2/src/franka_gripper_manager/launch/franka_gripper_client.launch.py new file mode 100644 index 0000000..ddce89d --- /dev/null +++ b/ros2/src/franka_gripper_manager/launch/franka_gripper_client.launch.py @@ -0,0 +1,15 @@ +from launch import LaunchDescription +from launch_ros.actions import Node + + +def generate_launch_description(): + return LaunchDescription( + [ + Node( + package="franka_gripper_manager", + executable="franka_gripper_client", + arguments=["franka_gripper_client"], + output="screen", + ), + ] + ) diff --git a/ros2/src/franka_gripper_manager/launch/robotiq_gripper_controller_client.launch.py b/ros2/src/franka_gripper_manager/launch/robotiq_gripper_controller_client.launch.py new file mode 100644 index 0000000..5fefbbd --- /dev/null +++ b/ros2/src/franka_gripper_manager/launch/robotiq_gripper_controller_client.launch.py @@ -0,0 +1,174 @@ +# Copyright (c) 2022 PickNik, Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of the {copyright_holder} nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import launch +from launch.substitutions import ( + Command, + FindExecutable, + LaunchConfiguration, + PathJoinSubstitution, +) +from launch.conditions import IfCondition +import launch_ros +import os + + +def generate_launch_description(): + description_pkg_share = launch_ros.substitutions.FindPackageShare( + package="robotiq_description" + ).find("robotiq_description") + default_rviz_config_path = os.path.join(description_pkg_share, "rviz", "view_urdf.rviz") + + gripper_pkg_share = launch_ros.substitutions.FindPackageShare( + package="franka_gripper_manager" + ).find("franka_gripper_manager") + default_model_path = os.path.join( + gripper_pkg_share, "urdf", "robotiq_2f_85_gripper.urdf.xacro" + ) + + args = [] + args.append( + launch.actions.DeclareLaunchArgument( + name="model", + default_value=default_model_path, + description="Absolute path to gripper URDF file", + ) + ) + args.append( + launch.actions.DeclareLaunchArgument( + name="rvizconfig", + default_value=default_rviz_config_path, + description="Absolute path to rviz config file", + ) + ) + args.append( + launch.actions.DeclareLaunchArgument( + name="launch_rviz", default_value="false", description="Launch RViz?" + ) + ) + args.append( + launch.actions.DeclareLaunchArgument(name="com_port", description="Default COM port") + ) + + robot_description_content = Command( + [ + PathJoinSubstitution([FindExecutable(name="xacro")]), + " ", + LaunchConfiguration("model"), + " ", + "use_fake_hardware:=false", + " ", + "com_port:=", + LaunchConfiguration("com_port"), + ] + ) + robot_description_param = { + "robot_description": launch_ros.parameter_descriptions.ParameterValue( + robot_description_content, value_type=str + ) + } + + update_rate_config_file = PathJoinSubstitution( + [ + description_pkg_share, + "config", + "robotiq_update_rate.yaml", + ] + ) + + controllers_file = "robotiq_controllers.yaml" + initial_joint_controllers = PathJoinSubstitution( + [gripper_pkg_share, "config", controllers_file] + ) + + control_node = launch_ros.actions.Node( + package="controller_manager", + executable="ros2_control_node", + namespace="robotiq", + parameters=[ + robot_description_param, + update_rate_config_file, + initial_joint_controllers, + ], + ) + + robot_state_publisher_node = launch_ros.actions.Node( + package="robot_state_publisher", + executable="robot_state_publisher", + parameters=[robot_description_param], + ) + + rviz_node = launch_ros.actions.Node( + package="rviz2", + executable="rviz2", + name="rviz2", + output="log", + arguments=["-d", LaunchConfiguration("rvizconfig")], + condition=IfCondition(LaunchConfiguration("launch_rviz")), + ) + + joint_state_broadcaster_spawner = launch_ros.actions.Node( + package="controller_manager", + executable="spawner", + arguments=[ + "joint_state_broadcaster", + "--controller-manager", + "robotiq/controller_manager", + ], + ) + + robotiq_gripper_controller_spawner = launch_ros.actions.Node( + package="controller_manager", + executable="spawner", + arguments=["robotiq_gripper_controller", "-c", "robotiq/controller_manager"], + ) + + robotiq_activation_controller_spawner = launch_ros.actions.Node( + package="controller_manager", + executable="spawner", + arguments=["robotiq_activation_controller", "-c", "robotiq/controller_manager"], + ) + + robotiq_gripper_client = launch_ros.actions.Node( + package="franka_gripper_manager", + executable="robotiq_gripper_client", + name="robotiq_gripper_client", + output="screen", + ) + + nodes = [ + control_node, + robot_state_publisher_node, + joint_state_broadcaster_spawner, + robotiq_gripper_controller_spawner, + robotiq_activation_controller_spawner, + rviz_node, + robotiq_gripper_client, + ] + + return launch.LaunchDescription(args + nodes) diff --git a/ros2/src/franka_gripper_manager/package.xml b/ros2/src/franka_gripper_manager/package.xml new file mode 100644 index 0000000..9db7ed1 --- /dev/null +++ b/ros2/src/franka_gripper_manager/package.xml @@ -0,0 +1,20 @@ + + + + franka_gripper_manager + 0.0.0 + Provisioning of gripper clients for gello + Franka Robotics GmbH + Apache 2.0 + ros2-robotiq-gripper + sensor-msgs + rclpy + + ament_copyright + ament_flake8 + ament_pep257 + python3-pytest + + ament_python + + diff --git a/ros2/src/franka_gripper_manager/resource/franka_gripper_manager b/ros2/src/franka_gripper_manager/resource/franka_gripper_manager new file mode 100644 index 0000000..e69de29 diff --git a/ros2/src/franka_gripper_manager/setup.cfg b/ros2/src/franka_gripper_manager/setup.cfg new file mode 100644 index 0000000..98c77ee --- /dev/null +++ b/ros2/src/franka_gripper_manager/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script_dir=$base/lib/franka_gripper_manager +[install] +install_scripts=$base/lib/franka_gripper_manager diff --git a/ros2/src/franka_gripper_manager/setup.py b/ros2/src/franka_gripper_manager/setup.py new file mode 100644 index 0000000..2eab62c --- /dev/null +++ b/ros2/src/franka_gripper_manager/setup.py @@ -0,0 +1,31 @@ +from setuptools import find_packages, setup +import glob + +package_name = "franka_gripper_manager" + +setup( + name=package_name, + version="0.1.0", + packages=find_packages(exclude=["test"]), + data_files=[ + ("share/ament_index/resource_index/packages", ["resource/" + package_name]), + ("share/" + package_name, ["package.xml"]), + ("share/" + package_name + "/config", glob.glob("config/*.yaml")), + ("share/" + package_name + "/launch", glob.glob("launch/*.launch.py")), + ("share/" + package_name + "/urdf", glob.glob("urdf/*.urdf.xacro")), + ], + include_package_data=True, + install_requires=["setuptools"], + zip_safe=True, + maintainer="Franka Robotics GmbH", + maintainer_email="support@franka.de", + description="Manages the grippers of the robot.", + license="Apache 2.0", + tests_require=["pytest"], + entry_points={ + "console_scripts": [ + "franka_gripper_client = franka_gripper_manager.franka_gripper_client:main", + "robotiq_gripper_client = franka_gripper_manager.robotiq_gripper_client:main", + ], + }, +) diff --git a/ros2/src/franka_gripper_manager/test/test_copyright.py b/ros2/src/franka_gripper_manager/test/test_copyright.py new file mode 100644 index 0000000..95f0381 --- /dev/null +++ b/ros2/src/franka_gripper_manager/test/test_copyright.py @@ -0,0 +1,25 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_copyright.main import main +import pytest + + +# Remove the `skip` decorator once the source file(s) have a copyright header +@pytest.mark.skip(reason="No copyright header has been placed in the generated source file.") +@pytest.mark.copyright +@pytest.mark.linter +def test_copyright(): + rc = main(argv=[".", "test"]) + assert rc == 0, "Found errors" diff --git a/ros2/src/franka_gripper_manager/test/test_flake8.py b/ros2/src/franka_gripper_manager/test/test_flake8.py new file mode 100644 index 0000000..04ef698 --- /dev/null +++ b/ros2/src/franka_gripper_manager/test/test_flake8.py @@ -0,0 +1,25 @@ +# Copyright 2017 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_flake8.main import main_with_errors +import pytest +from pathlib import Path + + +@pytest.mark.flake8 +@pytest.mark.linter +def test_flake8(): + config_path = Path(__file__).resolve().parents[3] / ".flake8" + rc, errors = main_with_errors(argv=["--config", str(config_path)]) + assert rc == 0, "Found %d code style errors / warnings:\n" % len(errors) + "\n".join(errors) diff --git a/ros2/src/franka_gripper_manager/test/test_pep257.py b/ros2/src/franka_gripper_manager/test/test_pep257.py new file mode 100644 index 0000000..a2c3deb --- /dev/null +++ b/ros2/src/franka_gripper_manager/test/test_pep257.py @@ -0,0 +1,23 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_pep257.main import main +import pytest + + +@pytest.mark.linter +@pytest.mark.pep257 +def test_pep257(): + rc = main(argv=[".", "test"]) + assert rc == 0, "Found code style errors / warnings" diff --git a/ros2/src/franka_gripper_manager/urdf/robotiq_2f_85_gripper.urdf.xacro b/ros2/src/franka_gripper_manager/urdf/robotiq_2f_85_gripper.urdf.xacro new file mode 100644 index 0000000..2a5142c --- /dev/null +++ b/ros2/src/franka_gripper_manager/urdf/robotiq_2f_85_gripper.urdf.xacro @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + +