{"id":28256390,"url":"https://github.com/timschneider42/franky","last_synced_at":"2025-06-16T17:32:04.422Z","repository":{"id":77928341,"uuid":"573152462","full_name":"TimSchneider42/franky","owner":"TimSchneider42","description":" High-Level Control Library for Franka Robots with Python and C++ Support ","archived":false,"fork":false,"pushed_at":"2025-06-14T12:11:41.000Z","size":6837,"stargazers_count":98,"open_issues_count":3,"forks_count":13,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-14T12:33:52.312Z","etag":null,"topics":["control","franka","franka-panda","franka-panda-python","franka-research-3","python","python3","robotics"],"latest_commit_sha":null,"homepage":"https://timschneider42.github.io/franky/","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"pantor/frankx","license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TimSchneider42.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-12-01T20:13:43.000Z","updated_at":"2025-06-14T12:11:44.000Z","dependencies_parsed_at":"2025-04-22T17:23:12.867Z","dependency_job_id":"a9566a59-2949-4189-8b39-03fb7fb66d73","html_url":"https://github.com/TimSchneider42/franky","commit_stats":null,"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"purl":"pkg:github/TimSchneider42/franky","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimSchneider42%2Ffranky","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimSchneider42%2Ffranky/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimSchneider42%2Ffranky/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimSchneider42%2Ffranky/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TimSchneider42","download_url":"https://codeload.github.com/TimSchneider42/franky/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TimSchneider42%2Ffranky/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259819795,"owners_count":22916516,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["control","franka","franka-panda","franka-panda-python","franka-research-3","python","python3","robotics"],"created_at":"2025-05-19T22:16:41.350Z","updated_at":"2025-06-16T17:32:04.397Z","avatar_url":"https://github.com/TimSchneider42.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg width=\"340\" src=\"https://raw.githubusercontent.com/timschneider42/franky/master/doc/logo.svg?sanitize=true\"\u003e\n  \u003ch3 align=\"center\"\u003e\n    High-Level Control Library for Franka Robots with Python and C++ Support\n  \u003c/h3\u003e\n\u003c/div\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/timschneider42/franky/actions\"\u003e\n    \u003cimg src=\"https://github.com/timschneider42/franky/workflows/CI/badge.svg\" alt=\"CI\"\u003e\n  \u003c/a\u003e\n\n  \u003ca href=\"https://github.com/timschneider42/franky/actions\"\u003e\n    \u003cimg src=\"https://github.com/timschneider42/franky/workflows/Publish/badge.svg\" alt=\"Publish\"\u003e\n  \u003c/a\u003e\n\n  \u003ca href=\"https://github.com/timschneider42/franky/issues\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/issues/timschneider42/franky.svg\" alt=\"Issues\"\u003e\n  \u003c/a\u003e\n\n  \u003ca href=\"https://github.com/timschneider42/franky/releases\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/v/release/timschneider42/franky.svg?include_prereleases\u0026sort=semver\" alt=\"Releases\"\u003e\n  \u003c/a\u003e\n\n  \u003ca href=\"https://github.com/timschneider42/franky/blob/master/LICENSE\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/license-LGPL-green.svg\" alt=\"LGPL\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nFranky is a high-level control library for Franka robots offering Python and C++ support.\nBy providing a high-level control interface, Franky eliminates the need for strict real-time programming at 1 kHz,\nmaking control from non-real-time environments, such as Python programs, feasible.\nInstead of relying on low-level control commands, Franky expects high-level position or velocity targets and\nuses [Ruckig](https://github.com/pantor/ruckig) to plan time-optimal trajectories in real-time.\n\nAlthough Python does not provide real-time guarantees, Franky strives to maintain as much real-time control as possible.\nMotions can be preempted at any moment, prompting Franky to re-plan trajectories on the fly.\nTo handle unforeseen situations—such as unexpected contact with the environment — Franky includes a reaction system that\nallows to update motion commands dynamically.\nFurthermore, most non-real-time functionality of [libfranka](https://frankaemika.github.io/docs/libfranka.html), such as\nGripper control is made directly available in Python.\n\nCheck out the [tutorial](#-tutorial) and the [examples](https://github.com/TimSchneider42/franky/tree/master/examples)\nfor an introduction.\nThe full documentation can be found\nat [https://timschneider42.github.io/franky/](https://timschneider42.github.io/franky/).\n\n## 🚀 Features\n\n- **Control your Franka robot directly from Python in just a few lines!**\n  No more endless hours setting up ROS, juggling packages, or untangling dependencies. Just `pip install` — no ROS at all.\n\n- **[Four control modes](#motion-types)**: [Cartesian position](#cartesian-position-control), [Cartesian velocity](#cartesian-velocity-control), [Joint position](#joint-position-control), [Joint velocity](#joint-velocity-control)\n  Franky uses [Ruckig](https://github.com/pantor/ruckig) to generate smooth, time-optimal trajectories while respecting velocity, acceleration, and jerk limits.\n\n- **[Real-time control from Python and C++](#real-time-motions)**\n  Need to change the target while the robot’s moving? No problem. Franky re-plans trajectories on the fly so that you can preempt motions anytime.\n\n- **[Reactive behavior](#-real-time-reactions)**\n  Robots don’t always go according to plan. Franky lets you define reactions to unexpected events—like contact with the environment — so you can change course in real-time.\n\n- **[Motion and reaction callbacks](#motion-callbacks)**\n  Want to monitor what’s happening under the hood? Add callbacks to your motions and reactions. They won’t block the control thread and are super handy for debugging or logging.\n\n- **Things are moving too fast? [Tune the robot's dynamics to your needs](#-robot)**\n  Adjust max velocity, acceleration, and jerk to match your setup or task. Fine control for smooth, safe operation.\n\n- **Full Python access to the libfranka API**\n  Want to tweak impedance, read the robot state, set force thresholds, or mess with the Jacobian? Go for it. If libfranka supports it, chances are Franky does, too.\n\n## 📖 Python Quickstart Guide\n\nReal-time kernel already installed and real-time permissions granted? Just install Franky via\n\n```bash\npip install franky-control\n```\n\notherwise, follow the [setup instructions](#setup) first.\n\nNow we are already ready to go!\nUnlock the brakes in the web interface, activate FCI, and start coding:\n```python\nfrom franky import *\n\nrobot = Robot(\"172.16.0.2\")  # Replace this with your robot's IP\n\n# Let's start slow (this lets the robot use a maximum of 5% of its velocity, acceleration, and jerk limits)\nrobot.relative_dynamics_factor = 0.05\n\n# Move the robot 20cm along the relative X-axis of its end-effector\nmotion = CartesianMotion(Affine([0.2, 0.0, 0.0]), ReferenceType.Relative)\nrobot.move(motion)\n```\n\nIf you are seeing server version mismatch errors, such as\n```\nfranky.IncompatibleVersionException: libfranka: Incompatible library version (server version: 5, library version: 9)\n```\nthen your Franka robot is either not on the most recent firmware version or you are using the older Franka Panda model.\nIn any case, it's no big deal; just check [here](https://frankaemika.github.io/docs/compatibility.html) which libfranka version you need and follow our [instructions](installing-frankly) to install the appropriate Franky wheels.\n\n## \u003ca id=\"setup\" /\u003e ⚙️ Setup\n\nTo install Franky, you have to follow three steps:\n\n1. Ensure that you are using a realtime kernel\n2. Ensure that the executing user has permission to run real-time applications\n3. Install Franky via pip or build it from source\n\n### Installing a real-time kernel\n\nIn order for Franky to function properly, it requires the underlying OS to use a realtime kernel.\nOtherwise, you might see `communication_constrains_violation` errors.\n\nTo check whether your system is currently using a real-time kernel, type `uname -a`.\nYou should see something like this:\n\n```\n$ uname -a\nLinux [PCNAME] 5.15.0-1056-realtime #63-Ubuntu SMP PREEMPT_RT ...\n```\n\nIf it does not say PREEMPT_RT, you are not currently running a real-time kernel.\n\nThere are multiple ways of installing a real-time kernel.\nYou\ncan [build it from source](https://frankaemika.github.io/docs/installation_linux.html#setting-up-the-real-time-kernel)\nor, if you are using Ubuntu, it can be [enabled through Ubuntu Pro](https://ubuntu.com/real-time).\n\n### Allowing the executing user to run real-time applications\n\nFirst, create a group `realtime` and add your user (or whoever is running Franky) to this group:\n\n```bash\nsudo addgroup realtime\nsudo usermod -a -G realtime $(whoami)\n```\n\nAfterward, add the following limits to the real-time group in /etc/security/limits.conf:\n\n```\n@realtime soft rtprio 99\n@realtime soft priority 99\n@realtime soft memlock 102400\n@realtime hard rtprio 99\n@realtime hard priority 99\n@realtime hard memlock 102400\n```\n\nLog out and log in again to let the changes take effect.\n\nTo verify that the changes were applied, check if your user is in the `realtime` group:\n\n```bash\n$ groups\n... realtime\n```\n\nIf realtime is not listed in your groups, try rebooting.\n\n### Installing Franky\n\nTo start using Franky with Python and libfranka *0.15.0*, just install it via\n\n```bash\npip install franky-control\n```\n\nWe also provide wheels for libfranka versions *0.7.1*, *0.8.0*, *0.9.2*, *0.10.0*, *0.11.0*, *0.12.1*, *0.13.3*,\n*0.14.2*, and *0.15.0*.\nThey can be installed via\n\n```bash\nVERSION=0-9-2\nwget https://github.com/TimSchneider42/franky/releases/latest/download/libfranka_${VERSION}_wheels.zip\nunzip libfranka_${VERSION}_wheels.zip\npip install numpy\npip install --no-index --find-links=./dist franky-control\n```\n\nFranky is based on [libfranka](https://github.com/frankaemika/libfranka), [Eigen](https://eigen.tuxfamily.org) for\ntransformation calculations and [pybind11](https://github.com/pybind/pybind11) for the Python bindings.\nAs the Franka is sensitive to acceleration discontinuities, it requires jerk-constrained motion generation, for which\nFranky uses the [Ruckig](https://ruckig.com) community version for Online Trajectory Generation (OTG).\n\nAfter installing the dependencies (the exact versions can be found [here](#-development)), you can build and install\nFranky via\n\n```bash\ngit clone --recurse-submodules git@github.com:timschneider42/franky.git\ncd franky\nmkdir -p build\ncd build\ncmake -DCMAKE_BUILD_TYPE=Release ..\nmake\nmake install\n```\n\nTo use Franky, you can also include it as a subproject in your parent CMake via `add_subdirectory(franky)` and then\n`target_link_libraries(\u003ctarget\u003e franky)`.\n\nIf you need only the Python module, you can install Franky via\n\n```bash\npip install .\n```\n\nMake sure that the built library `_franky.cpython-3**-****-linux-gnu.so` is in the Python path, e.g. by adjusting\n`PYTHONPATH` accordingly.\n\n#### Using Docker\n\nTo use Franky within Docker we provide a [Dockerfile](docker/run/Dockerfile) and\naccompanying [docker-compose](docker-compose.yml) file.\n\n```bash\ngit clone --recurse-submodules https://github.com/timschneider42/franky.git\ncd franky/\ndocker compose build franky-run\n```\n\nTo use another version of libfranka than the default (0.15.0) add a build argument:\n\n```bash\ndocker compose build franky-run --build-arg LIBFRANKA_VERSION=0.9.2\n```\n\nTo run the container:\n\n```bash\ndocker compose run franky-run bash\n```\n\nThe container requires access to the host machines network *and* elevated user rights to allow the docker user to set RT\ncapabilities of the processes run from within it.\n\n#### Building Franky with Docker\n\nFor building Franky and its wheels, we provide another Docker container that can also be launched using docker-compose:\n\n```bash\ndocker compose build franky-build\ndocker compose run --rm franky-build run-tests  # To run the tests\ndocker compose run --rm franky-build build-wheels  # To build wheels for all supported python versions\n```\n\n### Can I use CUDA jointly with Franky?\n\nYes. However, you need to set `IGNORE_PREEMPT_RT_PRESENCE=1` during the installation and all subsequent updates of the CUDA drivers on the real-time kernel.\n\nFirst, make sure that you have rebooted your system after installing the real-time kernel.\nThen, add `IGNORE_PREEMPT_RT_PRESENCE=1` to `/etc/environment`, call `export IGNORE_PREEMPT_RT_PRESENCE=1` to also set it in the current session and follow the instructions of Nvidia to install CUDA on your system.\n\nIf you are on Ubuntu, you can also use [this](tools/install_cuda_realtime.bash) script to install CUDA on your real-time system:\n```bash\n# Download the script\nwget https://raw.githubusercontent.com/timschneider42/franky/master/tools/install_cuda_realtime.bash\n\n# Inspect the script to ensure it does what you expect\n\n# Make it executable\nchmod +x install_cuda_realtime.bash\n\n# Execute the script\n./install_cuda_realtime.bash\n```\n\nAlternatively, if you are a cowboy and do not care about security, you can also use this one-liner to directly call the script without checking it:\n```bash\nbash \u003c(wget -qO- https://raw.githubusercontent.com/timschneider42/franky/master/tools/install_cuda_realtime.bash)\n```\n\n## 📚 Tutorial\n\nFranky comes with both a C++ and Python API that differ only regarding real-time capability.\nWe will introduce both languages next to each other.\nIn your C++ project, just include `include \u003cfranky.hpp\u003e` and link the library.\nFor Python, just `import franky`.\nAs a first example, only four lines of code are needed for simple robotic motions.\n\n```c++\n#include \u003cfranky.hpp\u003e\nusing namespace franky;\n\n// Connect to the robot with the FCI IP address\nRobot robot(\"172.16.0.2\");\n\n// Reduce velocity and acceleration of the robot\nrobot.setRelativeDynamicsFactor(0.05);\n\n// Move the end-effector 20cm in positive x-direction\nauto motion = std::make_shared\u003cCartesianMotion\u003e(RobotPose(Affine({0.2, 0.0, 0.0}), 0.0), ReferenceType::Relative);\n\n// Finally move the robot\nrobot.move(motion);\n```\n\nThe corresponding program in Python is\n\n```python\nfrom franky import Affine, CartesianMotion, Robot, ReferenceType\n\nrobot = Robot(\"172.16.0.2\")\nrobot.relative_dynamics_factor = 0.05\n\nmotion = CartesianMotion(Affine([0.2, 0.0, 0.0]), ReferenceType.Relative)\nrobot.move(motion)\n```\n\nBefore executing any code, make sure that you have enabled the Franka Control Interface (FCI) in the Franka UI web interface.\n\nFurthermore, we will introduce methods for geometric calculations, for moving the robot according to different motion\ntypes, how to implement real-time reactions and changing waypoints in real time as well as controlling the gripper.\n\n### 🧮 Geometry\n\n`franky.Affine` is a python wrapper for [Eigen::Affine3d](https://eigen.tuxfamily.org/dox/group__TutorialGeometry.html).\nIt is used for Cartesian poses, frames and transformation.\nfranky adds its own constructor, which takes a position and a quaternion as inputs:\n\n```python\nimport math\nfrom scipy.spatial.transform import Rotation\nfrom franky import Affine\n\nz_translation = Affine([0.0, 0.0, 0.5])\n\nquat = Rotation.from_euler(\"xyz\", [0, 0, math.pi / 2]).as_quat()\nz_rotation = Affine([0.0, 0.0, 0.0], quat)\n\ncombined_transformation = z_translation * z_rotation\n```\n\nIn all cases, distances are in [m] and rotations in [rad].\n\n### 🤖 Robot\n\nFranky exposes most of the libfanka API for Python.\nMoreover, we added methods to adapt the dynamics limits of the robot for all motions.\n\n```python\nfrom franky import *\n\nrobot = Robot(\"172.16.0.2\")\n\n# Recover from errors\nrobot.recover_from_errors()\n\n# Set velocity, acceleration and jerk to 5% of the maximum\nrobot.relative_dynamics_factor = 0.05\n\n# Alternatively, you can define each constraint individually\nrobot.relative_dynamics_factor = RelativeDynamicsFactor(velocity=0.1, acceleration=0.05, jerk=0.1)\n\n# Or, for more finegrained access, set individual limits\nrobot.translation_velocity_limit.set(3.0)\nrobot.rotation_velocity_limit.set(2.5)\nrobot.elbow_velocity_limit.set(2.62)\nrobot.translation_acceleration_limit.set(9.0)\nrobot.rotation_acceleration_limit.set(17.0)\nrobot.elbow_acceleration_limit.set(10.0)\nrobot.translation_jerk_limit.set(4500.0)\nrobot.rotation_jerk_limit.set(8500.0)\nrobot.elbow_jerk_limit.set(5000.0)\nrobot.joint_velocity_limit.set([2.62, 2.62, 2.62, 2.62, 5.26, 4.18, 5.26])\nrobot.joint_acceleration_limit.set([10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0])\nrobot.joint_jerk_limit.set([5000.0, 5000.0, 5000.0, 5000.0, 5000.0, 5000.0, 5000.0])\n# By default, these limits are set to their respective maxima (the values shown here)\n\n# Get the max of each limit (as provided by Franka) with the max function, e.g.:\nprint(robot.joint_jerk_limit.max)\n```\n\n#### Robot State\n\nThe robot state can be retrieved by accessing the following properties:\n\n* `state`: Object of type `franky.RobotState`, which extends the\n  libfranka [franka::RobotState](https://frankaemika.github.io/libfranka/structfranka_1_1RobotState.html) structure by\n  additional state elements.\n* `current_cartesian_state`: Object of type `franky.CartesianState`, which contains the end-effector pose and velocity\n  obtained\n  from [franka::RobotState::O_T_EE](https://frankaemika.github.io/libfranka/structfranka_1_1RobotState.html#a193781d47722b32925e0ea7ac415f442)\n  and [franka::RobotState::O_dP_EE_c](https://frankaemika.github.io/libfranka/structfranka_1_1RobotState.html#a4be112bd1a9a7d777a67aea4a18a8dcc).\n* `current_joint_state`: Object of type `franky.JointState`, which contains the joint positions and velocities\n  obtained\n  from [franka::RobotState::q](https://frankaemika.github.io/libfranka/structfranka_1_1RobotState.html#ade3335d1ac2f6c44741a916d565f7091)\n  and [franka::RobotState::dq](https://frankaemika.github.io/libfranka/structfranka_1_1RobotState.html#a706045af1b176049e9e56df755325bd2).\n\n```python\nfrom franky import *\n\nrobot = Robot(\"172.16.0.2\")\n\n# Get the current state as `franky.RobotState`. See the documentation for a list of fields.\nstate = robot.state\n\n# Get the robot's cartesian state\ncartesian_state = robot.current_cartesian_state\nrobot_pose = cartesian_state.pose  # Contains end-effector pose and elbow position\nee_pose = robot_pose.end_effector_pose\nelbow_pos = robot_pose.elbow_state\nrobot_velocity = cartesian_state.velocity  # Contains end-effector twist and elbow velocity\nee_twist = robot_velocity.end_effector_twist\nelbow_vel = robot_velocity.elbow_velocity\n\n# Get the robot's joint state\njoint_state = robot.current_joint_state\njoint_pos = joint_state.position\njoint_vel = joint_state.velocity\n\n# Use the robot model to compute kinematics\nq = [-0.3, 0.1, 0.3, -1.4, 0.1, 1.8, 0.7]\nf_t_ee = Affine()\nee_t_k = Affine()\nee_pose_kin = robot.model.pose(Frame.EndEffector, q, f_t_ee, ee_t_k)\n\n# Get the jacobian of the current robot state\njacobian = robot.model.body_jacobian(Frame.EndEffector, state)\n\n# Alternatively, just get the URDF as string and do the kinematics computation yourself (only for libfranka \u003e= 0.15.0)\nurdf_model = robot.model_urdf\n```\n\nFor a full list of state-related features, check\nthe [Robot](https://timschneider42.github.io/franky/classfranky_1_1_robot.html)\nand [Model](https://timschneider42.github.io/franky/classfranky_1_1_model.html) sections of the documentation.\n\n### \u003ca id=\"motion-types\" /\u003e 🏃‍♂️ Motion Types\n\nFranky currently supports four different impedance control modes: **joint position control**, **joint velocity control**, **cartesian position control**, and **cartesian velocity control**.\nEach of these control modes is invoked by passing the robot an appropriate _Motion_ object.\n\nIn the following, we provide a brief example for each motion type implemented by Franky in Python.\nThe C++ interface is generally analogous, though some variable and method names are different because we\nfollow [PEP 8](https://peps.python.org/pep-0008/) naming conventions in Python\nand [Google naming conventions](https://google.github.io/styleguide/cppguide.html) in C++.\n\nAll units are in $m$, $\\frac{m}{s}$, $\\textit{rad}$, or $\\frac{\\textit{rad}}{s}$.\n\n#### Joint Position Control\n\n```python\nfrom franky import *\n\n# A point-to-point motion in the joint space\nm_jp1 = JointMotion([-0.3, 0.1, 0.3, -1.4, 0.1, 1.8, 0.7])\n\n# A motion in joint space with multiple waypoints\nm_jp2 = JointWaypointMotion([\n    JointWaypoint([-0.3, 0.1, 0.3, -1.4, 0.1, 1.8, 0.7]),\n    JointWaypoint([0.0, 0.3, 0.3, -1.5, -0.2, 1.5, 0.8]),\n    JointWaypoint([0.1, 0.4, 0.3, -1.4, -0.3, 1.7, 0.9])\n])\n\n# Intermediate waypoints also permit to specify target velocities. The default target velocity is 0, meaning that the\n# robot will stop at every waypoint.\nm_jp3 = JointWaypointMotion([\n    JointWaypoint([-0.3, 0.1, 0.3, -1.4, 0.1, 1.8, 0.7]),\n    JointWaypoint(\n        JointState(\n            position=[0.0, 0.3, 0.3, -1.5, -0.2, 1.5, 0.8],\n            velocity=[0.1, 0.0, 0.0, 0.0, -0.0, 0.0, 0.0])),\n    JointWaypoint([0.1, 0.4, 0.3, -1.4, -0.3, 1.7, 0.9])\n])\n\n# Stop the robot in joint position control mode. The difference of JointStopMotion to other stop motions such as\n# CartesianStopMotion is that # JointStopMotion # stops the robot in joint position control mode while\n# CartesianStopMotion stops it in cartesian pose control mode. The difference becomes relevant when asynchronous move\n# commands are being sent or reactions are being used(see below).\nm_jp4 = JointStopMotion()\n```\n\n#### Joint Velocity Control\n\n```python\nfrom franky import *\n\n# Accelerate to the given joint velocity and hold it. After 1000ms stop the robot again.\nm_jv1 = JointVelocityMotion([0.1, 0.3, -0.1, 0.0, 0.1, -0.2, 0.4], duration=Duration(1000))\n\n# Joint velocity motions also support waypoints. Unlike in joint position control, a joint velocity waypoint is a\n# target velocity to be reached. This particular example first accelerates the joints, holds the velocity for 1s, then\n# reverses direction for 2s, reverses direction again for 1s, and finally stops. It is important not to forget to stop\n# the robot at the end of such a sequence, as it will otherwise throw an error.\nm_jv2 = JointVelocityWaypointMotion([\n    JointVelocityWaypoint([0.1, 0.3, -0.1, 0.0, 0.1, -0.2, 0.4], hold_target_duration=Duration(1000)),\n    JointVelocityWaypoint([-0.1, -0.3, 0.1, -0.0, -0.1, 0.2, -0.4], hold_target_duration=Duration(2000)),\n    JointVelocityWaypoint([0.1, 0.3, -0.1, 0.0, 0.1, -0.2, 0.4], hold_target_duration=Duration(1000)),\n    JointVelocityWaypoint([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),\n])\n\n# Stop the robot in joint velocity control mode.\nm_jv3 = JointVelocityStopMotion()\n```\n\n#### Cartesian Position Control\n\n```python\nimport math\nfrom scipy.spatial.transform import Rotation\nfrom franky import *\n\n# Move to the given target pose\nquat = Rotation.from_euler(\"xyz\", [0, 0, math.pi / 2]).as_quat()\nm_cp1 = CartesianMotion(Affine([0.4, -0.2, 0.3], quat))\n\n# With target elbow angle (otherwise, the Franka firmware will choose by itself)\nm_cp2 = CartesianMotion(RobotPose(Affine([0.4, -0.2, 0.3], quat), elbow_state=ElbowState(0.3)))\n\n# A linear motion in cartesian space relative to the initial position\n# (Note that this motion is relative both in position and orientation. Hence, when the robot's end-effector is oriented\n# differently, it will move in a different direction)\nm_cp3 = CartesianMotion(Affine([0.2, 0.0, 0.0]), ReferenceType.Relative)\n\n# Generalization of CartesianMotion that allows for multiple waypoints\nm_cp4 = CartesianWaypointMotion([\n    CartesianWaypoint(RobotPose(Affine([0.4, -0.2, 0.3], quat), elbow_state=ElbowState(0.3))),\n    # The following waypoint is relative to the prior one and 50% slower\n    CartesianWaypoint(Affine([0.2, 0.0, 0.0]), ReferenceType.Relative, RelativeDynamicsFactor(0.5, 1.0, 1.0))\n])\n\n# Cartesian waypoints also permit to specify target velocities\nm_cp5 = CartesianWaypointMotion([\n    CartesianWaypoint(Affine([0.5, -0.2, 0.3], quat)),\n    CartesianWaypoint(\n        CartesianState(\n            pose=Affine([0.4, -0.1, 0.3], quat),\n            velocity=Twist([-0.01, 0.01, 0.0]))),\n    CartesianWaypoint(Affine([0.3, 0.0, 0.3], quat))\n])\n\n# Stop the robot in cartesian position control mode.\nm_cp6 = CartesianStopMotion()\n```\n\n#### Cartesian Velocity Control\n\n```python\nfrom franky import *\n\n# A cartesian velocity motion with linear (first argument) and angular (second argument) components\nm_cv1 = CartesianVelocityMotion(Twist([0.2, -0.1, 0.1], [0.1, -0.1, 0.2]))\n\n# With target elbow velocity\nm_cv2 = CartesianVelocityMotion(RobotVelocity(Twist([0.2, -0.1, 0.1], [0.1, -0.1, 0.2]), elbow_velocity=-0.2))\n\n# Cartesian velocity motions also support multiple waypoints. Unlike in cartesian position control, a cartesian velocity\n# waypoint is a target velocity to be reached. This particular example first accelerates the end-effector, holds the\n# velocity for 1s, then # reverses direction for 2s, reverses direction again for 1s, and finally stops. It is important\n# not to forget to stop # the robot at the end of such a sequence, as it will otherwise throw an error.\nm_cv4 = CartesianVelocityWaypointMotion([\n    CartesianVelocityWaypoint(Twist([0.2, -0.1, 0.1], [0.1, -0.1, 0.2]), hold_target_duration=Duration(1000)),\n    CartesianVelocityWaypoint(Twist([-0.2, 0.1, -0.1], [-0.1, 0.1, -0.2]), hold_target_duration=Duration(2000)),\n    CartesianVelocityWaypoint(Twist([0.2, -0.1, 0.1], [0.1, -0.1, 0.2]), hold_target_duration=Duration(1000)),\n    CartesianVelocityWaypoint(Twist()),\n])\n\n# Stop the robot in cartesian velocity control mode.\nm_cv6 = CartesianVelocityStopMotion()\n```\n\n#### Relative Dynamics Factors\n\nEvery motion and waypoint type allows to adapt the dynamics (velocity, acceleration and jerk) by setting the respective\n`relative_dynamics_factor` parameter.\nThis parameter can also be set for the robot globally as shown below or in the `robot.move` command.\nCrucially, relative dynamics factors on different layers (robot, move command, and motion) do not override each other\nbut rather get multiplied.\nHence, a relative dynamics factor on a motion can only reduce the dynamics of the robot and never increase them.\n\nThere is one exception to this rule and that is if any layer sets the relative dynamics factor to\n`RelativeDynamicsFactor.MAX_DYNAMICS`.\nThis will cause the motion to be executed with maximum velocity, acceleration, and jerk limits, independently of the\nrelative dynamics factors of the other layers.\nThis feature should only be used to abruptly stop the robot in case of an unexpected environment contact as executing\nother motions with it is likely to lead to a discontinuity error and might be dangerous.\n\n#### Executing Motions\n\nThe real robot can be moved by applying a motion to the robot using `move`:\n\n```python\n# Before moving the robot, set an appropriate dynamics factor. We start small:\nrobot.relative_dynamics_factor = 0.05\n# or alternatively, to control the scaling of velocity, acceleration, and jerk limits separately:\nrobot.relative_dynamics_factor = RelativeDynamicsFactor(0.05, 0.1, 0.15)\n# If these values are set too high, you will see discontinuity errors\n\nrobot.move(m_jp1)\n\n# We can also set a relative dynamics factor in the move command. It will be multiplied with the other relative\n# dynamics factors (robot and motion if present).\nrobot.move(m_jp2, relative_dynamics_factor=0.8)\n```\n\n#### Motion Callbacks\n\nAll motions support callbacks, which will be invoked in every control step at 1kHz.\nCallbacks can be attached as follows:\n\n```python\ndef cb(\n        robot_state: RobotState,\n        time_step: Duration,\n        rel_time: Duration,\n        abs_time: Duration,\n        control_signal: JointPositions):\n    print(f\"At time {abs_time}, the target joint positions were {control_signal.q}\")\n\n\nm_jp1.register_callback(cb)\nrobot.move(m_jp1)\n```\n\nNote that in Python, these callbacks are not executed in the control thread since they would otherwise block it.\nInstead, they are put in a queue and executed by another thread.\nWhile this scheme ensures that the control thread can always run, it cannot prevent that the queue grows indefinitely\nwhen the callbacks take more time to execute than it takes for new callbacks to be queued.\nHence, callbacks might be executed significantly after they were queued if they take a long time to execute.\n\n### ⚡ Real-Time Reactions\n\nBy adding reactions to the motion data, the robot can react to unforeseen events.\nIn the Python API, you can define conditions by using a comparison between a robot's value and a given threshold.\nIf the threshold is exceeded, the reaction fires.\n\n```python\nfrom franky import CartesianMotion, Affine, ReferenceType, Measure, Reaction\n\nmotion = CartesianMotion(Affine([0.0, 0.0, 0.1]), ReferenceType.Relative)  # Move down 10cm\n\n# It is important that the reaction motion uses the same control mode as the original motion. Hence, we cannot register\n# a JointMotion as a reaction motion to a CartesianMotion.\nreaction_motion = CartesianMotion(Affine([0.0, 0.0, 0.01]), ReferenceType.Relative)  # Move up for 1cm\n\n# Trigger reaction if the Z force is greater than 30N\nreaction = Reaction(Measure.FORCE_Z \u003e 30.0, reaction_motion)\nmotion.add_reaction(reaction)\n\nrobot.move(motion)\n```\n\nPossible values to measure are\n\n* `Measure.FORCE_X,` `Measure.FORCE_Y,` `Measure.FORCE_Z`: Force in X, Y and Z direction\n* `Measure.REL_TIME`: Time in seconds since the current motion started\n* `Measure.ABS_TIME`: Time in seconds since the initial motion started\n\nThe difference between `Measure.REL_TIME` and `Measure.ABS_TIME` is that `Measure.REL_TIME` is reset to zero whenever a\nnew motion starts (either by calling `Robot.move` or as a result of a triggered `Reaction`).\n`Measure.ABS_TIME`, on the other hand, is only reset to zero when a motion terminates regularly without being\ninterrupted and the robot stops moving.\nHence, `Measure.ABS_TIME` measures the total time in which the robot has moved without interruption.\n\n`Measure` values support all classical arithmetic operations, like addition, subtraction, multiplication, division, and\nexponentiation (both as base and exponent).\n\n```python\nnormal_force = (Measure.FORCE_X ** 2 + Measure.FORCE_Y ** 2 + Measure.FORCE_Z ** 2) ** 0.5\n```\n\nWith arithmetic comparisons, conditions can be generated.\n\n```python\nnormal_force_within_bounds = normal_force \u003c 30.0\ntime_up = Measure.ABS_TIME \u003e 10.0\n```\n\nConditions support negation, conjunction (and), and disjunction (or):\n\n```python\nabort = ~normal_force_within_bounds | time_up\nfast_abort = ~normal_force_within_bounds | time_up\n```\n\nTo check whether a reaction has fired, a callback can be attached:\n\n```python\nfrom franky import RobotState\n\n\ndef reaction_callback(robot_state: RobotState, rel_time: float, abs_time: float):\n    print(f\"Reaction fired at {abs_time}.\")\n\n\nreaction.register_callback(reaction_callback)\n```\n\nSimilar to the motion callbacks, in Python, reaction callbacks are not executed in real-time but in a regular thread\nwith lower priority to ensure that the control thread does not get blocked.\nThus, the callbacks might fire substantially after the reaction has fired, depending on the time it takes to execute\nthem.\n\nIn C++ you can additionally use lambdas to define more complex behaviours:\n\n```c++\nauto motion = CartesianMotion(RobotPose(Affine({0.0, 0.0, 0.2}), 0.0), ReferenceType::Relative);\n\n// Stop motion if force is over 10N\nauto stop_motion = StopMotion\u003cfranka::CartesianPose\u003e()\n\nmotion\n  .addReaction(\n    Reaction(\n      Measure::ForceZ() \u003e 10.0,  // [N],\n      stop_motion))\n  .addReaction(\n    Reaction(\n      Condition(\n        [](const franka::RobotState\u0026 state, double rel_time, double abs_time) {\n          // Lambda condition\n          return state.current_errors.self_collision_avoidance_violation;\n        }),\n      [](const franka::RobotState\u0026 state, double rel_time, double abs_time) {\n        // Lambda reaction motion generator\n        // (we are just returning a stop motion, but there could be arbitrary\n        // logic here for generating reaction motions)\n        return StopMotion\u003cfranka::CartesianPose\u003e();\n      })\n    ));\n\nrobot.move(motion)\n```\n\n###  \u003ca id=\"real-time-motions\" /\u003e ⏱️ Real-Time Motions\n\nBy setting the `asynchronous` parameter of `Robot.move` to `True`, the function does not block until the motion\nfinishes.\nInstead, it returns immediately and, thus, allows the main thread to set new motions asynchronously.\n\n```python\nimport time\nfrom franky import Affine, CartesianMotion, Robot, ReferenceType\n\nrobot = Robot(\"172.16.0.2\")\nrobot.relative_dynamics_factor = 0.05\n\nmotion1 = CartesianMotion(Affine([0.2, 0.0, 0.0]), ReferenceType.Relative)\nrobot.move(motion1, asynchronous=True)\n\ntime.sleep(0.5)\n# Note that similar to reactions, when preempting active motions with new motions, the control mode cannot change.\n# Hence, we cannot use, e.g., a JointMotion here.\nmotion2 = CartesianMotion(Affine([0.2, 0.0, 0.0]), ReferenceType.Relative)\nrobot.move(motion2, asynchronous=True)\n```\n\nBy calling `Robot.join_motion` the main thread can be synchronized with the motion thread, as it will block until the\nrobot finishes its motion.\n\n```python\nrobot.join_motion()\n```\n\nNote that when exceptions occur during the asynchronous execution of a motion, they will not be thrown immediately.\nInstead, the control thread stores the exception and terminates.\nThe next time `Robot.join_motion` or `Robot.move` are called, they will throw the stored exception in the main thread.\nHence, after an asynchronous motion has finished, make sure to call `Robot.join_motion` to ensure being notified of any\nexceptions that occurred during the motion.\n\n### \u003ca id=\"gripper\" /\u003e 👌  Gripper\n\nIn the `franky::Gripper` class, the default gripper force and gripper speed can be set.\nThen, additionally to the libfranka commands, the following helper methods can be used:\n\n```c++\n#include \u003cfranky.hpp\u003e\n#include \u003cchrono\u003e\n#include \u003cfuture\u003e\n\nauto gripper = franky::Gripper(\"172.16.0.2\");\n\ndouble speed = 0.02; // [m/s]\ndouble force = 20.0; // [N]\n\n// Move the fingers to a specific width (5cm)\nbool success = gripper.move(0.05, speed);\n\n// Grasp an object of unknown width\nsuccess \u0026= gripper.grasp(0.0, speed, force, epsilon_outer=1.0);\n\n// Get the width of the grasped object\ndouble width = gripper.width();\n\n// Release the object\ngripper.open(speed);\n\n// There are also asynchronous versions of the methods\nstd::future\u003cbool\u003e success_future = gripper.moveAsync(0.05, speed);\n\n// Wait for 1s\nif (!success_future.wait_for(std::chrono::seconds(1)) == std::future_status::ready) {\n  // Get the result\n  std::cout \u003c\u003c \"Success: \" \u003c\u003c success_future.get() \u003c\u003c std::endl;\n} else {\n  gripper.stop();\n  success_future.wait();\n  std::cout \u003c\u003c \"Gripper motion timed out.\" \u003c\u003c std::endl;\n}\n```\n\nThe Python API follows the c++ API closely:\n\n```python\nimport franky\n\ngripper = franky.Gripper(\"172.16.0.2\")\n\nspeed = 0.02  # [m/s]\nforce = 20.0  # [N]\n\n# Move the fingers to a specific width (5cm)\nsuccess = gripper.move(0.05, speed)\n\n# Grasp an object of unknown width\nsuccess \u0026= gripper.grasp(0.0, speed, force, epsilon_outer=1.0)\n\n# Get the width of the grasped object\nwidth = gripper.width\n\n# Release the object\ngripper.open(speed)\n\n# There are also asynchronous versions of the methods\nsuccess_future = gripper.move_async(0.05, speed)\n\n# Wait for 1s\nif success_future.wait(1):\n    print(f\"Success: {success_future.get()}\")\nelse:\n    gripper.stop()\n    success_future.wait()\n    print(\"Gripper motion timed out.\")\n```\n\n### Accessing the Web Interface API\n\nFor Franka robots, control happens via the Franka Control Interface (FCI), which has to be enabled through the Franka UI in the robot's web interface.\nThe Franka UI also provides methods for locking and unlocking the brakes, setting the execution mode, and executing the safety self-test.\nHowever, sometimes you may want to access these methods programmatically, e.g. for automatically unlocking the brakes before starting a motion, or automatically executing the self-test after 24h of continuous execution.\n\nFor that reason, Franky provides a `RobotWebSession` class that allows you to access the web interface API of the robot.\nNote that directly accessing the web interface API is not officially supported and documented by Franka.\nHence, use this feature at your own risk.\n\nA typical automated workflow could look like this:\n\n```python\nimport franky\n\nwith franky.RobotWebSession(\"172.16.0.2\", \"username\", \"password\") as robot_web_session:\n    # First take control, in case some other web session is currently running\n    assert robot_web_session.take_control(), \"Control not granted\"\n\n    # Unlock the brakes\n    robot_web_session.unlock_brakes()\n\n    # Enable the FCI\n    robot_web_session.enable_fci()\n\n    # Create a franky.Robot instance and do whatever you want\n    ...\n\n    # Disable the FCI\n    robot_web_session.disable_fci()\n\n    # Lock brakes\n    robot_web_session.lock_brakes()\n```\n\nIn case you are running the robot for longer than 24h you will have noticed that you have to do a safety self-test every 24h.\n`RobotWebSession` allows to automate this task as well:\n\n```python\nimport time\nimport franky\n\nwith franky.RobotWebSession(\"172.16.0.2\", \"username\", \"password\") as robot_web_session:\n    # Execute self-test if the time until self-test is less than 5 minutes.\n    if robot_web_session.get_system_status()[\"safety\"][\"timeToTd2\"] \u003c 300:\n        robot_web_session.disable_fci()\n        robot_web_session.lock_brakes()\n        time.sleep(1.0)\n\n        robot_web_session.execute_self_test()\n\n        robot_web_session.unlock_brakes()\n        robot_web_session.enable_fci()\n        time.sleep(1.0)\n\n        # Recreate your franky.Robot instance as the FCI has been disabled and re-enabled\n        ...\n```\n\n`robot_web_session.get_system_status()` contains more information than just the time until self-test, such as the current execution mode, whether the brakes are locked, whether the FCI is enabled, and more.\n\nIf you want to call other API functions, you can use the `RobotWebSession.send_api_request` and `RobotWebSession.send_control_api_request` methods.\nSee [robot_web_session.py](franky/robot_web_session.py) for an example of how to use these methods.\n\n## 🛠️ Development\n\nFranky is currently tested against following versions\n\n- libfranka 0.7.1, 0.8.0, 0.9.2, 0.10.0, 0.11.0, 0.12.1, 0.13.3, 0.14.2, 0.15.0\n- Eigen 3.4.0\n- Pybind11 2.13.6\n- POCO 1.12.5p2\n- Pinocchio 3.4.0\n- Python 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13\n- Catch2 2.13.8 (for testing only)\n\n## 📜 License\n\nFor non-commercial applications, this software is licensed under the LGPL v3.0.\nIf you want to use Franky within commercial applications or under a different license, please contact us for individual\nagreements.\n\n## 🔍 Differences to frankx\n\nFranky started originally as a fork of [frankx](https://github.com/pantor/frankx), though both codebase and\nfunctionality differ substantially from frankx by now.\nAside of bug fixes and general performance improvements, Franky provides the following new features/improvements:\n\n* [Motions can be updated asynchronously.](#-real-time-motions)\n* [Reactions allow for the registration of callbacks instead of just printing to stdout when fired.](#-real-time-reactions)\n* [Motions allow for the registration of callbacks for profiling.](#motion-callbacks)\n* [The robot state is also available during control.](#robot-state)\n* A larger part of the libfranka API is exposed to python (e.g.,`setCollisionBehavior`, `setJoinImpedance`, and\n  `setCartesianImpedance`).\n* Cartesian motion generation handles boundaries in Euler angles properly.\n* [There is a new joint motion type that supports waypoints.](#-motion-types)\n* [The signature of `Affine` changed.](#-geometry) `Affine` does not handle elbow positions anymore.\n  Instead, a new class `RobotPose` stores both the end-effector pose and optionally the elbow position.\n* The `MotionData` class does not exist anymore.\n  Instead, reactions and other settings moved to `Motion`.\n* [The `Measure` class allows for arithmetic operations.](#-real-time-reactions)\n* Exceptions caused by libfranka are raised properly instead of being printed to stdout.\n* [We provide wheels for both Franka Research 3 and the older Franka Panda](#-setup)\n* Franky supports [joint velocity control](#joint-velocity-control)\n  and [cartesian velocity control](#cartesian-velocity-control)\n* The dynamics limits are not hard-coded anymore but can be [set for each robot instance](#-robot).\n\n## Contributing\n\nIf you wish to contribute to this project, you are welcome to create a pull request.\nPlease run the [pre-commit](https://pre-commit.com/) hooks before submitting your pull request.\nTo install the pre-commit hooks, run:\n\n1. [Install pre-commit](https://pre-commit.com/#install)\n2. Install the Git hooks by running `pre-commit install` or, alternatively, run `pre-commit run --all-files manually.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimschneider42%2Ffranky","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftimschneider42%2Ffranky","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimschneider42%2Ffranky/lists"}