From 596f6c5a2a8dc2cbfdca3807a83b902dbe24586f Mon Sep 17 00:00:00 2001
From: m-guberina <gubi.guberina@gmail.com>
Date: Sat, 23 Mar 2024 17:44:04 +0100
Subject: [PATCH] minor quality of life stuff. next up is real time plotting,
 then robustifying drawing.

---
 python/examples/clik.py                       |   7 +-
 .../__pycache__/managers.cpython-311.pyc      | Bin 17497 -> 17957 bytes
 .../clik_point_to_point.cpython-311.pyc       | Bin 9082 -> 9054 bytes
 .../clik/clik_point_to_point.py               |   4 +-
 python/ur_simple_control/managers.py          | 100 ++++++++++--------
 .../__pycache__/get_model.cpython-311.pyc     | Bin 4485 -> 4180 bytes
 .../__pycache__/logging_utils.cpython-311.pyc | Bin 2279 -> 2599 bytes
 python/ur_simple_control/util/get_model.py    |  65 +++++-------
 .../ur_simple_control/util/logging_utils.py   |  11 +-
 .../manipulator_visual_motion_analyzer.py     |   4 +-
 .../visualize/vedo_manipulator.py             |   9 --
 .../ur_simple_control/visualize/visualize.py  |  19 +++-
 12 files changed, 116 insertions(+), 103 deletions(-)

diff --git a/python/examples/clik.py b/python/examples/clik.py
index f066ec8..3a32ec2 100644
--- a/python/examples/clik.py
+++ b/python/examples/clik.py
@@ -58,6 +58,8 @@ def get_args():
             default='1.0', help='not actually_used atm')
     parser.add_argument('--debug-prints', action=argparse.BooleanOptionalAction, \
             help="print some debug info", default=False)
+    parser.add_argument('--save-log', action=argparse.BooleanOptionalAction, \
+            help="save log data folder in whatever directory you called this in. if it doesn't exists there, it's saved to /tmp/data", default=False)
 
     args = parser.parse_args()
     if args.gripper and args.simulation:
@@ -68,7 +70,7 @@ def get_args():
 if __name__ == "__main__": 
     args = get_args()
     robot = RobotManager(args)
