diff --git a/python/examples/.clik.py.swp b/python/examples/.clik.py.swp
new file mode 100644
index 0000000000000000000000000000000000000000..170a055deeb26d0d8c333e4a1a4337f2328b14a5
Binary files /dev/null and b/python/examples/.clik.py.swp differ
diff --git a/python/examples/clik.py b/python/examples/clik.py
index 332ab5635b51dd8a0be82cc707ea1937f94dde90..5d728005134fec929b8099dadf6b6e21235d8804 100644
--- a/python/examples/clik.py
+++ b/python/examples/clik.py
@@ -31,7 +31,12 @@ if __name__ == "__main__":
     else:
         Mgoal = robot.defineGoalPointCLI()
     #compliantMoveL(args, robot, Mgoal)
-    moveL(args, robot, Mgoal)
+    if robot.robot_name != "yumi"
+        moveL(args, robot, Mgoal)
+    else:
+        goal_transform = pin.SE3.Identity()
+        goal_transform.translation[0] = 0.1
+        moveLDualArm(args, robot, Mgoal, goal_transform)
     robot.closeGripper()
     robot.openGripper()
     if not args.pinocchio_only:
diff --git a/python/ur_simple_control/clik/.clik.py.swp b/python/ur_simple_control/clik/.clik.py.swp
new file mode 100644
index 0000000000000000000000000000000000000000..af83ac7f5436cadd1f4a612479c3a2b4869838d2
Binary files /dev/null and b/python/ur_simple_control/clik/.clik.py.swp differ
diff --git a/python/ur_simple_control/clik/clik.py b/python/ur_simple_control/clik/clik.py
index a9eb3f1f591c6d984b1594473aff140ff612f947..5e5ec88f0d4c288ad7a7c164efadf74fe092bd31 100644
--- a/python/ur_simple_control/clik/clik.py
+++ b/python/ur_simple_control/clik/clik.py
@@ -187,6 +187,50 @@ def controlLoopClik(robot : RobotManager, clik_controller, i, past_data):
     # hence the empty dict
     return breakFlag, {}, log_item
 
+
+def controlLoopClikDualArm(robot : RobotManager, clik_controller, goal_transform, i, past_data):
+    """
+    controlLoopClikDualArm
+    ---------------
+    do point to point motion for each arm and its goal.
+    that goal is generated from a single goal that you pass,
+    and a transformation on the goal you also pass.
+    the transformation is done in goal's frame (goal is and has
+    to be an SE3 object).
+    the transform is designed for the left arm,
+    and its inverse is applied for the right arm.
+    """
+    breakFlag = False
+    log_item = {}
+    q = robot.getQ()
+    T_w_e_left, T_w_e_right = robot.getT_w_e()
+    # 
+    Mgoal_left = robot.Mgoal.act(goal_transform)
+    Mgoal_right = robot.Mgoal.act(goal_transform.inverse())
+
+    SEerror_left = T_w_e_left.actInv(Mgoal_left)
+    SEerror_right = T_w_e_right.actInv(Mgoal_right)
+
+    err_vector_left = pin.log6(SEerror_left).vector 
+    err_vector_right = pin.log6(SEerror_right).vector 
+
+    if (np.linalg.norm(err_vector_left) < robot.args.goal_error) and (np.linalg.norm(err_vector_right) < robot.args.goal_error):
+        breakFlag = True
+    J_left = pin.computeFrameJacobian(robot.model, robot.data, q, robot.l_ee_frame_id)
+    J_right = pin.computeFrameJacobian(robot.model, robot.data, q, robot.r_ee_frame_id)
+    # compute the joint velocities based on controller you passed
+    qd_left = clik_controller(J_left, err_vector_left)
+    qd_right = clik_controller(J_right, err_vector_right)
+    qd = qd_left + qd_right
+    robot.sendQd(qd)
+    
+    log_item['qs'] = q.reshape((robot.model.nq,))
+    log_item['dqs'] = robot.getQd().reshape((robot.model.nv,))
+    log_item['dqs_cmd'] = qd.reshape((robot.model.nv,))
+    # we're not saving here, but need to respect the API, 
+    # hence the empty dict
+    return breakFlag, {}, log_item
+
 def controlLoopClikArmOnly(robot, clik_controller, i, past_data):
     breakFlag = False
     log_item = {}
@@ -291,6 +335,27 @@ def moveL(args, robot : RobotManager, goal_point):
     loop_manager = ControlLoopManager(robot, controlLoop, args, {}, log_item)
     loop_manager.run()
 
