diff --git a/python/examples/.drawing_from_input_drawing.py.swp b/python/examples/.drawing_from_input_drawing.py.swp
index f16debe88377c08adde32dd68a6a0aafb6b8ad24..a9ef8b4996e0bd1281f5d0bd4956cb4b044d3265 100644
Binary files a/python/examples/.drawing_from_input_drawing.py.swp and b/python/examples/.drawing_from_input_drawing.py.swp differ
diff --git a/python/examples/drawing_from_input_drawing.py b/python/examples/drawing_from_input_drawing.py
index bfa0b35bbd9bb523912534841b0b45c8be3a6293..d8760ef2902274655759a56484d1a4c854556aa7 100644
--- a/python/examples/drawing_from_input_drawing.py
+++ b/python/examples/drawing_from_input_drawing.py
@@ -59,8 +59,8 @@ def getArgs():
             TODO: check what this means", default=0.3)
     parser.add_argument('--speed-slider', type=float,\
             help="cap robot's speed with the speed slider \
-                    to something between 0 and 1, 0.5 by default \
-                    BE CAREFUL WITH THIS.", default=0.4)
+                    to something between 0 and 1, 1.0 by default because for dmp. \
+                    BE CAREFUL WITH THIS.", default=1.0)
     parser.add_argument('--max-iterations', type=int, \
             help="maximum allowable iteration number (it runs at 500Hz)", default=50000)
     #######################################################################
