diff --git a/CHANGELOG.md b/CHANGELOG.md
index 60226b8fdbe74903013fd85262df381ca3dd7692..b2c4a7f4609f47bf1813dadb61b6c0e1b2e1b587 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,10 +2,11 @@
 
 ## 0.2.0 - UNRELEASED
 
-  * Specified run-time dependencies on `franka_description`
+  * Added missing run-time dependencies to `franka_description` and `franka_control`
   * Added epsilon to grasp action
   * Added `m_ee`, `F_x_Cee`, `I_ee`, `m_total`, `F_x_Ctotal` and `I_total`
     to the robot state
+  * Use O_T_EE_d in examples
 
 ## 0.1.2 - 2017-10-10
 
diff --git a/franka_control/package.xml b/franka_control/package.xml
index a49e49e7036eef78e83738316f18a4f09b734012..02dfbe8f8dff6b4f0e0e316c707cce6b665dc25c 100644
--- a/franka_control/package.xml
+++ b/franka_control/package.xml
@@ -31,7 +31,9 @@
   <depend>tf</depend>
 
   <exec_depend>franka_description</exec_depend>
+  <exec_depend>joint_state_publisher</exec_depend>
   <exec_depend>message_runtime</exec_depend>
+  <exec_depend>robot_state_publisher</exec_depend>
 
   <export>
     <controller_interface plugin="${prefix}/franka_controller_plugins.xml"/>
diff --git a/franka_example_controllers/CMakeLists.txt b/franka_example_controllers/CMakeLists.txt
index 2bb94b410447d66e66e444322461e074050d33a7..a09f668201808ebb883174639db2f8c38ea8ddce 100644
--- a/franka_example_controllers/CMakeLists.txt
+++ b/franka_example_controllers/CMakeLists.txt
@@ -1,6 +1,8 @@
 cmake_minimum_required(VERSION 2.8.3)
 project(franka_example_controllers)
 
+set(CMAKE_BUILD_TYPE Release)
+
 set(CMAKE_CXX_STANDARD 14)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
