franka_ros

Before continuing with this chapter, please install or compile franka_ros.

_images/ros-architecture.png

Schematic overview of the franka_ros packages.

The franka_ros metapackage integrates libfranka into ROS and ROS control. Here, we introduce its packages and we also give a short how-to for writing controllers.

All parameters passed to launch files in this section come with default values, so they can be omitted if using the default network addresses and ROS namespaces. Make sure the source command was called with the setup script from your workspace:

source /path/to/catkin_ws/devel/setup.sh

franka_description

This package contains the description of our robots and end effectors in terms of kinematics, joint limits, visual surfaces and collision space. The collision space is a simplified version of the visual description used to improve performance of collision checks. The descriptions are based on the URDF format according to the URDF XML documentation .

franka_gripper

This package implements the franka_gripper_node for interfacing a gripper from ROS. The node publishes the state of the gripper and offers the following actions servers:

  • franka_gripper::MoveAction(width, speed): moves to a target width with the defined speed.
  • franka_gripper::GraspAction(width, epsilon_inner, epsilon_outer, speed, force): tries to grasp at the desired width with a desired force while closing with the given speed. The operation is successful if the distance \(d\) between the gripper fingers is: \(\text{width} - \epsilon_\text{inner} < d < \text{width} + \epsilon_\text{outer}\).
  • franka_gripper::HomingAction(): homes the gripper and updates the maximum width given the mounted fingers.
  • franka_gripper::StopAction(): aborts a running action. This can be used to stop applying forces after grasping.
  • control_msgs::GripperCommandAction(width, max_effort): A standard gripper action recognized by MoveIt!.

You can launch the franka_gripper_node with:

roslaunch franka_gripper franka_gripper.launch robot_ip:=<fci-ip>

Hint

Starting with franka_ros 0.6.0, specifying load_gripper:=true for roslaunch franka_control franka_control.launch will start a franka_gripper_node as well.

franka_hw

This package contains the hardware abstraction of the robot for the ROS control framework based on the libfranka API. The hardware class franka_hw::FrankaHW is implemented in this package offering the following interfaces to controllers:

Interface Function
hardware_interface::JointStateInterface Reads joint states.
hardware_interface::VelocityJointInterface Commands joint velocities and reads joint states.
hardware_interface::PositionJointInterface Commands joint positions and reads joint states.
hardware_interface::EffortJointInterface Commands joint-level torques and reads joint states.
franka_hw::FrankaStateInterface Reads the full robot state.
franka_hw::FrankaPoseCartesianInterface Commands Cartesian poses and reads the full robot state.
franka_hw::FrankaVelocityCartesianInterface Commands Cartesian velocities and reads the full robot state.
franka_hw::FrankaModelInterface Reads the dynamic and kinematic model of the robot.

To use ROS control interfaces, you have to retrieve resource handles by name:

Interface Resource handle name
hardware_interface::JointStateInterface “<arm_id>_joint1” to “<arm_id>_joint7”
hardware_interface::VelocityJointInterface “<arm_id>_joint1” to “<arm_id>_joint7”
hardware_interface::PositionJointInterface “<arm_id>_joint1” to “<arm_id>_joint7”
hardware_interface::EffortJointInterface “<arm_id>_joint1” to “<arm_id>_joint7”
franka_hw::FrankaStateInterface “<arm_id>_robot”
franka_hw::FrankaPoseCartesianInterface “<arm_id>_robot”
franka_hw::FrankaVelocityCartesianInterface “<arm_id>_robot”
franka_hw::FrankaModelInterface “<arm_id>_robot”

Hint

By default, <arm_id> is set to “panda”.

The franka_hw::FrankaHW class also implements the starting, stopping and switching of controllers.

franka_control

franka_control provides a variety of ROS services to expose the full libfranka API in the ROS ecosystem. The following services are provided:

  • franka_control::SetJointImpedance specifies joint stiffness for the internal controller (damping is automatically derived from the stiffness).
  • franka_control::SetCartesianImpedance specifies Cartesian stiffness for the internal controller (damping is automatically derived from the stiffness).
  • franka_control::SetEEFrame specifies the transformation from <arm_id>_EE to <arm_id>_link8 frame.
  • franka_control::SetKFrame specifies the transformation from <arm_id>_K to <arm_id>_EE frame.
  • franka_control::SetForceTorqueCollisionBehavior sets thresholds for external Cartesian wrenches to configure the collision reflex.
  • franka_control::SetFullCollisionBehavior sets thresholds for external forces on Cartesian and joint level to configure the collision reflex.
  • franka_control::SetLoad sets an external load to compensate (e.g. of a grasped object).

Important

The <arm_id>_EE frame is a child of the <arm_id>_link8 frame and denotes the configurable end effector frame. The <arm_id>_K frame is a child frame of <arm_id>_EE and marks the center of the internal Cartesian impedance. It also serves as a reference frame for external wrenches. Neither the <arm_id>_EE nor the <arm_id>_K are contained in the URDF as they can be changed at run time. By default, <arm_id> is set to “panda”.

To recover from errors and reflexes the franka_control::ErrorRecoveryAction can be called. That can be done from an action client or by simply publishing on the action goal topic:

rostopic pub -1 /franka_control/error_recovery/goal franka_control/ErrorRecoveryActionGoal "{}"

After recovery, the franka_control_node restarts the controllers that were running. That is possible as the node does not die when robot reflexes are triggered or errors are occurred. All of these functionalities are provided by the franka_control_node which can be launched with the following command:

roslaunch franka_control franka_control.launch robot_ip:=<fci-ip> load_gripper:=<true|false>

