{"id":16680953,"url":"https://github.com/marcbone/liborl","last_synced_at":"2025-04-09T22:40:52.166Z","repository":{"id":89009560,"uuid":"318210331","full_name":"marcbone/liborl","owner":"marcbone","description":"Cartesian motion generation for Franka Emika Panda robots","archived":false,"fork":false,"pushed_at":"2023-07-27T06:54:10.000Z","size":90,"stargazers_count":46,"open_issues_count":3,"forks_count":11,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-24T00:38:07.957Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"eupl-1.2","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marcbone.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}},"created_at":"2020-12-03T13:56:22.000Z","updated_at":"2025-03-06T09:34:55.000Z","dependencies_parsed_at":"2025-02-21T07:30:54.761Z","dependency_job_id":null,"html_url":"https://github.com/marcbone/liborl","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcbone%2Fliborl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcbone%2Fliborl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcbone%2Fliborl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcbone%2Fliborl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marcbone","download_url":"https://codeload.github.com/marcbone/liborl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248124848,"owners_count":21051757,"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":[],"created_at":"2024-10-12T13:44:30.639Z","updated_at":"2025-04-09T22:40:52.156Z","avatar_url":"https://github.com/marcbone.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LibORL\n\nLibORL is a high level motion planning library for [Franka Emika](https://www.franka.de/) Panda robots. It is a wrapper around [libfranka](https://github.com/frankaemika/libfranka) which provides\nsimple to use motion functionality in cartesian space.\n\nLibORL was created during the Open-Robotics-Lab (therefore the name ORL) at the  [Institute for Robotics and Process Control (IRP)](https://www.rob.cs.tu-bs.de/)\nat TU Braunschweig. The name is probably not the best but \"orl\" makes a nice C++ namespace :)\n\n## Requirements\n * [libfranka](https://frankaemika.github.io/docs/installation_linux.html)\n * Eigen3\n \n## Installation\n\n```bash\nmkdir build\ncd build\ncmake .. -DCMAKE_BUILD_TYPE=Release\ncmake --build .\nsudo make install\n```\nafter that you can use it in your own CMake project like this:\n```cmake\nfind_package(orl REQUIRED)\nadd_executable(your_target main.cpp)\ntarget_link_libraries(your_target orl::orl)\n```\n\n## Generate Documentation\n```bash\ncd doxygen\ndoxygen Doxyfile\nfirefox html/index.html\n```\n## Features\n LibORL is a library with which you can easily generate cartesian motions\n  and execute them on the robot.\n  \n  These are the key features of this library:\n  * Easy trajectory generation in cartesian space + optional elbow control\n  * Impedance mode which can follow an moving attractor Pose\n  * Different kinds of speed profiles\n  * Trajectory generation using B-Splines, Bezier-Curves or circles\n  * Abort movements when there is too much force applied on the end-effector\n  * Move to specific joint configurations\n  * Usage of the Gripper\n  \n  The basic workflow is like this;\n  * Define a PoseGenerator (a function which gets a progress of the movement, the initial pose of the robot and the current robot state and returns a pose)\n  * Apply a SpeedProfile (you can use a QuinticPolynomial a Cosine function or an S-Curve Speed Profile)\n  * (optional) create a StopCondition. For example you can say that the robot abort the motion if there is an external force pushing the robot. Be careful with this when you open the gripper afterwards! Look at the docs.\n  * Let the robot execute the PoseGenerator by providing a duration for the motion.\n  \n## Showcase\n[![Bartender Robots](https://img.youtube.com/vi/POVh1aRzogc/0.jpg)](https://www.youtube.com/watch?v=POVh1aRzogc)\n\nLibORL was used in a beer pouring task to execute the motions. You can find out more about this project in this [blog post](https://pouya-moh.com/bartender-robots).\n## Intro\nConsider the following example which moves the robot to the position (0.5,0,0.3) within 2 seconds\n```cpp\nusing namespace orl;\nconst double execution_time = 2.0;\nRobot franka(\"franka\"); // IP-Address or hostname of the robot\nauto pose_generator = PoseGenerators::AbsoluteMotion({0.5,0,0.3});\napply_speed_profile(pose_generator, SpeedProfiles::QuinticPolynomialProfile());\nfranka.move_cartesian(pose_generator, execution_time);\n``` \na simplified version of this is \n```cpp\nusing namespace orl;\nconst double execution_time = 2.0;\nRobot franka(\"franka\"); // IP-Address or hostname of the robot\nfranka.absolute_cart_motion({0.5,0,0.3}, execution_time); \n ``` \n\nOk but what about Orientations? For that you have to define a Pose. There are multiple ways. One way is to set a Position\nand an Orientation. We initialize the orientation with roll, pitch, yaw values\n```cpp\nusing namespace orl;\nconst double execution_time = 2.0;\nRobot franka(\"franka\"); // IP-Address or hostname of the robot\nPose goal_pose({0,2,3}, {-M_PI,0,0});\nauto pose_generator = PoseGenerators::MoveToPose(goal_pose);\napply_speed_profile(pose_generator, SpeedProfiles::QuinticPolynomialProfile());\nfranka.move_cartesian(pose_generator, execution_time);  \n``` \nor simplified\n```cpp\nusing namespace orl;\nconst double execution_time = 2.0;\nRobot franka(\"franka\"); // IP-Address or hostname of the robot\nPose goal_pose({0,2,3}, {-M_PI,0,0});\nfranka.move_cartesian(goal_pose, execution_time);\n\n``` \n\nBut can we calculate Position and Orientation separately?\n\nYes. you can compose a PoseGenerator by combining a PositionGenerator and an OrientationGenerator. So lets define an OrientationGenerator which does not change the orientation at all:\n```cpp\nOrientationGenerator constant_orientation_generator = [=](const PoseGeneratorInput \u0026input) -\u003e Eigen::Quaterniond {\n        return input.initial_pose.quaternion();\n    };\n``` \nWe can see that OrientationGenerators are just pure functions, just like Like Position- and PoseGenerators.\nSo our OrientationGenerator takes the PoseGeneratorInput and returns the Quaternion of the initial pose.\n\n\nLet us also define a PositionGenerator which does nothing:\n```cpp\nPositionGenerator constant_position_generator = [=](const PoseGeneratorInput \u0026input) -\u003e Eigen::Vector3d {\n        return input.initial_pose.getPosition();\n    };\n``` \nWhen you calculate a PositionGenerator you should try to provide equidistant points. That means if you take the distance\nfrom the points that you get by taking inputs like [0,0.01,0.02,...,0.99,1] that all output points should have the same distance from their neighbors.\nSadly, this is not always true in our library as the Bezier and B-Spline Motions do not have this property and that means that some parts of the movement are executed faster than others.\n\nSo now that we have Position and OrientationGenerators we can generate our PoseGenerator and directly apply a SpeedProfile\n\n```cpp\nPoseGenerator constant_pose_generator = generate_pose_generator(constant_position_generator,constant_orientation_generator,SpeedProfiles::QuinticPolynomialProfile())\n``` \nafter that we can send our PoseGenerator to the robot and give him 5 seconds to execute the PoseGenerator\n```cpp\nfranka.move_cartesian(constant_pose_generator,5)\n``` \n\nOf course the robot does nothing. But there are a set of Position-/OrientationGenerators which you can use. Look into the PoseGenerator.h\n\nThere are also a set of PoseGenerators in orl::PoseGenerators namespace. Lets look at a simple one:\n\n```cpp\n PoseGenerator RelativeMotion(const Position \u0026translation, Frame frame = Frame::RobotBase,\n                                      const boost::optional\u003cOrientationGenerator\u003e \u0026maybe_orientation_generator = boost::none);\n``` \nThis PoseGenerator describes a Relative Movement. We already used this one in the first example. But what about the other Parameters?\nWe have 3 different Frames. The most important one is the RobotBase Frame which describes a the Pose of the End-Effector with respect to the RobotBase.\nThere is also the UnitFrame which describes the motion with respect to a zero translation and rotation. As the RelativeMotion only changes the position you can give it a custom\nOrientationGenerator. If you do not provide an OrientationGenerator the orientation will stay the same.\n\nLets define a pose_generator which moves the from the current position of the robot 50 cm in x direction\n```cpp\nPoseGenerator pose_generator_robot = PoseGenerators::RelativeMotion({0.5,0,0}, Frame::RobotBase);\n``` \nAnd now we define one which moves from the Unit-Frame to 0.3 meter in z-direction\n```cpp\nPoseGenerator pose_generator_unit = PoseGenerators::RelativeMotion({0,0,0.3}, Frame::UnitFrame);\n``` \nYou can think of each Pose as homogenous matrix and you can multiply them. But we can do the same for PoseGenerators:\n```cpp\nPoseGenerator pose_generator_combined = pose_generator_unit * pose_generator_robot;\n``` \nSo we can treat PoseGenerators like Matrices. For example we can create a Screw motion by using by combining a\na Relative Motion with a ScrewMotion like in the pose_generators_example.cpp:\n```cpp\nPosition circle_center = Position(0.45, -0.1, 0.39);\nauto no_rotation = generate_constant_orientation_orientation_generator();\nauto circle = PoseGenerators::CirclePoseGenerator(circle_center, -M_PI * 2, Plane::YZ, no_rotation);\nauto move_horizontal = PoseGenerators::RelativeMotion(Position(0.2, 0, 0.), Frame::UnitFrame);\nauto pose_generator = move_horizontal * circle;\napply_speed_profile(pose_generator);\nrobot.move_cartesian(pose_generator, 5);\n``` \n![PoseGenerators](https://s8.gifyu.com/images/screw.gif)\n\n## PoseGenerators\n\nThe following PoseGenerators are already available:\n * CirclePoseGenerator()\n * RelativeMotion()\n * AbsoluteMotion()\n * BezierMotion()\n * BSplineMotion()\n \n But you can also define your own. There are also multiple Position- and OrientationGenerators predefined.\n\n## SpeedProfiles\nThe following SpeedProfiles are already available:\n * CosineProfile()\n * QuinticPolynomialProfile()\n * LinearProfile() \u003c- does nothing\n * SCurveProfile()\n \n Note that the S-Curve Profile is the fastest profile. However you can get joint_{velocity,acceleration}_discontinuities \n when the robot is not able to achieve your desired profile in joint space. In this case you have to slow down your S-Curve Profile.\n But you can also define your own SpeedProfile\n## StopConditions\nStopConditions can be used to abort a movement. It is a function which takes a PoseGenerator input and returns true if and only if\nthe motion should be aborted. See the fore_stop_example.cpp\n \n There is only one implemented StopCondition which is triggered by Force. But feel free to define your own.\n You can specify a force threshold and a minimum amount of time in percent\nwhen the StopCondition can be activated. It is important to realize that if you specify a StopCondition the program will\nnot crash due to a cartesian reflex error. It will catch this Exception and will continue. So be careful if you want open the gripper\nafter one. Also note that the measured Force on the end-effector is not always zero and it can be hard to tune the Force\nvalue so that it will not be triggered immediately.\n\n![StopCondition](https://s8.gifyu.com/images/stopconditiond18cb4d431ea96ce.gif)\n## Impedance Mode\nThere is also an impedance Mode where you can set the attractor point with a PoseGenerator. See the impedance_example.cpp\n\n## Licence\nThis library is copyrighted © 2020 Marco Boneberger, Maximilian von Unwerth, Pouya Mohammadi\n\n\nLicensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent versions of the EUPL (the \"Licence\");\n\nYou may not use this work except in compliance with the Licence.\nYou may obtain a copy of the Licence at:\n\n[https://joinup.ec.europa.eu/software/page/eupl](https://joinup.ec.europa.eu/software/page/eupl)\n \nUnless required by applicable law or agreed to in writing, software distributed under the Licence is distributed on an \"AS IS\" basis\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\nSee the Licence for the specific language governing permissions and limitations under the Licence.\n\nThis software includes third party source code from [libfranka](https://github.com/frankaemika/libfranka)\nin the folder [3rdparty/libfranka](3rdparty/libfranka) which has its own license. Please see [3rdparty/libfranka/README.md](3rdparty/libfranka/README.md) and\n[3rdparty/libfranka/LICENSE-APACHE](3rdparty/libfranka/LICENSE-APACHE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcbone%2Fliborl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarcbone%2Fliborl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcbone%2Fliborl/lists"}