@@ -166,8 +166,8 @@ Obviously it's just a hacked solution, but it works so who cares.
 """
 def getMarkerOffset(rotation_matrix, translation_vector, q_init):
     # put this in a correct place
-    old_speed_slider = robot.speed_slider
-    robot.setSpeedSlider(0.3)
+#    old_speed_slider = robot.speed_slider
+#    robot.setSpeedSlider(0.3)
     # TODO: this isn't gonna work because it's not going 
     # orthogonaly to the board
     # so TODO: calculate TCP speed based on the rotation matrix
@@ -191,7 +191,7 @@ def getMarkerOffset(rotation_matrix, translation_vector, q_init):
     print("translation_vector - current_translation", \
             translation_vector - current_translation)
     marker_offset = np.linalg.norm(translation_vector - current_translation)
-    robot.setSpeedSlider(old_speed_slider)
+#    robot.setSpeedSlider(old_speed_slider)
     return marker_offset
 
 #######################################################################
@@ -299,7 +299,7 @@ if __name__ == "__main__":
     # do calibration if specified
     if args.calibration:
         rotation_matrix, translation_vector, q_init = \
-            calibratePlane(robot, args.board_width, args.board_height, \
+            calibratePlane(args, robot, args.board_width, args.board_height, \
                            args.n_calibration_tests)
     else:
         # TODO: save this somewhere obviously
diff --git a/python/examples/path_in_pixels.csv b/python/examples/path_in_pixels.csv
index 7714927a78d98221b2209eaed8d32f2703856d96..6f2f56163bc2f98ba3e080c746ef686d05b2c35f 100644
--- a/python/examples/path_in_pixels.csv
+++ b/python/examples/path_in_pixels.csv
@@ -1,124 +1,49 @@
-0.27578,0.76759
-0.27727,0.76608
-0.28025,0.76458
-0.31307,0.73749
-0.33843,0.71793
-0.36976,0.69686
-0.38169,0.68933
-0.39064,0.68332
-0.41749,0.66676
-0.42346,0.66074
-0.42793,0.65623
-0.45478,0.63967
-0.45926,0.63817
-0.46522,0.63215
-0.49208,0.61560
-0.49655,0.61259
-0.50401,0.60657
-0.52191,0.59754
-0.52638,0.59603
-0.52788,0.59453
-0.53533,0.59152
-0.55920,0.58249
-0.56368,0.57948
-0.59053,0.56293
-0.59948,0.55992
-0.60992,0.55691
-0.62782,0.54938
-0.63826,0.54637
-0.63975,0.54487
-0.64572,0.53885
-0.66213,0.52982
-0.66362,0.52982
-0.66810,0.52681
-0.67257,0.52380
-0.67406,0.52229
-0.67555,0.52229
-0.68003,0.51628
-0.68152,0.51477
-0.68301,0.51327
-0.68749,0.50725
-0.68749,0.50574
-0.68898,0.50424
-0.68898,0.50273
-0.68898,0.50123
-0.68749,0.50123
-0.68301,0.49822
-0.65616,0.48919
-0.64721,0.48919
-0.61290,0.47865
-0.60544,0.47564
-0.59649,0.47564
-0.56219,0.46511
-0.52489,0.45909
-0.48313,0.45909
-0.47417,0.45608
-0.45777,0.45608
-0.44882,0.45307
-0.41152,0.44856
-0.39511,0.44856
-0.38169,0.44856
-0.34738,0.44254
-0.33694,0.44254
+0.18180,0.64870
+0.19075,0.65171
+0.24744,0.65171
+0.26534,0.64870
+0.29517,0.64268
+0.30263,0.64118
+0.30710,0.63817
+0.33395,0.62312
+0.37274,0.58249
+0.37423,0.57797
+0.37871,0.56744
+0.38169,0.55841
+0.38169,0.54788
+0.38169,0.54336
+0.38169,0.53885
+0.38169,0.51477
+0.38169,0.50574
+0.37871,0.49822
+0.37274,0.49220
+0.35782,0.46661
+0.35186,0.46210
+0.32500,0.44555
 0.32053,0.44254
-0.29219,0.44254
-0.27578,0.44254
-0.26534,0.44254
-0.21611,0.44254
-0.20567,0.44254
-0.19821,0.44254
-0.16390,0.44856
-0.16241,0.44856
-0.15943,0.45006
-0.14898,0.45006
-0.14749,0.45006
-0.14600,0.45157
-0.14451,0.45157
-0.14451,0.45307
-0.14451,0.45458
-0.14451,0.45608
-0.15048,0.45608
-0.15197,0.45759
-0.15793,0.46360
-0.16241,0.46511
-0.17434,0.47263
-0.18180,0.47564
-0.20269,0.48919
-0.21313,0.49220
-0.22208,0.49521
-0.26832,0.52229
-0.27876,0.52530
-0.28771,0.52831
-0.29965,0.53584
-0.32202,0.54637
-0.32948,0.54788
-0.33694,0.55390
-0.34589,0.55691
-0.37125,0.57196
-0.37572,0.57497
-0.38467,0.57647
-0.41003,0.59302
-0.41451,0.59453
-0.42495,0.60205
-0.44136,0.60958
-0.45180,0.61710
-0.45627,0.62011
-0.46522,0.62162
-0.49208,0.63817
-0.49804,0.64118
-0.50998,0.64870
-0.55025,0.67278
-0.56815,0.68181
-0.57710,0.68482
-0.61290,0.70137
-0.61738,0.70739
-0.62633,0.71040
-0.65318,0.72696
-0.66064,0.72846
-0.66511,0.73147
-0.67555,0.73448
-0.67705,0.73599
-0.67854,0.73599
-0.67854,0.73749
-0.68003,0.73749
-0.68003,0.73749
+0.31158,0.43652
+0.28771,0.43200
+0.28622,0.43200
+0.28473,0.43200
+0.28025,0.43351
+0.28025,0.43501
+0.27727,0.43953
+0.27727,0.44856
+0.27727,0.46511
+0.27727,0.47263
+0.27727,0.48919
+0.29666,0.54788
+0.30561,0.56744
+0.32202,0.60356
+0.39362,0.72094
+0.42495,0.75856
+0.45478,0.79919
+0.54727,0.91356
+0.58158,0.94065
+0.60395,0.96924
+0.60395,0.96924
+0.60395,0.96924
+0.60395,0.96924
+0.60395,0.96924
+0.60395,0.96924
+0.60395,0.96924
diff --git a/python/ur_simple_control/.managers.py.swp b/python/ur_simple_control/.managers.py.swp
new file mode 100644
index 0000000000000000000000000000000000000000..ff3d6e01f31f9e881f271a98f3a6be33a1e8c8a4
Binary files /dev/null and b/python/ur_simple_control/.managers.py.swp differ
diff --git a/python/ur_simple_control/__pycache__/managers.cpython-310.pyc b/python/ur_simple_control/__pycache__/managers.cpython-310.pyc
index ccb51da838093a6f4a35c6013d4b2b91b781502e..096da1369580f1e89df4205b2cd3de66a679d0b5 100644
Binary files a/python/ur_simple_control/__pycache__/managers.cpython-310.pyc and b/python/ur_simple_control/__pycache__/managers.cpython-310.pyc differ
diff --git a/python/ur_simple_control/clik/.clik_point_to_point.py.swp b/python/ur_simple_control/clik/.clik_point_to_point.py.swp
new file mode 100644
index 0000000000000000000000000000000000000000..50eb85a9a671af30316c6c890d7473e7b1ab7fcf
Binary files /dev/null and b/python/ur_simple_control/clik/.clik_point_to_point.py.swp differ
diff --git a/python/ur_simple_control/clik/__pycache__/clik_point_to_point.cpython-310.pyc b/python/ur_simple_control/clik/__pycache__/clik_point_to_point.cpython-310.pyc
index 35180bad01703e847b6908f23e4ac77c45afb34b..742b227418d085d2c798b868d28a01f1ebb18069 100644
Binary files a/python/ur_simple_control/clik/__pycache__/clik_point_to_point.cpython-310.pyc and b/python/ur_simple_control/clik/__pycache__/clik_point_to_point.cpython-310.pyc differ
diff --git a/python/ur_simple_control/clik/clik_point_to_point.py b/python/ur_simple_control/clik/clik_point_to_point.py
index a85c17654827d05f1528c7c78cfcedb1d9ad324c..adf18cf033ce705b1a220395e561f36a2b2c29a6 100644
--- a/python/ur_simple_control/clik/clik_point_to_point.py
+++ b/python/ur_simple_control/clik/clik_point_to_point.py
@@ -4,6 +4,7 @@ import copy
 import argparse
 from functools import partial
 from ur_simple_control.managers import ControlLoopManager, RobotManager
+import time
 
 
 
@@ -141,32 +142,97 @@ def controlLoopClik(robot, clik_controller, i, past_data):
     SEerror = robot.data.oMi[robot.JOINT_ID].actInv(robot.Mgoal)
     err_vector = pin.log6(SEerror).vector 
     if np.linalg.norm(err_vector) < robot.goal_error:
-      print("Convergence achieved, reached destionation!")
-      breakFlag = True
+#      print("Convergence achieved, reached destionation!")
+        breakFlag = True
     # pin.computeJointJacobian is much different than the C++ version lel
     J = pin.computeJointJacobian(robot.model, robot.data, q, robot.JOINT_ID)
     # compute the joint velocities.
     # just plug and play different ones
     qd = clik_controller(J, err_vector)
     robot.sendQd(qd)
+    # TODO: only print under a debug flag, it's just clutter otherwise
     # we do the printing here because controlLoopManager should be general.
     # and these prints are click specific (although i'm not 100% happy with the arrangement)
-    if not i % 1000:
-        print("pos", robot.data.oMi[robot.JOINT_ID])
-        print("linear error = ", pin.log6(SEerror).linear)
-        print("angular error = ", pin.log6(SEerror).angular)
-        print(" error = ", err_vector.transpose())
+#    if not i % 1000:
+#        print("pos", robot.data.oMi[robot.JOINT_ID])
+#        print("linear error = ", pin.log6(SEerror).linear)
+#        print("angular error = ", pin.log6(SEerror).angular)
+#        print(" error = ", err_vector.transpose())
     # we're not saving or loggin here, but need to respect the API, 
     # hence the empty dicts
     return breakFlag, {}, {}
 
 
-if __name__ == "__main__": 
-    args = get_args()
-    robot = RobotManager(args)
-    Mgoal = robot.defineGoalPoint()
+"""
+moveUntilContactControlLoop
+---------------
+generic control loop for clik (handling error to final point etc).
+in some version of the universe this could be extended to a generic
+point-to-point motion control loop.
+"""
+def moveUntilContactControlLoop(speed, robot, clik_controller, i, past_data):
+    breakFlag = False
+    save_past_dict = {}
+    # know where you are, i.e. do forward kinematics
+    q = robot.getQ()
+    pin.forwardKinematics(robot.model, robot.data, q)
+    # break if wrench is nonzero basically
+    wrench = robot.getWrench()
+#    print(np.linalg.norm(wrench))
+    # TODO: remove magick number
+    if np.linalg.norm(wrench) > 4:
+        breakFlag = True
+    # pin.computeJointJacobian is much different than the C++ version lel
+    J = pin.computeJointJacobian(robot.model, robot.data, q, robot.JOINT_ID)
+    # compute the joint velocities.
+    qd = clik_controller(J, speed)
+    robot.sendQd(qd)
+    return breakFlag, {}, {}
+
+"""
+moveUntilContact
+-----
+does clik until it feels something with the f/t sensor
+"""
+def moveUntilContact(args, robot, speed):
+    assert type(speed) == np.ndarray 
+    clik_controller = getClikController(args)
+    # TODO: just make the manager pass the robot or something, this is weird man
+    controlLoop = partial(moveUntilContactControlLoop, speed, robot, clik_controller)
+    # we're not using any past data or logging, hence the empty arguments
+    loop_manager = ControlLoopManager(robot, controlLoop, args, {}, {})
+    loop_manager.run()
+    # TODO: remove, this isn't doing anything
+    time.sleep(0.01)
+    print("Colision detected!!")
+
+"""
+moveL
+-----
+does moveL.
+send a SE3 object as goal point.
+if you don't care about rotation, make it np.zeros((3,3))
+"""
+def moveL(args, robot, goal_point):
+    assert type(goal_point) == pin.pinocchio_pywrap.SE3
+    robot.Mgoal = copy.deepcopy(goal_point)
     clik_controller = getClikController(args)
     controlLoop = partial(controlLoopClik, robot, clik_controller)
     # we're not using any past data or logging, hence the empty arguments
     loop_manager = ControlLoopManager(robot, controlLoop, args, {}, {})
     loop_manager.run()
+    # TODO: remove, this isn't doing anything
+    time.sleep(0.01)
+    print("MoveL done: convergence achieved, reached destionation!")
+
+
+# TODO: remove once you know shit works (you might be importing incorectly)
+#if __name__ == "__main__": 
+#    args = get_args()
+#    robot = RobotManager(args)
+#    Mgoal = robot.defineGoalPoint()
+#    clik_controller = getClikController(args)
+#    controlLoop = partial(controlLoopClik, robot, clik_controller)
+#    # we're not using any past data or logging, hence the empty arguments
+#    loop_manager = ControlLoopManager(robot, controlLoop, args, {}, {})
+#    loop_manager.run()
diff --git a/python/ur_simple_control/managers.py b/python/ur_simple_control/managers.py
index 0e39dc8dcc907ad874cdcb891d00ae6b7d50dab0..af7c8db4ba3aab9fa1ab51fcdfa5d24530ed913a 100644
--- a/python/ur_simple_control/managers.py
+++ b/python/ur_simple_control/managers.py
@@ -1,5 +1,9 @@
 # TODO clear out unnecessary imports
 # TODO rename all private variables to start with '_'
+# TODO: just read the q and update everything every timestep, don't deepcopy,
+# --> just create a RobotManager.step() function, update everything there
+# don't do forwardKinematics 2 extra times for no good reason. make that the libraries
+# responsibility, not the users
 import pinocchio as pin
 import numpy as np
 import time
@@ -13,7 +17,6 @@ import copy
 import signal
 # TODO make the package work
 from ur_simple_control.util.get_model import get_model
-from ur_simple_control.util.robotiq_gripper import RobotiqGripper
 from collections import deque
 
 """
