diff --git a/python/UNKNOWN.egg-info/PKG-INFO b/python/UNKNOWN.egg-info/PKG-INFO
deleted file mode 100644
index aed30b263857a903b62772da3b28efe924654cb5..0000000000000000000000000000000000000000
--- a/python/UNKNOWN.egg-info/PKG-INFO
+++ /dev/null
@@ -1,11 +0,0 @@
-Metadata-Version: 2.1
-Name: UNKNOWN
-Version: 0.0.0
-Summary: UNKNOWN
-Home-page: UNKNOWN
-License: UNKNOWN
-Platform: UNKNOWN
-License-File: LICENSE.txt
-
-UNKNOWN
-
diff --git a/python/UNKNOWN.egg-info/SOURCES.txt b/python/UNKNOWN.egg-info/SOURCES.txt
deleted file mode 100644
index 4130086e9a697c122668e00de1aade3de571722c..0000000000000000000000000000000000000000
--- a/python/UNKNOWN.egg-info/SOURCES.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-LICENSE.txt
-README.md
-pyproject.toml
-setup.cfg
-UNKNOWN.egg-info/PKG-INFO
-UNKNOWN.egg-info/SOURCES.txt
-UNKNOWN.egg-info/dependency_links.txt
-UNKNOWN.egg-info/top_level.txt
\ No newline at end of file
diff --git a/python/UNKNOWN.egg-info/dependency_links.txt b/python/UNKNOWN.egg-info/dependency_links.txt
deleted file mode 100644
index 8b137891791fe96927ad78e64b0aad7bded08bdc..0000000000000000000000000000000000000000
--- a/python/UNKNOWN.egg-info/dependency_links.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/python/UNKNOWN.egg-info/top_level.txt b/python/UNKNOWN.egg-info/top_level.txt
deleted file mode 100644
index 8b137891791fe96927ad78e64b0aad7bded08bdc..0000000000000000000000000000000000000000
--- a/python/UNKNOWN.egg-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/python/pyproject.toml b/python/alternative_packaging_BROKEN_atm/pyproject.toml
similarity index 100%
rename from python/pyproject.toml
rename to python/alternative_packaging_BROKEN_atm/pyproject.toml
diff --git a/python/alternative_packaging_BROKEN_atm/setup.cfg b/python/alternative_packaging_BROKEN_atm/setup.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/python/convenience_tool_box/.measuring_stick.py.swp b/python/convenience_tool_box/.measuring_stick.py.swp
new file mode 100644
index 0000000000000000000000000000000000000000..565f0d5acdeb8db8100b7857fee177e575e0ce70
Binary files /dev/null and b/python/convenience_tool_box/.measuring_stick.py.swp differ
diff --git a/python/ur_simple_control.egg-info/PKG-INFO b/python/ur_simple_control.egg-info/PKG-INFO
deleted file mode 100644
index 5f07aec0f6e65949b1decf64933d9c84e110f2be..0000000000000000000000000000000000000000
--- a/python/ur_simple_control.egg-info/PKG-INFO
+++ /dev/null
@@ -1,39 +0,0 @@
-Metadata-Version: 2.1
-Name: ur-simple-control
-Version: 0.0.1
-Summary: Collection of control algorithms for the UR5e arm based on the ur_rtde interface for communication and pinocchio for calculations.
-Author-email: Marko Guberina <marko.guberina@control.lth.se>
-Maintainer-email: Marko Guberina <marko.guberina@control.lth.se>
-License: Do whatever you want at your own risk.
-        
-Project-URL: Homepage, https://gitlab.control.lth.se/marko-g/ur_simple_control
-Project-URL: Documentation, https://gitlab.control.lth.se/marko-g/ur_simple_control
-Project-URL: Repository, https://gitlab.control.lth.se/marko-g/ur_simple_control
-Project-URL: Bug Tracker, https://gitlab.control.lth.se/marko-g/ur_simple_control
-Project-URL: Changelog, https://gitlab.control.lth.se/marko-g/ur_simple_control
-Keywords: manipulator,control algorithm
-Description-Content-Type: text/markdown
-License-File: LICENSE.txt
-Requires-Dist: ur_rtde
-
-# installation
-------------
-first you MUST update pip, otherwise it won't work:
-python3 -m pip install --upgrade pip setuptools wheel build
-then install with 
-pip install --user -e . 
-from this directory. now the package is editable, which there's a chance you'd want
-
-# description
----------
-- organized as a library called ur_simple_control, made into a python package. the hope is that if this
-  is done well enough, you could mix-and-match different components
-  and just have them work as intended. on the other hand,
-  the code should still be simple enough to afford the quickest possible prototyping,
-  which is the point of having it all in python anyway
-- initial python solution is age-old code which needs to be remapped into the 
-  libraries used here. it will sit here until everything it offers has been translated.
-
-# runnable things
----------------
-are in the examples folder
diff --git a/python/ur_simple_control.egg-info/SOURCES.txt b/python/ur_simple_control.egg-info/SOURCES.txt
deleted file mode 100644
index c8df78094ede90aec439c4d5d41d82172472d827..0000000000000000000000000000000000000000
--- a/python/ur_simple_control.egg-info/SOURCES.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-LICENSE.txt
-README.md
-pyproject.toml
-setup.py
-ur_simple_control/__init__.py
-ur_simple_control.egg-info/PKG-INFO
-ur_simple_control.egg-info/SOURCES.txt
-ur_simple_control.egg-info/dependency_links.txt
-ur_simple_control.egg-info/requires.txt
-ur_simple_control.egg-info/top_level.txt
-ur_simple_control/clik/__init__.py
-ur_simple_control/clik/clik.py
-ur_simple_control/dmp/__init__.py
-ur_simple_control/dmp/notes.md
-ur_simple_control/dmp/robotiq_gripper.py
-ur_simple_control/dmp/run_dmp.py
-ur_simple_control/util/__init__.py
-ur_simple_control/util/calib_board_hacks.py
-ur_simple_control/util/give_me_the_calibrated_model.py
-ur_simple_control/util/robotiq_gripper.py
\ No newline at end of file
diff --git a/python/ur_simple_control.egg-info/dependency_links.txt b/python/ur_simple_control.egg-info/dependency_links.txt
deleted file mode 100644
index 8b137891791fe96927ad78e64b0aad7bded08bdc..0000000000000000000000000000000000000000
--- a/python/ur_simple_control.egg-info/dependency_links.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/python/ur_simple_control.egg-info/requires.txt b/python/ur_simple_control.egg-info/requires.txt
deleted file mode 100644
index e95a92e86f6092929365f59c5d81dd0227e84e5b..0000000000000000000000000000000000000000
--- a/python/ur_simple_control.egg-info/requires.txt
+++ /dev/null
@@ -1 +0,0 @@
-ur_rtde
diff --git a/python/ur_simple_control.egg-info/top_level.txt b/python/ur_simple_control.egg-info/top_level.txt
deleted file mode 100644
index 8b137891791fe96927ad78e64b0aad7bded08bdc..0000000000000000000000000000000000000000
--- a/python/ur_simple_control.egg-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-
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..7e2d03a88b0a8fd17bdc30aa40008b6dee547ff7
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 0f5dccae2261d8c97021c2275cf8f2d53cccac1b..7f40d33c8673376eaf071c653fbb2ea878f8f92f 100644
--- a/python/ur_simple_control/clik/clik.py
+++ b/python/ur_simple_control/clik/clik.py
@@ -1,25 +1,22 @@
 import pinocchio as pin
 import numpy as np