+def moveLDualArm(args, robot : RobotManager, goal_point, goal_transform):
+    """
+    moveL
+    -----
+    does moveL.
+    send a SE3 object as goal point.
+    if you don't care about rotation, make it np.zeros((3,3))
+    """
+    #assert type(goal_point) == pin.pinocchio_pywrap.SE3
+    robot.Mgoal = copy.deepcopy(goal_point)
+    clik_controller = getClikController(args)
+    controlLoop = partial(controlLoopClikDualArm, robot, clik_controller, goal_transform)
+    # we're not using any past data or logging, hence the empty arguments
+    log_item = {
+            'qs' : np.zeros(robot.model.nq),
+            'dqs' : np.zeros(robot.model.nv),
+            'dqs_cmd' : np.zeros(robot.model.nv),
+        }
+    loop_manager = ControlLoopManager(robot, controlLoop, args, {}, log_item)
+    loop_manager.run()
+
 # TODO: implement
 def moveLFollowingLine(args, robot, goal_point):
     """
diff --git a/python/ur_simple_control/managers.py b/python/ur_simple_control/managers.py
index a9cd4475810cc6f52fc7cc8824e87f510b381b3d..1360ab9d4d3ffbcce64a81494774355bd3f909a1 100644
--- a/python/ur_simple_control/managers.py
+++ b/python/ur_simple_control/managers.py
@@ -563,20 +563,37 @@ class RobotManager:
         return self.v_q.copy()
 
     def getT_w_e(self, q_given=None):
-        if q_given is None:
-            return self.T_w_e.copy()
+        if self.robot_name != "yumi":
+            if q_given is None:
+                return self.T_w_e.copy()
+            else:
+                assert type(q_given) is np.ndarray
+                # calling these here is ok because we rely
+                # on robotmanager attributes instead of model.something
+                # (which is copying data, but fully separates state and computation,
+                # which is important in situations like this)
+                pin.forwardKinematics(self.model, self.data, q_given, 
+                                      np.zeros(self.model.nv), np.zeros(self.model.nv))
+                # NOTE: this also returns the frame, so less copying possible
+                #pin.updateFramePlacements(self.model, self.data)
+                pin.updateFramePlacement(self.model, self.data, self.ee_frame_id)
+                return self.data.oMf[self.ee_frame_id].copy()
         else:
-            assert type(q_given) is np.ndarray
-            # calling these here is ok because we rely
-            # on robotmanager attributes instead of model.something
-            # (which is copying data, but fully separates state and computation,
-            # which is important in situations like this)
-            pin.forwardKinematics(self.model, self.data, q_given, 
-                                  np.zeros(self.model.nv), np.zeros(self.model.nv))
-            # NOTE: this also returns the frame, so less copying possible
-            #pin.updateFramePlacements(self.model, self.data)
-            pin.updateFramePlacement(self.model, self.data, self.ee_frame_id)
-            return self.data.oMf[self.ee_frame_id].copy()
+            if q_given is None:
+                return self.T_w_e_left.copy(), self.T_w_e_right.copy().copy()
+            else:
+                assert type(q_given) is np.ndarray
+                # calling these here is ok because we rely
+                # on robotmanager attributes instead of model.something
+                # (which is copying data, but fully separates state and computation,
+                # which is important in situations like this)
+                pin.forwardKinematics(self.model, self.data, q_given, 
+                                      np.zeros(self.model.nv), np.zeros(self.model.nv))
+                # NOTE: this also returns the frame, so less copying possible
+                #pin.updateFramePlacements(self.model, self.data)
+                pin.updateFramePlacement(self.model, self.data, self.r_ee_frame_id)
+                pin.updateFramePlacement(self.model, self.data, self.l_ee_frame_id)
+                return self.data.oMf[self.l_ee_frame_id].copy(), self.data.oMf[self.r_ee_frame_id].copy()
 
 
     # this is in EE frame by default (handled in step which
@@ -650,8 +667,14 @@ class RobotManager:
         # (includes forward kinematics, all jacobians, all dynamics terms, energies)
         #pin.computeAllTerms(self.model, self.data, self.q, self.v_q)
         pin.forwardKinematics(self.model, self.data, self.q, self.v_q)
-        pin.updateFramePlacement(self.model, self.data, self.ee_frame_id)
-        self.T_w_e = self.data.oMf[self.ee_frame_id].copy()
+        if self.robot_name != "yumi":
+            pin.updateFramePlacement(self.model, self.data, self.ee_frame_id)
+            self.T_w_e = self.data.oMf[self.ee_frame_id].copy()
+        else:
+            pin.updateFramePlacement(self.model, self.data, self.l_ee_frame_id)
+            pin.updateFramePlacement(self.model, self.data, self.r_ee_frame_id)
+            self.T_w_e_left = self.data.oMf[self.l_ee_frame_id].copy()
+            self.T_w_e_right = self.data.oMf[self.r_ee_frame_id].copy()
         # wrench in EE should obviously be the default
         self._getWrenchInEE(step_called=True)
         # this isn't real because we're on a velocity-controlled robot, 
@@ -750,8 +773,14 @@ class RobotManager:
         q = np.array(q)
         self.q = q
         pin.forwardKinematics(self.model, self.data, q)
-        pin.updateFramePlacement(self.model, self.data, self.ee_frame_id)
-        self.T_w_e = self.data.oMf[self.ee_frame_id].copy()
+        if self.robot_name != "yumi":
+            pin.updateFramePlacement(self.model, self.data, self.ee_frame_id)
+            self.T_w_e = self.data.oMf[self.ee_frame_id].copy()
+        else:
+            pin.updateFramePlacement(self.model, self.data, self.l_ee_frame_id)
+            pin.updateFramePlacement(self.model, self.data, self.r_ee_frame_id)
+            self.T_w_e_left = self.data.oMf[self.l_ee_frame_id].copy()
+            self.T_w_e_right = self.data.oMf[self.r_ee_frame_id].copy()
 
     def _getQd(self):
         """