@@ -63,6 +66,7 @@ class ControlLoopManager:
     def __init__(self, robot_manager, controlLoop, args, save_past_dict, log_dict):
         self.max_iterations = args.max_iterations
         # i need rtde_control do to rtde_control
+        # TODO this thing might be re-initializing the robot
         self.robot_manager = robot_manager
         self.controlLoop = controlLoop
         self.args = args
@@ -108,28 +112,6 @@ class ControlLoopManager:
         # but i want to do it here. this is a big ick
         self.log_dict = log_dict
 
-        # if you just stop it the program with Ctrl-C, it will continue running
-        # the last speedj lmao.
-        # there's also an actual stop command, but sending 0s works so i'm not changing it
-        signal.signal(signal.SIGINT, self.stopHandler)
-
-
-    """
-    stopHandler
-    -----------
-    # can't have self as first argument, because the 
-    # signal handler has to have these 2 arguments (even though they're not used, but
-    # let's be correct if we can)
-    # so it's static, problem solved
-    """
-#    @staticmethod
-    def stopHandler(self, signum, frame):
-        print('sending 100 speedjs full of zeros and exiting')
-        for i in range(100):
-            vel_cmd = np.zeros(6)
-            self.robot_manager.rtde_control.speedJ(vel_cmd, 0.1, 1.0 / 500)
-        #exit()
-
     """
     run
     ---
@@ -167,12 +149,14 @@ class ControlLoopManager:
                 continue
             else:
                 time.sleep(self.dt - diff)
-        if i < self.max_iterations -1:
-            print("success in", i, "iterations!")
-        else:
-            print("FAIL: did not succed in", max_iterations, "iterations")
-        if not self.args.pinocchio_only:
-            self.stopHandler(None, None)
+# TODO: provide a debug flag for this
+#        if i < self.max_iterations -1:
+#            print("success in", i, "iterations!")
+#        else:
+#            print("FAIL: did not succed in", max_iterations, "iterations")
+# TODO: reintroduce maybe
+#        if not self.args.pinocchio_only:
+#            self.robot_manager.stopHandler(None, None)
 
 """
 robotmanager:
@@ -199,6 +183,11 @@ class RobotManager:
     #       like freedrive or forcemode or whatever.
     #       you shouldn't care about previous states
     def __init__(self, args):
+        # if you just stop it the program with Ctrl-C, it will continue running
+        # the last speedj lmao.
+        # there's also an actual stop command, but sending 0s works so i'm not changing it
+        signal.signal(signal.SIGINT, self.stopHandler)
+
         self.args = args
         self.pinocchio_only = args.pinocchio_only
         self.simulation = args.simulation
@@ -235,6 +224,8 @@ class RobotManager:
         if self.pinocchio_only:
             self.q = pin.neutral(self.model)
         elif not args.simulation:
+            # NOTE: you can't connect twice, dw 'bout that
+            print("CONNECTING TO UR5e!")
             self.rtde_control = RTDEControlInterface("192.168.1.102")
             self.rtde_receive = RTDEReceiveInterface("192.168.1.102")
             self.rtde_io = RTDEIOInterface("192.168.1.102")
@@ -279,6 +270,22 @@ class RobotManager:
         # TODO this is clik specific, put it in a better place
         self.goal_error = args.goal_error
 
+    """
+    stopHandler
+    -----------
+    TODO: make ifs for simulation too
+    can have self as first argument apparently.
+    upon receiving SIGINT it sends zeros for speed commands to
+    stop the robot
+    """
+    def stopHandler(self, signum, frame):
+        print('sending 100 speedjs full of zeros and exiting')
+        for i in range(100):
+            vel_cmd = np.zeros(6)
+            self.rtde_control.speedJ(vel_cmd, 0.1, 1.0 / 500)
+        exit()
+
+
     """
     setSpeedSlider
     ---------------
@@ -324,6 +331,41 @@ class RobotManager:
             return self.q
         return q
 
+    """
+    getMtool
+    -----
+    urdf treats gripper as two prismatic joints, 
+    but they do not affect the overall movement
+    of the robot, so we add or remove 2 items to the joint list.
+    also, the gripper is controlled separately so we'd need to do this somehow anyway 
+    NOTE: this gripper_past_pos thing is not working atm, but i'll keep it here as a TODO
+    """
+    def getMtool(self):
+        if not self.pinocchio_only:
+            q = self.rtde_receive.getActualQ()
+            if self.args.gripper:
+                # TODO: make it work or remove it
+                #self.gripper_past_pos = self.gripper_pos
+                # this is pointless by itself
+                self.gripper_pos = self.gripper.get_current_position()
+                # the /255 is to get it dimensionless.
+                # the gap is 5cm,
+                # thus half the gap is 0.025m (and we only do si units here).
+                q.append((self.gripper_pos / 255) * 0.025)
+                q.append((self.gripper_pos / 255) * 0.025)
+            else:
+                # just fill it with zeros otherwise
+                q.append(0.0)
+                q.append(0.0)
+        else:
+            q = self.q
+        q = np.array(q)
+        self.q = q
+        pin.forwardKinematics(self.model, self.data, q)
+        # TODO probably remove deepcopy
+        self.Mtool = self.data.oMi[self.JOINT_ID]
+        return copy.deepcopy(self.Mtool)
+
     """
     getQd
     -----