Besides loading the franka_control_node, the launch file also starts a franka_control::FrankaStateController for reading and publishing the robot states, including external wrenches, configurable transforms and the joint states required for visualization with rivz. For visualization purposes, a robot_state_publisher is started together with RViz.

franka_visualization

This package contains publishers that connect to a robot and publish the robot and gripper joint states for visualization in RViz. To run this package launch:

roslaunch franka_visualization franka_visualization.launch robot_ip:=<fci-ip> \
  load_gripper:=<true|false>

This is purely for visualization - no commands are sent to the robot. It can be useful to check the connection to the robot.

Important

Only one instance of a franka::Robot can connect to the robot. This means, that for example the franka_joint_state_publisher cannot run in parallel to the franka_control_node. This also implies that you cannot execute the visualization example alongside a separate program running a controller.

franka_example_controllers

In this package a set of example controllers for controlling the robot via ROS are implemented. The controllers depict the variety of interfaces offered by the franka_hw::FrankaHW class and the according usage. Each example comes with a separate stand-alone launch file that starts the controller on the robot and visualizes it.

To launch the joint impedance example, execute the following command:

roslaunch franka_example_controllers joint_impedance_example_controller.launch \
  robot_ip:=<fci-ip> load_gripper:=<true|false>

Other examples are started in the same way.

panda_moveit_config

This package contains partly auto generated files that provide an out-of-the-box MoveIt! configuration for Panda Arm and Hand.

To control the robot with MoveIt! and RViz launch the following file:

roslaunch panda_moveit_config panda_control_moveit_rviz.launch load_gripper:=<true|false> \
robot_ip:=<fci-ip>

For more details, documentation and tutorials, please have a look at the MoveIt! tutorials website.

Writing your own controller

All example controllers from the example controllers package are derived from the controller_interface::MultiInterfaceController class, which allows to claim up to four interfaces in one controller instance. The declaration of your class then looks like:

class NameOfYourControllerClass : controller_interface::MultiInterfaceController <
                              my_mandatory_first_interface,
                              my_possible_second_interface,
                              my_possible_third_interface,
                              my_possible_fourth_interface> {
  bool init (hardware_interface::RobotHW* hw, ros::NodeHandle& nh);  // mandatory
  void update (const ros::Time& time, const ros::Duration& period);  // mandatory
  void starting (const ros::Time& time)   // optional
  void stopping (const ros::Time& time);  // optional
  ...
}

The available interfaces are described in Section franka_hw.

Important

Note that the claimable combinations of commanding interfaces are restricted as it does not make sense to e.g. command joint positions and Cartesian poses simultaneously. Read-only interfaces like the JointStateInterface, the FrankaStateInterface or the FrankaModelInterface can always be claimed and are not subject to restrictions.

Possible claims are:

  • all possible single interface claims
  • EffortJointInterface + PositionJointInterface
  • EffortJointInterface + VelocityJointInterface
  • EffortJointInterface + FrankaCartesianPoseInterface
  • EffortJointInterface + FrankaCartesianVelocityInterface

The idea behind offering the EffortJointInterface in combination with a motion generator interface is to expose the internal motion generators to the user. The calculated desired joint pose corresponding to a motion generator command is available in the robot state one time step later. One use case for this combination would be following a Cartesian trajectory using your own joint-level torque controller. In this case you would claim the combination EffortJointInterface + FrankaCartesianPoseInterface, stream your trajectory into the FrankaCartesianPoseInterface, and compute your joint-level torque commands based on the resulting desired joint pose (q_d) from the robot state. This allows to use the robot’s built-in inverse kinematics instead of having to solve it on your own.

To implement a fully functional controller you have to implement at least the inherited virtual functions init and update. Initializing - e.g. start poses - should be done in the starting function as starting is called when restarting the controller, while init is called only once when loading the controller. The stopping method should contain shutdown related functionality (if needed).

Important

Always command a gentle slowdown before shutting down the controller. When using velocity interfaces, do not simply command zero velocity in stopping. Since it might be called while the robot is still moving, it would be equivalent to commanding a jump in velocity leading to very high resulting joint-level torques. In this case it would be better to keep the same velocity and stop the controller than sending zeros and let the robot handle the slowdown.

Your controller class must be exported correctly with pluginlib which requires adding:

#include <pluginlib/class_list_macros.h>
// Implementation ..
PLUGINLIB_EXPORT_CLASS(name_of_your_controller_package::NameOfYourControllerClass,
                       controller_interface::ControllerBase)

at the end of the .cpp file. In addition you need to define a plugin.xml file with the following content:

<library path="lib/lib<name_of_your_controller_library>">
  <class name="name_of_your_controller_package/NameOfYourControllerClass"
         type="name_of_your_controller_package::NameOfYourControllerClass"
         base_class_type="controller_interface::ControllerBase">
    <description>
      Some text to describe what your controller is doing
    </description>
  </class>
</library>

which is exported by adding:

<export>
  <controller_interface plugin="${prefix}/plugin.xml"/>
</export>

to your package.xml. Further, you need to load at least a controller name in combination with a controller type to the ROS parameter server. Additionally, you can include other parameters you need. An exemplary configuration.yaml file can look like:

your_custom_controller_name:
  type: name_of_your_controller_package/NameOfYourControllerClass
  additional_example_parameter: 0.0
  # ..

Now you can start your controller using the controller_spawner node from ROS control or via the service calls offered by the hardware_manager. Just make sure that both the controller_spawner and the franka_control_node run in the same namespace. For more details have a look at the controllers from the franka_example_controllers package or the ROS control tutorials.