-import sys
-import os
-from os.path import dirname, join, abspath
-import time
-from pinocchio.visualize import GepettoVisualizer
-#import gepetto.corbaserver
-from rtde_control import RTDEControlInterface as RTDEControl
-from rtde_receive import RTDEReceiveInterface as RTDEReceive
-from rtde_io import RTDEIOInterface
-from robotiq_gripper import RobotiqGripper
-import os
 import copy
-import signal
-sys.path.insert(0, '../util')
-from give_me_the_calibrated_model import get_model
 import argparse
 from functools import partial
-from ur_simple_control.util.boilerplate_wrapper import controlLoopTiming, RobotManager
-
+from ur_simple_control.util.boilerplate_wrapper import ControlLoopManager, RobotManager
 
+"""
+Every imaginable magic number, flag and setting is put in here.
+This way the rest of the code is clean.
+If you want to know what these various arguments do, just grep 
+though the code to find them (but replace '-' with '_' in multi-word arguments).
+All the sane defaults are here, and you can change any number of them as an argument
+when running your program. If you want to change a drastic amount of them, and
+not have to run a super long commands, just copy-paste this function and put your
+own parameters as default ones.
+NOTE: the args object obtained from args = parser.get_args()
+is pushed all around the rest of the code (because it's tidy), so don't change their names.
+"""
 def get_args():
     parser = argparse.ArgumentParser(description='Run closed loop inverse kinematics \
             of various kinds. Make sure you know what the goal is before you run!',
@@ -30,112 +27,111 @@ def get_args():
             help="whether you want to just integrate with pinocchio", default=False)
     parser.add_argument('--visualize', action=argparse.BooleanOptionalAction, 
             help="whether you want to visualize with gepetto, but NOTE: not implemented yet", default=False)