diff --git a/python/ur_simple_control/util/.calib_board_hacks.py.swp b/python/ur_simple_control/util/.calib_board_hacks.py.swp
index 0c65b52814ff442ca7d522cc8b831f8b20b5c347..c991d6f05588066e2000883dccfddc9f00ddec33 100644
Binary files a/python/ur_simple_control/util/.calib_board_hacks.py.swp and b/python/ur_simple_control/util/.calib_board_hacks.py.swp differ
diff --git a/python/ur_simple_control/util/__pycache__/calib_board_hacks.cpython-310.pyc b/python/ur_simple_control/util/__pycache__/calib_board_hacks.cpython-310.pyc
index 18ef112aef1717a31bc5af78a2f9a5da0aa64059..9fbe6dde187a2c199737f2ed5ab83fdad5896ef4 100644
Binary files a/python/ur_simple_control/util/__pycache__/calib_board_hacks.cpython-310.pyc and b/python/ur_simple_control/util/__pycache__/calib_board_hacks.cpython-310.pyc differ
diff --git a/python/ur_simple_control/util/calib_board_hacks.py b/python/ur_simple_control/util/calib_board_hacks.py
index c1db22c8d4dc7bbea96b28358e309068b79642b3..1d66a7abb6cc624ca99bb777841320578bf73529 100644
--- a/python/ur_simple_control/util/calib_board_hacks.py
+++ b/python/ur_simple_control/util/calib_board_hacks.py
@@ -7,6 +7,7 @@ import time
 import copy
 from ur_simple_control.managers import RobotManager
 from ur_simple_control.util.freedrive import freedrive