-    Mgoal = robot.defineGoalPoint()
+    Mgoal = robot.defineGoalPointCLI()
     clik_controller = getClikController(args)
     controlLoop = partial(controlLoopClik, robot, clik_controller)
     log_dict = {
@@ -81,5 +83,6 @@ if __name__ == "__main__":
     # something that generates a file name from something in args.
     # also save args there
     log_dict, final_iteration = loop_manager.run()
-    saveLog(log_dict, final_iteration, args)
+    if args.save_log:
+        saveLog(log_dict, final_iteration, args)
 
diff --git a/python/ur_simple_control/__pycache__/managers.cpython-311.pyc b/python/ur_simple_control/__pycache__/managers.cpython-311.pyc
index 053fed9e52e9806ecfd57867b690a7f41b92295f..5455df7279181101a5bbc6a30cb325d201481eab 100644
GIT binary patch
delta 5130
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

delta 4575
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
YvoeZ)V8Bj(1dD$GlbC9XSU{l*039JeG5`Po

diff --git a/python/ur_simple_control/clik/__pycache__/clik_point_to_point.cpython-311.pyc b/python/ur_simple_control/clik/__pycache__/clik_point_to_point.cpython-311.pyc
index 6b95c52eca9803e3be3409010974b2933bc51953..9b3a270c6e1c04ea59a98227f6d366babc3201fd 100644
GIT binary patch
delta 543
zcmez6cF&D>IWI340|NuYzIXppt2gp`2{EqN93kY($dtuDd9JV`qrl|-!jY4mM8qcR
ziHI-?O)eA{nH(;{%P2hgyomJV1`!TM5wO^OIX<QZsM3~<qLVpA(^-q;7#J8P7l`g+
zl%1?CR>i%<>xzoyC3edT?3R<)iEU%NG&xm#rbG!3$Qclv#SWnv7#47VO}CSfRuzVc
zF)*Ys6HqNM*@B;M@&*ZEMy|<6Bvcu>C%=*~X5;~zWh80J$U8YhQf2aN$q8H`3=9mL
z7#J9ek4-j}vS<3rvAJ1_pGglST*Sh_z;KJXs5I{uOG##K>MhpdoYd5UqCf@)22BpI
zuZr9m7#NCzCLfSVW{qQDU^u;5Lv|^XKsZP=0z^cDh``Br<rM{@K+I?m5d$J(C-W)D
zvZgXHFx;DLpm2>bX)?Q_J!8aVcSTulu<ao8ic%-%D@ynYrKF~1=B2vlC*}m?XXceS
z`*;@Ff(+3I5eS39_Cic&U|?9uP^1WwmuFyL_{Cw9o1apelWJF#H~Es{HGUyR*$)hG
aLSyoFrBE)FOFSwUdDO1(s7)4AP5}VB;DjOo

delta 554
zcmccT_REcTIWI340|NuY>NoFFD>w3b2{F#w93kY(IC;IWBqRUi*+NQ_UkjTv3QRsI
zEi&0$M0B!}h}h&aB0P*jlk-KC8HFbwlvbLoC?XCQlb-xVM2=Bpvao3S<a*IvjM9^>
z#Hu*2sF+-0H@U!WGI_t)HpXX@%fx5u)UekuX0d@R2jMC%28LSp5<U<cg0navGy}r|
zPO$a;5(1OGCAd{ZVd4x7Da-_P7&3BCJ}9Be$TRu9u=wPE5;BauV6*fkO&R$nr%I~u
z-{LJVO3h2oh|f<;D^4w$+$lMME0lqOVG{!bL-FOw%2M|H4|s(qq+SuzyvVEB;P#bm
zbBYu{lO9M-5eowY!!72b(!5(NC7HRYw^)mFQd0|xf*2SWG&zdo7#J9e+(AU}<b^WH
ztnmyC3}-fT$}VLRhyaO3f`}*(5j1(9yrMufh#3PSVnIaQ<oEKjtZ57k4EHAsDqLes
zp8P_=o-uN=hN3Jt*mjV4MQM`*6eSG!Q&Q71^HSaO6LSLcGxJJ{Y(XaIg9wDFU`wG!
r&sLOT%%8kX@fyD{qwEI;IH5Ipwo)jU;w2u%i#*C#c$6pqQc3{;mm`hh

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 b20bdc6..ba8399c 100644
--- a/python/ur_simple_control/clik/clik_point_to_point.py
+++ b/python/ur_simple_control/clik/clik_point_to_point.py
@@ -139,7 +139,7 @@ def controlLoopClik(robot, clik_controller, i, past_data):
     # 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) < robot.goal_error:
+    if np.linalg.norm(err_vector) < robot.args.goal_error:
 #      print("Convergence achieved, reached destionation!")
         breakFlag = True
     # pin.computeJointJacobian is much different than the C++ version lel
@@ -235,7 +235,7 @@ def moveL(args, robot, goal_point):
 if __name__ == "__main__": 
     args = get_args()
     robot = RobotManager(args)
-    Mgoal = robot.defineGoalPoint()
+    Mgoal = robot.defineGoalPointCLI()
     clik_controller = getClikController(args)
     controlLoop = partial(controlLoopClik, robot, clik_controller)
     # we're not using any past data or logging, hence the empty arguments
diff --git a/python/ur_simple_control/managers.py b/python/ur_simple_control/managers.py
index 605a788..716a238 100644
--- a/python/ur_simple_control/managers.py
+++ b/python/ur_simple_control/managers.py
@@ -17,6 +17,7 @@ import signal
 from ur_simple_control.util.get_model import get_model
 from collections import deque
 from ur_simple_control.visualize.visualize import plotFromDict
+from pinocchio.visualize import MeshcatVisualizer
 
 """
 general notes
@@ -156,6 +157,12 @@ class ControlLoopManager:
                     #self.robot_manager.stopHandler(None, None)
                 self.log_dict[key][i] = log_entry_dict[key]
             
+            # TODO: offload this to a different 
+            # process, it's hindering real-time performance (not by a lot,
+            # but it does)
+            if self.args.visualize:
+                if i % 20 == 0:
+                    self.robot_manager.viz.display(self.robot_manager.q)
             # break if done
             if breakFlag:
                 break
@@ -168,7 +175,7 @@ class ControlLoopManager:
             else:
                 time.sleep(self.robot_manager.dt - diff)
             self.final_iteration = i
-        if args.debug_prints:
+        if self.args.debug_prints:
             if i < self.max_iterations -1:
                 print("success in", i, "iterations!")
             else:
@@ -232,13 +239,24 @@ class RobotManager:
         # load model
         # collision and visual models are none if args.visualize == False
         self.model, self.collision_model, self.visual_model, self.data = \
-             get_model(args.visualize)
+             get_model()
+        # we're using meshcat exclusively.
+        # there are no good options, 
+        # but this does work and isn't a dead project
         if args.visualize:
-        # ---> TODO: write your own vedo 3D visualzier,
-        #            connect it with the real-time matplotlib line plots,
-        #            and add some basic gui control (show/don't show stuff,
-        #            define a goal point, choose controller etc)
-            pass
+            # for whatever reason the hand-e files don't have/
+            # meshcat can't read scaling information.
+            # so we scale manually
+            for geom in self.visual_model.geometryObjects:
+                if "hand" in geom.name:
+                    s = geom.meshScale
+                    # this looks exactly correct lmao
+                    s *= 0.001
+                    geom.meshScale = s
+            self.viz = MeshcatVisualizer(self.model, self.collision_model, self.visual_model)
+            self.viz.initViewer(open=True)
+            self.viz.loadViewerModel()
+
 
         # TODO: make general
         if self.gripper_flag and not self.pinocchio_only:
@@ -545,7 +563,7 @@ class RobotManager:
 
 
     """
-    defineGoalPoint
+    defineGoalPointCLI
     ------------------
     --> 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 
@@ -557,45 +575,41 @@ class RobotManager:
     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):
+    def defineGoalPointCLI(self):
         q = self.getQ()
         # define goal
         pin.forwardKinematics(self.model, self.data, np.array(q))
         T_w_e = self.data.oMi[self.JOINT_ID]
-        if not self.args.visualize:
-            print("You can only specify the translation right now.")
-            if not self.pinocchio_only:
-                print("In the following, first 3 numbers are x,y,z position, and second 3 are r,p,y angles")
-                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", \
-                        *self.data.oMi[6].translation.round(4), *pin.rpy.matrixToRpy(self.data.oMi[6].rotation).round(4))
-                print("UR5e TCP:", *np.array(self.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 = T_w_e.copy()
-            # 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.')
+        print("You can only specify the translation right now.")
+        if not self.pinocchio_only:
+            print("In the following, first 3 numbers are x,y,z position, and second 3 are r,p,y angles")
+            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", \
+                    *self.data.oMi[6].translation.round(4), *pin.rpy.matrixToRpy(self.data.oMi[6].rotation).round(4))
+            print("UR5e TCP:", *np.array(self.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 = T_w_e.copy()
+        # 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)
+
         # NOTE i'm not deepcopying this on purpose
         # but that might be the preferred thing, we'll see
         self.Mgoal = Mgoal
diff --git a/python/ur_simple_control/util/__pycache__/get_model.cpython-311.pyc b/python/ur_simple_control/util/__pycache__/get_model.cpython-311.pyc
index 177620f1965e84d10ab4a264f60aa2384ae543f1..625e9afb9c9bf6ff072333dee008f431cf9c1eb8 100644
GIT binary patch
delta 1148
zcmZowzM{aloR^o2fq{Wx^1XklwfqzLBpB@`s+%%$PK@5d$S_%eQH1FT+hjFHcSeTE
zd5pS(49gf97*;cabbxRz<K#7rvW!e1xyiHH_?c>%Cx2j)WM)~!IQa*o=44+cAx758
z2~5t4tf(4mSe7v{Fsx<*83Mw!Y#@D;?=lHZKFlOFnUh(Fkprxc15F=0nm*3S4b0k%
zTwvWtnS~g+!MeE7baA2S;sL7<Wf5ZJ1+j#=7crvhsO1Bj!-pzb!;5APKS(zx4@~#u
zcPz@2ZCQmF1;M%n(R2%-=@tU%o_v;7X!2%OsmVWCg&0LZ`szi{^a-Qs6HQ^L6-!~L
z6;ENPl_=o{g&zcGaYJYZh8lGLh@q(CfvXd%VJzW;@faAgco`TbpJQjU%7P1~Fw`(F
zV`X4i%?eY7W+|FslMk?q)Mvq6g+;q~4O@*^mH^DS6agCs28J3=6fr@t7!Qh=5Lk>K
zMNAkhCWIm;V&e-kP!vT_6r>O(5CmZks1ZXA@LI_lu^RCji5kf?mS6@=vCVTh<}ywW
z=aOd>nq1E1HTfJ@yr|SIq0*w1wD^L=l8pGG)SSeU%(B$UmRxe1!@2DlS)@Q&Y9Wuv
z<PAKmleh5H30W{OFeouFFckl0Vqj=scq%D3*^SqX|1Q7C1yPNQ{F+zzH5)wcZtmuN
z$XNe?N1(y=hL}`?{|#aB2A{7S47>tYc$6+En_lEGyTW7E;CfeB?253)3egJ&PDh=u
zxP)ABiM|k<cp<suqHyUI;nD`5yJC`8#I#qqUNCk&T71Pl{EB<xh2-=LnN=6Xs;`Ju
zH~4>GV^Gj(aJ|AJIYaRxi_8sq1ZT3hK(4qD0|Ub?*1Xc(g32OLOx<EGt}HI%oqS$E
zP6p(cl?+9e3=9lK<_ruBzc_4i^HWN5QtgTyCUXi#DT(kfvV4#-Vl?}}fJuP$0RX^5
B?dbpj

delta 1303
zcmcbj(5lS0oR^o2fq{Xc{P4e2cj1YA5{xzz)lDbHZ5C$)2}3bE0|Nsy0|Ue7BKFA|
zjP8t#lM5JiCr@P)U}T!Sj!~A88N_E~o~*{CIa!%Wh>>;jI%dhq{!BuQY?I}foyFOf
zF)}c$hMQQ!x{Qf|VKvj_7fi~N?=uN8a)8xyGYc_tg4J`Pspo*IU&5?Bc{Z~UBlqNW
z%-W1RV6{AGYPq3m6IhfdhqDMV@`256W)WiK2dn2tQ_lxgFT<)lS(sIbQ4p-&o>hoZ
z2&`TRO}!vg{S8*-$>&*x7)8M9f3pfPih|TTilV6(fvAUuBtxxO4MWk=6oy*y6oy)f
z6oy*K5^<Ou14D{HiR9#Wj8XwLOv{)V7*>M<9W2YpfTB#WL=r3vCTdvFtYpE^%RgCw
zU1ahPM!|aV5`M5IFp<RvrWhC)YQ$@pO5i*OhAg<MECHCB6owj>WvmPgt65<pwUVTo
zrj#WJ(~QM9i9CrCB&WhcsD`~pJWB|!Psj%3U@jCfVXzo4ikJvkOaMhp6f7o;A|?hF
z6GIUbw_&hgU_kMmFw6lp;;7+WD^(+2BT*w+BbCMy%%CZ;`2+i0#>v@Sa+7;G<rzgL
zujTaOVJ#@i%qzLYQc_uvI{6!Cf|m3x&a%wn(!`w1s?=LTr9~-e@db$`8SzD_If*5i
zWvNAM3=9lKpp3V<lgplwMVf(uVe&<8(a8_En?<Y{7#KjQrZ|?FfuVunD-VN|{Nz#|
zGybRiA{Rt8F7j($;n!^Nc)EEP&qK!g4{Qt^f*%+pSY;jvh;*>tkdo`*zacK&!E={`
z_X3~NQkM&w))!T5uBg~tunV}z5qO0o@B&BRT@K!kthp)|<P9!L8D5bxykP8dk;C;0
zhwB9n*RO00f+AN0)Gnx7UKFspB4E|QdRJWPinz{-&<iGRXPK{fL|pNRzmS-EA-&?F
zmw4qB@yZUKyHc`Or1UqiUNHAOYkb8!`ighzh4kzTxpfz%>aR%Eckp-ce-*<p7vb6m
z^4gPG_?yK=7#J9CvF4TL7E~6262&d%;>zM8{>k(B<zzqsvy!0*6rn{{3=9mvIBatB
cQ%ZAE?TVZy-{6l@lJH<;H2c7SodoLx02YiC)&Kwi

diff --git a/python/ur_simple_control/util/__pycache__/logging_utils.cpython-311.pyc b/python/ur_simple_control/util/__pycache__/logging_utils.cpython-311.pyc
index 759cafe5cf649d89b5731ceaa655e38fdaa3a52f..11e5dcc03d67ce58afde5d3306cdc033b3d7633e 100644
GIT binary patch
delta 789
zcmaDZxLky9IWI340|NuYzJLEx+c+ljNf;O~FfdGKNMT4}%wdRv(2P+`U_Nsca|%-n
zLljF2a|=ThYYIyXLloOYKPAR#6AMl2OF-s8aTX(#!LWb{#zLl37?&|JFsz2FDq>Ax
zs%1%Gu4T=WDuIhKFk~^qbfvJQu&!ZS#>Bv|8g3#ZLkfE>OA1FVYY8WVHcTZ$4ND3q
zs=`{f5^lIOLl)b_JMxU|lk*r=^h!7p;x(*<^iB3-glN>{;_|Cv)6-8$EJ>U^lhIbL
zic`NNw*br!(PX^Eky?>iToRw3T_p<=pFD?Aid6~B+QFzS%W;b-zxWnQL1Ib9EjF;q
z;#(ZKiP@<snMK8u?=XJn6=PsvU}a!nD89wOz%cnBtG4r3E(Q*+mh$$xrn(6k7un^n
zu*-j7U}LqrAz{+NdqdKygRg_{DF;^%_XO`1Di=8PFLLN#;n2Upp?^cd9HhXugKx7q
z^C?C~(aDck^cgiK3$bc5ichv<m0;szU|=ZXpB%#~?I_5=z;KHzDL+3aKC!4MvGNvs
zUVLUrYEf~KEXXzN3=9kkVBk<B3Raq&lbV<pS`bv4SELG(oV<f|1*7=nM7DcupmbHl
zJ=vALKu-XqnGGZl)(p`rl#`#B5>%S!mS5zUmzYyooLO81N_<9>AG0U=i!(4VXtEc9
zqU{!IUTJPYWsw5Nogh<+K*0mH53Hd`3M2v205<p+hfQvNN@-52U6Bz30|O{E6-Q0(
f<B*lQ!NA!7h96iY7)3uYzzMU-dpOitKqdeHO`NPz

delta 548
zcmZ23@?4N_IWI340|Ntt4O?2O3HwAo2@MSf28QVjDGVu$ISf${nlXwgg{g%hiaCY3
zg&~S1g{6fdigjX`5~I?@N>j5G##)vXrdrk#Mvy@eT*H#Wyo`x~VKozk&%jX2R>BNp
zGcaT^O^#rcXJnrIicv)mMI$Rgji;a*HJMoaCd)F}vfW}TPts(We4Xh#mmmWJ11kdq
zL-FFtH<<N!KCm%}8aDXe5H)M?+w945ijh%ZvIv_#qvB*2Hf=_s$pvf@Y@7@X3`Ja%
z``Dx%c^DWNZgC~$=jX&H78NB{-eS*-&n!tTDlU=$naR$;z@PvI4n+cBrO7#|iFu&~
zL8W;`3JeSkl9OMttzZ<IJemC-8^{$!Y?I453iP-^n%Ur*A$o;!@)J{nO7q<EiyZS3
zb1I87i;J`w7#OrCi*hE`3o$S-XtEW7G~Hs&E6pvaERqJf6XZXzRbaJ6q98GlTCkbF
zIBatBQ%ZAE?TU067#Kj&Q5?v?!0>^Yk&*ER18)Nu-e3^A07D<x1Q|s?Fu(~7enycG
P4E&6fnYk2LK;{Af#^P|;

diff --git a/python/ur_simple_control/util/get_model.py b/python/ur_simple_control/util/get_model.py
index 452c0d3..2d624ba 100644
--- a/python/ur_simple_control/util/get_model.py
+++ b/python/ur_simple_control/util/get_model.py
@@ -13,24 +13,29 @@ from importlib.resources import files
 # can't get the urdf reading with these functions to save my life, idk what or why
 
 #############################################################
-# PACKAGE_DIR IS THE WHOLE GIT REPO
-# PACKAGE:// IS WHAT'S BEING REPLACED WITH THE PACKAGE_DIR ARGUMENT
-# TODO: REWRITE URDFS TO THAT THEY FOLLOW THIS
-# YOU GIVE ABSOLUTE PATH TO URDF THO.
+# PACKAGE_DIR IS THE WHOLE UR_SIMPLE_CONTROL FOLDER (cos that's accessible from anywhere it's installed)
+# PACKAGE:// IS WHAT'S BEING REPLACED WITH THE PACKAGE_DIR ARGUMENT IN THE URDF.
+# YOU GIVE ABSOLUTE PATH TO THE URDF THO.
 #############################################################
 
-
-def get_model(visualize):
+"""
+loads what needs to be loaded.
+calibration for the particular robot was extracted from the yml
+obtained from ros and appears here as magic numbers.
+i have no idea how to extract calibration data without ros
+and i have no plans to do so.
+aligning what UR thinks is the world frame
+and what we think is the world frame is not really necessary,
+but it does aleviate some brain capacity while debugging.
+having that said, supposedly there is a big missalignment (few cm)
+between the actual robot and the non-calibrated model.
+NOTE: this should be fixed for a proper release
+"""
+def get_model():
     
-    #urdf_path_relative = "../robot_descriptions/urdf/ur5e_with_robotiq_hande.urdf"
-    # NOTE THIS ONE WORKS IF YOU'RE NOT LOADING VISUAL STUFF
-    #urdf_path_relative = files('ur_simple_control.robot_descriptions.urdf').joinpath('ur5e_with_robotiq_hande.urdf')
-    #urdf_path_relative = files('ur_simple_control.robot_descriptions').joinpath('ur5e_with_robotiq_hande.urdf')
     urdf_path_relative = files('ur_simple_control.robot_descriptions.urdf').joinpath('ur5e_with_robotiq_hande_FIXED_PATHS.urdf')
     urdf_path_absolute = os.path.abspath(urdf_path_relative)
-    #mesh_dir = "../robot_descriptions/"
     mesh_dir = files('ur_simple_control')
-    #mesh_dir_absolute = os.path.abspath(mesh_dir)
     mesh_dir_absolute = os.path.abspath(mesh_dir)
 
     shoulder_trans = np.array([0, 0, 0.1625134425523304])
@@ -57,33 +62,17 @@ def get_model(visualize):
     wrist_3_rpy = np.array([1.572215514545703, 3.141592653589793, 3.141592633687631])
     wrist_3_se3 = pin.SE3(pin.rpy.rpyToMatrix(wrist_3_rpy),wrist_3_trans)
 
-    # TODO fix paths via python packaging, get this to work
-    if visualize:
-        #raise NotImplementedError('Paths in the urdf or something else need to be fixed to use this first. Sorry. Coming soon.')
-        model = None
-        collision_model = None
-        visual_model = None
-        print("urdf_path_absolute", type(urdf_path_absolute), urdf_path_absolute)
-        print("mesh_dir", type(mesh_dir_absolute), mesh_dir_absolute)
-        #model, collision_model, visual_model = pin.buildModelsFromUrdf(urdf_path_absolute, mesh_dir_absolute)
-        #print(urdf_path_absolute)
-        model = pin.buildModelFromUrdf(urdf_path_absolute)
-        visual_model = pin.buildGeomFromUrdf(model, urdf_path_absolute, pin.GeometryType.VISUAL, None, mesh_dir_absolute)
-        collision_model = pin.buildGeomFromUrdf(model, urdf_path_absolute, pin.GeometryType.COLLISION, None, mesh_dir_absolute)
-        #visual_model = pin.buildGeomFromUrdf(model, urdf_path_absolute, pin.GeometryType.VISUAL, None, mesh_dir_absolute)
-        #collision_model = pin.buildGeomFromUrdf(model, urdf_path_absolute, pin.GeometryType.COLLISION)
-        #geom_model = None
-        #package_dir = None
-        #mesh_loader = None
-        #visual_model = pin.buildGeomFromUrdf(model, urdf_path_absolute, pin.GeometryType.VISUAL, geom_model, package_dirs=mesh_dir_absolute)
-        #visual_model = pin.buildGeomFromUrdf(model, urdf_path_absolute, pin.GeometryType.VISUAL, package_dirs=mesh_dir_absolute)
-        #collision_model = pin.buildGeomFromUrdf(model, urdf_path_absolute, pin.GeometryType.COLLISION)
-        #data = pin.Data(model)
-    else:
-        model = pin.buildModelFromUrdf(urdf_path_absolute)
-        collision_model = None
-        visual_model = None
+    model = None
+    collision_model = None
+    visual_model = None
+    # this command just calls the ones below it. both are kept here
+    # in case pinocchio people decide to change their api.
+    #model, collision_model, visual_model = pin.buildModelsFromUrdf(urdf_path_absolute, mesh_dir_absolute)
+    model = pin.buildModelFromUrdf(urdf_path_absolute)
+    visual_model = pin.buildGeomFromUrdf(model, urdf_path_absolute, pin.GeometryType.VISUAL, None, mesh_dir_absolute)
+    collision_model = pin.buildGeomFromUrdf(model, urdf_path_absolute, pin.GeometryType.COLLISION, None, mesh_dir_absolute)
 
+    # updating joint placements.
     model.jointPlacements[1] = shoulder_se3
     model.jointPlacements[2] = upper_arm_se3
     model.jointPlacements[3] = forearm_se3
diff --git a/python/ur_simple_control/util/logging_utils.py b/python/ur_simple_control/util/logging_utils.py
index c8f0c5d..99d4b3e 100644
--- a/python/ur_simple_control/util/logging_utils.py
+++ b/python/ur_simple_control/util/logging_utils.py
@@ -1,7 +1,7 @@
 import pickle
 import numpy as np
+import os
 
-# we're not 
 def saveLog(log_dict, final_iteration, args):
     # shave off the zeros, noone needs 'em
     for key in log_dict:
@@ -10,8 +10,13 @@ def saveLog(log_dict, final_iteration, args):
     #  - generate name based on args + add index
     #  - you need to run shell ls to get the index,
     #    there's at least one chalmers project with code for that
-    run_file_path = "./data/clik_run_001.pickle"
-    args_file_path = "./data/clik_run_001_args.pickle"
+    if os.path.exists('./data'):
+        run_file_path = "./data/clik_run_001.pickle"
+        args_file_path = "./data/clik_run_001_args.pickle"
+    else:
+        os.makedirs('/tmp/data', exist_ok=True)
+        run_file_path = "/tmp/data/clik_run_001.pickle"
+        args_file_path = "/tmp/data/clik_run_001_args.pickle"
     # save the logged data
     # you need to open it binary for pickling
     log_file = open(run_file_path, 'wb')
diff --git a/python/ur_simple_control/visualize/manipulator_visual_motion_analyzer.py b/python/ur_simple_control/visualize/manipulator_visual_motion_analyzer.py
index b4b9380..59952dc 100644
--- a/python/ur_simple_control/visualize/manipulator_visual_motion_analyzer.py
+++ b/python/ur_simple_control/visualize/manipulator_visual_motion_analyzer.py
@@ -151,8 +151,8 @@ class ManipulatorVisualMotionAnalyzer:
         # dicts not because they have to be, but just so that
         # they are named because that might be useful
         # and because names are the only thing keeping horrendous this code together
-            # TODO: current evil sharing of pointers (thanks python lmao) needs to be deleted
-            # ofc delete from ik_env, retain here (they're in both spots rn)
+        # TODO: current evil sharing of pointers (thanks python lmao) needs to be deleted
+        # ofc delete from ik_env, retain here (they're in both spots rn)
         # NOTE: you need to do ax.draw(artist) for blitting. so each artist needs to be connected
         # to its particular artist. so they can't all be in one dict. 
         # we'll just increase the dict depth by 1, and add a named tuple to layer 1 (layer of axis)
diff --git a/python/ur_simple_control/visualize/vedo_manipulator.py b/python/ur_simple_control/visualize/vedo_manipulator.py
index ec5c249..d4a2a38 100644
--- a/python/ur_simple_control/visualize/vedo_manipulator.py
+++ b/python/ur_simple_control/visualize/vedo_manipulator.py
@@ -65,15 +65,6 @@ def get_args():
 #######################################################################
 
 
-def drawStateAnim(self):
-    toBase = [np.array([[1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1]])]
-    drawOrientation(self.ax, toBase[-1][0:3,0:3], np.zeros((3,)), self.avg_link_lenth * 2)
-    for j in range(len(self.joints)):
-        toBase.append(toBase[-1] @ self.joints[j].HomMat)
-        drawOrientationAnim(self.ax, toBase[-1][0:3,0:3], self.lines[j][:-1], toBase[-1][0:3,3], self.avg_link_lenth)
-        drawVectorAnim(self.ax, -1* ( toBase[-2][0:3,3] - toBase[-1][0:3,3] ), self.lines[j][-1], toBase[-2][0:3,3],self.color_link)
-
-
 if __name__ == "__main__": 
     pin.seed(int(time.time()))
     args = get_args()
diff --git a/python/ur_simple_control/visualize/visualize.py b/python/ur_simple_control/visualize/visualize.py
index 9e8c467..4c7f71e 100644
--- a/python/ur_simple_control/visualize/visualize.py
+++ b/python/ur_simple_control/visualize/visualize.py
@@ -1,10 +1,13 @@
 import numpy as np
 import matplotlib.pyplot as plt
 
-# TODO let's try to make this general
-# make plot_data a dictionary.
-# every key is one subplot, and it's value
-# is what you plot
+""" 
+plotFromDict
+------------
+plots logs stored in a dictionary
+- every key is one subplot, and it's value
+  is what you plot
+""" 
 def plotFromDict(plot_data, final_iteration, args):
     # TODO cut zeros from data
     # TODO: replace with actual time
@@ -25,3 +28,11 @@ def plotFromDict(plot_data, final_iteration, args):
             ax_dict[data_key].plot(t, plot_data[data_key][:final_iteration,j], color=colors[j], label=data_key + "_" + str(j))
         ax_dict[data_key].legend()
     plt.show()
+
+
+# you need to write out specifications for this.
+# only then implement.
+# this is too fucked to try to do it from your head
+#class RealTimePlotter:
+#    def __init__(self, TODO):
+
-- 
GitLab