-    parser.add_argument('--gripper', action=argparse.BooleanOptionalAction, 
+    parser.add_argument('--gripper', action=argparse.BooleanOptionalAction, \
             help="whether you're using the gripper", default=False)
-    parser.add_argument('--goal-error', type=float, 
+    parser.add_argument('--goal-error', type=float, \
             help="the final position error you are happy with", default=1e-3)
-    parser.add_argument('--max-iterations', type=int, 
+    parser.add_argument('--max-iterations', type=int, \
             help="maximum allowable iteration number (it runs at 500Hz)", default=100000)
-    parser.add_argument('--acceleration', type=float, 
-            help="robot's acceleration, positive constant, max 1.7, 0.2 by default. \
-                   BE CAREFUL WITH THIS.", default=0.4)
-    parser.add_argument('--speed-slider', type=float,
+    parser.add_argument('--acceleration', type=float, \
+            help="robot's joints acceleration. scalar positive constant, max 1.7, and default 0.4. \
+                   BE CAREFUL WITH THIS. the urscript doc says this is 'lead axis acceleration'.\
+                   TODO: check what this means", default=0.4)
+    parser.add_argument('--speed-slider', type=float,\
             help="cap robot's speed with the speed slider \
                     to something between 0 and 1, 0.25 by default \
                     BE CAREFUL WITH THIS.", default=0.25)
-    parser.add_argument('--tikhonov-damp', type=float, 
-            help="damping scalar in tiknohov regularization", default=1e-6)
+    parser.add_argument('--tikhonov-damp', type=float, \
+            help="damping scalar in tiknohov regularization", default=1e-2)
+    # TODO add the rest
+    parser.add_argument('--controller', type=str, \
+            help="select which click algorithm you want", \
+            default='dampedPseudoinverse', choices=['dampedPseudoinverse', 'jacobianTranspose'])
 
-#    parser.add_argument(
-#        '--device', type=str, default='cuda' if torch.cuda.is_available() else 'cpu'
-#    )
     args = parser.parse_args()
     if args.gripper and args.simulation:
         raise NotImplementedError('Did not figure out how to put the gripper in \
                 the simulation yet, sorry :/ . You can have only 1 these flags right now')
-    if success: 
-        print("Convergence achieved!")
-    else:
-        print("Warning: the iterative algorithm has not reached convergence to the desired precision")
-
-    print("final error", err_vector.transpose()) 
     return args
 
-def clik(robot_manager):
-    # define goal
-    # TODO this should be handled in some better way, but one step at a time
-    q = robot.rtde_receive.getActualQ()
-    # 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 
-    q.append(0.0)
-    q.append(0.0)
-    pin.forwardKinematics(robot.model, robot.data, np.array(q))
-    Mtool = robot.data.oMi[JOINT_ID]
-    print("pos", Mtool)
-    #SEerror = pin.SE3(np.zeros((3,3)), np.array([0.0, 0.0, 0.1])
-    Mgoal = copy.deepcopy(Mtool)
-    Mgoal.translation = Mgoal.translation + np.array([0.0, 0.0, -0.1])
-    print("goal", Mgoal)
-    success = False
-    def controlLoop():
-        q = rtde_receive.getActualQ()
-        if not SIMULATION:
-            gripper_pos = gripper.get_current_position()
-            # all 3 are between 0 and 255
-            #gripper.move(i % 255, 100, 100)
-            # just padding to fill q, which is only needed for forward kinematics
-            #q.append(gripper_pos)
-            #q.append(gripper_pos)
-            q.append(0.0)
-            q.append(0.0)
-            # pinocchio wants an ndarray
-            q = np.array(q)
-        pin.forwardKinematics(model, data, q)
-        SEerror = data.oMi[JOINT_ID].actInv(Mgoal)
-        err_vector = pin.log6(SEerror).vector 
-        if np.linalg.norm(err_vector) < eps:
-          success = True
-          print("reached destionation")
-          break
-        if i >= IT_MAX: 
-            success = false
-            print("FAIL: did not succed in IT_MAX iterations")
-            break
-        # this does the whole thing unlike the C++ version lel
-        J = pin.computeJointJacobian(model, data, q, JOINT_ID)
-        #J = J + np.eye(J.shape[0], J.shape[1]) * 10**-4
-        # idk what i was thinking here lol
-        #v = np.matmul(np.linalg.pinv(J), pin.log(SEerror.inverse() * Mgoal).vector)
-        #v = J.T @ err_vector
-        #v = np.linalg.pinv(J) @ err_vector
-        v = J.T @ np.linalg.inv(J @ J.T + np.eye(J.shape[0], J.shape[0]) * 10**-2) @ err_vector
-        v_cmd = v[:6]
-        v_cmd = v_cmd * 5
-        v_cmd = np.clip(v_cmd, -2, 2)
-        if not SIMULATION:
-            rtde_control.speedJ(v_cmd, acceleration, dt)
-        else:
-            q = pin.integrate(model, q, v * dt)
-        if not i % 1000:
-            print("pos", data.oMi[JOINT_ID])
-            print("linear error = ", pin.log6(SEerror).linear)
-            print("angular error = ", pin.log6(SEerror).angular)
-            print(" error = ", err_vector.transpose())
+#######################################################################
+#                             controllers                             #
+#######################################################################
+def dampedPseudoinverse(tiknonov_damp, J, err_vector):
+    qd = J.T @ np.linalg.inv(J @ J.T + np.eye(J.shape[0], J.shape[0]) * tiknonov_damp) @ err_vector
+    return qd
+
+def jacobianTranspose(J, err_vector):
+    qd = J.T @ err_vector
+    return qd
+
+"""
+A string argument is used to select one of these.
+It's a bit ugly, bit totally functional and OK solution.
+we want all of theme to accept the same arguments, i.e. the jacobian and the error vector.
+if they have extra stuff, just map it in the beginning with partial
+NOTE: this could be changed to something else if it proves inappropriate later
+TODO: write out other algorithms
+"""
+def getController(args):
+    if args.controller == "dampedPseudoinverse":
+        return partial(dampedPseudoinverse, args.tiknonov_damp)
+    if args.controller == "jacobianTranspose":
+        return jacobianTranspose
+    # TODO implement and add in the rest
+    #if controller_name == "invKinmQPSingAvoidE_kI":
+    #    return invKinmQPSingAvoidE_kI
+    #if controller_name == "invKinm_PseudoInv":
+    #    return invKinm_PseudoInv
+    #if controller_name == "invKinm_PseudoInv_half":
+    #    return invKinm_PseudoInv_half
+    #if controller_name == "invKinmQP":
+    #    return invKinmQP
+    #if controller_name == "invKinmQPSingAvoidE_kI":
+    #    return invKinmQPSingAvoidE_kI
+    #if controller_name == "invKinmQPSingAvoidE_kM":
+    #    return invKinmQPSingAvoidE_kM
+    #if controller_name == "invKinmQPSingAvoidManipMax":
+    #    return invKinmQPSingAvoidManipMax
+
+    # default
+    return partial(dampedPseudoinverse, args.tiknonov_damp)
 
-    for i in range(robot.args.max_iterations):
-        controlLoopTiming(partial(clik, robot))
 
-    if success: 
-        print("Convergence achieved!")
-    else:
-        print("Warning: the iterative algorithm has not reached convergence to the desired precision")
+# modularity yo
+def controlLoopClik(robot, controller, i):
+    breakFlag = False
+    # know where you are, i.e. do forward kinematics
+    q = robot.getQ()
+    pin.forwardKinematics(robot.model, robot.data, q)
+    # first check whether we're at the goal
+    SEerror = robot.data.oMi[robot.JOINT_ID].actInv(robot.Mgoal)
+    err_vector = pin.log6(SEerror).vector 
+    if np.linalg.norm(err_vector) < eps:
+      print("Convergence achieved, reached destionation!")
+      breakFlag = True
+    # pin.computeJointJacobian is much different than the C++ version lel
+    J = pin.computeJointJacobian(model, data, q, JOINT_ID)
+    # compute the joint velocities.
+    # just plug and play different ones
+    qd = controller(J, err_vector)
+    robot.sendQd(qd)
+    # 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", data.oMi[JOINT_ID])
+        print("linear error = ", pin.log6(SEerror).linear)
+        print("angular error = ", pin.log6(SEerror).angular)
+        print(" error = ", err_vector.transpose())
+    return breakFlag
 
-    print("final error", err_vector.transpose()) 
-    handler(None, None)
 
 
 if __name__ == "__main__": 
     args = get_args()
     robot = RobotManager(args)
+    Mgoal = robot.defineGoalPoint()
+    controller = getController(args)
+    controlLoop = partial(controlLoopClik, robot, controller)
+    controlLoopManager(controlLoop, args)
+    controlLoopManager.run()
diff --git a/python/ur_simple_control/util/.boilerplate_wrapper.py.swp b/python/ur_simple_control/util/.boilerplate_wrapper.py.swp
new file mode 100644
index 0000000000000000000000000000000000000000..d0befeb34512b3259ffe76f42513039b4404548a
Binary files /dev/null and b/python/ur_simple_control/util/.boilerplate_wrapper.py.swp differ
diff --git a/python/ur_simple_control/util/boilerplate_wrapper.py b/python/ur_simple_control/util/boilerplate_wrapper.py
index 9441085d306384b22925a91e6edf85ffc6cd3309..99e0492d741f8c29f28b91c3f3d0308b1ca78474 100644
--- a/python/ur_simple_control/util/boilerplate_wrapper.py
+++ b/python/ur_simple_control/util/boilerplate_wrapper.py
@@ -15,33 +15,95 @@ from ur_simple_control.util.get_model import get_model
 from ur_simple_control.util.robotiq_gripper import RobotiqGripper
 import argparse
 
-# slightly fancier programming to get a timing wrapper around the control loop
-def controlLoopTiming(controlLoop):
-    start = time.time()
-    controlLoop()
-    end = time.time()
-    diff = end - start
-    if dt < diff:
-        print("missed deadline by", diff - dt)
-        continue
-    else:
-        time.sleep(dt - diff)
+"""
+controlLoopManager
+-------------------
+Slightly fancier programming to get a wrapper around the control loop.
+In other words, it's the book-keeping around the actual control loop.
+It's a class because it keeps non-directly-control-loop-related parameters
+like max_iterations, what data to save etc.
+NOTE: you give this the ready-made control loop.
+if it has arguments, bake them in with functools.partial.
+"""
+class ControlLoopManager(controlLoop, args):
+    def __init__(self, controlLoop, max_iterations):
+        self.max_iterations = args.max_iterations
+        self.controlLoop = controlLoop
 
-# robotmanager:
-# ---------------
-# - right now it is assumed you're running this on UR5e so some
-#   magic numbers are just put to it.
-#   this will be extended once there's a need for it.
-# - at this stage it's just a boilerplate reduction class
-#   but the idea is to inherit it for more complicated things
-#   with many steps, like dmp.
-#   or just showe additional things in, this is python after all
-# - you write your controller separately,
-#   and then drop it into this - there is a wrapper function you put
-#   around the control loop which handles timing so that you
-#   actually run at 500Hz and not more.
-# - this is probably not the most new-user friendly solution,
-#   but it's designed for fastest idea to implementation rate.
+        # 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)
+
+        # TODO handle data saving here.
+        # it can only be handled here because the control loop only cares about the present/
+        # a small time-window around it.
+        # of course have this under the save_plots flag or something similar
+        # save it all in a dictionary of ndarrays and return that
+        # these are all the same (mostly).
+        # if they're not consider inheriting and specializing this or sth similar,
+        # we'll see when we get to it
+
+    """
+    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(signum, frame):
+        print('sending 100 speedjs full of zeros and exiting')
+        for i in range(100):
+            vel_cmd = np.zeros(6)
+            rtde_control.speedJ(vel_cmd, 0.1, 1.0 / 500)
+        exit()
+
+    """
+    run
+    ---
+    do timing to run at 500Hz.
+    also handle the number of iterations.
+    it's the controlLoop's responsibility to break if it achieved it's goals.
+    this is done via the breakFlag
+    """
+    def run(self):
+        for i in range(self.max_iterations):
+            start = time.time()
+            breakFlag = self.controlLoop(i)
+            if breakFlag:
+                break
+            end = time.time()
+            diff = end - start
+            if dt < diff:
+                print("missed deadline by", diff - dt)
+                continue
+            else:
+                time.sleep(dt - diff)
+        if i < self.max_iterations -1:
+            print("success in", i, "iterations!")
+        else:
+            print("FAIL: did not succed in," max_iterations, "iterations")
+        self.stopHandler(None, None)
+
+"""
+robotmanager:
+---------------
+- right now it is assumed you're running this on UR5e so some
+  magic numbers are just put to it.
+  this will be extended once there's a need for it.
+- at this stage it's just a boilerplate reduction class
+  but the idea is to inherit it for more complicated things
+  with many steps, like dmp.
+  or just showe additional things in, this is python after all
+- you write your controller separately,
+  and then drop it into this - there is a wrapper function you put
+  around the control loop which handles timing so that you
+  actually run at 500Hz and not more.
+- this is probably not the most new-user friendly solution,
+  but it's designed for fastest idea to implementation rate.
+"""
 class RobotManager:
     # just pass all of the arguments here and store them as is
     # so as to minimize the amount of lines.
@@ -66,6 +128,7 @@ class RobotManager:
         if args.gripper:
             self.gripper = RobotiqGripper()
         # initialize and connect the interfaces
+        self.simulation = args.simulation
         if not args.simulation:
             self.rtde_control = RTDEControlInterface("192.168.1.102")
             self.rtde_receive = RTDEReceiveInterface("192.168.1.102")
@@ -78,29 +141,137 @@ class RobotManager:
             self.rtde_control = RTDEControlInterface("127.0.0.1")
             self.rtde_receive = RTDEReceiveInterface("127.0.0.1")
 
-        # 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)
         # ur specific magic numbers 
-        self.JOINT_ID = 6 # last joint because pinocchio adds base frame as 0th joint
+        self.n_joints = 6
+        # last joint because pinocchio adds base frame as 0th joint.
+        # and since this is unintuitive, we add the other variable too
+        # so that the control designer doesn't need to think about such bs
+        self.JOINT_ID = 6
         self.update_rate = 500 #Hz
         self.dt = 1 / self.update_rate
+        # you better not give me crazy stuff
+        # and i'm not clipping it, you're fixing it
+        assert args.acceleration < 1.7 and args.acceleration > 0.0
+        # we're not saying it's qdd because it isn't directly (apparently)
+        # TODO: check that
         self.acceleration = args.acceleration
-        rtde_io.setSpeedSlider(args.speed_slider)
+        self.rtde_io.setSpeedSlider(args.speed_slider)
+        self.max_iterations = args.max_iterations
+        # TODO: these are almost certainly higher
+        # maybe you want to have them high and cap the rest with speed slider?
+        # idk really, but it's better to have this low and burried for safety and robot longevity reasons
+        self.max_qdd = 1.7
+        # NOTE: i had this thing at 2 in other code
+        self.max_qd = 0.5
+        # maybe you want to scale the control signal
+        self.controller_speed_scaling = 1.0
+        
+    """
+    getQ
+    -----
+    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 
+    """
+    def getQ(self):
+        q = robot.rtde_receive.getActualQ()
+        if self.args.gripper:
+            self.gripper_pos = 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)
+        # let's just have both options for getting q, it's just a 8d float list
+        # readability is a somewhat subjective quality after all
+        q = np.array(q)
+        self.q = q
+        return q
 
+    """
+    sendQd
+    -----
+    different things need to be send depending on whether you're running a simulation,
+    you're on a real robot, you're running some new simulator bla bla. this is handled
+    here because this things depend on the arguments which are manager here (hence the 
+    class name RobotManager)
+    """
+    def sendQd(self, qd):
+        # we're hiding the extra 2 prismatic joint shenanigans from the control writer
+        # because there you shouldn't need to know this anyway
+        qd_cmd = qd[:6]
+        # maybe you want to scale the control signal
+        qd_cmd = qd * self.controller_speed_scaling
+        # np.clip is ok with bounds being scalar, it does what it should
+        # (but you can also give it an array)
+        qd_cmd = np.clip(qd_cmd, -1 * self.max_qd, self.max_qd)
+        if not self.simulation:
+            # speedj(qd, scalar_lead_axis_acc, hangup_time_on_command)
+            rtde_control.speedJ(qd_cmd, self.acceleration, self.dt)
+        else:
+            # this one takes all 8 elements of qd since we're still in pinocchio
+            self.q = pin.integrate(model, self.q, qd * self.dt)
+
+
+    """
+    defineGoalPoint
+    ------------------
+    --> best way to handle the goal is to tell the user where the gripper is
+        in both UR tcp frame and with pinocchio and have them 
+        manually input it when running.
+        this way you force the thinking before the moving, 
+        but you also get to view and analyze the information first
+    TODO get the visual thing you did in ivc project with sliders also.
+    it's just text input for now because it's totally usable, just not superb.
+    but also you do want to have both options. obviously you go for the sliders
+    in the case you're visualizing, makes no sense otherwise
+    """
+    def defineGoalPoint(self):
+        q = self.getQ()
+        # define goal
+        pin.forwardKinematics(self.model, self.data, np.array(q))
+        Mtool = self.data.oMi[self.JOINT_ID]
+        if not self.args.visualize:
+            print("You can only specify the translation right now.")
+            print("Here's where the robot is currently. Ensure you know what the base frame is first.")
+
+            print("base frame end-effector pose from pinocchio:\n", \
+                    *data.oMi[6].translation.round(4), *pin.rpy.matrixToRpy(data.oMi[6].rotation).round(4))
+            print("UR5e TCP:", *np.array(rtde_receive.getActualTCPPose()).round(4))
+            # remain with the current orientation
+            # TODO: add something, probably rpy for orientation because it's the least number
+            # of numbers you need to type in
+            Mgoal = copy.deepcopy(Mtool)
+            # this is a reasonable way to do it too, maybe implement it later
+            #Mgoal.translation = Mgoal.translation + np.array([0.0, 0.0, -0.1])
+            # do a while loop until this is parsed correctly
+            while True:
+                goal = input("Please enter the target end effector position in the x.x,y.y,z.z format: ")
+                try:
+                    e = "ok"
+                    goal_list = goal.split(',')
+                    for i in range(len(goal_list)):
+                       goal_list[i] = float(goal_list[i])
+                except:
+                    e = sys.exc_info()
+                    print("The input is not in the expected format. Try again.")
+                    print(e)
+                if e == "ok":
+                    Mgoal.translation = np.array(goal_list)
+                    break
+            print("this is goal pose you defined:\n", Mgoal)
+        else:
+            raise NotImplementedError('Paths in the urdf or something else need to \
+                    be fixed to use this first. Also need to program in the sliders \
+                    and visualizing the goal frame. Sorry. Coming soon.')
+        # NOTE i'm not deepcopying this on purpose
+        # but that might be the preferred thing, we'll see
+        self.Mgoal = Mgoal
+        return Mgoal
 
-        # this does not need to be kept here
-        self.q = rtde_receive.getActualQ()
 
-    # 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(signum, frame):
-        print('sending 100 speedjs full of zeros and exiting')
-        for i in range(100):
-            vel_cmd = np.zeros(6)
-            rtde_control.speedJ(vel_cmd, 0.1, 1.0 / 500)
-        exit()