+from ur_simple_control.clik.clik_point_to_point import moveL, moveUntilContact
 # used to deal with freedrive's infinite while loop
 import threading
 
@@ -35,26 +36,47 @@ you should do a weighting of these points in the least squares
 def fitNormalVector(positions):
     positions = np.array(positions)
 # non-weighted least square
-#    n = np.linalg.lstsq(positions, np.ones(len(positions)), rcond=None)[0]
+    # none
+    n_non_weighted = np.linalg.lstsq(positions, np.ones(len(positions)), rcond=None)[0]
+    n_non_weighted = n_non_weighted / np.linalg.norm(n_non_weighted)
+    print("n_non_weighted", n_non_weighted)
+    for p in positions:
+        print("cdot none", p @ n_non_weighted)
     # bs weights, just to weight the new things more and linear is probably good
     # (altough it could/should be even more agressive probably)
     # +1 to start with 1 (0 both doesn't make sense and is makes W singular)
-    W = np.diag(np.arange(1, len(positions) + 1))
 #    print("W", W)
 #    print("positions", positions)
 #    print("positions.T", positions.T)
 #    print("positions.T @ W @ positions", positions.T @ W @ positions)
 #    print("np.linalg.inv(positions.T @ W @ positions)", np.linalg.inv(positions.T @ W @ positions))
-    n = np.linalg.inv(positions.T @ W @ positions) @ positions.T @ W @ np.ones(len(positions))
+    # weak
+    try:
+        W = np.diag(np.linspace(1, 1.2, len(positions)))
+        n_slightly_weighted = np.linalg.inv(positions.T @ W @ positions) @ positions.T @ W @ np.ones(len(positions))
+        n_slightly_weighted = n_slightly_weighted / np.linalg.norm(n_slightly_weighted)
+        print("n_slightly_weighted", n_slightly_weighted)
+        for p in positions:
+            print("cdot weak", p @ n_slightly_weighted)
+    except numpy.linalg.LinAlgError:
+        print("n_slightly_weighted is singular bruh")
+
+    # strong
+    W = np.diag(np.arange(1, len(positions) + 1))
+    n_strongly_weighted = np.linalg.inv(positions.T @ W @ positions) @ positions.T @ W @ np.ones(len(positions))
     #print("n", *n, sep=',')
     # normalize
     # TODO why am i not doing this again?
     #n = n - 1