@@ -11,6 +13,7 @@ find_package(catkin REQUIRED COMPONENTS
   realtime_tools
   roscpp
   message_generation
+  dynamic_reconfigure
 )
 
 add_message_files(FILES
@@ -19,6 +22,11 @@ add_message_files(FILES
 
 generate_messages()
 
+generate_dynamic_reconfigure_options(
+  cfg/compliance_param.cfg
+  cfg/desired_mass_param.cfg
+)
+
 catkin_package(
   INCLUDE_DIRS include
   LIBRARIES franka_example_controllers
@@ -34,12 +42,16 @@ add_library(franka_example_controllers
   src/joint_velocity_example_controller.cpp
   src/model_example_controller.cpp
   src/joint_impedance_example_controller.cpp
+  src/cartesian_impedance_example_controller.cpp
+  src/force_example_controller.cpp
 )
 
 add_dependencies(franka_example_controllers
   ${${PROJECT_NAME}_EXPORTED_TARGETS}
   ${catkin_EXPORTED_TARGETS}
   ${PROJECT_NAME}_generate_messages_cpp
+  ${PROJECT_NAME}_gencpp
+  ${PROJECT_NAME}_gencfg
 )
 
 target_link_libraries(franka_example_controllers ${catkin_LIBRARIES})
diff --git a/franka_example_controllers/cfg/compliance_param.cfg b/franka_example_controllers/cfg/compliance_param.cfg
new file mode 100755
index 0000000000000000000000000000000000000000..c5a448923cb8dd3bbd19eb5d53c8bf9166a3f567
--- /dev/null
+++ b/franka_example_controllers/cfg/compliance_param.cfg
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+PACKAGE = "franka_example_controllers"
+
+from dynamic_reconfigure.parameter_generator_catkin import *
+
+gen = ParameterGenerator()
+
+gen.add("translational_stiffness", double_t, 0, "Cartesian translational stiffness", 200, 0, 400)
+gen.add("rotational_stiffness", double_t, 0, "Cartesian rotational stiffness", 20, 0, 100)
+gen.add("nullspace_stiffness", double_t, 0, "Stiffness of the joint space nullspace controller (the desired configuration is the one at startup)", 0, 0, 100)
+
+exit(gen.generate(PACKAGE, "dynamic_compliance", "compliance_param"))
diff --git a/franka_example_controllers/cfg/desired_mass_param.cfg b/franka_example_controllers/cfg/desired_mass_param.cfg
new file mode 100755
index 0000000000000000000000000000000000000000..15fc30ea764e7838cfac4fe54da9ed7697b5abed
--- /dev/null
+++ b/franka_example_controllers/cfg/desired_mass_param.cfg
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+PACKAGE = "franka_example_controllers"
+
+from dynamic_reconfigure.parameter_generator_catkin import *
+
+gen = ParameterGenerator()
+
+gen.add("desired_mass", double_t, 0, "desired mass for rendered force due to gravity applied in the z axis", 0.0,  0.0, 5.0)
+gen.add("k_p", double_t, 0, "force P gain", 0.0,  0.0, 20.0)
+gen.add("k_i", double_t, 0, "force I gain", 0.0,  0.0, 20.0)
+
+exit(gen.generate(PACKAGE, "dynamic_mass", "desired_mass_param"))
diff --git a/franka_example_controllers/config/franka_example_controllers.yaml b/franka_example_controllers/config/franka_example_controllers.yaml
index 699fea415dc3e22d21c6cb908866a1cf75b2b603..43f97246de922f0adcdafbca2e4a27768dad9247 100644
--- a/franka_example_controllers/config/franka_example_controllers.yaml
+++ b/franka_example_controllers/config/franka_example_controllers.yaml
@@ -14,6 +14,18 @@ model_example_controller:
     type: franka_example_controllers/ModelExampleController
     arm_id: panda
 
+force_example_controller:
+    type: franka_example_controllers/ForceExampleController
+    arm_id: panda
+    joint_names:
+        - panda_joint1
+        - panda_joint2
+        - panda_joint3
+        - panda_joint4
+        - panda_joint5
+        - panda_joint6
+        - panda_joint7
+
 joint_impedance_example_controller:
     type: franka_example_controllers/JointImpedanceExampleController
     arm_id: panda
@@ -46,3 +58,15 @@ joint_impedance_example_controller:
     vel_max: 0.15
     publish_rate: 10.0    
     coriolis_factor: 1.0
+
+cartesian_impedance_example_controller:
+    type: franka_example_controllers/CartesianImpedanceExampleController
+    arm_id: panda
+    joint_names:
+        - panda_joint1
+        - panda_joint2
+        - panda_joint3
+        - panda_joint4
+        - panda_joint5
+        - panda_joint6
+        - panda_joint7
diff --git a/franka_example_controllers/franka_example_controllers_plugin.xml b/franka_example_controllers/franka_example_controllers_plugin.xml
index 99277291cf9da9d36564d14a4b573fdd3b2c62cf..25382fe23b836af4546cd279e9441fe49e48aaf8 100644
--- a/franka_example_controllers/franka_example_controllers_plugin.xml
+++ b/franka_example_controllers/franka_example_controllers_plugin.xml
@@ -24,9 +24,20 @@
       A controller that evaluates and prints the dynamic model of Franka
     </description>
   </class>
-    <class name="franka_example_controllers/JointImpedanceExampleController" type="franka_example_controllers::JointImpedanceExampleController" base_class_type="controller_interface::ControllerBase">
+  <class name="franka_example_controllers/JointImpedanceExampleController" type="franka_example_controllers::JointImpedanceExampleController" base_class_type="controller_interface::ControllerBase">
     <description>
       A controller that tracks a cartesian path with a joint impedance controller that compensates coriolis torques. The torque commands are compared to measured torques in Console outputs.
     </description>
   </class>
+  <class name="franka_example_controllers/CartesianImpedanceExampleController" type="franka_example_controllers::CartesianImpedanceExampleController" base_class_type="controller_interface::ControllerBase">
+    <description>
+      A controller that renders a spring damper system in cartesian space. Compliance parameters and the equilibrium pose can be modified online with dynamic reconfigure and an interactive marker respectively. 
+    </description>
+  </class>
+  <class name="franka_example_controllers/ForceExampleController" type="franka_example_controllers::ForceExampleController" base_class_type="controller_interface::ControllerBase">
+    <description>
+      A PI controller that applies a force corresponding to a user-provided desired mass in the z axis. The desired mass value can be modified online with dynamic reconfigure. 
+    </description>
+  </class>
+
 </library>
diff --git a/franka_example_controllers/include/franka_example_controllers/cartesian_impedance_example_controller.h b/franka_example_controllers/include/franka_example_controllers/cartesian_impedance_example_controller.h
new file mode 100644
index 0000000000000000000000000000000000000000..06ae8f69eea3364bd7d08bbcc9977c56449dc1f2
--- /dev/null
+++ b/franka_example_controllers/include/franka_example_controllers/cartesian_impedance_example_controller.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2017 Franka Emika GmbH
+// Use of this source code is governed by the Apache-2.0 license, see LICENSE
+#pragma once
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <controller_interface/multi_interface_controller.h>
+#include <dynamic_reconfigure/server.h>
+#include <geometry_msgs/PoseStamped.h>
+#include <hardware_interface/joint_command_interface.h>
+#include <hardware_interface/robot_hw.h>
+#include <ros/node_handle.h>
+#include <ros/time.h>
+#include <boost/scoped_ptr.hpp>
+#include <eigen3/Eigen/Dense>
+
+#include <franka_example_controllers/compliance_paramConfig.h>
+#include <franka_hw/franka_model_interface.h>
+#include <franka_hw/franka_state_interface.h>
+
+namespace franka_example_controllers {
+
+class CartesianImpedanceExampleController : public controller_interface::MultiInterfaceController<
+                                                franka_hw::FrankaModelInterface,
+                                                hardware_interface::EffortJointInterface,
+                                                franka_hw::FrankaStateInterface> {
+ public:
+  CartesianImpedanceExampleController();
+  bool init(hardware_interface::RobotHW* robot_hw, ros::NodeHandle& node_handle);
+  void starting(const ros::Time&);
+  void update(const ros::Time&, const ros::Duration& period);
+
+ private:
+  std::string arm_id_;
+  ros::NodeHandle node_handle_;
+  std::unique_ptr<franka_hw::FrankaStateHandle> state_handle_;
+  std::unique_ptr<franka_hw::FrankaModelHandle> model_handle_;
+  std::vector<hardware_interface::JointHandle> joint_handles_;
+  std::vector<std::string> joint_names_;
+  double filter_params_{0.005};
+  double nullspace_stiffness_{20.0};
+  double nullspace_stiffness_target_{20.0};
+  const double delta_tau_max_{1.0};
+  Eigen::Matrix<double, 6, 6> cartesian_stiffness_;
+  Eigen::Matrix<double, 6, 6> cartesian_stiffness_target_;
+  Eigen::Matrix<double, 6, 6> cartesian_damping_;
+  Eigen::Matrix<double, 6, 6> cartesian_damping_target_;
+  Eigen::Matrix<double, 7, 1> q_d_nullspace_;
+  Eigen::Vector3d position_d_;
+  Eigen::Quaterniond orientation_d_;
+  Eigen::Vector3d position_d_target_;
+  Eigen::Quaterniond orientation_d_target_;
+
+  // Saturation
+  Eigen::Matrix<double, 7, 1> saturateTorqueRate(
+      const Eigen::Matrix<double, 7, 1>& tau_d_calculated,
+      const Eigen::Matrix<double, 7, 1>& tau_J_d,  // NOLINT (readability-identifier-naming)
+      const Eigen::Matrix<double, 7, 1>& gravity);
+
+  // Dynamic reconfigure
+  boost::scoped_ptr<dynamic_reconfigure::Server<franka_example_controllers::compliance_paramConfig>>
+      dynamic_server_compliance_param_;
+  ros::NodeHandle dynamic_reconfigure_compliance_param_node_;
+  void complianceParamCallback(franka_example_controllers::compliance_paramConfig& config,
+                               uint32_t level);
+
+  // Equilibrium pose subscriber
+  ros::Subscriber sub_equilibrium_pose_;
+  void equilibriumPoseCallback(const geometry_msgs::PoseStampedConstPtr& msg);
+};
+
+}  // namespace franka_example_controllers
diff --git a/franka_example_controllers/include/franka_example_controllers/force_example_controller.h b/franka_example_controllers/include/franka_example_controllers/force_example_controller.h
new file mode 100644
index 0000000000000000000000000000000000000000..a9f36a688150df18b3001b8eb6dadfd80cc21e9e
--- /dev/null
+++ b/franka_example_controllers/include/franka_example_controllers/force_example_controller.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2017 Franka Emika GmbH
+// Use of this source code is governed by the Apache-2.0 license, see LICENSE
+#pragma once
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <controller_interface/multi_interface_controller.h>
+#include <hardware_interface/joint_command_interface.h>
+#include <hardware_interface/robot_hw.h>
+#include <ros/node_handle.h>
+#include <ros/time.h>
+
+#include <franka_hw/franka_model_interface.h>
+#include <franka_hw/franka_state_interface.h>
+
+#include <eigen3/Eigen/Core>
+
+#include <dynamic_reconfigure/server.h>
+#include <franka_example_controllers/desired_mass_paramConfig.h>
+#include <boost/scoped_ptr.hpp>
+
+namespace franka_example_controllers {
+
+class ForceExampleController : public controller_interface::MultiInterfaceController<
+                                   franka_hw::FrankaModelInterface,
+                                   hardware_interface::EffortJointInterface,
+                                   franka_hw::FrankaStateInterface> {
+ public:
+  ForceExampleController();
+  bool init(hardware_interface::RobotHW* robot_hw, ros::NodeHandle& node_handle);
+  void starting(const ros::Time&);
+  void update(const ros::Time&, const ros::Duration& period);
+
+ private:
+  std::unique_ptr<franka_hw::FrankaModelHandle> model_handle_;
+  std::unique_ptr<franka_hw::FrankaStateHandle> state_handle_;
+  std::vector<hardware_interface::JointHandle> joint_handles_;
+  double desired_mass_{0.0};
+  double target_mass_{0.0};
+  double k_p_{0.0};
+  double k_i_{0.0};
+  double target_k_p_{0.0};
+  double target_k_i_{0.0};
+  double filter_gain_{0.001};
+  Eigen::Matrix<double, 7, 1> tau_ext_initial_;
+  Eigen::Matrix<double, 7, 1> tau_error_;
+
+  // Dynamic reconfigure
+  boost::scoped_ptr<
+      dynamic_reconfigure::Server<franka_example_controllers::desired_mass_paramConfig>>
+      dynamic_server_desired_mass_param_;
+  ros::NodeHandle dynamic_reconfigure_desired_mass_param_node_;
+  void desiredMassParamCallback(franka_example_controllers::desired_mass_paramConfig& config,
+                                uint32_t level);
+};
+
+}  // namespace franka_example_controllers
diff --git a/franka_example_controllers/launch/cartesian_impedance_example_controller.launch b/franka_example_controllers/launch/cartesian_impedance_example_controller.launch
new file mode 100644
index 0000000000000000000000000000000000000000..d4de9d4773252380b80d243d05726800a6d0fbfd
--- /dev/null
+++ b/franka_example_controllers/launch/cartesian_impedance_example_controller.launch
@@ -0,0 +1,18 @@
+<?xml version="1.0" ?>
+<launch>
+  <arg name="robot_ip" default="robot.franka.de" />
+  <arg name="arm_id" value="panda" />
+  <arg name="load_gripper" default="true" />
+  <include file="$(find franka_control)/launch/franka_control.launch" >
+    <arg name="robot_ip" value="$(arg robot_ip)" />
+    <arg name="arm_id" value="$(arg arm_id)" />
+    <arg name="load_gripper" value="$(arg load_gripper)" />
+  </include>
+  <group ns="$(arg arm_id)">
+    <rosparam command="load" file="$(find franka_example_controllers)/config/franka_example_controllers.yaml" />
+    <node name="controller_spawner" pkg="controller_manager" type="spawner" respawn="false" output="screen"  args="cartesian_impedance_example_controller --shutdown-timeout 2"/>
+    <node pkg="rviz" type="rviz" output="screen" name="rviz" args="-d $(find franka_example_controllers)/launch/rviz/franka_description_with_marker.rviz"/>
+    <node name="interactive_marker" pkg="franka_example_controllers" type="interactive_marker.py" required="true" output="screen" />
+    <node name="rqt_reconfigure" pkg="rqt_reconfigure" type="rqt_reconfigure" required="false" />
+  </group>
+</launch>
diff --git a/franka_example_controllers/launch/force_example_controller.launch b/franka_example_controllers/launch/force_example_controller.launch
new file mode 100644
index 0000000000000000000000000000000000000000..76f16691ae1c00ab6607217fd67c804e6b5ee467
--- /dev/null
+++ b/franka_example_controllers/launch/force_example_controller.launch
@@ -0,0 +1,17 @@
+<?xml version="1.0" ?>
+<launch>
+  <arg name="robot_ip" default="robot.franka.de" />
+  <arg name="arm_id" value="panda" />
+  <arg name="load_gripper" default="true" />
+  <include file="$(find franka_control)/launch/franka_control.launch" >
+    <arg name="robot_ip" value="$(arg robot_ip)" />
+    <arg name="arm_id" value="$(arg arm_id)" />
+    <arg name="load_gripper" value="$(arg load_gripper)" />
+  </include>
+  <group ns="$(arg arm_id)">
+    <rosparam command="load" file="$(find franka_example_controllers)/config/franka_example_controllers.yaml" />
+    <node name="controller_spawner" pkg="controller_manager" type="spawner" respawn="false" output="screen"  args="force_example_controller --shutdown-timeout 2"/>
+    <node pkg="rviz" type="rviz" output="screen" name="rviz" args="-d $(find franka_description)/rviz/franka_description.rviz"/>
+    <node name="rqt_reconfigure" pkg="rqt_reconfigure" type="rqt_reconfigure" required="false" />
+  </group>
+</launch>
diff --git a/franka_example_controllers/launch/rviz/franka_description_with_marker.rviz b/franka_example_controllers/launch/rviz/franka_description_with_marker.rviz
new file mode 100644
index 0000000000000000000000000000000000000000..d48a6db40c95fd1f65ee86e4d899bcb51bbe987c
--- /dev/null
+++ b/franka_example_controllers/launch/rviz/franka_description_with_marker.rviz
@@ -0,0 +1,181 @@
+Panels:
+  - Class: rviz/Displays
+    Help Height: 78
+    Name: Displays
+    Property Tree Widget:
+      Expanded:
+        - /Global Options1
+        - /Status1
+      Splitter Ratio: 0.5
+    Tree Height: 573
+  - Class: rviz/Selection
+    Name: Selection
+  - Class: rviz/Tool Properties
+    Expanded:
+      - /2D Pose Estimate1
+      - /2D Nav Goal1
+      - /Publish Point1
+    Name: Tool Properties
+    Splitter Ratio: 0.5886790156364441
+  - Class: rviz/Views
+    Expanded:
+      - /Current View1
+    Name: Views
+    Splitter Ratio: 0.5
+  - Class: rviz/Time
+    Experimental: false
+    Name: Time
+    SyncMode: 0
+    SyncSource: ""
+Visualization Manager:
+  Class: ""
+  Displays:
+    - Alpha: 0.5
+      Cell Size: 1
+      Class: rviz/Grid
+      Color: 160; 160; 164
+      Enabled: true
+      Line Style:
+        Line Width: 0.029999999329447746
+        Value: Lines
+      Name: Grid
+      Normal Cell Count: 0
+      Offset:
+        X: 0
+        Y: 0
+        Z: 0
+      Plane: XY
+      Plane Cell Count: 10
+      Reference Frame: <Fixed Frame>
+      Value: true
+    - Alpha: 1
+      Class: rviz/RobotModel
+      Collision Enabled: false
+      Enabled: true
+      Links:
+        All Links Enabled: true
+        Expand Joint Details: false
+        Expand Link Details: false
+        Expand Tree: false
+        Link Tree Style: Links in Alphabetic Order
+        panda_link0:
+          Alpha: 1
+          Show Axes: false
+          Show Trail: false
+          Value: true
+        panda_link1:
+          Alpha: 1
+          Show Axes: false
+          Show Trail: false
+          Value: true
+        panda_link2:
+          Alpha: 1
+          Show Axes: false
+          Show Trail: false
+          Value: true
+        panda_link3:
+          Alpha: 1
+          Show Axes: false
+          Show Trail: false
+          Value: true
+        panda_link4:
+          Alpha: 1
+          Show Axes: false
+          Show Trail: false
+          Value: true
+        panda_link5:
+          Alpha: 1
+          Show Axes: false
+          Show Trail: false
+          Value: true
+        panda_link6:
+          Alpha: 1
+          Show Axes: false
+          Show Trail: false
+          Value: true
+        panda_link7:
+          Alpha: 1
+          Show Axes: false
+          Show Trail: false
+          Value: true
+        panda_link8:
+          Alpha: 1
+          Show Axes: false
+          Show Trail: false
+      Name: RobotModel
+      Robot Description: robot_description
+      TF Prefix: ""
+      Update Interval: 0
+      Value: true
+      Visual Enabled: true
+    - Class: rviz/InteractiveMarkers
+      Enable Transparency: true
+      Enabled: true
+      Name: InteractiveMarkers
+      Show Axes: false
+      Show Descriptions: true
+      Show Visual Aids: false
+      Update Topic: /panda/equilibrium_pose_marker/update
+      Value: true
+  Enabled: true
+  Global Options:
+    Background Color: 48; 48; 48
+    Fixed Frame: panda_link0
+    Frame Rate: 30
+  Name: root
+  Tools:
+    - Class: rviz/Interact
+      Hide Inactive Objects: true
+    - Class: rviz/MoveCamera
+    - Class: rviz/Select
+    - Class: rviz/FocusCamera
+    - Class: rviz/Measure
+    - Class: rviz/SetInitialPose
+      Topic: /initialpose
+    - Class: rviz/SetGoal
+      Topic: /move_base_simple/goal
+    - Class: rviz/PublishPoint
+      Single click: true
+      Topic: /clicked_point
+  Value: true
+  Views:
+    Current:
+      Class: rviz/Orbit
+      Distance: 4.644041061401367
+      Enable Stereo Rendering:
+        Stereo Eye Separation: 0.05999999865889549
+        Stereo Focal Distance: 1
+        Swap Stereo Eyes: false
+        Value: false
+      Focal Point:
+        X: 0
+        Y: 0
+        Z: 0
+      Focal Shape Fixed Size: true
+      Focal Shape Size: 0.05000000074505806
+      Invert Z Axis: false
+      Name: Current View
+      Near Clip Distance: 0.009999999776482582
+      Pitch: 0.785398006439209
+      Target Frame: <Fixed Frame>
+      Value: Orbit (rviz)
+      Yaw: 0.785398006439209
+    Saved: ~
+Window Geometry:
+  Displays:
+    collapsed: false
+  Height: 846
+  Hide Left Dock: false
+  Hide Right Dock: false
+  QMainWindow State: 000000ff00000000fd00000004000000000000016a000002c5fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c0061007900730100000027000002c5000000c600fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f000002c5fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a005600690065007700730100000027000002c50000009e00fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004b000000042fc0100000002fb0000000800540069006d00650100000000000004b00000024400fffffffb0000000800540069006d006501000000000000045000000000000000000000022b000002c500000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
+  Selection:
+    collapsed: false
+  Time:
+    collapsed: false
+  Tool Properties:
+    collapsed: false
+  Views:
+    collapsed: false
+  Width: 1200
+  X: 295
+  Y: 39
diff --git a/franka_example_controllers/launch/scripts/interactive_marker.py b/franka_example_controllers/launch/scripts/interactive_marker.py
new file mode 100755
index 0000000000000000000000000000000000000000..d691044687fe32c0de31f5818d6521a97a3ffc80
--- /dev/null
+++ b/franka_example_controllers/launch/scripts/interactive_marker.py
@@ -0,0 +1,140 @@
+#!/usr/bin/python
+
+import rospy
+import tf.transformations
+import numpy as np
+
+from interactive_markers.interactive_marker_server import \
+    InteractiveMarkerServer, InteractiveMarkerFeedback
+from visualization_msgs.msg import InteractiveMarker, \
+    InteractiveMarkerControl
+from geometry_msgs.msg import PoseStamped
+from franka_msgs.msg import FrankaState
+
+marker_pose = PoseStamped()
+initial_pose_found = False
+pose_pub = None
+# [[min_x, max_x], [min_y, max_y], [min_z, max_z]]
+position_limits = [[-0.6, 0.6], [-0.6, 0.6], [0.05, 0.9]]
+
+
+def publisherCallback(msg, arm_id):
+    marker_pose.header.frame_id = arm_id + "_link0"
+    marker_pose.header.stamp = rospy.Time(0)
+    pose_pub.publish(marker_pose)
+
+
+def franka_state_callback(msg):
+    initial_quaternion = \
+        tf.transformations.quaternion_from_matrix(
+            np.transpose(np.reshape(msg.O_T_EE,
+                                    (4, 4))))
+    marker_pose.pose.orientation.x = initial_quaternion[0]
+    marker_pose.pose.orientation.y = initial_quaternion[1]
+    marker_pose.pose.orientation.z = initial_quaternion[2]
+    marker_pose.pose.orientation.w = initial_quaternion[3]
+    marker_pose.pose.position.x = msg.O_T_EE[12]
+    marker_pose.pose.position.y = msg.O_T_EE[13]
+    marker_pose.pose.position.z = msg.O_T_EE[14]
+    global initial_pose_found
+    initial_pose_found = True
+
+
+def processFeedback(feedback):
+    if feedback.event_type == InteractiveMarkerFeedback.POSE_UPDATE:
+        marker_pose.pose.position.x = max([min([feedback.pose.position.x,
+                                          position_limits[0][1]]),
+                                          position_limits[0][0]])
+        marker_pose.pose.position.y = max([min([feedback.pose.position.y,
+                                          position_limits[1][1]]),
+                                          position_limits[1][0]])
+        marker_pose.pose.position.z = max([min([feedback.pose.position.z,
+                                          position_limits[2][1]]),
+                                          position_limits[2][0]])
+        marker_pose.pose.orientation = feedback.pose.orientation
+    server.applyChanges()
+
+
+if __name__ == "__main__":
+    rospy.init_node("equilibrium_pose_node")
+    state_sub = rospy.Subscriber("franka_state_controller/franka_states",
+                                 FrankaState, franka_state_callback)
+    listener = tf.TransformListener()
+    arm_id = rospy.get_param("arm_id")
+
+    # Get initial pose for the interactive marker
+    while not initial_pose_found:
+        rospy.sleep(1)
+    state_sub.unregister()
+
+    pose_pub = rospy.Publisher(
+        "/equilibrium_pose", PoseStamped, queue_size=10)
+    server = InteractiveMarkerServer("equilibrium_pose_marker")
+    int_marker = InteractiveMarker()
+    int_marker.header.frame_id = arm_id + "_link0"
+    int_marker.scale = 0.3
+    int_marker.name = "equilibrium_pose"
+    int_marker.description = ("Equilibrium Pose\nBE CAREFUL! "
+                              "If you move the \nequilibrium "
+                              "pose the robot will follow it\n"
+                              "so be aware of potential collisions")
+    int_marker.pose = marker_pose.pose
+    # run pose publisher
+    rospy.Timer(rospy.Duration(0.005),
+                lambda msg: publisherCallback(msg, arm_id))
+
+    # insert a box
+    control = InteractiveMarkerControl()
+    control.orientation.w = 1
+    control.orientation.x = 1
+    control.orientation.y = 0
+    control.orientation.z = 0
+    control.name = "rotate_x"
+    control.interaction_mode = InteractiveMarkerControl.ROTATE_AXIS
+    int_marker.controls.append(control)
+
+    control = InteractiveMarkerControl()
+    control.orientation.w = 1
+    control.orientation.x = 1
+    control.orientation.y = 0
+    control.orientation.z = 0
+    control.name = "move_x"
+    control.interaction_mode = InteractiveMarkerControl.MOVE_AXIS
+    int_marker.controls.append(control)
+    control = InteractiveMarkerControl()
+    control.orientation.w = 1
+    control.orientation.x = 0
+    control.orientation.y = 1
+    control.orientation.z = 0
+    control.name = "rotate_y"
+    control.interaction_mode = InteractiveMarkerControl.ROTATE_AXIS
+    int_marker.controls.append(control)
+    control = InteractiveMarkerControl()
+    control.orientation.w = 1
+    control.orientation.x = 0
+    control.orientation.y = 1
+    control.orientation.z = 0
+    control.name = "move_y"
+    control.interaction_mode = InteractiveMarkerControl.MOVE_AXIS
+    int_marker.controls.append(control)
+    control = InteractiveMarkerControl()
+    control.orientation.w = 1
+    control.orientation.x = 0
+    control.orientation.y = 0
+    control.orientation.z = 1
+    control.name = "rotate_z"
+    control.interaction_mode = InteractiveMarkerControl.ROTATE_AXIS
+    int_marker.controls.append(control)
+    control = InteractiveMarkerControl()
+    control.orientation.w = 1
+    control.orientation.x = 0
+    control.orientation.y = 0
+    control.orientation.z = 1
+    control.name = "move_z"
+    control.interaction_mode = InteractiveMarkerControl.MOVE_AXIS
+    int_marker.controls.append(control)
+    server.insert(int_marker, processFeedback)
+
+    server.applyChanges()
+
+    rospy.spin()
diff --git a/franka_example_controllers/package.xml b/franka_example_controllers/package.xml
index 22a0a4059fe199ad92b04411fe373a80ab106dac..71f68269626adf75d634958e795da20a33be2e8c 100644
--- a/franka_example_controllers/package.xml
+++ b/franka_example_controllers/package.xml
@@ -15,6 +15,7 @@
 
   <build_depend>roscpp</build_depend>
   <build_depend>message_generation</build_depend>
+  <build_depend>dynamic_reconfigure</build_depend>
 
   <depend>controller_interface</depend>
   <depend>franka_hw</depend>
diff --git a/franka_example_controllers/src/cartesian_impedance_example_controller.cpp b/franka_example_controllers/src/cartesian_impedance_example_controller.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..81e78c27444cb172033b73c725973771806d31e2
--- /dev/null
+++ b/franka_example_controllers/src/cartesian_impedance_example_controller.cpp
@@ -0,0 +1,259 @@
+// Copyright (c) 2017 Franka Emika GmbH
+// Use of this source code is governed by the Apache-2.0 license, see LICENSE
+#include <franka_example_controllers/cartesian_impedance_example_controller.h>
+
+#include <cmath>
+
+#include <controller_interface/controller_base.h>
+#include <pluginlib/class_list_macros.h>
+#include <ros/ros.h>
+
+#include <franka/robot_state.h>
+#include "pseudo_inversion.h"
+
+namespace franka_example_controllers {
+
+CartesianImpedanceExampleController::CartesianImpedanceExampleController() = default;
+
+bool CartesianImpedanceExampleController::init(hardware_interface::RobotHW* robot_hw,
+                                               ros::NodeHandle& node_handle) {
+  std::vector<double> cartesian_stiffness_vector;
+  std::vector<double> cartesian_damping_vector;
+  node_handle_ = node_handle;
+
+  sub_equilibrium_pose_ = node_handle_.subscribe(
+      "/equilibrium_pose", 20, &CartesianImpedanceExampleController::equilibriumPoseCallback, this,
+      ros::TransportHints().reliable().tcpNoDelay());
+
+  if (!node_handle_.getParam("arm_id", arm_id_)) {
+    ROS_ERROR_STREAM("CartesianImpedanceExampleController: Could not read parameter arm_id");
+    return false;
+  }
+  if (!node_handle_.getParam("joint_names", joint_names_) || joint_names_.size() != 7) {
+    ROS_ERROR(
+        "CartesianImpedanceExampleController: Invalid or no joint_names parameters provided, "
+        "aborting "
+        "controller init!");
+    return false;
+  }
+
+  franka_hw::FrankaModelInterface* model_interface =
+      robot_hw->get<franka_hw::FrankaModelInterface>();
+  if (model_interface == nullptr) {
+    ROS_ERROR_STREAM(
+        "CartesianImpedanceExampleController: Error getting model interface from hardware");
+    return false;
+  }
+  try {
+    model_handle_.reset(
+        new franka_hw::FrankaModelHandle(model_interface->getHandle(arm_id_ + "_model")));
+  } catch (hardware_interface::HardwareInterfaceException& ex) {
+    ROS_ERROR_STREAM(
+        "CartesianImpedanceExampleController: Exception getting model handle from interface: "
+        << ex.what());
+    return false;
+  }
+
+  franka_hw::FrankaStateInterface* state_interface =
+      robot_hw->get<franka_hw::FrankaStateInterface>();
+  if (state_interface == nullptr) {
+    ROS_ERROR_STREAM(
+        "CartesianImpedanceExampleController: Error getting state interface from hardware");
+    return false;
+  }
+  try {
+    state_handle_.reset(
+        new franka_hw::FrankaStateHandle(state_interface->getHandle(arm_id_ + "_robot")));
+  } catch (hardware_interface::HardwareInterfaceException& ex) {
+    ROS_ERROR_STREAM(
+        "CartesianImpedanceExampleController: Exception getting state handle from interface: "
+        << ex.what());
+    return false;
+  }
+
+  hardware_interface::EffortJointInterface* effort_joint_interface =
+      robot_hw->get<hardware_interface::EffortJointInterface>();
+  if (effort_joint_interface == nullptr) {
+    ROS_ERROR_STREAM(
+        "CartesianImpedanceExampleController: Error getting effort joint interface from hardware");
+    return false;
+  }
+  for (size_t i = 0; i < 7; ++i) {
+    try {
+      joint_handles_.push_back(effort_joint_interface->getHandle(joint_names_[i]));
+    } catch (const hardware_interface::HardwareInterfaceException& ex) {
+      ROS_ERROR_STREAM(
+          "CartesianImpedanceExampleController: Exception getting joint handles: " << ex.what());
+      return false;
+    }
+  }
+
+  dynamic_reconfigure_compliance_param_node_ =
+      ros::NodeHandle("dynamic_reconfigure_compliance_param_node");
+
+  dynamic_server_compliance_param_.reset(
+      new dynamic_reconfigure::Server<franka_example_controllers::compliance_paramConfig>(
+          dynamic_reconfigure_compliance_param_node_));
+  dynamic_server_compliance_param_->setCallback(
+      boost::bind(&CartesianImpedanceExampleController::complianceParamCallback, this, _1, _2));
+
+  position_d_.setZero();
+  orientation_d_.coeffs() << 0.0, 0.0, 0.0, 1.0;
+  position_d_target_.setZero();
+  orientation_d_target_.coeffs() << 0.0, 0.0, 0.0, 1.0;
+
+  cartesian_stiffness_.setZero();
+  cartesian_damping_.setZero();
+
+  return true;
+}
+
+void CartesianImpedanceExampleController::starting(const ros::Time& /*time*/) {
+  // compute initial velocity with jacobian and set x_attractor and q_d_nullspace
+  // to initial configuration
+  franka::RobotState initial_state = state_handle_->getRobotState();
+  // get jacobian
+  std::array<double, 42> jacobian_array =
+      model_handle_->getZeroJacobian(franka::Frame::kEndEffector, initial_state);
+  // convert to eigen
+  Eigen::Map<Eigen::Matrix<double, 6, 7> > jacobian(jacobian_array.data());
+  Eigen::Map<Eigen::Matrix<double, 7, 1> > dq_initial(initial_state.dq.data());
+  Eigen::Map<Eigen::Matrix<double, 7, 1> > q_initial(initial_state.q.data());
+  Eigen::Affine3d initial_transform(Eigen::Matrix4d::Map(initial_state.O_T_EE.data()));
+
+  // set equilibrium point to current state
+  position_d_ = initial_transform.translation();
+  orientation_d_ = Eigen::Quaterniond(initial_transform.linear());
+  position_d_target_ = initial_transform.translation();
+  orientation_d_target_ = Eigen::Quaterniond(initial_transform.linear());
+
+  // set nullspace equilibrium configuration to initial q
+  q_d_nullspace_ = q_initial;
+}
+
+void CartesianImpedanceExampleController::update(const ros::Time& /*time*/,
+                                                 const ros::Duration& /*period*/) {
+  // get state variables
+  franka::RobotState robot_state = state_handle_->getRobotState();
+  std::array<double, 7> gravity_array =
+      model_handle_->getGravity(robot_state.m_load, robot_state.F_x_Cload);
+  std::array<double, 7> coriolis_array =
+      model_handle_->getCoriolis(robot_state.I_load, robot_state.m_load, robot_state.F_x_Cload);
+  std::array<double, 42> jacobian_array =
+      model_handle_->getZeroJacobian(franka::Frame::kEndEffector, robot_state);
+
+  // convert to Eigen
+  Eigen::Map<Eigen::Matrix<double, 7, 1> > coriolis(coriolis_array.data());
+  Eigen::Map<Eigen::Matrix<double, 7, 1> > gravity(gravity_array.data());
+  Eigen::Map<Eigen::Matrix<double, 6, 7> > jacobian(jacobian_array.data());
+  Eigen::Map<Eigen::Matrix<double, 7, 1> > q(robot_state.q.data());
+  Eigen::Map<Eigen::Matrix<double, 7, 1> > dq(robot_state.dq.data());
+  Eigen::Map<Eigen::Matrix<double, 7, 1> > tau_ext(robot_state.tau_ext_hat_filtered.data());
+  Eigen::Map<Eigen::Matrix<double, 7, 1> > tau_J_d(  // NOLINT (readability-identifier-naming)
+      robot_state.tau_J_d.data());
+  Eigen::Affine3d transform(Eigen::Matrix4d::Map(robot_state.O_T_EE.data()));
+  Eigen::Vector3d position(transform.translation());
+  Eigen::Quaterniond orientation(transform.linear());
+
+  // compute error to desired pose
+  // position error
+  Eigen::Matrix<double, 6, 1> error;
+  error.head(3) << position - position_d_;
+
+  // orientation error
+  // "difference" quaternion
+  Eigen::Quaterniond error_quaternion(orientation * orientation_d_.inverse());
+  // convert to axis angle
+  Eigen::AngleAxisd error_quaternion_angle_axis(error_quaternion);
+  // compute "orientation error"
+  error.tail(3) << error_quaternion_angle_axis.axis() * error_quaternion_angle_axis.angle();
+
+  // compute control
+  // allocate variables
+  Eigen::VectorXd tau_task(7), tau_nullspace(7), tau_d(7);
+
+  // pseudoinverse for nullspace handling
+  // kinematic pseuoinverse
+  Eigen::MatrixXd jacobian_transpose_pinv;
+  pseudo_inverse(jacobian.transpose(), jacobian_transpose_pinv);
+
+  // Cartesian PD control with damping ratio = 1
+  tau_task << jacobian.transpose() *
+                  (-cartesian_stiffness_ * error - cartesian_damping_ * (jacobian * dq));
+  // nullspace PD control with damping ratio = 1
+  tau_nullspace << (Eigen::MatrixXd::Identity(7, 7) -
+                    jacobian.transpose() * jacobian_transpose_pinv) *
+                       (nullspace_stiffness_ * (q_d_nullspace_ - q) -
+                        (2.0 * sqrt(nullspace_stiffness_)) * dq);
+  // Desired torque
+  tau_d << tau_task + tau_nullspace + coriolis - tau_ext;
+  // Saturate torque rate to avoid discontinuities
+  tau_d << saturateTorqueRate(tau_d, tau_J_d, gravity);
+  for (size_t i = 0; i < 7; ++i) {
+    joint_handles_[i].setCommand(tau_d(i));
+  }
+
+  // update parameters changed online either through dynamic reconfigure or through the interactive
+  // target by filtering
+  cartesian_stiffness_ =
+      filter_params_ * cartesian_stiffness_target_ + (1.0 - filter_params_) * cartesian_stiffness_;
+  cartesian_damping_ =
+      filter_params_ * cartesian_damping_target_ + (1.0 - filter_params_) * cartesian_damping_;
+  nullspace_stiffness_ =
+      filter_params_ * nullspace_stiffness_target_ + (1.0 - filter_params_) * nullspace_stiffness_;
+  position_d_ = filter_params_ * position_d_target_ + (1.0 - filter_params_) * position_d_;
+  Eigen::AngleAxisd aa_orientation_d(orientation_d_);
+  Eigen::AngleAxisd aa_orientation_d_target(orientation_d_target_);
+  aa_orientation_d.axis() = filter_params_ * aa_orientation_d_target.axis() +
+                            (1.0 - filter_params_) * aa_orientation_d.axis();
+  aa_orientation_d.angle() = filter_params_ * aa_orientation_d_target.angle() +
+                             (1.0 - filter_params_) * aa_orientation_d.angle();
+  orientation_d_ = Eigen::Quaterniond(aa_orientation_d);
+}
+
+Eigen::Matrix<double, 7, 1> CartesianImpedanceExampleController::saturateTorqueRate(
+    const Eigen::Matrix<double, 7, 1>& tau_d_calculated,
+    const Eigen::Matrix<double, 7, 1>& tau_J_d,  // NOLINT (readability-identifier-naming)
+    const Eigen::Matrix<double, 7, 1>& gravity) {
+  Eigen::Matrix<double, 7, 1> tau_d_saturated{};
+  for (size_t i = 0; i < 7; i++) {
+    // TODO(sga): After gravity is removed from tau_J_d, do not subtract it any more.
+    double difference = tau_d_calculated[i] - (tau_J_d[i] - gravity[i]);
+    tau_d_saturated[i] =
+        (tau_J_d[i] - gravity[i]) + std::max(std::min(difference, delta_tau_max_), -delta_tau_max_);
+  }
+  return tau_d_saturated;
+}
+
+void CartesianImpedanceExampleController::complianceParamCallback(
+    franka_example_controllers::compliance_paramConfig& config,
+    uint32_t /*level*/) {
+  cartesian_stiffness_target_.setIdentity();
+  cartesian_stiffness_target_.topLeftCorner(3, 3)
+      << config.translational_stiffness * Eigen::Matrix3d::Identity();
+  cartesian_stiffness_target_.bottomRightCorner(3, 3)
+      << config.rotational_stiffness * Eigen::Matrix3d::Identity();
+  cartesian_damping_target_.setIdentity();
+  // Damping ratio = 1
+  cartesian_damping_target_.topLeftCorner(3, 3)
+      << 2.0 * sqrt(config.translational_stiffness) * Eigen::Matrix3d::Identity();
+  cartesian_damping_target_.bottomRightCorner(3, 3)
+      << 2.0 * sqrt(config.rotational_stiffness) * Eigen::Matrix3d::Identity();
+  nullspace_stiffness_target_ = config.nullspace_stiffness;
+}
+
+void CartesianImpedanceExampleController::equilibriumPoseCallback(
+    const geometry_msgs::PoseStampedConstPtr& msg) {
+  position_d_target_ << msg->pose.position.x, msg->pose.position.y, msg->pose.position.z;
+  Eigen::Quaterniond last_orientation_d_target(orientation_d_target_);
+  orientation_d_target_.coeffs() << msg->pose.orientation.x, msg->pose.orientation.y,
+      msg->pose.orientation.z, msg->pose.orientation.w;
+  if (last_orientation_d_target.coeffs().dot(orientation_d_target_.coeffs()) < 0.0) {
+    orientation_d_target_.coeffs() << -orientation_d_target_.coeffs();
+  }
+}
+
+}  // namespace franka_example_controllers
+
+PLUGINLIB_EXPORT_CLASS(franka_example_controllers::CartesianImpedanceExampleController,
+                       controller_interface::ControllerBase)
diff --git a/franka_example_controllers/src/cartesian_pose_example_controller.cpp b/franka_example_controllers/src/cartesian_pose_example_controller.cpp
index bd00e6ae7b09d7bd2953611b77bc0d0be8b4ae38..441119d4703d41786c9217ae220b0f7d348917db 100644
--- a/franka_example_controllers/src/cartesian_pose_example_controller.cpp
+++ b/franka_example_controllers/src/cartesian_pose_example_controller.cpp
@@ -47,7 +47,7 @@ bool CartesianPoseExampleController::init(hardware_interface::RobotHW* robot_har
 }
 
 void CartesianPoseExampleController::starting(const ros::Time& /*time*/) {
-  initial_pose_ = cartesian_pose_handle_->getRobotState().O_T_EE;
+  initial_pose_ = cartesian_pose_handle_->getRobotState().O_T_EE_d;
 }
 
 void CartesianPoseExampleController::update(const ros::Time& /*time*/,
diff --git a/franka_example_controllers/src/force_example_controller.cpp b/franka_example_controllers/src/force_example_controller.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a44c55f2d3bf18c662b20e2a0cb5ee8b3ba14a3a
--- /dev/null
+++ b/franka_example_controllers/src/force_example_controller.cpp
@@ -0,0 +1,141 @@
+// Copyright (c) 2017 Franka Emika GmbH
+// Use of this source code is governed by the Apache-2.0 license, see LICENSE
+#include <franka_example_controllers/force_example_controller.h>
+
+#include <cmath>
+
+#include <controller_interface/controller_base.h>
+#include <pluginlib/class_list_macros.h>
+#include <ros/ros.h>
+
+#include <franka/robot_state.h>
+
+namespace franka_example_controllers {
+
+ForceExampleController::ForceExampleController() = default;
+
+bool ForceExampleController::init(hardware_interface::RobotHW* robot_hw,
+                                  ros::NodeHandle& node_handle) {
+  std::vector<std::string> joint_names;
+  std::string arm_id;
+  ROS_WARN(
+      "ForceExampleController: Make sure your robot's endeffector is in contact "
+      "with a horizontal surface before starting the controller!");
+  if (!node_handle.getParam("arm_id", arm_id)) {
+    ROS_ERROR("ForceExampleController: Could not read parameter arm_id");
+    return false;
+  }
+  if (!node_handle.getParam("joint_names", joint_names) || joint_names.size() != 7) {
+    ROS_ERROR(
+        "ForceExampleController: Invalid or no joint_names parameters provided, aborting "
+        "controller init!");
+    return false;
+  }
+
+  franka_hw::FrankaModelInterface* model_interface =
+      robot_hw->get<franka_hw::FrankaModelInterface>();
+  if (model_interface == nullptr) {
+    ROS_ERROR_STREAM("ForceExampleController: Error getting model interface from hardware");
+    return false;
+  }
+  try {
+    model_handle_.reset(
+        new franka_hw::FrankaModelHandle(model_interface->getHandle(arm_id + "_model")));
+  } catch (hardware_interface::HardwareInterfaceException& ex) {
+    ROS_ERROR_STREAM(
+        "ForceExampleController: Exception getting model handle from interface: " << ex.what());
+    return false;
+  }
+
+  franka_hw::FrankaStateInterface* state_interface =
+      robot_hw->get<franka_hw::FrankaStateInterface>();
+  if (state_interface == nullptr) {
+    ROS_ERROR_STREAM("ForceExampleController: Error getting state interface from hardware");
+    return false;
+  }
+  try {
+    state_handle_.reset(
+        new franka_hw::FrankaStateHandle(state_interface->getHandle(arm_id + "_robot")));
+  } catch (hardware_interface::HardwareInterfaceException& ex) {
+    ROS_ERROR_STREAM(
+        "ForceExampleController: Exception getting state handle from interface: " << ex.what());
+    return false;
+  }
+
+  hardware_interface::EffortJointInterface* effort_joint_interface =
+      robot_hw->get<hardware_interface::EffortJointInterface>();
+  if (effort_joint_interface == nullptr) {
+    ROS_ERROR_STREAM("ForceExampleController: Error getting effort joint interface from hardware");
+    return false;
+  }
+  for (size_t i = 0; i < 7; ++i) {
+    try {
+      joint_handles_.push_back(effort_joint_interface->getHandle(joint_names[i]));
+    } catch (const hardware_interface::HardwareInterfaceException& ex) {
+      ROS_ERROR_STREAM("ForceExampleController: Exception getting joint handles: " << ex.what());
+      return false;
+    }
+  }
+
+  dynamic_reconfigure_desired_mass_param_node_ =
+      ros::NodeHandle("dynamic_reconfigure_desired_mass_param_node");
+  dynamic_server_desired_mass_param_.reset(
+      new dynamic_reconfigure::Server<franka_example_controllers::desired_mass_paramConfig>(
+          dynamic_reconfigure_desired_mass_param_node_));
+  dynamic_server_desired_mass_param_->setCallback(
+      boost::bind(&ForceExampleController::desiredMassParamCallback, this, _1, _2));
+
+  return true;
+}
+
+void ForceExampleController::starting(const ros::Time& /*time*/) {
+  franka::RobotState robot_state = state_handle_->getRobotState();
+  std::array<double, 7> gravity_array =
+      model_handle_->getGravity(robot_state, 0.0, {{0.0, 0.0, 0.0}});
+  Eigen::Map<Eigen::Matrix<double, 7, 1> > tau_measured(robot_state.tau_J.data());
+  Eigen::Map<Eigen::Matrix<double, 7, 1> > gravity(gravity_array.data());
+  // Bias correction for the current external torque
+  tau_ext_initial_ = tau_measured - gravity;
+  tau_error_.setZero();
+}
+
+void ForceExampleController::update(const ros::Time& /*time*/, const ros::Duration& period) {
+  franka::RobotState robot_state = state_handle_->getRobotState();
+  std::array<double, 42> jacobian_array =
+      model_handle_->getZeroJacobian(franka::Frame::kEndEffector, robot_state);
+  std::array<double, 7> gravity_array =
+      model_handle_->getGravity(robot_state, 0.0, {{0.0, 0.0, 0.0}});
+  Eigen::Map<Eigen::Matrix<double, 6, 7> > jacobian(jacobian_array.data());
+  Eigen::Map<Eigen::Matrix<double, 7, 1> > tau_measured(robot_state.tau_J.data());
+  Eigen::Map<Eigen::Matrix<double, 7, 1> > gravity(gravity_array.data());
+
+  Eigen::VectorXd tau_d(7), desired_force_torque(6), tau_cmd(7), tau_ext(7);
+  desired_force_torque.setZero();
+  desired_force_torque(2) = desired_mass_ * -9.81;
+  tau_ext = tau_measured - gravity - tau_ext_initial_;
+  tau_d << jacobian.transpose() * desired_force_torque;
+  tau_error_ = tau_error_ + period.toSec() * (tau_d - tau_ext);
+  // FF + PI control (PI gains are intially all 0)
+  tau_cmd = tau_d + k_p_ * (tau_d - tau_ext) + k_i_ * tau_error_;
+  for (size_t i = 0; i < 7; ++i) {
+    joint_handles_[i].setCommand(tau_cmd(i));
+  }
+
+  // Update signals changed online through dynamic reconfigure
+  desired_mass_ = filter_gain_ * target_mass_ + (1 - filter_gain_) * desired_mass_;
+  k_p_ = filter_gain_ * target_k_p_ + (1 - filter_gain_) * k_p_;
+  k_i_ = filter_gain_ * target_k_i_ + (1 - filter_gain_) * k_i_;
+}
+
+void ForceExampleController::desiredMassParamCallback(
+    franka_example_controllers::desired_mass_paramConfig& config,
+    uint32_t /*level*/) {
+  target_mass_ = config.desired_mass;
+  target_k_p_ = config.k_p;
+  target_k_i_ = config.k_i;
+}
+
+}  // namespace franka_example_controllers
+
+PLUGINLIB_EXPORT_CLASS(franka_example_controllers::ForceExampleController,
+                       controller_interface::ControllerBase)
diff --git a/franka_example_controllers/src/joint_impedance_example_controller.cpp b/franka_example_controllers/src/joint_impedance_example_controller.cpp
index a5cea2bde74fb3f5c0a24ab350143aac1af95af4..bf6aad553a56315ace6ae2ebfbd5f022b5ea3c5c 100644
--- a/franka_example_controllers/src/joint_impedance_example_controller.cpp
+++ b/franka_example_controllers/src/joint_impedance_example_controller.cpp
@@ -140,7 +140,7 @@ bool JointImpedanceExampleController::init(hardware_interface::RobotHW* robot_hw
 }
 
 void JointImpedanceExampleController::starting(const ros::Time& /*time*/) {
-  initial_pose_ = cartesian_pose_handle_->getRobotState().O_T_EE;
+  initial_pose_ = cartesian_pose_handle_->getRobotState().O_T_EE_d;
 }
 
 void JointImpedanceExampleController::update(const ros::Time& /*time*/,
diff --git a/franka_example_controllers/src/pseudo_inversion.h b/franka_example_controllers/src/pseudo_inversion.h
new file mode 100644
index 0000000000000000000000000000000000000000..6170154323cabdd02642b5054dc4c239c2cf31f1
--- /dev/null
+++ b/franka_example_controllers/src/pseudo_inversion.h
@@ -0,0 +1,32 @@
+// Author: Enrico Corvaglia
+// https://github.com/CentroEPiaggio/kuka-lwr/blob/master/lwr_controllers/include/utils/pseudo_inversion.h
+// File provided under public domain
+// pseudo_inverse() computes the pseudo inverse of matrix M_ using SVD decomposition (can choose
+// between damped and not)
+// returns the pseudo inverted matrix M_pinv_
+
+#ifndef PSEUDO_INVERSION_H
+#define PSEUDO_INVERSION_H
+
+#include <eigen3/Eigen/Core>
+#include <eigen3/Eigen/LU>
+#include <eigen3/Eigen/SVD>
+using namespace Eigen;
+
+inline void pseudo_inverse(const Eigen::MatrixXd& M_,
+                           Eigen::MatrixXd& M_pinv_,
+                           bool damped = true) {
+  double lambda_ = damped ? 0.2 : 0.0;
+
+  JacobiSVD<MatrixXd> svd(M_, ComputeFullU | ComputeFullV);
+  JacobiSVD<MatrixXd>::SingularValuesType sing_vals_ = svd.singularValues();
+  MatrixXd S_ = M_;  // copying the dimensions of M_, its content is not needed.
+  S_.setZero();
+
+  for (int i = 0; i < sing_vals_.size(); i++)
+    S_(i, i) = (sing_vals_(i)) / (sing_vals_(i) * sing_vals_(i) + lambda_ * lambda_);
+
+  M_pinv_ = MatrixXd(svd.matrixV() * S_.transpose() * svd.matrixU().transpose());
+}
+
+#endif  // PSEUDO_INVERSION_H