-    n = n / np.linalg.norm(n)
-    print("if the following give you roughly the same number, it worked")
-    for p in positions:
-        print("cdot", p @ n)
-    return n
+    try:
+        n_strongly_weighted = n_strongly_weighted / np.linalg.norm(n_strongly_weighted)
+        print("n_strongly_weighed", n_strongly_weighted)
+        print("if the following give you roughly the same number, it worked")
+        for p in positions:
+            print("cdot strong", p @ n_strongly_weighted)
+    except numpy.linalg.LinAlgError:
+        print("n_strongly_weighted is singular bruh")
+    return n_non_weighted
 
 """
 constructFrameFromNormalVector
@@ -207,7 +229,9 @@ def getSpeedInDirectionOfN(R):
     # make speed small no matter what
     speed = speed / np.linalg.norm(speed)
     # nice 'n' slow
-    speed = speed / 10
+    # TODO: remove magic number
+    speed = speed / 40
+    speed[2] = -1 * speed[2]
     return speed
 
 # TODO
@@ -218,21 +242,31 @@ def getSpeedInDirectionOfN(R):
 # --> then you don't even need to use the stupid rpy,
 # but instead rock only rotation matrices the way it should be.
 # TODO: replace all moveUntilContacts with your own implementation
-def calibratePlane(robot, plane_width, plane_height, n_tests):
+# and of course replace the stupid speeds
+# TODO move width and height, rock just args
+def calibratePlane(args, robot, plane_width, plane_height, n_tests):
     # i don't care which speed slider you have,
     # because 0.4 is the only reasonable one here
-    old_speed_slider = robot.speed_slider
-    robot.setSpeedSlider(0.4)
+#    old_speed_slider = robot.speed_slider
+#    robot.setSpeedSlider(0.7)
     handleUserToHandleTCPPose(robot)
     q_init = copy.deepcopy(robot.getQ())
+    # GET TCP
+    Mtool = robot.getMtool()
 
-    init_pose = copy.deepcopy(robot.rtde_receive.getActualTCPPose())
+    init_pose = copy.deepcopy(Mtool)
     new_pose = copy.deepcopy(init_pose)
 
-    q = robot.getQ()
-    pin.forwardKinematics(robot.model, robot.data, q)
+    # you just did it dawg.
+    # i mean i know it's in the robot
+    # TODO: don't manage forward kinematics yourself,
+    # just create a RobotManager.step() function, update everything there
+    #q = robot.getQ()
+    #pin.forwardKinematics(robot.model, robot.data, q)
     # this apsolutely has to be deepcopied aka copy-constructed
-    R_initial_estimate = copy.deepcopy(robot.data.oMi[robot.JOINT_ID].rotation)
+    #R_initial_estimate = copy.deepcopy(robot.data.oMi[robot.JOINT_ID].rotation)
+    R_initial_estimate = copy.deepcopy(Mtool.rotation)
+    R = copy.deepcopy(R_initial_estimate)
 
     # go in the TCP z direction
     speed = getSpeedInDirectionOfN(R_initial_estimate)
@@ -240,35 +274,37 @@ def calibratePlane(robot, plane_width, plane_height, n_tests):
 
     positions = []
     for i in range(n_tests):
-        robot.rtde_control.moveUntilContact(speed)
+        time.sleep(0.01)
+        print("iteration number:", i)
+        #robot.rtde_control.moveUntilContact(speed)
+        moveUntilContact(args, robot, speed)
+        # TODO: replace forwardkinematics call with robot.step()
         q = robot.getQ()
         pin.forwardKinematics(robot.model, robot.data, np.array(q))
-        print("pin:", *robot.data.oMi[6].translation.round(4), \
-                *pin.rpy.matrixToRpy(robot.data.oMi[6].rotation).round(4))
+        print("pin:", *robot.data.oMi[robot.JOINT_ID].translation.round(4), \
+                *pin.rpy.matrixToRpy(robot.data.oMi[robot.JOINT_ID].rotation).round(4))
         print("ur5:", *np.array(robot.rtde_receive.getActualTCPPose()).round(4))
 
-        positions.append(copy.deepcopy(robot.data.oMi[6].translation))
+        positions.append(copy.deepcopy(robot.data.oMi[robot.JOINT_ID].translation))
         if i < n_tests -1:
-            current_pose = robot.rtde_receive.getActualTCPPose()
+            current_pose = robot.getMtool()
             new_pose = copy.deepcopy(current_pose)
             # go back up (assuming top-left is highest incline)
             # TODO: make this assumption an argument, or print it at least
-            new_pose[2] = init_pose[2]
+            new_pose.translation[2] = init_pose.translation[2]
             # TODO make these moveLs slower, they're way too fast
-            robot.rtde_control.moveL(new_pose)
+            #robot.rtde_control.moveL(new_pose)
+            moveL(args, robot, new_pose)
+            q = robot.getQ()
+            pin.forwardKinematics(robot.model, robot.data, np.array(q))
             # TODO: make this not base-orientation dependent,
             # this is also an ugly ugly hack
-            new_pose[0] = init_pose[0] + np.random.random() * plane_width
-            new_pose[1] = init_pose[1] - np.random.random() * plane_height
-            robot.rtde_control.moveL(new_pose)
+            new_pose.translation[0] = init_pose.translation[0] + np.random.random() * plane_width
+            new_pose.translation[1] = init_pose.translation[1] - np.random.random() * plane_height
+            moveL(args, robot, new_pose)
             # fix orientation
-            rpy = pin.rpy.matrixToRpy(R_initial_estimate)
-            rpy = fixPinsRPY(rpy)
-            # TODO: ensure these rpy's make sense (no weird trig messing it up)
-            new_pose[3] = rpy[0]
-            new_pose[4] = rpy[1]
-            new_pose[5] = rpy[2]
-            robot.rtde_control.moveL(new_pose)
+            new_pose.rotation = R
+            moveL(args, robot, new_pose)
         # skip the first one
         if i > 0:
             n = fitNormalVector(positions)
@@ -277,27 +313,26 @@ def calibratePlane(robot, plane_width, plane_height, n_tests):
 
     print("finished estimating R")
 
-    current_pose = robot.rtde_receive.getActualTCPPose()
+    current_pose = robot.getMtool()
     new_pose = copy.deepcopy(current_pose)
     # go back up
-    new_pose[2] = init_pose[2]
-    robot.rtde_control.moveL(new_pose)
+    new_pose.translation[2] = init_pose.translation[2]
+    moveL(args, robot, new_pose)
     # go back to the same spot
-    new_pose[0] = init_pose[0]
-    new_pose[1] = init_pose[1]
-    new_pose[2] = init_pose[2]
+    new_pose.translation[0] = init_pose.translation[0]
+    new_pose.translation[1] = init_pose.translation[1]
+    new_pose.translation[2] = init_pose.translation[2]
     # but in new orientation
-    rpy = pin.rpy.matrixToRpy(R)
-    rpy = fixPinsRPY(rpy)
-    new_pose[3] = rpy[0]
-    new_pose[4] = rpy[1]
-    new_pose[5] = rpy[2]
-    robot.rtde_control.moveL(new_pose)
+    new_pose.rotation = R
+    moveL(args, robot, new_pose)
+    
+    # TODO there's certainly no need for all of these moves bro
     # --> now you're ready to measure the translation vector correctly
     # for this we want to go directly into the board
     print("i'll estimate the translation vector now")
     speed = getSpeedInDirectionOfN(R)
-    robot.rtde_control.moveUntilContact(speed)
+
+    moveUntilContact(args, robot, speed)
 
     q = robot.getQ()
     pin.forwardKinematics(robot.model, robot.data, np.array(q))
@@ -307,27 +342,28 @@ def calibratePlane(robot, plane_width, plane_height, n_tests):
     # TODO: get rid of all movels, just your own stuff,
     # or at least shove them avait to the RobotManager
     # and now go back up
-    current_pose = robot.rtde_receive.getActualTCPPose()
+    current_pose = robot.getMtool()
     new_pose = copy.deepcopy(current_pose)
-    new_pose[2] = init_pose[2]
-    robot.rtde_control.moveL(new_pose)
+    new_pose.translation[2] = init_pose.translation[2]
+    moveL(args, robot, new_pose)
     q = robot.getQ()
     init_q = copy.deepcopy(q)
     print("went back up, saved this q as initial q")
     
     # put the speed slider back to its previous value
-    robot.setSpeedSlider(old_speed_slider)
+#    robot.setSpeedSlider(old_speed_slider)
     print('also, the translation vector is:', translation)
     return R, translation, q_init
 
-if __name__ == "__main__":
-    robot = RobotManager()
-    # TODO make this an argument
-    n_tests = 10
-    # TODO: 
-    # - tell the user what to do with prints, namely where to put the end-effector
-    #   to both not break things and also actually succeed
-    # - start freedrive
-    # - use some keyboard input [Y/n] as a blocking call,
-    #   release the freedrive and then start doing the calibration process
-    calibratePlane(robot, n_tests)
+# TODO: remove once you know shit works (you might be importing incorectly)
+#if __name__ == "__main__":
+#    robot = RobotManager()
+#    # TODO make this an argument
+#    n_tests = 10
+#    # TODO: 
+#    # - tell the user what to do with prints, namely where to put the end-effector
+#    #   to both not break things and also actually succeed
+#    # - start freedrive
+#    # - use some keyboard input [Y/n] as a blocking call,
+#    #   release the freedrive and then start doing the calibration process
+#    calibratePlane(robot, n_tests)