From 19caf2f31051e1be7e503242326844757262c824 Mon Sep 17 00:00:00 2001 From: m-guberina <gubi.guberina@gmail.com> Date: Mon, 27 Nov 2023 11:55:11 +0100 Subject: [PATCH] smack in the middle of creating a GUI class --- .../__pycache__/make_run.cpython-310.pyc | Bin 0 -> 1506 bytes .../visualize/arms/another_debug_dh | 9 + .../visualize/arms/j2n6s300_dh_params | 6 + .../visualize/arms/j2s7s300_dh_params | 7 + .../visualize/arms/kinova_jaco_params | 7 + .../visualize/arms/kuka_lbw_iiwa_dh_params | 7 + .../visualize/arms/lbr_iiwa_jos_jedan_dh | 7 + .../visualize/arms/robot_parameters2 | 9 + .../visualize/arms/testing_dh_parameters | 6 + .../arms/ur10e_dh_parameters_from_the_ur_site | 6 + .../ur_simple_control/visualize/arms/ur5e_dh | 6 + python/ur_simple_control/visualize/main.py | 78 ++ .../ur_simple_control/visualize/make_run.py | 45 ++ .../manipulator_visual_motion_analyzer.py | 709 ++++++++++++++++++ .../robot_stuff/InverseKinematics.py | 226 ++++++ .../InverseKinematics.cpython-310.pyc | Bin 0 -> 4796 bytes .../InverseKinematics.cpython-311.pyc | Bin 0 -> 7195 bytes .../__pycache__/drawing.cpython-310.pyc | Bin 0 -> 1443 bytes .../__pycache__/drawing.cpython-311.pyc | Bin 0 -> 2272 bytes .../drawing_for_anim.cpython-310.pyc | Bin 0 -> 1239 bytes .../drawing_for_anim.cpython-311.pyc | Bin 0 -> 2032 bytes .../__pycache__/follow_curve.cpython-310.pyc | Bin 0 -> 2382 bytes .../__pycache__/follow_curve.cpython-311.pyc | Bin 0 -> 4189 bytes .../__pycache__/forw_kinm.cpython-310.pyc | Bin 0 -> 11484 bytes .../__pycache__/forw_kinm.cpython-311.pyc | Bin 0 -> 27512 bytes .../__pycache__/inv_kinm.cpython-310.pyc | Bin 0 -> 6584 bytes .../__pycache__/inv_kinm.cpython-311.pyc | Bin 0 -> 14346 bytes .../joint_as_hom_mat.cpython-310.pyc | Bin 0 -> 3448 bytes .../joint_as_hom_mat.cpython-311.pyc | Bin 0 -> 7553 bytes .../__pycache__/utils.cpython-310.pyc | Bin 0 -> 1481 bytes .../__pycache__/utils.cpython-311.pyc | Bin 0 -> 2594 bytes .../webots_api_helper_funs.cpython-310.pyc | Bin 0 -> 4122 bytes .../webots_api_helper_funs.cpython-311.pyc | Bin 0 -> 11565 bytes .../visualize/robot_stuff/drawing.py | 36 + .../visualize/robot_stuff/drawing_for_anim.py | 38 + .../visualize/robot_stuff/follow_curve.py | 102 +++ .../visualize/robot_stuff/forw_kinm.py | 544 ++++++++++++++ .../visualize/robot_stuff/inv_kinm.py | 434 +++++++++++ .../visualize/robot_stuff/joint_as_hom_mat.py | 146 ++++ .../visualize/robot_stuff/utils.py | 36 + .../robot_stuff/webots_api_helper_funs.py | 191 +++++ 41 files changed, 2655 insertions(+) create mode 100644 python/ur_simple_control/visualize/__pycache__/make_run.cpython-310.pyc create mode 100644 python/ur_simple_control/visualize/arms/another_debug_dh create mode 100644 python/ur_simple_control/visualize/arms/j2n6s300_dh_params create mode 100644 python/ur_simple_control/visualize/arms/j2s7s300_dh_params create mode 100644 python/ur_simple_control/visualize/arms/kinova_jaco_params create mode 100644 python/ur_simple_control/visualize/arms/kuka_lbw_iiwa_dh_params create mode 100644 python/ur_simple_control/visualize/arms/lbr_iiwa_jos_jedan_dh create mode 100644 python/ur_simple_control/visualize/arms/robot_parameters2 create mode 100644 python/ur_simple_control/visualize/arms/testing_dh_parameters create mode 100644 python/ur_simple_control/visualize/arms/ur10e_dh_parameters_from_the_ur_site create mode 100644 python/ur_simple_control/visualize/arms/ur5e_dh create mode 100644 python/ur_simple_control/visualize/main.py create mode 100644 python/ur_simple_control/visualize/make_run.py create mode 100644 python/ur_simple_control/visualize/manipulator_visual_motion_analyzer.py create mode 100644 python/ur_simple_control/visualize/robot_stuff/InverseKinematics.py create mode 100644 python/ur_simple_control/visualize/robot_stuff/__pycache__/InverseKinematics.cpython-310.pyc create mode 100644 python/ur_simple_control/visualize/robot_stuff/__pycache__/InverseKinematics.cpython-311.pyc create mode 100644 python/ur_simple_control/visualize/robot_stuff/__pycache__/drawing.cpython-310.pyc create mode 100644 python/ur_simple_control/visualize/robot_stuff/__pycache__/drawing.cpython-311.pyc create mode 100644 python/ur_simple_control/visualize/robot_stuff/__pycache__/drawing_for_anim.cpython-310.pyc create mode 100644 python/ur_simple_control/visualize/robot_stuff/__pycache__/drawing_for_anim.cpython-311.pyc create mode 100644 python/ur_simple_control/visualize/robot_stuff/__pycache__/follow_curve.cpython-310.pyc create mode 100644 python/ur_simple_control/visualize/robot_stuff/__pycache__/follow_curve.cpython-311.pyc create mode 100644 python/ur_simple_control/visualize/robot_stuff/__pycache__/forw_kinm.cpython-310.pyc create mode 100644 python/ur_simple_control/visualize/robot_stuff/__pycache__/forw_kinm.cpython-311.pyc create mode 100644 python/ur_simple_control/visualize/robot_stuff/__pycache__/inv_kinm.cpython-310.pyc create mode 100644 python/ur_simple_control/visualize/robot_stuff/__pycache__/inv_kinm.cpython-311.pyc create mode 100644 python/ur_simple_control/visualize/robot_stuff/__pycache__/joint_as_hom_mat.cpython-310.pyc create mode 100644 python/ur_simple_control/visualize/robot_stuff/__pycache__/joint_as_hom_mat.cpython-311.pyc create mode 100644 python/ur_simple_control/visualize/robot_stuff/__pycache__/utils.cpython-310.pyc create mode 100644 python/ur_simple_control/visualize/robot_stuff/__pycache__/utils.cpython-311.pyc create mode 100644 python/ur_simple_control/visualize/robot_stuff/__pycache__/webots_api_helper_funs.cpython-310.pyc create mode 100644 python/ur_simple_control/visualize/robot_stuff/__pycache__/webots_api_helper_funs.cpython-311.pyc create mode 100644 python/ur_simple_control/visualize/robot_stuff/drawing.py create mode 100644 python/ur_simple_control/visualize/robot_stuff/drawing_for_anim.py create mode 100644 python/ur_simple_control/visualize/robot_stuff/follow_curve.py create mode 100644 python/ur_simple_control/visualize/robot_stuff/forw_kinm.py create mode 100644 python/ur_simple_control/visualize/robot_stuff/inv_kinm.py create mode 100644 python/ur_simple_control/visualize/robot_stuff/joint_as_hom_mat.py create mode 100644 python/ur_simple_control/visualize/robot_stuff/utils.py create mode 100644 python/ur_simple_control/visualize/robot_stuff/webots_api_helper_funs.py diff --git a/python/ur_simple_control/visualize/__pycache__/make_run.cpython-310.pyc b/python/ur_simple_control/visualize/__pycache__/make_run.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc59155c44e721a1377bb4db4c29fbecfe833313 GIT binary patch literal 1506 zcmd1j<>g{vU|=XnO-c3VU|@I*;vi!t1_lNP1_p*=3kC*;6owSW9EM!RC`LvQn<<AW z1VS@IX%;ZeoWmN$mco+4+QJaUp28f=pvm?UWTqzLEfLSWvecsDRPW5Z)ZE09%;aL% zyfRJ3TZ~$M$t)l>P|VN3zyNZXGsx;Wj0_AV3=0@*m}(fam};0M8M2tum=-eP7E577 z$fq!-Fs3oXRHMi+AymQS7c!<W&*7+LDPgH$YG!m{i0!Xstzm%KTf>;e+RRkTR>Qb} zt%fayrG|MSGb6}t>?y1%Y%OqgSsX45&5R2fYuRhqYgkg)B^hd1Z5V3UYFJV@plp~= zIBVDh7;0EjIMKwpV0LD4WpN8I)N+(CEZ`|&$l_hVSHrOoWHx6FXAMgV7epUeh94vo z!(7W%%U#O@QeVTpkg<j(g&U%#hNp%lg$K&!uHjxFu#iELp_Uh{R}gNW5XhGj3=0`+ z`D*xTSW<YQI$$mqs$ogtgNPN{)UX6IX!85LWM*Jsc*(}Vz@W)-i>a{q7F%I_N`6W4 zE#ch6yv&05)SR67)XenMyt2d`B*Dyr_~NpZ_#%)>mV)@y;#*8*#kY7<GK))!<4f}6 z)AJK^Zt+5l$jnPgO-?ONhoq1X_Q{M87MKJjQ;-vaLFq+^fq@~Np@t!rwU)7lF^gd$ zQz26@!%7BC##^i<8L1_SnoPHt^bBsXX60w*l@#$YFff3LUs?JY`MIh3>G{P4`6-D- zV4e|zn^T&XqF+#ySdm$*Us@DjoS9pYlNz6#pI1_ppQB$;S(1^Thee_+v$!-dC$lP5 zKQ}QuHNL1cPp_cz7MD#<W^qY!er|!CEXdIy9|$p2NrTh`mFDT$<m4wO<`moMA=DLt z!ncSW<XmnL!3!cdKm;d<-~kb!Ort4$iz%<*7Hd^%QGW3)wxawbaQNn><fq+YElSKw zPrbzg4p}aU7lKPt3vRI{78NB{-eLoXN%1X?ti<H}q|C&;TkKhh$?+vcnYS22Zm|>> z7M0v$%gM}3%t^n+oRV5{i@CTg<rZ6FQF?A>-Yu5A{G!}j%mwkOn*6u8pdpi!T6Bvo zGdn&tuk03kUVLUrYEkhmZm_H3L6KT<izOwoB=HtwCfIMc*uZgAe2dZd7B?d5D{e8y z-{MHgOiTwQnOn?7`6agmi*plma#D*+;A!g?4=C-xk`6dlAp|H*++v3YE-0jm%@`OM zM3_Ywc^LT^c^G*ZIT$$@*?#bF3o-IB$}oXMB$%Wag&27lg&2jvd?`jAMlnVf#v)Tl zI+um`p}3?pElm%jU@HQ}=q+JnH7P}j<(YZuw?vQyGxN&gvorH@ZwVrcmX>7Z6yIXa wE6pvaED{05EXY!bU*HM02<$5ko80`A(wtN~P=+W5`HhDG<QWb|4kjKp0KeRu0RR91 literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/arms/another_debug_dh b/python/ur_simple_control/visualize/arms/another_debug_dh new file mode 100644 index 0000000..ab4e0ac --- /dev/null +++ b/python/ur_simple_control/visualize/arms/another_debug_dh @@ -0,0 +1,9 @@ +0.0;0.0;1.0;0.0 +0.0;0.0;0.5;0.0 +0.0;0.0;1.0;0.0 +0.0;0.0;0.2;0.0 +0.0;0.0;1.0;0.0 +0.0;0.0;0.2;0.0 +0.0;0.0;1.0;0.0 +0.0;0.0;1.0;0.0 +0.0;0.0;1.0;0.0 diff --git a/python/ur_simple_control/visualize/arms/j2n6s300_dh_params b/python/ur_simple_control/visualize/arms/j2n6s300_dh_params new file mode 100644 index 0000000..44eed8e --- /dev/null +++ b/python/ur_simple_control/visualize/arms/j2n6s300_dh_params @@ -0,0 +1,6 @@ +0.2766;0.0;0.0;1.5707966 +0.0;0.0;0.41;3.14159265 +-0.0098;0.0;0.0;1.57079632 +-0.25008;0.0;0.0;1.04719755 +-0.085563;0.0;0.0;1.04719755 +-0.202781;0.0;0.0;3.1415926 diff --git a/python/ur_simple_control/visualize/arms/j2s7s300_dh_params b/python/ur_simple_control/visualize/arms/j2s7s300_dh_params new file mode 100644 index 0000000..7d1b3fa --- /dev/null +++ b/python/ur_simple_control/visualize/arms/j2s7s300_dh_params @@ -0,0 +1,7 @@ +-0.2755;0.0;0.0;1.5708 +0.0;0.0;0.0;1.5708 +-0.41;0.0;0.0;1.5708 +-0.0098;0.0;0.0;1.5708 +-0.31110;0.0;0.0;1.5708 +0.0;0.0;0.0;1.5708 +-0.20380;0.0;0.0;3.14159 diff --git a/python/ur_simple_control/visualize/arms/kinova_jaco_params b/python/ur_simple_control/visualize/arms/kinova_jaco_params new file mode 100644 index 0000000..57b26c9 --- /dev/null +++ b/python/ur_simple_control/visualize/arms/kinova_jaco_params @@ -0,0 +1,7 @@ +-0.2755;;0;1.5707963 +0;;0;1.5707963 +-0.4100;;0;1.5707963 +-0.0098;;0;1.5707963 +-0.3111;;0;1.5707963 +0;;0;1.5707963 +-0.2638;;0;3.1415926 diff --git a/python/ur_simple_control/visualize/arms/kuka_lbw_iiwa_dh_params b/python/ur_simple_control/visualize/arms/kuka_lbw_iiwa_dh_params new file mode 100644 index 0000000..3411eae --- /dev/null +++ b/python/ur_simple_control/visualize/arms/kuka_lbw_iiwa_dh_params @@ -0,0 +1,7 @@ +0.36;0.0;0.0;-1.5708 +0.0;0.0;0.0;1.5708 +0.42;0.0;0.0;1.5708 +0.0;0.0;0.0;-1.5708 +0.40;0.0;0.0;-1.5708 +0.0;0.0;0.0;1.5708 +0.126;0.0;0.0;0.0 diff --git a/python/ur_simple_control/visualize/arms/lbr_iiwa_jos_jedan_dh b/python/ur_simple_control/visualize/arms/lbr_iiwa_jos_jedan_dh new file mode 100644 index 0000000..69b01f6 --- /dev/null +++ b/python/ur_simple_control/visualize/arms/lbr_iiwa_jos_jedan_dh @@ -0,0 +1,7 @@ +0.34;0.0;0.0;-1.5708 +0.0;0.0;0.0;1.5708 +0.40;0.0;0.0;1.5708 +0.0;0.0;0.0;-1.5708 +0.40;0.0;0.0;-1.5708 +0.0;0.0;0.0;1.5708 +0.125;0.0;0.0;0.0 diff --git a/python/ur_simple_control/visualize/arms/robot_parameters2 b/python/ur_simple_control/visualize/arms/robot_parameters2 new file mode 100644 index 0000000..6487a20 --- /dev/null +++ b/python/ur_simple_control/visualize/arms/robot_parameters2 @@ -0,0 +1,9 @@ +.20;.02;.37;.00 +.00;.55;.91;.17 +.05;.39;.76;.00 +-.10;.10;.48;-.17 +.05;.19;.58;.00 +-.10;.10;.50;-.17 +.05;.19;.60;.00 +-.10;.10;.52;-.17 +.05;.19;.61999;.00 diff --git a/python/ur_simple_control/visualize/arms/testing_dh_parameters b/python/ur_simple_control/visualize/arms/testing_dh_parameters new file mode 100644 index 0000000..8c60750 --- /dev/null +++ b/python/ur_simple_control/visualize/arms/testing_dh_parameters @@ -0,0 +1,6 @@ +0.1273;0.0;0.0;1.5708 +0.0;0.0;-0.612;0.0 +0.0;0.0;-0.5723;0.0 +0.1639;0.0;0.0;1.5708 +0.1157;0.0;0.0;-1.5708 +0.0922;0.0;0.0;0.0 diff --git a/python/ur_simple_control/visualize/arms/ur10e_dh_parameters_from_the_ur_site b/python/ur_simple_control/visualize/arms/ur10e_dh_parameters_from_the_ur_site new file mode 100644 index 0000000..991315d --- /dev/null +++ b/python/ur_simple_control/visualize/arms/ur10e_dh_parameters_from_the_ur_site @@ -0,0 +1,6 @@ +0.1807;0.0;0;1.5707963 +0.0;0.0;-0.6127;0.0 +0.0;0.0;-0.57155;0.0 +0.17415;0.0;0.0;1.5707963 +0.11985;0.0;0.0;-1.5707963 +0.11655;0.0;0.0;0.0 diff --git a/python/ur_simple_control/visualize/arms/ur5e_dh b/python/ur_simple_control/visualize/arms/ur5e_dh new file mode 100644 index 0000000..09af722 --- /dev/null +++ b/python/ur_simple_control/visualize/arms/ur5e_dh @@ -0,0 +1,6 @@ +0.163;0.0;0;1.5707963 +0.0;0.0;-0.4250;0.0 +0.0;0.0;-0.39225;0.0 +0.134;0.0;0.0;1.5707963 +0.100;0.0;0.0;-1.5707963 +0.100;0.0;0.0;0.0 diff --git a/python/ur_simple_control/visualize/main.py b/python/ur_simple_control/visualize/main.py new file mode 100644 index 0000000..19b3626 --- /dev/null +++ b/python/ur_simple_control/visualize/main.py @@ -0,0 +1,78 @@ +# idc if i need this +# Implement the default Matplotlib key bindings. +from robot_stuff.InverseKinematics import InverseKinematicsEnv +from robot_stuff.drawing import * +from robot_stuff.inv_kinm import * + +import numpy as np +import matplotlib.pyplot as plt +import time + +from rtde_control import RTDEControlInterface as RTDEControl +from rtde_receive import RTDEReceiveInterface as RTDEReceive + +#rtde_c = RTDEControl("192.168.1.102") +rtde_c = RTDEControl("127.0.0.1") + +#rtde_r = RTDEReceive("192.168.1.102") +rtde_r = RTDEReceive("127.0.0.1") +q = rtde_r.getActualQ() + +# Parameters +acceleration = 0.5 +dt = 1.0/500 # 2ms + +ik_env = InverseKinematicsEnv() +ik_env.damping = 25 +ik_env.goal[0] = -0.21 +ik_env.goal[1] = -0.38 +ik_env.goal[2] = 0.2 +ik_env.render() + + +ik_env.robot.setJoints(q) +ik_env.robot.calcJacobian() +print(ik_env.robot.p_e) +#print(type(init_q)) +#exit() + +# putting it into this class so that python remembers it 'cos reasons, whatever +controller = invKinm_dampedSquares +#controller = invKinmSingAvoidanceWithQP_kI +#while True: +while True: + start = time.time() + q = rtde_r.getActualQ() + ik_env.robot.setJoints(q) + ik_env.robot.calcJacobian() + q_dots = controller(ik_env.robot, ik_env.goal) + thetas = np.array([joint.theta for joint in ik_env.robot.joints]) + ik_env.render() + #print(ik_env.robot.p_e.copy()) + #print(ik_env.goal) + distance = np.linalg.norm(ik_env.robot.p_e.copy() - ik_env.goal) + print(distance) + if distance < 0.01: + t_start = rtde_c.initPeriod() + t_start = rtde_c.initPeriod() + rtde_c.speedJ(np.zeros(6), acceleration, dt) + rtde_c.waitPeriod(t_start) + break + +# print(q_dots) +# print(thetas) + + end = time.time() + print("time on rendering", end - start) + print("fps: ", 1/ (end - start)) + t_start = rtde_c.initPeriod() + rtde_c.speedJ(q_dots, acceleration, dt) + rtde_c.waitPeriod(t_start) + +for i in range(20): + t_start = rtde_c.initPeriod() + rtde_c.speedJ(np.zeros(6), acceleration, dt) + rtde_c.waitPeriod(t_start) + + +print("done") diff --git a/python/ur_simple_control/visualize/make_run.py b/python/ur_simple_control/visualize/make_run.py new file mode 100644 index 0000000..240c7fd --- /dev/null +++ b/python/ur_simple_control/visualize/make_run.py @@ -0,0 +1,45 @@ +from robot_stuff.InverseKinematics import InverseKinematicsEnv +from robot_stuff.drawing import * +from robot_stuff.inv_kinm import * +from robot_stuff.utils import * + +import numpy as np + +# starting off with random goals, who cares rn +# policy is one of the inverse kinematics control algoritms +# TODO (optional): make it follow a predefined trajectory +# TODO (optional): vectorize it if it isn't fast enough (at least you avoid dynamic allocations) +def makeRun(controller, ik_env, n_iters, robot_index): + # we'll shove everything into lists, and have it ready made + data = { + "qs" : np.zeros((n_iters, ik_env.robots[robot_index].ndof)), + "q_dots" : np.zeros((n_iters, ik_env.robots[robot_index].ndof)), + "manip_ell_eigenvals" : np.zeros((n_iters, 3)) , + "manip_elip_svd_rots" : np.zeros((n_iters, 3, 3)), + "p_es" : np.zeros((n_iters, 3)), + "vs" : np.zeros((n_iters, 6)), # all links linear velocities # NOT USED ATM + "dists_to_goal" : np.zeros(n_iters), + "manip_indeces" : np.zeros(n_iters), + } + + for i in range(n_iters): + q_dots = controller(ik_env.robots[robot_index], ik_env.goal) + ik_env.simpleStep(q_dots, 1.0, robot_index) + + thetas = np.array([joint.theta for joint in ik_env.robots[robot_index].joints]) + data['qs'][i] = thetas + data['q_dots'][i] = q_dots + # NOTE: this is probably not correct, but who cares + data['vs'][i] = ik_env.robots[robot_index].jacobian @ q_dots + M = ik_env.robots[robot_index].jac_tri @ ik_env.robots[robot_index].jac_tri.T + manip_index = np.sqrt(np.linalg.det(M)) + data['manip_indeces'][i] = manip_index + _, diag_svd, rot = np.linalg.svd(M) + # TODO check this: idk if this or the square roots are eigenvalues + data["manip_ell_eigenvals"][i] = np.sqrt(diag_svd) + data["manip_elip_svd_rots"][i] = rot + smallest_eigenval = diag_svd[diag_svd.argmin()] + dist_to_goal = np.linalg.norm(ik_env.robots[robot_index].p_e - ik_env.goal) + data['dists_to_goal'][i] = dist_to_goal + data['p_es'][i] = ik_env.robots[robot_index].p_e + return data diff --git a/python/ur_simple_control/visualize/manipulator_visual_motion_analyzer.py b/python/ur_simple_control/visualize/manipulator_visual_motion_analyzer.py new file mode 100644 index 0000000..11fb982 --- /dev/null +++ b/python/ur_simple_control/visualize/manipulator_visual_motion_analyzer.py @@ -0,0 +1,709 @@ +# override ugly tk widgets with system-based ttk ones +# change the import * thing if it's better for your ide +from tkinter import * +from tkinter.ttk import * + +from matplotlib.backends.backend_tkagg import ( + FigureCanvasTkAgg, NavigationToolbar2Tk) +import matplotlib.pyplot as plt +# Implement the default Matplotlib key bindings. +from matplotlib.backend_bases import key_press_handler +from matplotlib.figure import Figure +import pyautogui +from matplotlib.gridspec import GridSpec + +from robot_stuff.InverseKinematics import InverseKinematicsEnv +from robot_stuff.drawing import * +from robot_stuff.inv_kinm import * +from make_run import makeRun + +import numpy as np +import sys +# needed to communicate with the gui in real time +from multiprocessing import Queue +# don't want to refactor yet, but obv +# TODO: refactor, have normal names for things +# it's just for getting the fps reading while optimizing +import time as ttime + + + +# TODO: call update_point function with the current slider value after updating +# stuff like eliipse on/off so that you don't need to move the button to get it +# just call the slider string value to get the current slider value +# TODO: finish writing up reseting to the same starting position (joint values) + + +####################################################################### +# CONTROL STUFF # +####################################################################### + +# TODO: remove this/import it from clik +def getController(controller_name): + + if controller_name == "invKinmQPSingAvoidE_kI": + return invKinmQPSingAvoidE_kI + if controller_name == "invKinm_Jac_T": + return invKinm_Jac_T + if controller_name == "invKinm_PseudoInv": + return invKinm_PseudoInv + if controller_name == "invKinm_dampedSquares": + return invKinm_dampedSquares + 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 + + return invKinm_dampedSquares + +""" +GetCommandThread +----------------- +- NOTE: NOT USED ATM +- requires separate thread to accomodate the updating + weirdness of tkinter. +- i know how this works, so i'm running with it, + if you want to make it better, be my guest, code it up +- TODO: make it work for the new application +--> just send q commands here +- there are 2 queues because i didn't know what i was doing honestly, + but hey, it works and it ain't hurting nobody +- the point is that you generate an event for the gui, and then get what + that event actually was by poping from the queue. + and you can shove anything, ex. whole dicts into the queue because python +""" +class GetCommandThread(threading.Thread): + def __init__(self, queue, localQueue, _check): + super(GetCommandThread, self).__init__() + self.queue = queue + self.localQueue = localQueue + self._check = _check + + def run(self): + #print("worker thread doing stuff") + commands = self.queue.get() + # maybe just wait untill it's non-empty with another blocking call? + # then .get() from the main thread + self.localQueue.put(commands) +# if random.randint(0, 1) == 0: +# self.localQueue.put({'set_image': 'smile', 'set_text': "ready to work" }) +# else: +# self.localQueue.put({'set_image': 'thumb_up', 'set_text': "updated" }) + + if self._check.get() == 0: + self._check.set(1) + # print('registered new callback') + else: + self._check.set(0) + # print('registered new callback') + + +""" +ManipulatorVisualMotionAnalyzer +---------------------------------- +- for now leaving run generation here for easier testing +- later load run and run on that +- add option to load run while the robot is running +- some possibly unused stuff will be added here as a weird transplant + from an old project, but will be trimmed later +""" +# BIG TODO: +# shove all artists to be updated into a single list called updating_artists +# and then update all of them in a single for loop. +# shove all other non-updating artists into a single list. +# this list is to be updated only if you load a new run. +# or even skip this and reload everything, who cares. +# NOTE: better alternative: shove them into dicts, not lists, +# so that they have names. the for loop functions the same way, +# but you get to know what you have, which might be useful later. +class ManipulatorVisualMotionAnalyzer: + def __init__(self, root, queue, data=data): + # need to put this in the main program, + # so you need to pass it here to use it + self.root = root + self.root.wm_title("Embedding in Tk") + # for real-time updates + self.queue = queue + ####################### + # visual parameters # + ####################### + self.DPI = 80 + self.SLIDER_SIZE = 200 + #DPI = 200 + screensize = pyautogui.size() + self.SCREEN_WIDTH = screensize.width + self.SCREEN_HEIGHT = screensize.height + #self.SCALING_FACTOR_HEIGHT = 0.8 + #self.SCALING_FACTOR_WIDTH = 0.5 + self.SCALING_FACTOR_HEIGHT = 0.3 + self.SCALING_FACTOR_WIDTH = 0.3 + + ##################################################### + # LAYOUT OF THE GUI # + # putting plots into: frames -> notebooks -> tabs # + ##################################################### + self.notebook_left = Notebook(root, height=int(SCREEN_HEIGHT)) + self.notebook_right = Notebook(root, height=int(SCREEN_HEIGHT)) + self.frame_manipulator = Frame(notebook_left) + self.frame_manip_graphs = Frame(notebook_right) + self.frame_imu = Frame(notebook_right) + self.frame_ee = Frame(notebook_left) + self.tabs_left = notebook_left.add(frame_manipulator, text='manipulator') + self.tabs_left = notebook_left.add(frame_ee, text="end-effector") + self.tabs_right = notebook_right.add(frame_manip_graphs, text='manipulator-graphs') + self.tabs_right = notebook_right.add(frame_imu, text="ee-graphs") + self.notebook_left.grid(row=0, column=0, sticky='ew') + self.notebook_right.grid(row=0, column=1, sticky='ew') + + ######################## + # run related things # + ######################## + # TODO: remove from here, + # we won't be generating runs here later + self.n_iters = 200 + # the word time is used for timing + self.t = np.arange(n_iters) + # local kinematics integrator + # but this thing handles plotting + # only plotting needs to be run, so feel free to + # TODO butcher this later on. + # NOTE: this thing holds axes and artists. + # make this class hold them, because then it can manage + # all of them in all figures, this is scitzo af bruv + self.ik_env = InverseKinematicsEnv() + # TODO: do this depending on the number of runs you'll be loading + #self.ik_env.robots.append(Robot_raw(robot_name="no_sim")) + self.ik_env.damping = 25 + # putting it into this class so that python remembers it 'cos reasons, whatever + # TODO: load this from run log files later + # or just remove it since you're not generating runs from here + self.controller1 = getController("") + self.controller2 = getController("invKinm_Jac_T") + # TODO: load runs, not make them + # but deliver the same format. + # TODO: ensure you're saving AT LEAST what's required here + # NOTE: the jacobian svd is computed offline in these + self.ik_env.data.append(makeRun(controller1, ik_env, n_iters, 0)) + self.ik_env.data.append(makeRun(controller2, ik_env, n_iters, 1)) + + + + # how to add plots will be documented below on an easier example + ####################################################################### + # UNUSED PLOT # + ####################################################################### + self.fig_ee = Figure(figsize=(SCREEN_WIDTH/DPI*SCALING_FACTOR_WIDTH, SCREEN_HEIGHT/DPI*SCALING_FACTOR_HEIGHT), dpi=DPI) + self.rec3D_ax = self.fig_ee.add_subplot(projection='3d') + self.rec3D_ax.set(xticklabels=[], yticklabels=[], zticklabels=[]) + + ####################################################################### + # MANIPULATOR PLOT # + ####################################################################### + # robot plot + self.fig_manipulator = Figure(figsize=(SCREEN_WIDTH/DPI*SCALING_FACTOR_WIDTH, SCREEN_HEIGHT/DPI*SCALING_FACTOR_HEIGHT), dpi=DPI) + self.ik_env.ax = self.fig_manipulator.add_subplot(projection='3d') + # fix array sizes to be the workspace as that won't change. otherwise it's updated as you go, + # and that's impossible to look at. + self.ik_env.ax.plot(np.array([0]), np.array([0]), np.array([1.5]), c='b') + self.ik_env.ax.plot(np.array([0]), np.array([0]), np.array([-1.5]), c='b') + self.ik_env.ax.set_xlim([-1.0,1.0]) + self.ik_env.ax.set_ylim([-1.0,1.0]) + # TODO: generate this from a colormap + self.link_colors = ['black', 'violet'] + self.trajectory_plots = [] + # this way you can have more robots plotted and see the differences between algorithms + # but it of course works on a single robot too + for robot_index, robot in enumerate(self.ik_env.robots): + robot.initDrawing(self.ik_env.ax, self.link_colors[robot_index]) + # ee point + # TODO: blit the point because it moves + self.ik_env.p_e_point_plots.append(drawPoint(ik_env.ax, ik_env.data[robot_index]['p_es'][0], 'red', 'o')) + # plot ee position trajectory. not blitted because it is fixed + # TODO: blit it for real-time operation + self.trajectory_plot, = ik_env.ax.plot(ik_env.data[robot_index]['p_es'][:,0], ik_env.data[robot_index]['p_es'][:,1], ik_env.data[robot_index]['p_es'][:,2], color='blue') + trajectory_plots.append(trajectory_plot) + # goal point + # only makes sense for clik, but keep it here anyway, it doesn't hurt + # TODO: add a button to turn it off + self.ik_env.goal_point_plot = drawPoint(ik_env.ax, ik_env.goal, 'maroon', '*') + + # the manipulability ellipses + # TODO: BLIT THEM + for robot_index, robot in enumerate(self.ik_env.robots): + radii = 1.0/self.ik_env.data[robot_index]["manip_ell_eigenvals"][0] * 0.1 + u = np.linspace(0.0, 2.0 * np.pi, 60) + v = np.linspace(0.0, np.pi, 60) + x = radii[0] * np.outer(np.cos(u), np.sin(v)) + y = radii[1] * np.outer(np.sin(u), np.sin(v)) + z = radii[2] * np.outer(np.ones_like(u), np.cos(v)) + for i in range(len(x)): + for j in range(len(x)): + # TODO: add if to compute this in real-time if you're running real-time + # although, again, primary purpose is to log stuff for later + [x[i,j],y[i,j],z[i,j]] = np.dot([x[i,j],y[i,j],z[i,j]], self.ik_env.data[robot_index]["manip_elip_svd_rots"][0]) + self.ik_env.data[robot_index]['p_es'][0] + + self.ik_env.ellipsoid_surf_plots.append(self.ik_env.ax.plot_surface(x, y, z, rstride=3, cstride=3, + color='pink', linewidth=0.1, + alpha=0.3, shade=True)) + + ####################################################################### + # manipulability plots # + ####################################################################### + # manipulability ellipsoid eigenvalues + self.fig_manip_graphs = Figure(figsize=(SCREEN_WIDTH/DPI*SCALING_FACTOR_WIDTH, SCREEN_HEIGHT/DPI*SCALING_FACTOR_HEIGHT), dpi=DPI) + # NOTE: this ax object lives on in some other matplotlib object + # so i don't have to save it + ax = self.fig_manip_graphs.add_subplot(311) + ax.set_title('Manipulability ellipsoid eigenvalues') + ax.grid() + ax.set_xticks([]) + self.eigen1_lines = [] + self.eigen2_lines = [] + self.eigen3_lines = [] + for robot_index, robot in enumerate(ik_env.robots): + eigen1_line, = ax.plot(time, ik_env.data[robot_index]["manip_ell_eigenvals"][:,0], color=link_colors[robot_index]) + self.eigen1_lines.append(eigen1_line) + eigen2_line, = ax.plot(time, ik_env.data[robot_index]["manip_ell_eigenvals"][:,1], color=link_colors[robot_index]) + self.eigen2_lines.append(eigen2_line) + eigen3_line, = ax.plot(time, ik_env.data[robot_index]["manip_ell_eigenvals"][:,2], color=link_colors[robot_index]) + self.eigen3_lines.append(eigen3_line) + # TODO: blit this line + self.point_in_time_eigen1_line = ax.axvline(x=0, color='red') + + # manipulability index (volume of manipulability ellipsoid) + ax = self.fig_manip_graphs.add_subplot(312) + ax.set_title('Manipulability index') + ax.grid() + ax.set_xticks([]) + self.manip_index_lines = [] + for robot_index, robot in enumerate(ik_env.robots): + manip_index_line, = ax.plot(time, ik_env.data[robot_index]['manip_indeces'], color=link_colors[robot_index]) + self.manip_index_lines.append(manip_index_line) + self.point_in_time_manip_index_line = ax.axvline(x=0, color='red') + + # dist to goal (this could be/should be elsewhere) + ax = self.fig_manip_graphs.add_subplot(313) + ax.set_title('Distance to goal') + ax.grid() + ax.set_xlabel('iter') + self.dist_to_goal_lines = [] + for robot_index, robot in enumerate(ik_env.robots): + self.dist_to_goal_line, = ax.plot(time, ik_env.data[robot_index]['dists_to_goal'], color=link_colors[robot_index]) + dist_to_goal_lines.append(dist_to_goal_line) + # TODO: blit this line + self.point_in_time_dist_to_goal_line = ax.axvline(x=0, color='red') + + + ####################################################################### + # IMU plots # + ####################################################################### + self.fig_imu = Figure(figsize=(SCREEN_WIDTH/DPI*SCALING_FACTOR_WIDTH, SCREEN_HEIGHT/DPI*SCALING_FACTOR_HEIGHT), dpi=DPI) + self.v_x_lines = [] + self.v_y_lines = [] + self.v_z_lines = [] + self.omega_x_lines = [] + self.omega_y_lines = [] + self.omega_z_lines = [] + + ax_v_x = self.fig_imu.add_subplot(321) + ax_v_x.grid() + ax_v_y = self.fig_imu.add_subplot(322) + ax_v_y.grid() + ax_v_z = self.fig_imu.add_subplot(323) + ax_v_z.grid() + ax_omega_x = self.fig_imu.add_subplot(324) + ax_omega_x.grid() + ax_omega_y = self.fig_imu.add_subplot(325) + ax_omega_y.grid() + ax_omega_z = self.fig_imu.add_subplot(326) + ax_omega_z.grid() + ax_v_x.set_title('Linear velocity x') + ax_v_y.set_title('Linear velocity y') + ax_v_z.set_title('Linear velocity z') + ax_omega_x.set_title('Angular velocity x') + ax_omega_y.set_title('Angular velocity y') + ax_omega_z.set_title('Angular velocity z') + ax_v_x.set_xticks([]) + ax_v_y.set_xticks([]) + ax_v_z.set_xticks([]) + ax_omega_x.set_xticks([]) + ax_omega_y.set_xticks([]) + ax_omega_z.set_xticks([]) + for robot_index, robot in enumerate(ik_env.robots): + v_x_line, = ax_v_x.plot(self.t, self.ik_env.data[robot_index]["vs"][:,0], color=self.link_colors[robot_index]) + v_y_line, = ax_v_y.plot(self.t, self.ik_env.data[robot_index]["vs"][:,1], color=self.link_colors[robot_index]) + v_z_line, = ax_v_z.plot(self.t, self.ik_env.data[robot_index]["vs"][:,2], color=self.link_colors[robot_index]) + omega_x_line, = ax_omega_x.plot(self.t, self.ik_env.data[robot_index]["vs"][:,3], color=self.link_colors[robot_index]) + omega_y_line, = ax_omega_y.plot(self.t, self.ik_env.data[robot_index]["vs"][:,4], color=self.link_colors[robot_index]) + omega_z_line, = ax_omega_z.plot(self.t, self.ik_env.data[robot_index]["vs"][:,5], color=self.link_colors[robot_index]) + self.v_x_lines.append(v_x_line) + self.v_y_lines.append(v_y_line) + self.v_z_lines.append(v_z_line) + self.omega_x_lines.append(omega_x_line) + self.omega_y_lines.append(omega_y_line) + self.omega_z_lines.append(omega_z_line) + + # TODO: blit these + self.point_in_time_ax_v_x_line = ax_v_x.axvline(x=0, color='red') + self.point_in_time_ax_v_y_line = ax_v_y.axvline(x=0, color='red') + self.point_in_time_ax_v_z_line = ax_v_z.axvline(x=0, color='red') + self.point_in_time_ax_omega_x_line = ax_omega_x.axvline(x=0, color='red') + self.point_in_time_ax_omega_y_line = ax_omega_y.axvline(x=0, color='red') + self.point_in_time_ax_omega_z_line = ax_omega_z.axvline(x=0, color='red') + + + +# tkinterize these plots +canvas_manipulator = FigureCanvasTkAgg(fig_manipulator, master=frame_manipulator) +canvas_manipulator.draw() +# NEW +# TODO maybe you want another background idk +# worked elsewhere with figure.bbox +background_manipulator = canvas_manipulator.copy_from_bbox(fig_manipulator.bbox) +#background_manipulator = canvas_manipulator.copy_from_bbox(canvas_manipulator.bbox) + +canvas_manipulator_widget = canvas_manipulator.get_tk_widget() +canvas_manipulator_widget.grid(row=0, column=0) +canvas_manipulator._tkcanvas.grid(row=1, column=0) +canvas_manipulator.draw() + +canvas_ee = FigureCanvasTkAgg(fig_ee, master=frame_ee) +canvas_ee_widget = canvas_ee.get_tk_widget() +canvas_ee_widget.grid(row=0, column=0) +canvas_ee._tkcanvas.grid(row=1, column=0) +canvas_ee.draw() + +canvas_manip_graphs = FigureCanvasTkAgg(fig_manip_graphs, master=frame_manip_graphs) +canvas_manip_graphs_widget = canvas_manip_graphs.get_tk_widget() +canvas_manip_graphs_widget.grid(row=0, column=0) +canvas_manip_graphs._tkcanvas.grid(row=1, column=0) +canvas_manip_graphs.draw() + +canvas_imu = FigureCanvasTkAgg(self.fig_imu, master=frame_imu) +canvas_imu_widget = canvas_manip_graphs.get_tk_widget() +canvas_imu_widget.grid(row=0, column=0) +canvas_imu._tkcanvas.grid(row=1, column=0) +canvas_imu.draw() + +# pack_toolbar=False will make it easier to use a layout manager later on. +toolbar_manipulator = NavigationToolbar2Tk(canvas_manipulator, frame_manipulator, pack_toolbar=False) +toolbar_manipulator.update() +toolbar_manipulator.grid(column=0, row=2) +toolbar_manip_graphs = NavigationToolbar2Tk(canvas_manip_graphs, frame_manip_graphs, pack_toolbar=False) +toolbar_manip_graphs.update() +toolbar_manip_graphs.grid(column=0, row=2) +toolbar_ee = NavigationToolbar2Tk(canvas_ee, frame_ee, pack_toolbar=False) +toolbar_ee.update() +toolbar_ee.grid(column=0, row=2) +toolbar_imu = NavigationToolbar2Tk(canvas_imu, frame_imu, pack_toolbar=False) +toolbar_imu.update() +toolbar_imu.grid(column=0, row=2) + +####################################################################### +# functions for widgets to control the plots # +####################################################################### + +# keyboard inputs +# whatever, doesn't hurt, could be used +#canvas.mpl_connect( +# "key_press_event", lambda event: print(f"you pressed {event.key}")) +#canvas.mpl_connect("key_press_event", key_press_handler) + +# new_val is what the widget gives you +# you define what needs to happen to plots in Figure below, +# and call canvas.draw +def update_points(new_val): +# ee plot +########################################################################### + start = ttime.time() + #fig_manipulator.canvas.restore_region(background_manipulator) + canvas_manipulator.restore_region(background_manipulator) + index = int(np.floor(float(new_val))) + + # Update 3D reconstruction plot + # plot3D_camera(recP[index], scale=scale) +# C, Q = camera_data_for_plot3D(recP[cam_indices[index]]) +# camera_center.set_data_3d(*C) +# C = C.flatten() +# for ii in range(len(camera_coordinate_system)): +# camera_coordinate_system[ii].set_data([C[0], C[0] + scale * Q[0,ii]], +# [C[1], C[1] + scale * Q[1,ii]]) +# camera_coordinate_system[ii].set_3d_properties([C[2], C[2] + scale * Q[2,ii]]) +# recX_ = recX[:,X_indices[:num_pts_subset*(index+1)]] +# rec_points3D.set_data_3d(recX_[0,:], recX_[1,:], recX_[2,:]) +########################################################################### + + ik_env.ax.set_title(str(index) + 'th iteration toward goal') + # animate 3d manipulator + for robot_index, robot in enumerate(ik_env.robots): + ik_env.robots[robot_index].setJoints(ik_env.data[robot_index]["qs"][index]) + ik_env.robots[robot_index].drawStateAnim() + + # NEW AND BROKEN + for link in robot.lines: + for line in link: + ik_env.ax.draw_artist(line) + # all these are in lists as that's what the line plot wants, + # despite the fact that we have single points + point_in_time_eigen1_line.set_xdata([time[index]]) + point_in_time_manip_index_line.set_xdata([time[index]]) + point_in_time_dist_to_goal_line.set_xdata([time[index]]) + point_in_time_ax_v_x_line.set_xdata([time[index]]) + point_in_time_ax_v_y_line.set_xdata([time[index]]) + point_in_time_ax_v_z_line.set_xdata([time[index]]) + point_in_time_ax_omega_x_line.set_xdata([time[index]]) + point_in_time_ax_omega_y_line.set_xdata([time[index]]) + point_in_time_ax_omega_z_line.set_xdata([time[index]]) + + # ellipsoid update + radii = 1.0/ik_env.data[robot_index]["manip_ell_eigenvals"][index] * 0.1 + u = np.linspace(0.0, 2.0 * np.pi, 60) + v = np.linspace(0.0, np.pi, 60) + x = radii[0] * np.outer(np.cos(u), np.sin(v)) + y = radii[1] * np.outer(np.sin(u), np.sin(v)) + z = radii[2] * np.outer(np.ones_like(u), np.cos(v)) + for i in range(len(x)): + for j in range(len(x)): + [x[i,j],y[i,j],z[i,j]] = np.dot([x[i,j],y[i,j],z[i,j]], ik_env.data[robot_index]["manip_elip_svd_rots"][index]) + ik_env.data[robot_index]['p_es'][index] + + if ellipse_on_off_var.get(): + try: + ik_env.ellipsoid_surf_plots[robot_index].remove() + except ValueError: + pass + ik_env.ellipsoid_surf_plots[robot_index] = ik_env.ax.plot_surface(x, y, z, rstride=3, cstride=3, + color='pink', linewidth=0.1, + alpha=0.3, shade=True) + + ik_env.p_e_point_plots[robot_index].set_data([ik_env.data[robot_index]['p_es'][index][0]], [ik_env.data[robot_index]['p_es'][index][1]]) + ik_env.p_e_point_plots[robot_index].set_3d_properties([ik_env.data[robot_index]['p_es'][index][2]]) + canvas_ee.draw() + # NEW AND BROKEN +# fig_manipulator.canvas.blit(fig_manipulator.bbox) +# fig_manipulator.canvas.flush_events() + canvas_manipulator.blit(fig_manipulator.bbox) + canvas_manipulator.flush_events() + #canvas_manipulator.draw() + # might need to manually update all artists here from ax_something + #canvas_manipulator.blit(fig_manipulator.bbox) + canvas_manip_graphs.draw() + canvas_imu.draw() + # TODO update may not be needed as we're going by slider here + root.update() + end = ttime.time() + print("time per update:", end - start) + print("fps", 1 / (end - start)) + + +def update_goal_x(new_val): + goal_x = float(new_val) + ik_env.goal[0] = goal_x + ik_env.goal_point_plot.set_data([ik_env.goal[0]], [ik_env.goal[1]]) + ik_env.goal_point_plot.set_3d_properties([ik_env.goal[2]]) + canvas_ee.draw() + canvas_manipulator.draw() + canvas_manip_graphs.draw() + # TODO update may not be needed as we're going by slider here + root.update() + +def update_goal_y(new_val): + goal_y = float(new_val) + ik_env.goal[1] = goal_y + ik_env.goal_point_plot.set_data([ik_env.goal[0]], [ik_env.goal[1]]) + ik_env.goal_point_plot.set_3d_properties([ik_env.goal[2]]) + canvas_ee.draw() + canvas_manipulator.draw() + canvas_manip_graphs.draw() + # TODO update may not be needed as we're going by slider here + root.update() + +def update_goal_z(new_val): + goal_z = float(new_val) + ik_env.goal[2] = goal_z + ik_env.goal_point_plot.set_data([ik_env.goal[0]], [ik_env.goal[1]]) + ik_env.goal_point_plot.set_3d_properties([ik_env.goal[2]]) + canvas_ee.draw() + canvas_manipulator.draw() + canvas_manip_graphs.draw() + # TODO update may not be needed as we're going by slider here + root.update() + + + +def reset(): + ik_env.reset() +# ik_env.goal_point_plot.remove() +# ik_env.goal_point_plot = drawPoint(ik_env.ax, ik_env.goal, 'red', 'o') +# print(controller_string1.get()) +# print(controller_string2.get()) + controller1 = getController(controller_string1.get()) + controller2 = getController(controller_string2.get()) + controllers = [controller1, controller2] + for robot_index, robot in enumerate(ik_env.robots): + ik_env.data.append(makeRun(controllers[robot_index], ik_env, n_iters, robot_index)) + trajectory_plots[robot_index].set_data(ik_env.data[robot_index]['p_es'][:,0], ik_env.data[robot_index]['p_es'][:,1]) + trajectory_plots[robot_index].set_3d_properties(ik_env.data[robot_index]['p_es'][:,2]) + eigen1_lines[robot_index].set_ydata(ik_env.data[robot_index]["manip_ell_eigenvals"][:,0]) + eigen2_lines[robot_index].set_ydata(ik_env.data[robot_index]["manip_ell_eigenvals"][:,1]) + eigen3_lines[robot_index].set_ydata(ik_env.data[robot_index]["manip_ell_eigenvals"][:,2]) + manip_index_lines[robot_index].set_ydata(ik_env.data[robot_index]['manip_indeces']) + dist_to_goal_lines[robot_index].set_ydata(ik_env.data[robot_index]['dists_to_goal']) + update_points(0) + canvas_ee.draw() + canvas_manipulator.draw() + canvas_manip_graphs.draw() + root.update() + +def play(): + pass + +# ellipse on/off +def add_remove_ellipse(): + try: + for robot_index, robot in enumerate(ik_env.robots): + ik_env.ellipsoid_surf_plots[robot_index].remove() + except ValueError: + pass + + +# NOTE: this thing is not used +same_starting_position_on_off_var = IntVar() +same_starting_position_on_off_var.set(0) +same_starting_position_checkbutton= Checkbutton(root, text = "same starting position on/off", + variable = same_starting_position_on_off_var, + onvalue = 1, + offvalue = 0, +# command=same_starting_position_cmd, + ) + + +####################################################################### +# PLACING ELEMENTS IN THE GUI # +####################################################################### + +# LEFT PANE BELOW MANIP/EE PLOTS +frame_below_manipulator_plot = Frame(frame_manipulator) +frame_below_manipulator_plot.grid(column=0, row=3) + +# set controller 1 +frame_controller_menu1 = Frame(frame_below_manipulator_plot) +controller_string1 = StringVar(frame_controller_menu1) +controller_string1.set("invKinm_dampedSquares") # default value +controller_menu1 = OptionMenu(frame_controller_menu1, controller_string1, + "invKinmQPSingAvoidE_kI", + "invKinm_Jac_T", + "invKinm_PseudoInv", + "invKinm_dampedSquares", + "invKinm_PseudoInv_half", + "invKinmQP", + "invKinmQPSingAvoidE_kI", + "invKinmQPSingAvoidE_kM", + "invKinmQPSingAvoidManipMax", + ) +controller_string1.set("invKinm_dampedSquares") +controller_menu1.grid(column=1, row=0) +Label(frame_controller_menu1, text="Robot 1 controller:", background='yellow').grid(row=0, column=0, pady=4, padx = 4) +frame_controller_menu1.grid(column=0, row=0) + +# set controller 2 +frame_controller_menu2 = Frame(frame_below_manipulator_plot) +controller_string2 = StringVar(frame_controller_menu2) +controller_menu2 = OptionMenu(frame_controller_menu2, controller_string2, + "invKinmQPSingAvoidE_kI", + "invKinm_Jac_T", + "invKinm_PseudoInv", + "invKinm_dampedSquares", + "invKinm_PseudoInv_half", + "invKinmQP", + "invKinmQPSingAvoidE_kI", + "invKinmQPSingAvoidE_kM", + "invKinmQPSingAvoidManipMax", + ) +controller_string2.set("invKinm_Jac_T") # default value +Label(frame_controller_menu1, text="Robot 1 controller:", background='black', foreground='white').grid(row=0, column=0, pady=4, padx = 4) +controller_menu2.grid(column=1, row=0) +Label(frame_controller_menu2, text="Robot 2 controller:", background='#EE82EE').grid(row=0, column=0, pady=4, padx = 4) +frame_controller_menu2.grid(column=0, row=1) + + +ellipse_on_off_var = IntVar() +ellipse_on_off_var.set(1) + +ellipse_checkbutton= Checkbutton(frame_below_manipulator_plot, text = "ellipse on/off", + variable = ellipse_on_off_var, + onvalue = 1, + offvalue = 0, + command=add_remove_ellipse, + ) +ellipse_checkbutton.grid(column=1, row=0) + + +frame_goal_x = Frame(frame_below_manipulator_plot) +Label(frame_goal_x, text="goal x").grid(row=0, column=0, pady=4, padx = 4) +slider_goal_x = Scale(frame_goal_x, from_=-1.0, to=1.0, length=SLIDER_SIZE, orient=HORIZONTAL, + command=update_goal_x) +slider_goal_x.grid(column=1, row=0) +frame_goal_x.grid(column=1, row=1) + +frame_goal_y = Frame(frame_below_manipulator_plot) +Label(frame_goal_y, text="goal y").grid(row=0, column=0, pady=4, padx = 4) +slider_goal_y = Scale(frame_goal_y, from_=-1.0, to=1.0, length=SLIDER_SIZE, orient=HORIZONTAL, + command=update_goal_y) +slider_goal_y.grid(column=1, row=0) +frame_goal_y.grid(column=1, row=2) + + +frame_goal_z = Frame(frame_below_manipulator_plot) +Label(frame_goal_z, text="goal z").grid(row=0, column=0, pady=4, padx = 4) +slider_goal_z = Scale(frame_goal_z, from_=-1.0, to=1.0, length=SLIDER_SIZE, orient=HORIZONTAL, + command=update_goal_z) +slider_goal_z.grid(column=1, row=0) +frame_goal_z.grid(column=1, row=3) + + +frame_update = Frame(frame_manip_graphs) +Label(frame_update, text="Time").grid(row=0, column=0, pady=4, padx = 4) +slider_update = Scale(frame_update, from_=0, to=n_iters - 1, length=SLIDER_SIZE, orient=HORIZONTAL, + command=update_points)#, label="Frequency [Hz]") +slider_update.grid(column=1, row=0) +frame_update.grid(column=0, row=3) + +frame_update2 = Frame(frame_imu) +Label(frame_update2, text="Time").grid(row=0, column=0, pady=4, padx = 4) +slider_update = Scale(frame_update2, from_=0, to=n_iters - 1, length=SLIDER_SIZE, orient=HORIZONTAL, + command=update_points)#, label="Frequency [Hz]") +slider_update.grid(column=1, row=0) +frame_update2.grid(column=0, row=3) + + +button_quit = Button(master=frame_manip_graphs, text="Quit", command=root.destroy) +button_reset = Button(master=frame_manip_graphs, text="New run", command=reset) +button_reset.grid(column=0, row=4) +button_quit.grid(column=0, row=5) + +button_quit2 = Button(master=frame_imu, text="Quit", command=root.destroy) +button_reset2 = Button(master=frame_imu, text="New run", command=reset) +button_reset2.grid(column=0, row=4) +button_quit2.grid(column=0, row=5) + +update_points(0) +slider_goal_x.set(ik_env.goal[0]) +slider_goal_y.set(ik_env.goal[1]) +slider_goal_z.set(ik_env.goal[2]) + +if name == "__main__": + queue = Queue() + root = Tk() + gui = ManipulatorVisualMotionAnalyzer(root, queue) + + # have it 'cos from tkinter import * + mainloop() + # alternative if someone complains + #root.mainloop() + diff --git a/python/ur_simple_control/visualize/robot_stuff/InverseKinematics.py b/python/ur_simple_control/visualize/robot_stuff/InverseKinematics.py new file mode 100644 index 0000000..9cd579d --- /dev/null +++ b/python/ur_simple_control/visualize/robot_stuff/InverseKinematics.py @@ -0,0 +1,226 @@ +# kinematics related imports +from robot_stuff.forw_kinm import * +from robot_stuff.inv_kinm import * +from robot_stuff.follow_curve import * +from robot_stuff.utils import * + +# general imports +import numpy as np +import matplotlib.pyplot as plt + +# ######################################### NOTE ######################### +# i'm half-adding another robot for the purposes of the plot. +# this means that half of the code will end up being broken (as a lot of it is already anyway). +# in case you want to use this elsewhere, just find the commit before the change +# and run with that. +################################################################## + + +class InverseKinematicsEnv: + +# set damping (i.e. dt i.e. precision let's be real) + def __init__(self, model_path=None, initial_qpos=None, n_actions=None, n_substeps=None ): + print('env created') + self.chill_reset = False + # TODO write a convenience dh_parameter loading function + self.robot = Robot_raw(robot_name="no_sim") + self.robots = [self.robot] + self.init_qs_robot = [] + self.damping = 5 + self.error_vec = None + self.n_of_points_done = 0 + # keep track of the timesteps (to be reset after every episode) + self.n_of_tries_for_point = 0 + # for storing whole runs of all robots + self.data = [] + self.p_e_point_plots = [] + self.ellipsoid_surf_plots = [] + self.same_starting_position_on_off = 0 + + # init goal + self.goal = np.random.random(3) * 0.7 + # needed for easy initialization of the observation space + + # TODO enable setting the other one with greater ease + self.reward_type = 'dense' + #self.reward_type = 'sparse' + self.episode_score = 0 + + +# NOTE: BROKEN AS IT'S FOR ONLY ONE ROBOT + def compute_reward(self, achieved_goal, goal, info): + # Compute distance between goal and the achieved goal. + if self.reward_type == 'sparse': + if not error_test(self.robot.p_e, self.goal): + return np.float32(-1.0) + else: + return np.float32(10.0) + if self.reward_type == 'dense': + distance = goal_distance(achieved_goal, goal) + if not error_test(self.robot.p_e, self.goal): + #reward = -1 * distance + 1 / distance + reward = -1 * distance + else: + reward = 100 + return reward + + + + def simpleStep(self, q_dots, damping, index): + self.robot.forwardKinmViaPositions(q_dots / self.damping, damping) + self.n_of_tries_for_point += 1 +# done = False +# if error_test(self.robot.p_e, self.goal): +# info = { +# 'is_success': np.float32(1.0), +# } +# else: +# info = { +# 'is_success': np.float32(0.0), +# } +# return done + + +# NOTE: BROKEN AS IT'S FOR ONLY ONE ROBOT + def step(self, action): + self.robot.forwardKinmViaPositions(action[:-1] / self.damping, action[-1]) + self.n_of_tries_for_point += 1 + obs = self._get_obs() + + done = False + if error_test(self.robot.p_e, self.goal): + info = { + 'is_success': np.float32(1.0), + } + else: + info = { + 'is_success': np.float32(0.0), + } + reward = self.compute_reward(obs['achieved_goal'], self.goal, info) + self.episode_score += reward + return obs, reward, done, info + + + + + def reset(self): + # TODO: initialize robot joints state to a random (but valid (in joint range)) initial state + # when using joint clamping + self.episode_score = 0 + self.n_of_points_done += 1 + self.n_of_tries_for_point = 0 + self.data = [] + + # generate new point + # NOTE: simply taken away 'cos now it's set with a slider + #self.goal = np.array([random.uniform(-0.70, 0.70), random.uniform(-0.70, 0.70), random.uniform(-0.70, 0.70)]) + + # initialize to a random starting state and check whether it makes any sense + thetas = [] + for joint in self.robot.joints: + thetas.append(6.28 * np.random.random() - 3.14) + self.robot.forwardKinmViaPositions(thetas, 1) + + if self.chill_reset == True: + sensibility_check = False + while not sensibility_check: + thetas = [] + for joint in self.robot.joints: + thetas.append(6.28 * np.random.random() - 3.14) + self.robot.forwardKinmViaPositions(thetas , 1) + if calculateManipulabilityIndex(self.robot) > 0.15: + sensibility_check = True + +# for i, joint in enumerate(self.robots[robot_index].joints): +# joint.theta = self.robots[0].joints[i].theta + +# NOTE: not needed and i'm not fixing _get_obs for more robots + obs = self._get_obs() + return obs + + def render(self, mode='human', width=500, height=500): + try: + self.drawingInited == False + except AttributeError: + self.drawingInited = False + + if self.drawingInited == False: + #plt.ion() + self.fig = plt.figure() + #self.ax = self.fig.add_subplot(111, projection='3d') + self.ax = self.fig.add_subplot(111, projection='3d') + # these are for axes scaling which does not happen automatically + self.ax.plot(np.array([0]), np.array([0]), np.array([1.5]), c='b') + self.ax.plot(np.array([0]), np.array([0]), np.array([-1.5]), c='b') + plt.xlim([-1.5,1.5]) + plt.ylim([-0.5,1.5]) + color_link = 'black' + self.robot.initDrawing(self.ax, color_link) + self.drawingInited = True + plt.pause(0.1) + # put this here for good measure + self.robot.drawStateAnim() + self.bg = self.fig.canvas.copy_from_bbox(self.fig.bbox) + # manual blitting basically + # restore region + self.fig.canvas.restore_region(self.bg) + # updating all the artists (set new properties (position bla)) + self.robot.drawStateAnim() + # now you need to manually draw all the artist (otherwise you update everything) + # thank god that's just lines, all in a list. + # btw, that should definitely be a dictionary, not a list, + # this makes the code fully unreadable (although correct). + for link in self.robot.lines: + for line in link: + self.ax.draw_artist(line) + self.fig.canvas.blit(self.fig.bbox) + # NOTE: this might not work + self.ax.set_title(str(self.n_of_tries_for_point) + 'th iteration toward goal') + drawPoint(self.ax, self.goal, 'red', 'o') + # if no draw it is kinda faster + #self.fig.canvas.draw() + # this is even faster + #self.fig.canvas.update() + self.fig.canvas.flush_events() + + + def reset_test(self): + self.episode_score = 0 + self.n_of_points_done += 1 + self.n_of_tries_for_point = 0 + + # generate new point + self.goal = np.array([random.uniform(-0.70, 0.70), random.uniform(-0.70, 0.70), random.uniform(-0.70, 0.70)]) + + # DO NOT initialize to a random starting state, keep the previous one + + obs = self._get_obs() + return obs + + + def close(self): + # close open files if any are there + pass + + + # various uitility functions COPIED from fetch_env + + def seed(self, seed=None): + self.np_random, seed = seeding.np_random(seed) + return [seed] + + + def _get_obs(self): + thetas = [] + for joint in self.robot.joints: + thetas.append(joint.theta) + thetas = np.array(thetas , dtype=np.float32) + obs = self.robot.p_e.copy() + obs = np.append(obs, thetas) + + return { + 'observation': obs, + 'achieved_goal': self.robot.p_e.copy(), + 'desired_goal': self.goal.copy(), + } + diff --git a/python/ur_simple_control/visualize/robot_stuff/__pycache__/InverseKinematics.cpython-310.pyc b/python/ur_simple_control/visualize/robot_stuff/__pycache__/InverseKinematics.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c59cab0f7519bb21e35ad76ebfa41a84b0376333 GIT binary patch literal 4796 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 ByTJee literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/robot_stuff/__pycache__/InverseKinematics.cpython-311.pyc b/python/ur_simple_control/visualize/robot_stuff/__pycache__/InverseKinematics.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e19567ae0945d05a0ef9d5b7593f2eaf8ca7e1b2 GIT binary patch literal 7195 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 HfCC2rDB=i& literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/robot_stuff/__pycache__/drawing.cpython-310.pyc b/python/ur_simple_control/visualize/robot_stuff/__pycache__/drawing.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4ec066dd49fb82dc8ca4b1a060b56e912243739 GIT binary patch literal 1443 zcmd1j<>g{vU|=XnO-T)4Wng#=;vi!t1_lNP1_p-WC<X?G6owSW9EK=HFwK<99L2&2 z5o66|V`5-r$Yqb>0E;l?aOQGFae>*4Io!ECQM`-{Da<J>Eeuh7DXb}MEeuioDeNg6 zEeuftDU87knw&2|w)$x@-r{vD%}aL7%gjwI$;{77W&w#qG03Ux3=9m;AO}rgU|=X= zSiqRVn8K98ypXwuIfX@<L4=`}C7mIKbrEANYY9^dTMBzKlM6$vVJ%w?Qwp0TLk(jM zTMAnmlO#h8iwJ`_LoIs^Qwj%EoC8UmqlPJk1uD*hB+gmGki}fXUc*ttS;JbxCdrV( z*~`MnP$-hZ5X_*-<@X%wRt6~JgZ=YaGeKad{Yy|_YBJwqOuEIGev7fFh>?MT;UzPO z0I^mw-eOJ8&&e;+WVywZS8$6hu_!SwJ@pnVgkmYk$uH65y2X@Oaf>^@C^I#$1RO%Q zm`mczZt*3SrN`%F=4Hp{q~?`m++r;*Ni8V8#hjd<bBi(a7GuRN#>!iaRVx{aWEdD2 zewFHH<maa9r{@<J<fkMSfq6y<Zcb@lihe;+Vnt@LerZvBab|8oPHKE|eqKpYevW=Y zWl2VU9u|qR%;M6-oXo0J{i6J&{F3<MlG3y^{gk4_^31$+y@JYH{2;bJ!tETOh!kL8 zU=U;FVCG|#fI^l(MRE)b3@C9AQpyI4`vkPOuVsP77C6ozG0R%Rl)?&%K`;psgQo&; zT7ZbL*Dz!;f#R};y@ol39Tc13xa9CF0>w=cC<PX=GB7ZJ<Ew}p!~(gbh=+lJL6f<N z4<x`3A_PGK97RG53=FqeK#@@-0%CH3V-*}bMPeXzk|06~L`Z`OP>g`1rAQX6927`l zsmUezMG~O!0i}9ACJ{yu6k{w>L=PE|(?Dv#xx$EnfuV*Wi=l=wg;A0row0^7g$YVC zgK4H3CQwcRWx`&L7{*$b8kS%NO?E#t&!t0>v6w^p*)t&U#$J>47JqJHQFdxkT4Hi4 z*h?^h)Rc6n02@TbEiMRMoLQB6i#0K)AR|$eu?QSItdQVgEV#u3HX}YWuQ)R$wMYfz zb=HFX%)AmvSb#$X93Y&a00{ueD1pKQ6d!twER023px_p~#hO=|TTls(hFgNUi6x+X zl9QRFS5OIJ-C~2%%mq0mx1@6ma^g$!^K-H@ON#Y!K?;mh^b#vli;Yumu|xT{*q{PT z1;)2TkW5U3l}bgRoQz@_*e}Jm*q}5^a(+$`I1z*63+z-RZz24~VUwGmQks)$2Py%I XL0N%=frn9uk%y6ok%O5-fQJhJ7UyAa literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/robot_stuff/__pycache__/drawing.cpython-311.pyc b/python/ur_simple_control/visualize/robot_stuff/__pycache__/drawing.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..60bcda8221c70f3842b39da90028011be8e56d07 GIT binary patch literal 2272 zcmZ3^%ge>Uz`zhWS1mPwm4V?ghy%k+P{!vl1_p-d3@HpLj5!QZj9{86mpO`s5hBK# z%a+W<$dJn(#Q_#!%HhoAisAyZ8FRREd7^k38B&;A7^3)6SXvmO_)}P07@`DH7=sx! z*<OMy@zZ3y#p_m@m+Y9AnVVRWnV*-;0uqN}4h9AWW(Ed^&w7j?hm<fv#TghFvf%6# z#uTO$=4H$b468wE!AfeFQ&_6F85pWq85n9=(iu`%7cthdmau{)!9)sM3i~o928Puz z&oa0$#3t9W)i9;7NrF{?h#JNkwiLEBCL|FSkeO8s3=FmGHB2cSNHQR`95~c+)G(#6 zps8iSp_a3TVFBDHAXmU}4SNko4QCB&4U!Aja4utEU|0<gHAaS>loW<w22C!%=P-Rx z#s~Z7vu1+8PWzW2Uu!boVobWln0||~s0hS=39|GhC|p-E-eOJ8&&e;+WVywZS8$6h zu_!SwJ@pnVgkmYk$uH65y2X@Oaf>^@C^I#$1RQs_m`mczZt*3SrN`%F=4Hp{q~?`m z++r;*Ni8V8#hjd<bBi(a7GuRN#>!iaRmGt6rvL}PlJqn3b5r%x^NS1eQxc27JR<}* zr!+4`zo00wBC}Y(v?#tfGq)foH9k2%ucRnHM?W_)FSDRDC$S{ID84MSO1~&SDZeDX zxTG{KO+Te5u{<*`U9X_>7C(sXk8p62JOcv*D3KK(VqjosVED?#z{}s`IU#w5@dD<H zJStasR95iq5WJ{ib&<!q!Sw?h1GiL%eusX8^Icw{35F8_JEMA{J}|Jbs)LCR#v45R z6Bv8kCPXg~Twu6B_=>3N3f7CFx)*tLDUjpf>Sym_pTIc5a6;mY;su6t$~xFDa;RP5 zP`dy|A2?wS_za41lpMqc%RzU*S(*48RLg>%Vo}mBA{VgMFr}~}r)(4+LLElVLdj<c zb?h|^3qYw6<P{VQO7}JFHOy<+(NjD)y>s{#fwFfI$j3$O3=9n5G+o3CVu7N#h>wAR zL6f;i03;v?B7{K#97Q4w3=FqeKnb=;48-IDr+jb<E|LJLlLiqoAVQXbfuR^=7%bTq z$$@o%l2ur0a!GzskqQF?14vhKIXFc<<>5n3PYtd&IJi3;d)Ow3Oh}v}KErgP{6!9h z21k-aS=id$o7_7bI|?T#&ai55zsRC?g+=WG3>7IbFfgDb15jQA<?hdU;ABw4umB}! zVVoMq6h=f;rZd(srXUr4U_LX7JW~x5sHjb0UBkAFgMnc+C~Uxn#V~>j-Wrx*22FN9 zw3tW-t6&gwNI!c91m4(dvfkp)O)SbzElNvFP6dZQOdvHS9V);EQE`h4LKkONrQTvq z%qhr7)MP9I$0aKyE*T4M@qo>U&&(^%Oi3-$07VjOL4IalNiismfMX95b)2B63joO! z=`k=cD1jo_4;*thc=`LoyTT_V&S0F9G9he6;sVAwDKo+rB(7jwlCmIdN6dxj^a}-L z9~c;1nB18@FfjNt1u=Dme`H|b3ugKPCL7%vTyJm-H28ocL{sn<YhGz?K_xf|-V)4B zECH3HIhjd%1(hJyEjB34T#!?8OFFk8C%z;<KPNl0q*yN(q`){uFR>!E*f`}DJCuKm z4JyD?V0=pi$;3oheOd&Hb`;CNK~sE-4N9{l=jRlG3qo+(1qZ`Q2C$34LG+8mCO1E& zG$+-r$cBM|0aQH{&tza=_`uA_$asT6=mHGgV35Cnif%AyT)=^DFz8)CMK>4}E})_h cY`l!>9~j_-0vm`6Cq$SS1wSxg5@4qT01#pxO8@`> literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/robot_stuff/__pycache__/drawing_for_anim.cpython-310.pyc b/python/ur_simple_control/visualize/robot_stuff/__pycache__/drawing_for_anim.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..edfb210d0a6e98b4d5e63a27394a09f5931708f4 GIT binary patch literal 1239 zcmd1j<>g{vU|=XnO-XfTWng#=;vi!t1_lNP1_p-WAO;486owSW9EK=HFwK<99L2&2 z5o66|V`5-r$Yqb>0E;l?aOQGFae>*4Io!ECQM`-{Da<J>Eeuh7DXb}MEeuioDU87k zn(Qw@w)tr?-r{vD%}aL7%gjwI$;{77W(A2uF*gGP1BmYoa>xt@28I%b1&k?-DNHHM z3z=(JQdp!JL>OvW(-~4&7cthdl`y5SrLZ?MxiG|<*0R?yr?5#f)G*etr?90lNix*1 ziZF;X)N<4?r*J^UIgrFTYnW45pyDh@;#@UMHSCfM3z%y-YB(1%F)~2;EHzvU85tQ0 zMN$}o88kWlo<rTt0A+lze?Ds_2<)_f2?|V2=39(Ow;0oJF%}guGB7Z_WCjr+mL}^h zro4h%Y>7pQdFiRQSRoWgacW6?N@7XkEkO{=I3>QID8C@Js3bGBSd;q}Q)0y}?);+6 z)VvaKxZM&&;>71<=A{<jVlIg<yTzASmJZ@)$LFNxm1NvvEiOqdD89v<oS$=xG4mE< z#Vy9lTZ~mJ8H!{W7#M!l>1X8Urs}8X7Z>EGBo={rMhI?BX<mwcK~Z8wX0d*0QG9V` zZb43Jd~$wXNl|`|enDkPMt&X^iL%V%(!`w1s#N`={G|Mn_~Me%v^4#cqQvsdy!80A z{G#~8yv$s^g34RMAVGhGzd#9t0~Gy23=9lntQ^dIj1pidz%0PX^0P>efq?-f&2S>8 znH01%Q_BjAWpG?WQUqHKa|$ac=D{RH44!(xX$T_5QNy%=sfMkF9g+$_DFT!N!12%S zR|JaBA{GV)h9Xds1;=p_C;=C-F)%P_vJ~-vq<BFDKZp<jiE<VRGB7aQV#&$O%f7`4 zB0*7=S|kDz<x0-a$u9y4WEY8nv`B&oDG(tIB0%v4j<h0Kka9jy$c3dQm*f|L0u7|D z7-T0OlL#XS@-Xo+7Ab*j6}ZKkSDIT;33k#g!Q8}>f}H%4oXjM>f=UqU78{giF32gl zC7oN46JL^_pOc+gQmmH?Qed2-mspWnY@Bk79m>DO1{Gi`Fuo;%WFjbD!3neolnGEQ z1ADLd78{giNzTtH0>>xVl@Ony`H{mWH$SB`C)Ey=Z;JUC7#KJhco>Bkc^G*ZIhc8b E0PCAMBLDyZ literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/robot_stuff/__pycache__/drawing_for_anim.cpython-311.pyc b/python/ur_simple_control/visualize/robot_stuff/__pycache__/drawing_for_anim.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b39611c8807a0497f50ad6faa2f76392b6793fb9 GIT binary patch literal 2032 zcmZ3^%ge>Uz`zhWS1r|<m4V?ghy%k+P{!u~1_p-d3@HpLj5!QZj9{86mpO`s5hBK# z%a+W<$dJn(#Q_#!%HhoAisAyZ8FRREd7^k38B&;A7^3)6SXvmO_){2z88lg6f{gdm zWW2@eR+^XWn3tKGSdy8am&^(hhhh+$nSp`fa~vbc2_=kBaRvs4EI2!bF@-6Gc^NYU z!)lOPu#y^<6qYJ(28JqD28LSJbcPhxMU1s<B_Q*_@(c_qY$@!^m>3vV!@SAh!Vp_s z%U;8r!X^n-!@y9(Si_#emd1o6!U{68ih+TlmZOF_g#$?jq?QARTFx5g6c#kKEI8D1 z)iBktBh)T{`wH$X1_p*2jv7u>_cJn}>0?1w4`bJGEn{S0SPc(7Muwi66oz01O-{e( zFf~xd2m9x<W`e*@`<I|l(qz8Hm~@LV{T5?U5s3d1<gk|@lQdaxG36E9VoNMa%u7$b z#R{P~ic?GCQxZ!OZwZ1}#wqayMfnA(MJ1W3#hTo=m=Y^)apxChrskD^WB!&P5+^<< zGcUFH7IR5_*)6`rvUCtXJ3c2huO#CZYjH_xLGdl-<oujljG4C>D{e7X-eRmO=4N1E zP(XlR+4>pzxvBc;`NakKDTzg3o)LnZQ<|5eUr>}-ky)%?S`=TLnOl&P8lRk>S5lOp zqo13YmswDnlUR~p6knEErC*evlwT5GTvD2rrk_%jSe}`e9-o$96rY%vnX6Y&c}o~1 z=#TIXC_xv=GcYi4FfcF_-(p~3Xkhrt#lXwo<2fODhVcUCi##e<cvM#K?GU`EVRezm zy214W8w0mghkl2CgY#Wpp$Uc)0z0F6qCPONvl@Yk4#pci{1X^^+$KaX5L{rmK=_KN z>I&A2qPiD(bjg?dz{bESHAC?Vhs<>j)k_?z3ldidUgXfe!l8YEL;D7YBveZ85{KT3 z!iyY+S2zqWfYAqjn8QAU5)Sr^%Rpkrtz|{eC@9$mk@?tam{V90nWTmhg@;gwk)ctt zH$ojp4buWpq6T>d1=q0EpcNFLjD{;yqGv8})?)W70+l>PYzzzxMeHB~WK$6@hy_X> znk+?pAT~dU5CjoI3=9mKoJGP63=Fqeax(L>Z*hW1P%cO<5(9~HCFkel7l8z_izGl= zq(OuXh>!)PI8NluP$UP^!UsyCVX4U_`9<K=smj2>AO%v}4o;m<dH7J%W`pYu4(<-e z9<~W06B4J0&oG@Rf009>!I2cv2Q0kTS;Q`}h)pP*p?Hx+<_e3<1s0hbAW`{CEb<E& zFS00IVNtpOLq!T8p9|b#%`43<s00VgEy3Kxl7gK4lAO#Wy@E;*>lPc7W-iDnxh0)j zkP}~$pP!SRSyHT*3sPX5qL)~aT5Ozhiyg|p#Re5%Dlon!f@C5n1B1(-B2aOLVi`Dy zi*K<(X_n;toFZ_ZE&>JJN(QiF!D0K0!zMRBr8FniuE>~y0hH(&in|yX7(OsFGBVy^ z5V`<EHyGqEprRWL8W(V&8w`3EP|*zrg$t<Y1DgP&(FX=Np~Ata{DA?J0J{|cA<^Kl literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/robot_stuff/__pycache__/follow_curve.cpython-310.pyc b/python/ur_simple_control/visualize/robot_stuff/__pycache__/follow_curve.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..82f14ea95893dce69a57d1c4cc66d153d88e93c2 GIT binary patch literal 2382 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 literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/robot_stuff/__pycache__/follow_curve.cpython-311.pyc b/python/ur_simple_control/visualize/robot_stuff/__pycache__/follow_curve.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8bf02d5aa7da680701e2164359fa19bae13fb63f GIT binary patch literal 4189 zcmZ3^%ge>Uz`zhWS1py1kAdMahy%k+P{!vp1_p-d3@Hpz3@MB$OgW4p5Sj^0Gv_cz zv7|7jFz2vFv4Ux~C^j(79>oEsIiomJSXvmOxKdbK7^1jS*jgB(cv9F~7@~MnI9eE@ z_)<7q7^3)7n1dNKxvC_R^YcoI@^j)#Qj1Fzpq!l4BE6R&=V&tCV$||WW(2XJn3aKn zfti7U;j<sunI%YaS#UOrD9mFFH4Mub85mZ>RiW~N88jLFG?{NP<rUmwODsw*&dj^T zT%4Jw$#{!3F)uwQwHTyP;a9qTMt*LpetLd!L4HbN5twI$;O3O(rRWzFC01k>>z5YA z7iZ=c<fO(!J*J<Vn3q{lnv+<PUld=KS*2f;pOjw`UtCg}mZqPUpOcec9-mxVRF<k& zP<czbq_il{5oB{nen3uXv2SKxX>mYiP-0$sY7r<Ng&7zailrDB7#bMva`X0BP7Rt8 zHp6vd?3CDx+{#zDl`pU;e+GqGGAO)23=n3Ah2I)H;a9_u!dS)1z<@m*OV~gLK`?3< zg7nm&>SBSYV_?9qD}^bYxdh}XsPZ+;%a|A#R>R{im_d`p@8$ph|Nj?(#8xuiVofQj zEJ$6+Rs>2Bw^$R4iV`b}K)$=h4-Ufkg2bZ4+|-iPqFc<#`Ng-`({l0?ON@<HGT&k= zN=(TtExyH;k(!yFQF2Q#xwNP#HLnDrr5F@s3JMBEoD2*Mx5U%)J@Xu$GmCOk!!i?H zQj0Rn5=%16Qi~)R7#I{mvET%c1&aw`Gm<7oFEv_XwpRb5vegx3tBaymJKT;29SJ+@ zf6+DOifhb8o0to6@fSoAu4d$4;4ZkxU2uiF-~vlQ5jO(^1NPY3fH$`27f&^e>C7e2 zI08{wpwI^~7}hYN$Jk0Hzal;c1_n(?92E&NFfbH>5^a$Xhy{wPA`uWv6cmIgAufXw z;sN=^nI)O|d5%T-rFkh)MKTNw3~C@fj>y42H)&4V()f#VrdQ-lFN&IOC_Bn@gzaek zMThV!4&fKg!!JZaf;{U=M$rZC;)~qHSGbEWuoOc=9iC1Z7#Kj24sy=tZTLc+2`#0A z(j_<~@Tc?|Ml^kJoAK+bVqjn(KJ1I6K>;fO3VoI$F%VlEl#-b%;th%<KtTtxRRNyD zmGFeOPi9(4YDz?rJOcxRHb`qIQm6;dP(w-TQdT?MZU{-tHJW3#RR5y1(G_W<i$X>p znHg=3KQJ(gn%uRv-DCBEnUTkx@goC>`~oIFFfg*ZGeTkl9*ztQ3?Ns6W1<TqCTbW< zkW^;D*;QN&Xytq@Qw>uJBPg$eqlY1dDI3fvlB!`!Va6iPSHqOTf<>Gug*BKVg)Nvt zlid$o45j~B`SFr(Qh@zSP~Gqn<c1J8P3Bw7iAlw`7>jN(7sRJ(GT&ka7xT9mOKvfy zqLwUNsYONkMc~S>NRxqq!2lG#(87d+yTfrR;}q6T&rZG`z6Qq|>>M3}EhQbPO*IWQ zGZ<$m&S9BRI*Vrp&kDg6hHFGNu&$C>A#;OYxTE9&zeq<ZBrrkl0(+K$fdQ1^z=8RW z2~@+Q6dG`K=?p21ix|@xQkcLbGniyq#F);4lI=lW1)IBu6*b$}GS)Dru%)oKGNrJm zF(Cr6mI*EnmPL_c#_pCB4is^a+rf6CSLC2-2Sp_)*<e+f!iJWGaQlO$hPj5NiiLrp zmKDWpcu1j$;<OP(6;2<da1!k<HWU-##cvH;3RfBvYIR)8R>N4s2KECx4qZ$sY#6$j zYS?R-K-s94BZa$`vxXBzFTC=v;Yi^^#BU8p3LAolnkGQ;Q_EGt4~`NrQNxAmQk1mC z0an7mzyK=pYPi5I;i}<E;Q^V0LnSK%Lk%ZL9;%+ZhP#Fp#INP4;R$BY<gJ>ekdmL7 zm#&bgkd|4Inxc@LS(KcUs!*O;lA!=<ttljeYZrwI2rnr$IkB`jl?zlxW-8?8<y0!< zrKYAR<maWrv?k`ID3oMm7As^HE2JhCSAx|h7NuJ$6f5W|lw_nTK-(1x@TQ1DW*%4q zDo~V}tdLr$r^)gh=H((#2@B<Y*bnAuvVf@~9Z)F_k_L0(dOp~L`3ORjCH-NQ$U66j zk0APCJo_SDkO`m~8C-b11f|9zUXU2rR8T`Eeb!76xM^Rc1gbhX!Hp|O#SN~hK}86- z3ctmcQCyOkoPCQuu_(DDG0zCxBE7{_kg3Ugiw6?6kmeMG7oU-z8=sq4a*HiFH4oe- z3o=N)B?eWPm6)8Ll$n?p58>Yufza`A@ubA!)LViD`I&ho@%ef2$uNcdNJ3yWoS>#% zR(x4%GPu^e#aMicDYdvr15~bpyb6j=IEJ+D#9(z_GPrr?1$9f2Edv9C1xVj4Mo<&` zs|bUL*c6KkvPKYeL&op|7~PODxBx~sWQ;F>(Fb;BS<|m<47T=rygslqi+C`8WB`$0 zz+{8Z2Mz{4sTqnh9A_xcD4d~og;%k`?FN_Fgu)qu6N+az&Pbf$JfY$WmqLT{S2hMY zg*g!miq<-<aob>XQODtmj>AQDhoeeI)K17;bPBuT6m}sZ@`75_)ufawNjVo}b1%x~ zUXjhcz#?@+TJD0P`9*2-3oH^}1sEiy=Wr}lTB5c>=Ax3>6(zI1OncaN)LgXixnkjS zBKd-1(AB7zD^V#IBvLO*q+XFoy}%-RS3+tI=LKbx4T%?&EG|k|UXif8z#@7>OzDEE z*+nt42LG>Y415ATku%s9Fixnx$g6yXSGmFMhJgG6wTl884W18p1sdEQaPm)8nxZu$ zazWvom=%^A0@v7F6tcJ?WO0$xqQUVghw=i)iyUfKIMmh(tr5M*p|?TsB8O3f;|)%( zj=)aQsX|jkJ7s5xUF4Kspm>o}xxwiH2iFB&#fuz@4URXsc^W*x^_-@_E%x~Ml>FrQ z_*<gj+7{BO(o4%PDv!_3%*(wcf-IbwR|XMf%`43<s01hTTg=6k#kaVMOOpzU@{?1G zi*NB2CubH^>g5-dWaeg8rQTu%^FiH*yp;T0aEb?a0l*DXaQg^ciGx$lN(QjQz)9>E zhfQvNN@-52U6BU^0|TfADL%o#!0>^Yk&*ERgWLrMxd#l&7hvcHgVF^UdcYub0fs(s zF)(sBaDm_r2Ehw3bb~?k0u0??;B5fI4_u6lDjyi&gbX8NAtQ(nC%G6IOTZ#<l8cGa co$&($63NBNDEff`P6%i-@_t~zB)~oe0EDEavj6}9 literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/robot_stuff/__pycache__/forw_kinm.cpython-310.pyc b/python/ur_simple_control/visualize/robot_stuff/__pycache__/forw_kinm.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d2d9b38d7d0ce710da387e407cfcc3cab8e0f3f GIT binary patch literal 11484 zcmd1j<>g{vU|=XnO-a>@U|@I*;vi#Y1_lNP1_p*=9|i`76owSW9EK>y6owS09HtNm zpE-&J%x4AjnR3{mG<ygGCxbgf3Udle3quM^GgA~t3Tp~m3qurV3S%&XCi_c}J${;u zw-~jOK}=)}x3-Ugfgu%aWfW72QVLTFa|>e>a|%leYYRgZOA1>GdkaGpYYImSXA46V zTMAbScMC%ldkRkqZwo^dM+#pGKiEQn6u}mTD6SNt6yX+zDDD)I6wwxjD4rCt6!8{@ zDBcu_6v-BbD83Y_6zLX*DE<_g6xkMrD1j8Y6!{j0D8Uqk6vY;XC?T-Jm2Ytd<tOEr z#1|!&`z13WIh&1vfdLep-k<<m#mK-=B~rtb!YIj5!w}C<!<51VW--<<r7(k8Oerii z4Drn240&=a3?(d;MvM#~Si)Mv)XbQ|lFd@2Rl=6S+RVtvP{N+V0;2QSQrJLxQrK%4 z;+Y~D@)*k*QaEZD;yJ+LoarFDxM~=(m{QoYnTothxKlV&xSE-2nM>GGxIrd><#@80 ziXuw5Q+VNWd~iAbY^I{L67CcMxSSwdPAHqHsHB8DMHnt8f+E+1LryfCsc1q8cZwLA zKIR2H3mF#hrid?OtYxWTS-`iDfsvtx#f2eOrIxjZbpdaR#6ree)*6N^t_A!h0wsbq ztSM}g49$#S77Lsu31>-xSTzh;oXxBY85tQ0=aztVH#62S#0%9hFAxUV01hjW8ioa; zU>-v-gQm3KEw<eJlKi6LTkOTDdBq?GR}na#^AdAYUotW<FjOs8$V^ix&dgQFELJGV zS4c`#$Vg1lQK-x>Rmd$ZE>S2*EG|~yQoscu=G#W+WEPji=cmO(__26&C?w{kC_tTq z&<7QWRmjX!068v2A+adESfRKwuOzXe2;|aRY<c<d#hJOcSW?RpbE^3C^b?D6i}g#3 zOjF}iGHx*zRas`L=PHyIXXd3_DU_rZmt^Lp$ERe(7bF%X=BAdU78NUKlxJinXDB2V zr7DC585*Q2faQucZ?S^8Rhm#E4GlnMBI%4zE6UG}FUd%aFD;5M&MZl-(#wR~Ra!(? z{VmR<#NyQWoXouJDjBdX*`?Wu@i|H5@tK+BiEwuoS1BR+0*9Jg99c$rX2r$^236u< zvtV2#V`Pzx!IZuwlx0+G4l)7kq<E0y;ATN3U_n=Wi;?RWqxDNrvG5X9ifM`!u`n<& z6tRK`Pyum^85A!Z-l>(YMMe2Vx0rKM^KP*g6lLa>++s<s$Sk?VlAE8BdW$tVCo#9+ z7F$++W?o70EtdR()Vy0PMX8A?w^)k{axzP9F&E?)++s^CC`ipqxy9-QQg@3rEhj&* z<Q7X_N`4y1i2UNzTO3)5$@xi{iFvnpk`r^1y`Vfzwp%R4sX1x4m=Y^)v1ONo($FpD zw9K4aY>=3~#aOVCp-6{;f#FxVenx(7s(yNYaY24cViA~Ugy80s=B4Nt6eU(<g3=f$ z;T7bh#wX|Jl@#UY=oeI$WaQ`RgA+q8vP4;CacN>sW>u;_B%>9Vl%}QWr{x!w$7g5e z<?0nw-r|Ul&&<m#iI2Aem8oG23=B++Tu{iv$iv9N$i>XY#Ky?N$n>3!n~jl;iHnJg zje`lKh6Ah)g$)s5<YVFh>14EF<YD4qVq@fCtP+Bk>3T3LlR@zZE8ck-7#KJh7#N&E z?kHhoV5k9=C=4}BH4O2L;8K?<g|U{ogt>+xi=~;-g&}rfElUkU7HbWQBtsTk3S$aW zD^m(%8j~agxI9A@X9kP2VW?*Ti?d^hGl9i9Y8bK@ve-*F7I3CBEM!EdLDg*vdoODY zV=X5El^md&6s!_dci=OV6RwgQznL`*S&VSCJT(j~3|UMyoHblE+%-JuEa{Bq3``91 zOf{hJf`kRgW(bR0oWY!-mbp*>EXyJds?&H<K&3R7UlFKyD-r_bCUy`Z2qHk0Zipu1 zEsn&z%-qD1)RbF{>9-h@G<l2E85kIDaV6*H<QIW6?=9Ax%)Hd%TdYNidFiP|N+8`* zAT?|r`MJKJoKujKUvi5nuizGIVo_0I<t@gPB9QU7_!7&~!A8XAq~?`mXz~`Rfi#0G zC^7=EKq>SVW7aL!iujDgl3T2m5UL796={R*0NEX1af=O1S3>D3C|v-~Y7jyfq?{X+ z@?DA&%QN%RD?rIkfq{X6jg5^Fl-&6kC7@7?O^Z>BQHN22Nsdu~sY(=)kdgE!gMtl~ zmO<$no|csu85l|!7BHqWq%bZ5kxXEc8BDS)VoYaQ$e7Mp%UHrx!;r;X!<fP<$pDJi zRwf7uN{J;53s_P(7BYg;X$q$ZLo*8_LkU|6DBW^2GlFwNY%Nm_;{r~I{u+iXjv6LO zhAb`t22d?s0!m9b%&uX`;sL2mVMt+Z1+}1%UBC{q9b{`WD<eY*FUW-{tTl`&TyQr+ zRAA_lU=U$|o6IW7fJF~O79YseLPK!M<Mvz0QUp$Mppb;5LJ(^u3xtg-1rr6SK<Gjd zg-gBs|NsAgh#{b4y^;}<x<Lv1<-h;`|7-FWnS;`o1t?AMrxYcY`xj-V=9MItWaj5V z5;$vBYEgc15hw)~nSj)af(TH$E|LeaOhH<O(X=||W#$$cgJk$XGGVF7CHX~QK~QpD z$qY{4x7bSZof3;vi>yKBfa(II1j-B27hIB9k_yr`1C&Jh7#J8-7!4SO7^N897$X>2 z{#J=25+sUxlq3nN89^9aydGcxB}q_`+00bSSi`u0aUsJ3rWEFdjNnAX3~IEMFr+Yo zdKI;dHB1XwKq4uuwag`~C2XL^GdRf^*Rs?wfr{S+Oet&&89{6tP^7bi#6SrUM5nOZ zFqE*SaDa74*0R<xmvDkqN;1^2f|`<`f~bZyg)^JE$iId;g$=|nw5nlFVTP~@6>At5 zaMdt_l5q{=0<hb_35Lt>mH?z$1J|bDHWj!eDdGo(Ib-=Ph9Ut_pn_7)OK=+j6#bgq zMbaQq84w{0B9uXd9Ed=T+FPt88L1_Sx0s7dif*x%7iE^DYO;YNtw<B3S_@<iUs7Un zc6>>Gd~r!pW?uR&#!Rd+$yc0Mmg=0Jmll%mmYI{f2o#l|vXP6C4_r}lFtRWTFbXk> zF!C^pfJL~Nn0|Ay@i10NAfgx55R~{8VPIeYc@!MKY|Nne%>orRjEfjcK%Et)UM6rM z!vGd#MiFHyfrx?%BZd@KaKXd|CfPy76HI?CGgKW1LN}<ZR06Js!SSn5%L1-a*g!F# z#V){5%Ur{_fTM;P)JbY(O5p|-C*YW5LK5YIi56PbFs1NFf+}z@4K^L(HeQ6C3phdD z&V@|1Z0QWO?CA`(97ry(sAVZ(Sin`o2y#IUD<q{!Ff3%M<*eaYz)`~qww-eUX9`ye zKUfs3vX-lceE|nV7gz-sTm@GN!vbzl#hS*nkg=A#gkb?s4O<O2n8#LFUBi~bnZnn~ zl)|6F&<bug)G)+@3QAt6pBa$E`O+C`dD0nbd7&YR%~yOWoVENZoV5Z-zM4|Yg2YRx z6|7;Y5zJ<sz*r;?_ANNa)v(tHN-`{Dg1ej_Nsa?eP5|sHff|0WKZL-(;;Rt?^Y{wi zLfwZH(`cr0fWsHvMl@4Xcxr@17-}(0ui--vvsyl|7*{J(3Ou}Og==^gaMbXD<AZkr zM~yIu1$7zLkSlyv!&Ac$&tJn^!w@e3E+qu~G?^h~KfFFz$#{!3rKGYT6-5Rt?E=cz zt{}n<M7T3BFlaJAhgIuEaUel30agtvPKrR)NRbCfAsdoCV0i=q%9$@g#V@$7(iB9h zaM-~e3S*;N%mwkOkje#AFcg6+7f`(du3>Jm7o`?wBo=_WEk*go#kbhXic1ocvv09w zfaxL?P+`QLm6#k~Qj~d%B{wTE`4$I=1o1URz{S=rma6#7;#({Q@tMU%0U*DCE1g7; zR%Va}maO>v;#(|P@dd@Vn6u&wKtw)>$Sg_+DbE0@WCNR;1+pVPA50b$MS&z(a<k$y zZ*f7SLDoWQGH{&+YHt;(GB7ag2bFvY3=9lnjAD!$jAD#@OahEzj1u57Pll0&nT3&$ zNrI6J-1XpN<YDAv1j{qC@USq-FtIR#<oTGm7-bk`z;ZIoAew_wfKh~5gqe$xi$#u+ zjggD7N(@nMBKOKrN=;7afD*XWWB_$K7_vY$7(*7L00XFI1l5tCZUv}w0V<_H&9nu~ zHH@H|vV>^?OAV+@V6NgQVaj4%zy=m)31`StWMKe_WU*JeFf!D#GBQ*NmvE#ogNAZS zK>3p~9n_TL5@A@#7|y`P5Xq3jP|I4!8Vse&8Os^c8EV<U%{DG@$*NNeYV7gUu!9D+ zvcT>Gbq~NzEOtpSU!0+qqlN>+#T+$k3wTplYM4bBN|+Wf*KpLZfEtA$yBQe@eL(K1 zVOz*p%T~h{%%I7N)B&hc2=e##3-wS4RS5R<a}V`#4Dt+d4N*jZA}>(F=Yym^#t?9_ zzQtNnl$e})i#aDh9o+Ix%t^n+oS9d4i`yqN&oL(*+$}E>10^U>lMB*E09U=>L<VY5 zX>vjmETiu&#_U_H+3_H=SbgI`rZA@7;x9@qF3kZCsU+v8rWHfdACGT*N@`JjQe}K$ zd?s4jyCs~Qn3L=qlJDwXl$Zi)-Nk473Ntb=6oWDjACm~E<I2p$4yuDW80DBa8kqjF zu=6phu!=CsF>*1oFjdK-rYAJBP?8m>0RrmNfRj}iiOH&l36!u>m=-dMFn}6dS!@f~ zLCv9sOrR0G8b$#IPz2Vpmat~AE#RnO1w|dGA}j&*K_Q7ny%yAS<*wntNGxD6P~sM6 zsO7BT#7HchHS7y`YFKJGA+~@D+(PpbrUkq;><bxdxzZVGxsmL#s^utQTEJJrw1B^c zwT261AE*KW@nL>|(Gm<btROiMuH}Kcoo69qEpH7k)D<kSin5lkhI;`|3M)9d^3||` z+zSc~uxqlxuHjw?8scU{O01x?!@$7sVE@a%pyocPSk@E(C(NQUP*MXo@ykKw6mx22 z>Mhpd<jjJ~qDqi}cv?|nGN`eim=m9ySW=W(5nqsBo?3K^B_}^U7pY7J*#Rz&i@*st z1!Qh2C<pL^lWS28NT3#EBx`YIdT!z^E-)2eTv$|ci!}t?n&iyMPmhN%1;G?J(UupL zfKx4~AT4SFnZW2;)Ci*aa}!JA)AEbr(~44)Gg3=#aYGp(eYbc)DIVNR1#1VFwO|5N z;9^Vko|d3Q4^je2^n8qbOgxM{OrT;_g;9i2gprGhg_Vy{gi(f(?LQ9_h~^@Y_&re) zKWHEhRIY*(Kd8?FEvLX`6!tPTouQVggb7wYmM~|rEC3BEGc05Tmzbbo8+LFhs8|ac z&futFmSjLyCBOjci7enuVJTr=zy+4!29?F2L{q{HO3)w{IQgiSFfZT*mD*@!-7S`a z%)By4MGh&?Ajt+?Oo7u5D5(@p2E{PAtO8fk@C0#-1suoBDXAqzphN~vAeyX5$pRFa zkaCLA5xsB{02fY)d6@;ii4~v%DjF0?pymZAB3KwX7=;)?;}IGxTufC`Xyp)+Hk5ck z8HD%-?(tD)5TcoxkpVpD&jyYYUvQt19c|!&6I91AGT<B3farh>a-@KV5x^#chN+mr zgAANR4KgsMpsQoZ;sy<n6l#N8fLwk>SekX<n1(diK;c~E35r;7ZN>wx#lR6*1e$09 zj{p>brst4K0#GG|XrkTX0(HD09el`Gl|RThP%eW+H8?kNf)s&!^Le1?h4kb#82K2b z7>yWxKs|Y3MAX97qQohv7Yk}eg7elaXq-aAxP&2#C55pU)E)t~Em%{SvYCqHpsi&{ ze-A7Vime(ZafVt(@Bk7cbofXKlCx4+vYCpkK+`**Jhp(Vgu8|j#A*ha$+3`$k)cop zAq$ZM4K~4KK+{c(4B&8Q^(%@2g#u_`SCcJWt0XRY`ij2&>C0bu=>1pxY5yF`*k1(l zNP1qI^oO5{oChF{Y*2$)lNS*axA;H<^WY(Pa0o(%`$6r<B2aihVg@`YBMGt-)Ofxn zRFn@II*HFK%}p)JOis+nsl3Gn9`py7yWr-iCJQ)=-eLonp~bhD3Jt-T6cTOV2n5HS zcxgdOVo5640mY7aDX=mAa!^cy5|9`p7c;2iF2KyiD90$p#KTynh=@%bx=|)KLHQNr z7jWEy2I5N?N*HSp@dFwjWJ+PoW-5{a4a0$k>=v+;u+}hwSm5|!0>=WVv}6W}EntDj zfhHs&GN5=_$>ay=gs@=?w4ynn;G4_9zyKYeDw+op09Q-k&;S$QAmJ=dErEnb2PhCg z?HvI|i04`UR3Rm0xH^>aeo#_|kC7Tc+el#VN!BoyFlI58FlI3$W6<1mI$H^27E20C zFC+nj2BTmBlmJe^>?y3-OhuufBnf8OArdRNG0g)?%vrpUge%U_%m}U&<iX8oW=Vzx ze3+_gn4}p%Dr*?B*ulXFQq2K2JsV*<%mjql=q5w#L{bMf7mKOgH4F>*!2@m#!Jy8F zKnhznQ_-{<h6RE!QBXu`vilW*@)kJ5q~Fg~2>q@2(;kxNK-s4VR0(SeLGlN<2D1RQ zCzuK{i@^ED1QfA|EF%o!f+FP>cX4V-Kz?y1s6|u+N(zvI4wNJz$zuUXsRW1s_2{5k z2%J!E@upUk6eY%|B<2=m=A~oLN}&0t#G(}M%)H#N%tV+?#S=lP2sGr+#UjMW!N~NV zg^}aG01For7o!@Z9upU{5~CIq7ZVR72($c0&R#f7#GSoBi5ew)xz!@Fmk}s?fm0$V zrdhyQ37pbEENF@WWioaUe*s4c8zh?{vS1Cv0!~m&6@pYTgH5ujVOYQg7lZ1A=!0cX zgf39*7A*v|E4k70C_I*mmV&|`5j)^~3yO{+aO{8v7}3ix8KhwHgOrKI{&_i-E~!bS z=^H?y4{GcPFoQx|ictpCmuBW-l3?OuWcg5~hA7PN8G}2>L2YBum@>FU2dYh&z^w-* zaKJNzYb&h54T=nKwgYu<ppgL&IA(}U2?Mn4h(^~iEMNx(Iw(LjS^TQhY>5Kk^06oc z6agHNekwSxfLna<>IIS&z!3lrc5ERoi!Z2*j)8(4RCjPN^09ymPG$~f(7Y)RW0g8_ z!EJ;R-k@9$uR$g-Fn|gVP|F%LwFE0B1!|dVm_UW#0w|pYswJ2u8N|U8MTMN8t{LvB zsuI>L_7v7$rdpN~kf}`2)&plPYYj^c6KF&~iv!deFEptEFCJ=Tg0$?QqYE6^+hX8$ z0(i6x6danIkOBuB=%51X78|4{F9H`q@S+S-jDd#8G}*zmIHU+-hfL@jK?)z#VJ`G6 zA)cBFD}Nl*N>Yp5@{7tr<<fmn)PN=r*cb&E<rp=X1egRE1sGXSDs>!sQEE9*bbz7- zT&Wj98*HHdZVK~4=33?&W*3H7<yw{!rV?iOw2BCL{+b08nBZ;@n3cr_3EmnO5m;0S zf|7R)V+so-6Vxy)-~c6ds3<E)6rAv3y=0I(UV`QcZm}h078jPL78!z)I48K3b&De> zGq1QHF&R1r2?}>eG^_#@G~ke*31Wc~J8T*RJU0s}OK-6jXXJwxQGh5&z=InKU;^A) z5KJm6g-m0?!uSm+j6scH&<uzGqZ)W7M1ZkM8j<ud^k_2rX$phZEPz(!#K+&_ijU9D zPbtkwjgP;@6CYn#nwSHTDbfSE6%^`4fgn~ihyXdgs2RkX1R|z`2(X{PMdo4<cR7ey z0U}m{h&3Q$6Nmu&1VMoOR0L|Vf$G#^P|3i-z{SL2$DzQ%#UaZP!lA~Y#39VV$sx$W z$sx}n#v#eW3s#>D>K?-eltHN-6e}Qi7CSL9fCl|oA@x!gENk)CvemGHGb@`ULkeRJ zXkL|BngKd|$63R;fCV%L1nQDyv86MlpwBKtCUjW98J7(_3B;blp3Vrv>CCkpFgA!z zXRhT0(J-9OT+0PxgXnZ-&=d%$VG4KQ_FA?Y_65u}>?xq}CdMqL8a7D=P%!{%0YOAl z*i$$`MFEHe52Nzb@W8}Cd=yojAQ5qfT3)ys@B|T7HGFV2;FTX()$pfq)Uc;;OEN%h z1e=ek5<Fv#p-!BkR-l9-g%ey^Io1l+fG1KGFxT*cCsY_28EQB|tOd+9d>|U^XD$$H z0doyMhz5&rgIEigYXm?vScEZ)6BJIM&g%lM8txR(z%DmvULBNgYmjBRYq&vc96`DY z&lg73Fr;v0Gl8alY8XL78!0>>9iW^J!kWB(kSPgleO^R43!1QiOoM~cA9xrSJa?+e zi`3!(^`gMTUAMTADl%|!0qGut+mWCGxClJW%~70M5}%S-l6XrH#4=8aFDS|{NG&SK zOf4<~Wi(BpTdZlI^>1LiAjLIz9%!W=ShN`2)xO0G>5Ih|=VT_Qra-1sLB;qjPVmAH zkPWvu!HYj2Oz;8_Fq5?aOmXKU&EMOAa<46jumcgGbs|^`C_aQ2L6hyjL8X%!0|NsK zqYyKwEu_T=ssTV4H1#gQr~-x#j694&P$<VP#mMx9i%)=&<$uvZP|?bDi#4w_x1bUn zWVe(cv(?~5SbF8DN%<wk@rebQ@foQ(1*t{xX{C9^w|KzAl6pCrd5JmcMQxx4y9}~6 zaNQQ4SR9{`p9>nRyd{h*3mP;4O+uq+feM1h5JAg#z{L>QeMq$-xMTwrYPUFSASJ3D ds6H+RwemR_I9NFtAx#q|CO$2OdJyDs0|3ugQNaKJ literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/robot_stuff/__pycache__/forw_kinm.cpython-311.pyc b/python/ur_simple_control/visualize/robot_stuff/__pycache__/forw_kinm.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ec97b7a1cca924c836514e165d58713cb9915c19 GIT binary patch literal 27512 zcmZ3^%ge>Uz`&5RR4p|&jDg`Xhy%kcP{!v71_p-d3@HpLj5!QZj42E$OgT&;5I%Dh z3z*Le<}>B6L233722KVh26u)O<`#w&mSs!~46B)-<}pNZq_DOyL~*7t1~X`~y#xvS zX)@ko)Jg_1VHl?Ta{<`;RIt@iOesPsOf8I2%pilKSW;M87@}Cg#<8WawJ=1nr?9tx zEZ_iJz}dnO#g)R<!Vtxs!rj6U#goF*!Vtxq!rQ_S#h1d@!Vtxu!r#IWC6FS}!Vo2x zBG|$ZB?NYs@GZ`u{G|Mn_@cyezhszOKyKq<U|`^8U|{$h#R>`*PKFw$6h=vq3>eoi z#Di%NQNxtNgd)QT5(VKJrW9rr8MqB8EHw=A%pf%&oEM<XP{ILXL9hx#kuxJh3CMnk z07DJaGDZf5)o_zjShB$pS0rD;3su6vz>va<s)~`Jgda(k1zk3eFNF=+9VzTJ4DoO` zMl$5FlrvN?M>3Q%rf}3S#KT<(*2M`kIh~<~As(E-Kq55^S#T3l*t5axBKZ<=kQ@Z3 zaHepfCY4%d6kl+oyA#Ds9;{~a5-^hwubKQ<%@iPDrXXH3g|M0_Ou$SLx|=DA)tzD_ zyOVhVD9=Em7>QiQz`(E?&R>9(TH%5WDdMQUu4SoVS-^*oN3c*$V`Qjdabbv^T+3R+ zio;|HG?Q6t7_#7LbpcZDK-f{Dh{RS#uxnUT*buoH)!Y(Pbu2`xlO$4|6fSi&3|a7e zxQvy7VKpnvy{PVEWaycKQl21%0;<XyhIk>E88yrcgkfxC8a)=k<$(xHMGeCOQ5YMU z#?ZwO%%CalcZ)4Iza+n?_!fI{YF;sj!Bqq<lk*aDQ(uBgysE_tnQ02enYjv?#R?_) z3Q4I78Hp)63YGb#3c01lB?<+J#l;F-3b+8oeB0=p%;J*x{IqxoKNgP;g~YrR1*mfn z`k(@_3YmEdAjhRBBo?I?D->7el_XXafl9htY<c<d#hJOcSW?RpbE^3C^b?D6i}g#3 zOjF}iGHx*zRas`L=PHyIXXd3_DU_rZmt^Lp$ERe(7bF%X=BAdU78NUKlxJinXDB2V zr7DC585*Q2faQucZ?S^8Rhm#E4GlnMBI%4zE6UG}FUd%aFD;5M&MZl-(#wR~Ra!(? z{VmR<#NyQWoXouJDjBdX*`?Wu@i|H5@tK+BiEwuoS1BR+0*9Jg99c$rX2r$^236u< zvtV2#V`Pzx!IZuwlx0+G4l)7kq<E0y;ATN3U_n>>i;?RWqxDNrB)kOGtD0g(><kPH zMW8yX2$V%`F@xfT!#lOowWuh+=oWKMYThl@f}+g4l3OgP6`3WsSaS1IQg5*)=OpG9 z++xeh&&(?+zQvMXkeYXkr6@Hq<rZskK~84LE#`v!f?I5f1qG>jDYsa?K<aL>rsd=( zmfT{=OUX|I8IfO{dW$0~F*!deGcoTLPjX^TvKN%6$##pSI5j8j7E@xyEw=1(P#U_$ zoR*n$iwzRfw-^hG*%%lYAj$bxihf3ZZmNEIesMv5N@5Y1XN2J9l;)-A7ZfE{WP;Ke zDDf5Kq{b)b=am%Y=ji7q=4BR?<|LNn7sZ!lR_Q}>R&hybTAF@Zeo=XRc4l6#UP0w8 zj`;Y@yv&mL_$ncIi%1V9X2rn3P&|u?fuVun0}~&o;0-B-1rk@Jv^rdRyl+S=F3`Lp zt<&Ms<8wn!WkJjpIin7j9{(HS5*;o*9vvP(?(&O&VBq8wL=boRg(jGFR`*oT2<)ow zsJ|;9`hkH-+?Vkq1CyX1;}<ab0Ze{nV-OUb5IB*23i|~13G5G~WhSsp;kcosHGyRc z{|z324wnzYoScF`e*E|##vm%)!P3KdLsYDTrHAW*m_!Fl5BCFZ{vP`o8JD=#E^w=T z<zWyOzaXx@LinPP?iC^34(F#lf)|8*FY@?Z;qkk`<97qBI{gy2>IH7qyFy|Y#GMW} zUlj7VBIMEGd>5qF=^~Hw6&~jcJkAic*_XIAFK}yu)tYW7z9?jUMaUXzuGvK%^D8{& z7kJDeYO^kJYh2*g0IN0FpnOrt?23>X)Lg@hJVsY|j4tpPk*xNDoY5t2qYK<d1Pp#4 zC^B7QlEg%*DN@%36)y=YE>O8BsD4FIy@Txr2VXx|7gr}w4^Icr4MFh_3{0G9j1aQJ z@rHolboNQ?GngmxOyTKpyul;T<9(e+?h=pO+|s3?D-2fzt}$5{y(D_8>JG*oihEc# zYH!irTY4byK;e;)oz;7)&$?a+3ce5$dL=0Ar1u%`3z4x`B9kx5r(BUwxyX}xg(vj_ z7(EpcpTRPR`=W@-6%iFsOg`Y??&s{{?Bwp@?%=*5A=$yw!+V2+=Q@YvB@W4p9MV@f zq%Xiwu>b=DLzA8%!$wCgM?2<&wv3K;%*mjV09FNoN&*o7^8^-9i@t^twGCgx5D%(r zz^cJ5T2N5}=A|&!GM9h~6EKf~p@ty~-1Y`>QR_|@h6zluoV6@93|a7cxCW^$kOj)t zAZ-jOj44d5Oeu_MNIi#IP}_t!eatBO*od}|1w|h_(fXKB^l{WMWPyDFA{HQp42TcG zC4vwdL?T)!=?tjhMNL&Htjky!7*@k$CWeuLp_X${x|9uzOSuN6OWCowlzT9`w1y!I zo_n#lnWu)Kf)UX#t>LWUs^PBTNoPrCtm0*0NM=IxdTW?*<_dVf0jCOhkG+b4fdOQ4 zE%OAX9&WIi@RV7_4Jt$8;k~64P&=5z58Qw(0=1lrKo%8&+S5g#?$RyB^jnNcn!H6? z3=9mnxRUd8@{8g@jkH^=IhlE>#kW|C67$kii&R1F9a)eXHjn&VUr=+TASb`%7E@lq zE!M=MqQuHuj42Qk`4Y?0!A8XAq~?`mXz~_mf;59HC^7-DY(T~^X5C_~h|fqYxy4!u zp{hVskse5r4P<wG#VrV338kx`bU`u5<&YK(lq}K*Y2ya9GF*xh%QN%Rt3(k^4J6?T zP;28jBe=D}!yqi$!P3KiLkd(Q@ZS&>?_lZSx*;jm!FN|sY(mMD%8P=ER|FM5Fz|3X zF@6M-9ga6dB&K*TV4UK2MMQIj;tIzV$}19As9q5<=x`-dN<d@^_YB7=d{+ci7bLD= zT#&p%aY5=80i6!V2VzoF(x>N4%2{f(#A1chLYpNvTbZ_S>=4?>vxR4G(4L3`Njqcq z#GEj`DCU1f%)isU!|leP(qeMP{-Rj)6|rcL(;n~$ba?f6&2U@bw!&?L+XZRs3sN>0 zd2B#}H+ZCGq|C`)p*Sb+ik#jCkuB0Y0=LLrF?75r=XjCFvBR&^?}41oijp;z7v(HE zSm3quXHX@O49}hn3=EtM3=H6^_#P9eDlS2iLuBK0h7`s{jOh$1Okk23OtLIuOh+5W zN@uKPEP+>>psbYzG8<$mLk(jJE28V1!j{6`$^;>iD>M}Q;n_5W1GTb3&h#mqRjdpQ zs09rp1M(mi^1xdSLl&s<0d^6pnW#J$hS+DdOf`%PK;Z*cjZC2W1=-9Rq(%f{aHEQa z0j;`aWI!H<t6?BDoN5@d;Dr{lohb|{tgT3HKy@i3yi?ebLmW9QQHL!U8A_xPzD9Oq z3Tq8x3KvFfqMC~>zEM<EaWOD};tr=@kkShg_Az9^hpEv0(eoT!uygyZWPudupavzR zkO#3=vOw72vK}M==E6k50&po5QMlC0|NsC02kQdY8Y>yW6u91a`S1V#|C;<omY}j6 z)O)+dpHh@q?q8Idnpcuol9`_esZ3a_Qj7A7i$JA)kr_y>1c(6DAw^0cmN}@%7Dm(R zn3tJbWD1hu1IdJ?CYR(FfdxTT$4X{!U2==9B;P5qIJL+YRGNb_q=JG1qzd2#=?X4M zEJ+1vsuD+30w@wwKpJO)tAV>hB2zSGc!J=9N)X(j34%K!LGXgKJEVtpLC~{<?JftX z6T!YfYz5O2sTGbZ5?45HVBDa%fq8}d6-AS+Nn0{@lx)n~l6N5Gid*D`nAnSMu@^bw zu5iR%;E1~`C^ofthRGc3i{k26#MLhfYFrW2_`tx->B$5kL46;ge%~(N8EOlJ=4dS_ zSyH{ha7o=2b(@QzQquMckL?8>+q*pS3s{!0uMpe7v_@)!<A%fy&Y*tL4(1K+S2S&Q zr0mH$A-E^^g!74@6V6xc0xkqaT!@Um5)^YGHts@v!iCs`3yDb=Vw0`}C0|I*ypmdR zk*D$sPvr%k%DX&LGfd`~FK}9+wn1o()`q|hg&Tr*2yO_yqGi1!?LZL7;1h-?gii#X z5WQmOe^JZ-qDtU};P5NKu@~avuLLJtNKC#GoN^&8=R$7&m9&Bjg+&*NOD+_aTqrHO zP*`>)t^7ho%@+oSN=6T+j|>dFo=jiB<OgJu9T6NwZVU_zC<QyHF93?`&p8+cJE+Hl zHtNq<!?*yc=tpFG)RDpkpcIdggtAhYQ43FS!3nY(DvzL0O=Dz0F78tp(Wk9y8Ecpp zz$*cSDGUtg=A^LJGMB(xd<-bXKl*$Ps#{$cVryzyYM7AwhB$o5hN=^!2b5cD7_vZN z4R#)~DTs;&q!OvHWC!U&b}f4K=E4wbSj$?&T!J*>i)uy<QqhR&Do9JWhBbvV8yo{g zY&Fa&Y!J~Nof_s8W+>aJr?!T10jOL9yBwLQL5ojDh8o5NVAGIQf!G-F0BS#T`P~wL z3~qr3(ZEBg;I?iNXf*p4WBDzHB9PC(Me$2e{DMnMP3|H&P(ddTA{0P`8i)YR0H76! zw^&OuQcDtVF&CE<-C`{-$}CCMWCIs<MLHlYy5IsaDKR-az9c`sxTGjEFZ~u{CZzlW z_4^^sU`P?jSDaXu>YSgK7LxClnUh*2fhYq}W#)kjK_y5b$R~6`Sam`1MLz8-eA*q% zH}DPTAPt<{Mc0Qi)*~t1!3Qb#%qCV(slF(vd__?C0|N)AHkjyeydfbsqi_!AeEwPd zE0`}z=v<M|>2RB1*y(vgT7Hi8g2Fj=SEThpB0ZiH5<7kFib~BeoER}B;sXOKuQr&N zz<5JIZmIHuv=xC%GB+?T$+@Uxd_~FlqJZ%Pz8k_KGZ-hCO)$G5qO>6SqKM`MzX^U1 zM3fe!Tolm)u|IHwjNxJ6<N*x@P0+i@p?HNu@dAh94dlV04(<+aJf+be4nob$5>Uc| zVnjc24HIgK1#W60l~^c^Rpv#EM4Q5dTn3?-0&3tgq_Bb;yKG>R9n|C{&g5EVoaS;6 z>pJB5;1Z;UC`ywJWiHx<AvUy@1>DC4rE_p7qLfMSk_yx$t!1uZTmWwlgB3%G8fMVU za4S;^H*!-EwWMKWs9_?Yn+u=rp0FCG6dpvmSj&paNAW$bnC2xmEU`D_Q}|N&QPWi| zTRKB6dpbic2hkzEt(FBPuHj{B4I^^c*RZ0N;h^R(n%$f=95_Oh6C><7arlS}!$+K` z?x^LeVaH($ihD6^<RaQeE|l<q*UO->o;0-86SdqZY7w0pwi<2>wQN1xYuHjaQ}|k$ zQutFCT2aPAYZ&4|10CQLT*HtBXG2pm0~Yo04nsOaEl)Z_EiZ~&;At+MA%*(sj4y?= zmOq8FR)E-aHi0qrVl4{^s%>foYglRov%&r-nvUW(L>gsBNu%sFf`}Z6nmUP&eSV^B z;-G>}0w^{i(wRUFKStUSLQ#uIXM8n67;5=?7NDpGhaf0nr8A@uU3ya-UK~`98Ir;) zg{MXc)IF~yDdZ+F_Sn_%;mV`6d|)}QR;Cn;{8%eo!-Jz-!dx-Li$kR_dZ~po9%+!H zCouM0uHmU+h=;Fbso||*hzCuFff5e`co0IsPm>ukQUK~pf!n|<8E>(slvEa^qR4=y zJwWYQPY~e+BD@(G7&MumgG~eRQ{V(xHF(4tJXq}mQpkp64_F>Sfb#!KP!j>{08K%p z;R$x|{JF8wE#`vwR7lGkKAc?y8ifFlN8DmBN-fSvEC9{Y7v&ch-(o8(E=f$zzQvXS zri;`;jd1p?#N_yrqRd+?xmk(Hw>Ur~h_5LEZm!>Asfy1mzQs}ypIKZK0`d!Jh@vPR zq?H+@fh8+GzxWnQR(wJ6E#|EF0uYf8A~K6|LCW($D%rrMW`XR8&j*tQMW7{tU}okm zE{HV9+G0@HKpOw3B)BV3qyd^K5JR;8k(UbW2DSc;SwJJ<cLju|aLf<~!L>|lxGo5Q z+Rs-6j4$w;bg<x;6cnB!F~brB7i5Cq21yXy;R%8lq}<LXoyoc&7<o}J@`_+&2isj? z(J2-)yg+bm(weLb!j>0>Ew2b$cChqtJVhOHXXXTtj%ai^-Vl{W3=JD!6gBQ}yTK#S z@7?7+gKb9P0>K%@3mj*Z&EdPqqke@){Q{3Vep!$LKA{OlJsBO$H-yD!Fio)lS$~&L zpeJKOEeNi4TH|$r&-@~v`4v9%>wNZ?`0Nib9#Fi<=W>P5rGxnc8-svQ2g?n9!44KQ zM{|H2&B3YB;RqR3GFiYl$9#eF3Y8UwYt$F|UKBFAB4pIz409m!MIM<eJTez}WNrwF zPS>8KJtJ`e<3%B*D?&;g&Nqa_CKOIlN2q3*5jcnaB9Foq9)$}$3RrZ2?9`kQyg+$| z*F_=qD?;iW&RB#xoNq`-&v2ZPIKz1X;{v7yOf%f(aD!&QKd>=K$V{nS5V)XlLGTJe z5L^&?MO?dsrHAK(1jNt6q8%Jh!EvrTLwAPm+M+d87X+;?3R+zew7M?nbV<<ZfZ>6_ zi-PV~1l>E>Ztx3$!bt!loTNB?7(X(A$PUM+qM(5%%Zs9_S434mF!1o2e*_T|7~$Qw zyVA0AEEc$|5M1KEB4mT&n(&L#rdOm*uS?rrlD6HEc!2Suw9^%7rwJ}3DYRUWvchFS z%tdLVE7C?2T<*%s&xu^Ywt;0q-9=fmE3#%2+$OkvWn++$n`5!S3k0_&ZOOVI?RZhz z@rtzLb!pE_(w-+2PdHwb_PZkON15d}q-7Vd%(TSniivI?*cilQP-ARLTn9*4N?`%x z0>uT)3mn0ab&hO@7c6nHUF4C!!XtlyNB#o`1FvYmf0zFZFL)lgD=I!EVn!|qZZ+Cs zbwSkbqNv>!QM>D+ZkI&e4iugcyeR5@Mbx{)t<&v>2xzqn=M4!3P%`P@?cl}i4ud*f zpaGT7+rWc!$kVvUY(#ey++Aa=VFV4`ffg%(mMkE5u2AP)kY^K6E5uqR<oOxI;tBLY zvRY<Nh7!=w47932ETUKdsx_dZ2&#q=%^sFKUu7I7Aco6S7>dklS&^3>pbsT7GN8B@ z-U&`&OlL%1J_>56pt>NLDUu<Dp_a9dH5f{lGs2cor!&;Dq0PU5Dnzh%P)%`Rh%K!J zEzUqR7}*gGWZZFuEQ2z?futKXc0tqBwH!4ZIAfTjh7HHUT9z7SkpEFa3Y5CQ{;A=p zVF9HcRDXfgqlz#x^pv5P01xXLw1LlBwi>oz22EC^Y4R$CAb)SaP!EMrg<wxV_fQ|l zAkPrj5Jdzi@&mO<_#n*!#t?9O;}&a4QDSoHE#{p3bns+#Vov%k=FGgZTiiaGd5$^h z;N^iupztmN&744{)xnb^;MyNFFQUl_X-P2p-eSzY#hM)tGK<wW9%Kq*>Mj1F)Z)?{ z@Y2rY{M58!NW*}~H$Ek`C_bq&zA!$s801lSa{$~DxFrm#`h7$4UEPZkQ$Q0n@!7sr zvZyTsG--ZD28QA|@bKUR9)TX;87^~t=lEab(e7Zp!Nc2;IDxSvd4l1DzzN1389m+| zjCXnXdORisc6x(2ptW_vQ`jaHPUM`z*}?jOkCjvRu87za&*{FCd>1G}*I~JKxc<1y zFW6Igg<l0U3!;l4zzgC`F7lXO;W53yWBL@_atNH^yg+4%=1l*K!a7%kbuRFrqaUEf zdhE=cx*(d5L0YzhA8jh1oil*(0|SWcaQwi<AfqtHazWr6n-wZ+bl2!#l(yXAvd8y? z%NfrLVc}<d&iGxliMl8q)!}nPT73oM3dI%7D*{&(t_WViaYfp&!{-K%^mQJkOFT*o zj8?ENv;xn~%bZ}nXz6>=!0(EI-$iA=i#+~Uc>FJb(OoH-IT{P>FG?9-kuvV!f50zy zfkUnsG)8=oN8V9~;h>I)lNs|N25l!p=0n<yAhsc^lNmG0Gzb@Lep?T+)C_646mlL# z&5N~EDQRnPl(i{nLm;5MSj$`jaw#~+GB6+(r!9aF*g>Vh6lfS9wQ~+yreDJd%IA>W zUdxKZJW$O8HUpWcLEEeX83aR_e!#g%-Gw1Gp%%0Z4blDP0OeSS`MARvS*C^|3#)F_ zSOJ%ioHd*{OGwTdcI+D;YFKJG5n+Q-?j@p>d+>py8g^8bwOr{8wcJF9%9&aY6m#K& zO(>#>t|Ds<R}E6gp$|%6*M%YqudmTl7;&mV1ClkY==P%XYk5dYSv+WA$Xmk;jXxIj z0l8Yf8gA_Im%@tT14P}%SHp&yMo?`7<t$VYMuwiVC}tsI8*Qr-Xr~-f4F~FzGB7Ya z*#Gh`XsRF7k<=6b*Jeev3=9n5`mCr9)E#9`txUbeTAZ9&P+8Of5)e--N=yb#^e5)T z=O&gEWmd!&<d>%w-D1hfPtQf_VZ$pmNNopN*jbbfsy6w-bzD(1$f6dIk*vj;>A8uw zxWH6=abZ!(E!Gh5oET?LetJBFDF~*(HC=g838<<A*M3EvAQKo}i#k9we{Nz)d|G}{ zd|FXzaz<*&Ep8|Sr0*6lsG0;%wt=)4gAyyeri3tX)|a06>q}2lP<{ED%=+?%fZ$Z8 zDQ+_iCwfltoXFe3`czbEO3Zap%S)n`J5&#dToiS?BI?$`bwfaEM&Lxg1yUCUv^!Xz zibziJxh|r5Nknx)>O~QqD<V1_oHu0TE^tVq^-P&LeHlTOt1n}RBWQAsqDoKpI*-aF z9+d@A8+0zJ+8vO(sOo)@$L9)<&jm1g3Tg;k5LJV8HaZwTaAB*6Svma}KQMsE4#&H4 z3Ue~o3aydc;JU+bN8lcl4Yn6G9j|CQf(9Qr=Nip1Tc9}8a*pNFq9tW35*Jo3sk|s{ zutD{Ll*L5>%PRtwAD9_6tv@m_%G-Pa5gq<drR3-6T$j?mB&EH=|Du%56)Bqz?;A3( zIy`7aFt`qvG3@X~)ZvCpi<VSu;61>3g!`g__eFJ|E9yQMm3=Pq_+H`hy#Pi}`2~9_ zX7FF+*SNy3ae+evvm*p*dx5g^XB(<@gwh#MJ308f6eURA8ANwz0n#EiNJR!Bk*h4! zVg;3ly8H}zeE_OD7lzmgwV*Yw@JhJ`X^$#(Tn4K4K<gWDj7GB{uck*VSi@mAypjfm z1`MNj{ZRb~DuPk$Ky;+g77v0{qUvR2=$VM37g1QFHKEYDBez%zGV{ucz=NQWE(fHL z2DM8dB{isEE}8=>Pr=;@@Bk;g_`SsfE>W3NQcJ)^I=DF2WJM~3K}9FHFuld-SPV)l z;KC4677Bp-4xr6GzKIo}{zH`%TE_uNE*w+}f;NFSFo5=Q5bQPFkdo=(zstefk=DuE z!`s1omxH%AsVB2PuPbjx$edWnf`p44I#)P!E^z3eHRf441HolTAgELm5ShX`Lvaev z+@d*^^Xq2Stw>pudr{uzioDH50h<m-&?txuthK(DX$|{UrY)S?c{lOyNW5s^aK*sk zqK4xY4abYhjz^u2c$^44=zYZdqO$)*o`5Sn0T;lCgxntm&iy#Y>Ic>0(`C#I468w9 z7B~Z-?F~n2x}&CL7lznf;B`^h8}ulPC2=k&1(lXyTS2)R)qK=}66D32SeJl;m){}} zAYoO7vRo6x-=birfC*&#z>7mUL0Jtf#ej9uDT;rwsQ@)bK_x;BV+tX&kU9^`Q1+cL zGW6^Q&$)2<6=9i30hbSuDH4!ZihM!E0eF~#2U19Y#~l1YqTto|0U#ElxB!hl6oJ=g zf+j_7aitU`mO~a`Le}jEgJujs=>)#ok`tr^yxOu#7*Qm^1wk#@VrOuR2s98)<yDp! zIb5%BxL)9JMJp7TIg7weogz@7z(dTEO6eIcbKDn%t#Dcr1zFDtSy;J&{fer^4z&Y9 zd$d6N%?<>g5Ihii#R}2Zxyu9EB~ZSAX+heGpe0!w1UDFN5Z(~DLG+56IW~Q2-WL^o zPn2B<i#SttAu9SpOzedy(CWzxQSn#&6D}mCUP&yt$WwTQr|<$8ePBoU1+%3A%6FjH z_$-E}tU>M?Ag5`RH9zof)*43CPB?0gtOX5Fg6alvHb`ND?(J8{S<0cWTEb}$s2r(b z0(XZQaV~U68<0hHn+rp1F>1>rg$3$9E98Cb$n6pAYXwn;Q5lidquPjErlE!!BSVin zX?E72*n`*Pk{Sl=T}rGg7g_y^l0capG-IpDmabJ2mppw%-~ROFFFf@AEB>^94rS~w zTFSt{ke=5j{o$t~=Yb;dG%RTRMw1s&a@^tr?NkQuNe1V6$o}Uvkoll;9MbOtFY=ZF ziGfOzTS7(opvBnnd8N6jMVZNoIXRWLxWK!h!F@UK45cOuxFK?j4csp)zQt5%2yTiL zgVGDA9Rg{Fh?f?mB$lLt?JsuBOM&fqu2Mu4JvfvUgBm688NhR$pxLXrdMiZM$m|W; z6Lmq#<D!(u6)BI4f}U4EYrH?OG4KfV*w29Nrv)j$Ams}|S3|?EgvMVGOt>hRa78eo z!wEDunKUuF12j`4BtErx2FgN57EVvb4-6o(!|?{ca8DIvOY#lahM(&KhL;2kw`%PO zJK(q{@}iOZ6(jc(B4?y73WQt{2)O`84^S0vP}?E6MRP~Uo~Vlg9#;fBE`SlPjjNDC z12jnv&aw;)4DdojjS+p>VhN})fvUsb1O{g|CX|Cy7?Cm?+#KY69N3pVBQF}RVMJDs zk=fwBz?#`euoKlDWM5#ITmq_WS2FoQ))cT|OW{S!L1}#j0|Nteb3)NdkN{}9z8K^v zL|s{&S^`PC$aN)LumzMvyP!!Fv{hhWQql)bP;!MNT1e^vc><a$5PPfMFoRMLa`!O> zmI4B67?D#FV!>+(JO&w%b_Y=|n!=RAoX&<~4yfP&yLk=Txlzz62C3VJT-^{#^%EFl z&B2unC>MdvPho|o`$fowHe8g%Y5=9`LEaCA)QLtc14OMfKt4mwGpIa9hMokJc{?OC zu^)#+iEC<@KpRmp-Gnl9fO4W2BSX&ytYJuE^^MDq#D^OrL(f-SreF^#)R@4f7s&<i z(7_iT6Bv6ev4)6P4FmSWg|IaNY8bLWl|Ll4vO!ZT$ONQK^k7Lafz1R^8K}wbR|GN! z+%QPLpQ{l1Tk)qoq-_CeCKQ1NK{SQHhoK-f7?=t&i@@yzGf<g`Xc~xtxS&jQi@P|r zBp|;y6EvD!1S&-#9gt{{0&vL+I^L-Wv}+H!sQ@lxZ}Fy9loTb#rzGYUWagz~Z8Ct4 zAxSJs@y^W44a-b~SyYVNWWb@M7gQdf2lrh*aG*2^Ziq_HO`4OjmT3*^R;DeidztpI zf@U^dXE-m=`oPSnp?BBRd`r{^W=45OM$nj~BO_=~63iBJWbAPJ%E5ryRN&z(0q-y_ zVeD{xfYekF7N24<qi~Y#1Y7v#;_D)Mmqhf|R_##PqkSN8kKPq4zYBrk7a}6B1V&x7 zin^#DeMLX|qDagYk(di$^Z>SF`MQYdB@xrDSqDUpNS_EiB6r0x^g;w^U)Yt1#0x2f z7b6NUIu>3uE4pG<bfKi;N=e;Ck@_nl^%ubC0~>>g1P;fnC|Ogz!*EUA6@BLeX(s}Y zWM0(wzbF!LMI_(?7@@gfL)wnOEtv;Yj%c4qJfe5SDeOXI+C`_di)LvTGIFkD6kQZ4 zz9Le5L8SPiNHOS)6^jXlUA7&zH+TfD^JrY+(YVN?b%jUk0t|t|t8#|hC4Qv~{7N?j zM6L@cUJ_8eD4={rK=}d;ePCt~P`<#g1gX@Jsv<;_$ONMwg<O#lsvKf})nacF^&<DX z;MJrF@<|`a4Ik_ywW!q;vU=2F9+k()(Bp$##31X#zTT8rd+;@zY8bGO&7v1BJ+rWe zjA#u5_MRDr{#~T`jrfKi(LVSMDjbW}f~K^%(OZY`Lat~Ns5C<qZs2kav^28_T)3^n z7*>%%DwF&m9j9Xdyqrpx)TGk%Dm6rR2cNn%paP5qT!4LL!&2f1fYvGsfYvI4@(-+Q zg0H+$J0Q46^P;}X6@8bBBCc0NTrYsp2Q~&#Nyw21@B&8dgy0d)i)KDo%zRFyoXNT< z5_v@=@`6a@4G{^*bk#)>^(!Lk7r+RmWO)EmVQ@*r;G&4p6%nHgV1%pu(7Gt%dqu?e zf`~76gB4+(#FOi{VRVF2&_)^<8Bodrq%$Z{a~r%{1aC6K$4Eep7<i);B^ScSQ^55v zlprA&!tKK7ynq^?B-n|OJ8+v^f;{4bWi|@FyMTd#RM{E^><dKD^FAowX|niLso4?* zz#X`vNKiiIfGocPw@ARVB=ErsL{|>n)+oZvn6mgXoKclJaXHiI5GZS&fb{h^crWmo zUF0yo!eM@a!yMc;Kt6ed6+DChBFP^@_y9WRYzp{@5y*%G_CbWNYz&-y9Z8*>J)9k! zpcxrVCK=-lb>QrU+*7P&Le5So1C(eP4<maS)-u;HArHx6-^7cm1~CbW+D^ypoiH-= z7$J{HAXY8mJ}(4i?Fgtz3(mr8SW&ZMEelRRqfPu__*tlywFb1E8)<_yVk0(a+N74H zN4SOs+mr)zT_Xqf$pY}eAgCc!1ez?+<b<@m!TAEzQoqFp8M-S1x5eRyJwV#?pa}v^ zcJQzrq%F=4InvMw+zLnA`A`fB7SK2zd@MvfH5JwtcT6iuEpp2*DhIXEk=x}sl-vg8 zonzp<b3;gMigpM4UGT{YD8nU;oHidA7&&b~XDSF!aP0BA&Lew?M|MW~g2Xvlpz~$K zb}(*{yeMaNk;nQ9kM#vGf~;#NdN0FMu(jqBYo^p(6jZq)r~<Ol08BvkEI=*OzbLBT z;nv}HgHL2a;Do{n!7~IWg!bfiFyG)8>fnM@Oz@%>d4#SAoO6(iTF`t7IC+7OcuQeM zoxrMPu3-jOK`h953VFI2eN8k*TC)Qm6NjiYkkTCXW+_S|7D+WC=hQGEZ|$#P0ZoBn z&3X2?ivAjmV-y$}dO*YA@I?lYFoBTh6$Kts(efxGWIGcZbd40KTl*5UD(V(nQf6^s zX=;%%sQTapcW-ZT<YeX*7bGS__Y;9K3?%>T0=4zPS!OYa1#0HOw(^7L+(4s)w^)la z@<Hc+f+%o~K{}HYGT1GcR8$H%!vvl;q!G0XhU5!St^irxzyMk@!#%@sM&be{NEfJs z^@fNfXzh<CnCNi1D=IbDct+Vw>p9jJMb)o}s()Z$<n?ESkf06q(DSIS3#eWaP+gF+ zBzuG6lDsQwR*-S(6CxLFeJ`r{UKH@VBH(ucj6j<X{JQ*R7|p1-$fI$EN8<t*Vb&Kn zIC%Q`y7(p(%`m>mA%BHK{sIhvbAl$LpQbSAyim{)xbg9~xZ>k;^HWN5Qsd)q@x;d$ zmL}#vWQq(xq3a4FLP10VhyVq45oipxXcmaO07QUe0o=}B58`eC5!*n-4iK>iL>vYU z-ZFsg2M2x;s7(eYEI>_jNpKswfdK+PuvoFmd|*H&Y8WM0Ek7{82@5t>(6KH^q=+D^ z$Oi^EVdcxns`!BciOgV9VwL~E04IDH<ykd9Fu(~HMqUsfPN=c5f~Gl<NFhFu!{CGx zD=TRB0f`inW>xvX04MB(Sv5W|zzH1zkajp>#KkK7fdNiPv9l_CV8A3Gp#}=DWDo(# zgGkd1E#MRG(-~@6kqa{9%CLqJF+_}7AffVH7-H3G*=ksER{2QhXQwdMKn`MJ1`k7m zj?+RpRe+J9N4<s-`;-Lw(qYu~5Qu^e%i&>=vlUoyGzr*Hn*`~Mwd^VE>5MR(&Roj@ zV}s~)=2}h=4a4cowOlYZh)!ol-Dyr<m`-4fy;RFq!;T|V*;9}f@FK@l4N_>MHb+on zjgg@S#f%j86wX#A2#LH}qn4+J2O^4gm=`wPoX9#sl}RlxTsO)I4Os2Q)XfLijdErx zk-GU)IBM8axRKU}K>Ube4-sa;4i6(}7TCuqJD*d~PE&AUh<#csSi^z5BOON!^CGLk zh&xVXSsW(tA*;bKfeTp{hY9@1YA{UTMwZ24f&j7_3=<d;r^F(sTjX^(*hiddxKog} zH`Q<>9}$O|n?WZE5n&6`#1pKjWMHV_M!x+6-Ss`sCNTC~sbNUrf)3tG)G%US0Z_w| z!h`NAP(K-jHF^EOhqqx}HiYO2fEE~ny91C8GidD)_@E<AUZl}2(8NAu%QQDqe;C{@ z0xv8?bP*sYxp5Symc*wdmL%R11hI@$;tPuM3sQ?pGE<9-K=rex&@I+9&>8h$yC5A3 z?mW<G`e4yw$a!+SkmUjK#W|VDsVU&IkH8&`Tb$r?L_s#(;sl>13SolJ69qF_3&0e2 zKGG3&cA%Qy9z-~R2+*;kkd_$eNGE7#1&IPT3~uou{0ll>uILH_1H*Sv)9VlT{PG(j zVhz4u*%<hQry5TvoKi8-c8cvqUd1cCiXRx5SzSP@l2~0BI~YH(F>s1q=a9L?Au}Uv zf#aOW6^biTS88s^zNqDRQP$}qhw~K<=L=wTgN3!-x5;;c?nM@bD=Z2ZSQNk;m1l5* z-~vk!T#*TaJ0wByg1GZpqcc_)_`)vog<atbyTBXX;C4e$Xae(uzzNPX1ZOaJ)HZk$ zA;8Dq5j;V0g7AdI3Bet{4X$^2g)sdj%xd<LL73I7gYgCjUq5db?}W4&K~u687%s6` z;kd;5in8fNG1H42W>+}OE^wIL5R(DR3NA2QAPm`Myg>Ylm}Up}4FULu<QcA41eCBW zr@AO$-oc8Y4t2>E>lIbAivngOsN?0IU^YW(iWO+z^^D{NiZfEL2&*h8TamP+YJ=hi z#|_FG5;v$`QMb4#Y;lnn)ckeY6Lo^|gyIS26OJcXuh{xs<n`-d1g$?SoG39x0(4NU z8JGZdJVhrY_V`@qk-x+vzkqc?;u4OFJepT{G%xUI-ryluPH=)^j|X@wuj7owIW8A@ z6tD0oUf@yuz{kML*W(WA>P{%^@r5pPHC-XN#9~G02E#QG7kSLC@R(iTF}uOTi>yxj z5|8!@*A0qmJm6dHF7TLsl;q(w`yj)>!8?Jmsj8vs1_x(9cNg~rry0x_Ib^PI$XtM- z56lc4LJd{mW}qh5E!Mo!+=5DQQE*EMa%39#J^;P))TI29;`qdZ%=nDdoPyM%__Wfz z;#)l6T_t)snR$sh=|z2@{TnjK+Q6d<@rlLp8Tq-OttPjGk!3*(4M8WNp=f~$g13Mq zf=-hJwM)S{wdgn~r@Mg&&~d@PIBXz&3%jD*3=9mQLCfNW3=9k(m>C%vZ!id5fT0gu z42*^s7z`lj27~Yg82Z4*z^HwJK?{OzFeqMtp&Ky43k+HxSY#PlK7=rqvoR`sV8Bj( X1dD$GlTbBvyef>c9~dwRa9{%fjO3uy literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/robot_stuff/__pycache__/inv_kinm.cpython-310.pyc b/python/ur_simple_control/visualize/robot_stuff/__pycache__/inv_kinm.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..019ec0fc3322ff2667d95fd3933437d829cd2882 GIT binary patch literal 6584 zcmd1j<>g{vU|=XnO-c2!WMFs<;vi!t1_lNP1_p-WXABGsDGVu$ISe5nnkk1diYbL5 zg(-(QmnDjo5hBKx%g)5W$dJns#R(Q+&f&`Cj^YNhnR0k?d87ECZ2l;Iux^1U!4!rR zmK>p6;V5CS9BYnHu1J&!n9Y_WnkyD1#>kMup2E?>5G9_%nZnh=5G9eqox;<?5G9$y zo5I(^5G9qupCZu05G9==m?G4|5G9i$oFdY~5G9);nj+T15G9u)o+8o05G9`?nIhG~ z5T%eJog&l15T%$Rn<Cf35T%qNpQ6yh5T%^L6wIKh_!1NXnvAy?wfr<0Z}Ga7<|RAk zW#%T9Waj5-GT!1S&d(`JjV~+!Nv5Rc6{p67MT&|w8E>%`7Z#P|CNqMJfMQS>vM?|( zID^8`j)8%pgrS77hM}3UnbCzIR;-q>hM|U0k|BjLo25u71<bQyfQm6?GZe|BFoAeB z425hp48aVV%zn=yCUZEz7zZ?&Zm||6=B20JV$Mm;(`35Ek&>DdUy_kpa*Hi9FC{g* zcqKy-F9QR^uQL6N{M=Oi^!(z2{FKBZFwY3V%_+@G(Jv@UtjH|ZFD;5M&de>yNsUj= z&nqd)&(SZaEXl~v!y-|ZSzMZ!lUbFjUzDGeUlLzjQks^gpP5${pPiYPt5;BYi#ItZ zF}ENrH77qgv!qf86c&683=9H{Jd7NSJWL#nObslIMf?m549TDXgZPkvft`VYffW{b z1q=)fHH<Y3Squ^kwM-?93z%w{Y8bMZK{$&=fT4y-fMFq1Ei+gKO&p{Kgpt&-)G*hu zNHf&3*04fkM8GE0vX!vbur)K*vemE!GpuCv(`38FTo9jniz%<*7IR8|$u0J*#N_yr zqRd;2Az&A4vfpAXy2V&>i!t>Ub6#ogE#{Qeyj!e^IRzPsMcfPwke~;Le-X&jw|F!2 z%Dgl4a^t-cljB3=VS&NLD8;D3$iXPU$id9QS|o!lFhBtUPDxYH0warQ0Vp6?7Bbc{ z*D!;EqLw9{p_a7-<T*BQS`)2ht6^Qh4oYKLY|;!h3|SmCY?2IFoF$A|Tq#T<4AKlD z3=0_<84ATq7_+!*SW6hQcv6^qnQB>USV|Zd@YXP7u`Oh*0ox<MAi+?>D!^dQP|H%n zSHsfGSj$qw63n2<;`j3Z|NsBfAyN3lekJ2A)|8UUf>cfZB0*4KfFl1ETTW(PVov%k zmV(T@vLX?XI41)G!!4G)l>D??Y>5R0sd*{4*s}67^Gb?uv4WCv;w`3v%v-F9MMa5~ zx7gEi@)JvpjczfgR;EH@p-2p5j5vsp0GY*EP@I~Q9}lts6brWmz^25f<rl^0Waeg; z6yIXZhQtaeYe1q#5EeB7#i^w!`JQ=YhM?#HIgpEykBNhkg_Dm_gHeD{j){Ybg{epv zJ(57Fl?|3+L1iZ*#nv!nF=jE9FlVtWU@c(-@jy6>Nr0i5aUr7s10v!|*bxC*B*4JH zpveIVJCHkyL_t0Tg<_Ec$eYX{4}z?O1OqEtFcfKll!F2Q-LGO8evQva%t`YCc^FhL za4{;ea4>OjurL+rWAi4wRPz9rYFVHR$&kgokg*n&F(DpeSirK7p_ZkFxrAW>I3+G* zgk)N9`e6a5ALbJF8s=tjx(#N4c^B@#B1Mq<K_0&)oSc}G>>HBr>RyzXl9`%U5})l` zqzRH%0ui9nLlf$KPy#^s{1!)gQDTa3Nxmz37|21xAUHEG-LWh`GbJ%EIW-<0`H7&g z;9+23;A7@s<lvNJ21mLPdT4+`AB4d<GYchWrZ9pEq%5X%h7_hnjOh$1%wUpb5o0=Y z31b#>3TrP@ElUkU7E2Z=zp*V~2eCmoi&cQ3hB<{zn4yM6gh8AEltjS!nze+ZhP9ay zRB#0|XtMjI8-u}Rdrf9&jqt%9%3xq9G65wYW-wJ`3<@S>0T3G^D#yUU@Dfz?YH~u7 z8aM*fK^inb1SkR^DG!wBiu6EY-~eYULd$lnIf=O`iMLoXQ&RJ=7Js79Fpp13%q>Vw z2`(&6EJ`h|f(11fBMXZXqaG-K2^U$Q2Q?@%K#3U~)HTqASHqCSl){+8)XP-MRKk$O zoWh(AswbF47@(reDWF6M5@V476^fwRffXvp2GUW(kOkwjr!drVq%hQSrZCiUm9W(? zf$E7Gt{V0lwi=Ea&J>mu)?Ut9?i%i322D1<TWl%$rAaxdMW6sI0#*FCSW=Voi&rw< zVuQ2`G}(*5VTnkn;7|pZh2T&H<)f86h+w?M7;uZR@D`(c5hxYkVobWl=y;1MC+QYb zY0@pm46I?w2@TW0fLWk$1VxDeqZlI#6R6Y@U}RweK`CYy<{~%r5Cu5|UacrFGJwOZ zg`t)yg*k<#1ym}5!VOgCf-97ChFZ21))clDhFbO##u9K13~kKt)N<6YE?`Mv2UQ|9 zYztUYI6$mInG)74P{L-dVOz*l%UQy)0ED?}SZY`nGA(4R<w|F$<pwp1vbez&6?ZL9 z4fg_2reLn&kz~l?0h{7b!jZ*W!(GCW#h1d_%T&u<!(PKs!_v%D3#yp-Q@FC3in2g9 zWeL<U*KntB*YMVGrttK#*7D_Xp^B&Q_OjISr8Cs>gMGl4B?$L{Kn?!_mJ*>FzAV8S z0ZDMl%*aq!TOwS;-^^IcU&9~FpvmW#%nYgCz$7T=gK#hd1H)%fW~pIZz)-^gu8Emy zm?c0pB;x{5gM}IHW!4&2Xj!+C<(7DAYCwK*Ca8hwm{yWn<d$Dl4r(8O)6FdwP@A$y zn1O*|CEG1Fa2-~RNGy=d3Qj1jsYONkMd&3QM|x_Bt7|}h@ph2&I2afhxEMJYg&0{F zs}!M$22`B7q!uUZ+2rIWC*~B}>B01%v}8ev8<bW-{wxON!xDxCj5Q#iGC}>K$pm&X z*s+?7MLwWH0$jI(32@toy(Hhipd>SQFDRLU${QZ0Dg~VGfGS356M{5>YFUsoK7(>r z2?MO^sbQ>PT*wS{%u1G9ti{Qh1(mlr@<BFcR;At&@X5?eO)PTG&nqq|O3chF0XIEX zG8KU`3^==?2On25T<sB10D^26Vy;rg>12fZB2Zu#fwC63aCiv{zgw)qKEZ(jn#{M@ za#Kq(@>6bcBQzA>VoS;|%}XiPWGadVg<TA&>=uDUF{pV|1ge&cR6tV7AOaKwMIoR@ z0Gkt7BP94iH8?m9ioo@vDo81~%mF3NTdd%w_$`jy%)HE8P@phY++qbcr@#$}m4cAK z0Jo$3Kw1Mp1So|SfePIs7Z5ibWGPcpeo-ul=L_QTK$2#BaY<rP$t|Xmj9Yv;nR%ej z12n8}F&Cv4gG;Yl%t<+k=poII7NlE2Da3$*fq{dGgN=)sg^`7kgGq>ui;;(sk5Pz; zg^`a@gi(Nzi;0U-f>DHtg;j`AjFE$jg|P@!1EVAvP!R`;43PUjlUl@qDi?6E1S#H_ z;l&#>w0M&$VaZ}$z*Yk*-q;s#l&~-0gy(fc@x}x;2U5I&TG3qK;*A?zyxEnoXYtf< zm#}B?VkzGEz{Oi8$fhiQQ1Mp7059O!YWecG(PdJ2K?NVQh!X%6XDCG+Gx<eakvk~L zL9NmvP{Xgt8^i@?a4-SN<wc-$^9>~K&%nS?1?mZc$}^NkR(NJfMqogEwl9`a2b9Uc zMJodX1E{2hXLnHB15~;$gmnPHr4P8n$5g|xkV%{Yn)x-Ei$F;Sk{`fjCo`lt0%i1D zoS^1ld`W&hdV8=4l-B=&@;RuW<6xFxs?vidOG1t(R8$oOg2DqFY(XGa7>EFe7?=PD z8z@(?Gcqs~MSz0MkVu;m1qfP+QxplZBMMY=u-sxvEJ`m%6f5A2yOO!65TpcN*sMe@ zY`}#FYB^IB2T~3ybc+y$47fZ(Eo6$~LFy7f1h~iowWEuYKwMCpu_zhD0(%=wfc$<- z7O!u=fZ_xcqo5867aO=RVC2JE7)UXSF|n|L3j=Nz#-b8z?HyQQP?QZSVe!=G#3pt# zP^=M7?4Bef_M#k6D1j1;CNs9Kk0&B=7v+MKg2NI_fC3bpl<h#y$_FJTyvZ2JS=36# z?jV~9Cu1a=iAcsppdp~5B9Lc`K?KR^$kPPmtWuEPa`bcoYSF-Y7N09{^ek$?9a?D5 z0nw{qmB7`jV1ue<N9k3tWArMRQ&?cVij}N>MV25Zg1Zz}AeJ>KnQ}lPToWV!&YR#m zy$r+!RdC>@OcAKuDzXKMfl~sQ040Z8u-<-P0K6n~jnDQhVPs$^1{I0|pgNVAi;<5J zsk^{b1nQQd)TPM5R0Im9qDtJ31Lasq^9A11A;EFJJt&TY^<_X!0VXMC7N(+Vd~sMj zhw5<%>M3IMEjhrVENDGWNbeCdo>^1aVDYTUjug)?|Nj5~|0SrhD<Tl%w*<h$Ac=XI z1-^+Dptd_PAuI(gUXh~T7iz>3q)=vLVT2?BF;MT3Q3^E1RS$|^m0O}nW4n53`9<a6 z!QES|d8N4pmEfd#OE5RFq#!51BquXTub>jdy2S>inG14CZb|1B<iwZc=jUW+mK5vd zf)p61=p|O978|GBVu$i?u|Wlx3XE@wAeop58-*_d1rv&8$@w|?Ma8$+pfpQzeooOX z=HklYTYTUyfF7&|Py}i+-{LGRfb`9aA^8Q=zPQB$Q3)BCECO}t!0lo1I4pRa4?GH1 z1RDGS4@-c1(%`lxxQ;0T6)52B0#24ipio0<;vzC4hfQvNN@-3isBC5^2IUYA1`Z}3 qMj=KX@Mt&(nC4;RVB_HB5aN*J5a*EOP~ec}u;*~#aNv;OkpKY1{d=?k literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/robot_stuff/__pycache__/inv_kinm.cpython-311.pyc b/python/ur_simple_control/visualize/robot_stuff/__pycache__/inv_kinm.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..523e8c16464f8c63490ac3fca3f2ef086e483ae1 GIT binary patch literal 14346 zcmZ3^%ge>Uz`zhWS1r}Yl7Zndhy%k+P{!wT3=9m@8B!Qh7;_jxKr~YhV-!;gLkd$4 zb1q91D<edVEtfr+iIE|fBZ?C&!kojE%N@lHW;5mR<nl)GLD~FK{9xSzQGzK9DJ(fc zxx!JxU^&(tp<IzD5ipxAM>JO~N{o>qg}sF#N<4+5g&|5Jg|merN-~A3g&|5Rg}a3z zN;-w7g&|5Ng|~$vN;ZYBg&|5Vg};R%N<KxPg&|5IMX-e-N-;&Kg&|5QMYx3_N;!oo zm_bwICCGD{jJFuI{4^PF@w%1fB|GM2<|dY8=I3cL-r^|E&nZidFDw8_rljT-r^bUt zii$NEZ?P5^7M0{CGlGnOVpawQ24)5ZhR+?~uqr{4D*=f^mDMmTV`N}h4VOjbxiG}4 z)H2pE)G$gy)iW@pFlMuWI7O@}U>O?*1_m@0OxYj>3`MLdOduJMiXPP(hF}IwX20iP zDFzM)7~_B@(=FDb#Ju#>Tg*ABd74bOI8st`;!848OK!1c=B1=&7Z-yfUP0kkvVKN> zZmNEIesMv5N@5Y1XN2J9l;)-A7ZfE{WESg}7R485<`(3n#wX|Jl@#UY=;tQpWfqj? zB$nhC#g}DP=@;cE<(I@4mz1WZ>1XDZ#b;;c<?0nw-r`NpNz5$>OU=no&Mc`c5@29p z5Mp3pD2`-cU}#{tD<nRbd4|VK-Z{J%h19MHseNEzWmN$a9c(wag(tXO;g+6}e2H7} z0=MD~d8H1I31Js`WFEj|QZ8{TUEo%_A+HRT`Ei3=xWWH3C{&U`-UKl~7#@EXjG(k$ z!&t+R1ri72DlP_wS|;Rp%!2E!VX9%sg2ym2I}03dAOosc7#M1pK;$we28Pvey|v6Z z%p}1?WE+v$MA*(!!(78s#m&G_%UZ*VLoX}L<+W@j+#tU|a19%3%B^LqVGCwh$>^ua zc8j?nKJ^w;UcoKql>Cxg>{*G)@g+r>w-`gfk*mpmi?QeyW63SX)LYDXrMb75Q&RJ8 zu_opeWF!{xf?}SXfq_8*0*d$<7#MExX6BW7XXfR`dnG2vhZM;(Fff2{@kDSExxvBN z;n?Ha$=Tp|my4%IZGzwg$0?o*7?*G^;l3!My(00Vi2g+`gDYGH4bD&nQzT~u&WW58 zeNj+jh2cd(ofU~|GIuEM(cGhbQP1@R<3&Bsi(Fn;xV##iZ}9L>Q10|>aJ|7VGNEuv z<&^4+{7MS~FY>E3czk7J5Rjb_agkr`3W)oGi-AX^!M{k5fq?-fX@UF$N+6#v;7(fb zB*DOdNLmX(x**|=MAk4Mq82R@m}{6J$*h(oouQT$CBhi8*kFdB>T+R-b*g2nVO;=n zCrk;P1{FF8ouI^0!;r-RldfSy6jWK9FwqiZ-+-$ZkW30w6)OV+$OI6L>IO!J9%q!S z08eT)tSF}OfJ_46HOy#PiM56W#RO1N0x4vu!RhlFX5_F0WlvB>uVDqVco`UKSxRKV zMu3SL7F0Laved8yGib8-z5M_G|NnHb8ipVCD;aOGrj%3`q-ydPi7+rQ6p4b8Fk4P$ zUSdxAEtZ1Jys{#3kT}STw^;I0^3!gyB^DH<=B3<X%gWEpD=EIk3M!2gZ!r~Q-eOHG zDoU)p#h#XvpIBmSbc;E)G8LMSia=psBn2`{8e|7&L2+tIemuwmkmqj+fK7={%P)%0 z$;{0xDZa&+T?~q3NZwL_WG+Ei<_aiIEltVy%quH0WME(b;o?i^8SJTm&=k(;e3STQ z6wax<D4=;oK(oQ~2EW7%!_L|Uj}Jl&5>gjfMDFqmO)#8d)*0Iq`+<RxHG~mDb}-)H zkhsnve~CkWfzpD&C1DFSH!yDD*de%u`-*|%(V!!tCxQ<~Uo;53s2FsSBlrqO@C7is zD=s;u;(~&~UZy=<7sQ<}iaTEscfP<P@_>bBs?Zd%8H{t-Cdy5bTc8Qb#ut^$FAADp zu=c(v;C+$ByTRirzYHReJ^&T|cyj0saOI7y@ULOWf>+vz99ja;Qw&JCasj9^fusi{ zvIG<<P(HG0$ZUkkpfre@-cWfU`C6p>UIHp9VWwkEsYSvJ3=EnakVFYiffAsk14^Dn zDxmbk3`#km^Z`j<tZ3=0NDrhO<cVUC|G<d|l7z%Cl2Cj`Voq9-2?GOz7pO#!Mo&i{ zI2Z(lrf^Q-o|`l$bAIlu+_kP76t`&ZNZgaTC;Ot2=S3Z_D>_~m<-D)Rfg-ZO^8qZ4 zeHB0{ba6x|sOAL~o}WK~BeVn>FdzyMjSG;9DUb{VBiDndL0-#L!&Jiv4S0qHAUhyh zkjP~W3=FH`{92Y8X5^M&7AWArvUmz5l=cM3HgKWD0%m~<C1#YUW=3-(a}9Ga11zfH zaa*JciaSuG-x5wv%t`hQ$#-=xN=(U2%`1t|_ASx{NvnYfQ2R?08ug%31rh7FIMRy} zQ+!MEUD4xT4jTW#nR)4sW%-#YiFwJX@rd%of`Ne{5tI({!0F&C8-t)Q{$fNx@H)TB zC4Lon-O9$m%8M(r@36Qi;CzwAnYg?T%CX?2p@g$GPGQ8=FiU4hVOqqP&XB?kCRr9S zrZc0|Pau=Q5xj;Kt$t!bu4@np0Yx{+ZD8Hl>tbXRkl6^+K#8P=Ifbo?gMp!j1ys*g zF)*NYIlyYbjnP`x5>R6WY$F3h4J%qwVy$5fX3%8!OE(6C%l4Yg&`!h$dnki}p~xCk zsxX78A}de|LKXnAA)-nQ3=A(p8BUWEQUZa~rZz}}4u}Ay%_4At1S*S)j6q`Hw8(~5 z4BcYQNz6@2yv354l9~r;FoJ>&TpU5GG*M`Jj893-El5oXE-XzfN-Zw3XJB9e1xIlY zI2j_f8$WO`@Cr1z-sR@)v6v7Af(xWTa77ddUJ$W3%5;S50=M@?ZtpAH-VH7f_yu|@ zd#dLK&54{JJ1cgD;Tp>gg<C4ORA1C~IuLro@nFP-fRKan7qvq!%7$K%4ZX-8dLbh6 zB7Y=W68)ftqsa(r8-SA1X9I8|MJYoVq45Q3*nk2Q%uQij!-SULm{4?qOoOOoP6zdE z;5tEx1dDFw6oy(Ba773&=RoC9Eh~0YYgn<E!G=RE10um<(Z!y^P|K0RP|KOZP|Jl< zX)=NOD>Ym->@{pP95tLNENfVoaWXKhhBrKGxofzC88q4aZn350mnP+;7J-so5y-E% zSW=Voi&rw<VuN(!HQB)pGo+FaoEE_?HgH-571b+w5Q*;=W56xO!dr~)MWABw7Gu&a zM#o!BIZ3yeN|SCeW<XLRsCf!Wi4YP}KXO9TV_-m$9|Hr!EKpGhswf&5?s9PTuyqvn zl+B2^z@c`LL+uKOT7x4<lw*S81mOvVQ_3dFEQq+kp?8r(?+S-rgX04Zo(4yxr2CYE ztA}HP;0%om9Lg6tl&^3o!}JSIkzAm0fkXQuhxQc??FPpOoV*QA5CUWdsP`f<gK>u8 zjKl?k3mg{|u25Q$v_tFyzr#g-hb#OJ4IUrZaHLL98v&G_KF?tW^_5c?TNrAYQkYs8 zYMH?)2As}71USWjyCUffwQMQOEey5nB_N$3Nd^Xn5>OyR*|_Un)MCknAy%iBqlOiG z{hNZ;^kHPEVZ)&kt+-=k=+P~KcUBmXIt@4qW7Zlrv>eBYT%2aXQzeQh+=d#K8WvRj zsQ#?wN@u9$Mh>kShAdFI01huywJr>?>a{#I+}M4J9I`b$piTqW2!<?BY6UY<O=D!} z(LixCsLcmfSHoQbDwDx%28Jv?D1%`QdPs0HG1RcvFrfRembZo>3*JyoVb2ETOok#= z9N~^+7tAIGh8pG??i7w1-Wtv|oXc1l7*@m6P%U4cGC3x#;abMRz_1!#&eigzGt}}U zhdfF;<;#LsnyBH1%5!0e)vpz(;l~~)C8|h1LNc*N01-c^=ArT!8F~y#)RD~KN0qPT zui+17(B$?@M#^pspo$XI3xEz%)-WzWn1IAWAEc~hu3@TShGb^sU_ta&P(<N25=`RY z76!Pnx{~FVcxq}uesLyf{M9k7B(=ybzo;BE>H{vPZ?S-eT#LjQ7#LQv-C_gxY>N?v zGo(oYE|^(Ui;D7#ia}mQE`vGJQ%hW31M-Wj6rrUtsQv4bTAZwBlarsEm{V-02UD^g zG>!`zMQvcX!N-4{PyG^~`U<v-eEL`T^gEbu@bFGx?sV^9yel9yg?%p59JU#S3j}9! z&*8o(pmIe(rGxbbzeooUq?ZY=tQi;>KnV;S`k)5SbOw~l5w5O=p@spqSb#O>nZN-7 z4gyWaqEJvJ0@4L-^RSoX`xlgC=2j`-^d3~jUXV^uJ=ehSlwYu?Y(m<Mw2S<zSNK&g zaHv9D4bGO(1OdukU{`~xuIUWqcBN_<YZ#X?Gcc@%rw*vUR<hh;El$oXsJz9I5Ar}} zRq8DPpUk|}#3JYXyyB9g#LT=B@QBGuCP;*Xq8U0w4XIzblHm%glyQ0=q38%Geigut zKu|3x+EaI(U+)sX-ipFCl{*x71YYE~zrt^Sfx{jg8bzQYy9iX8gIg0XL4kgYHP|OO zFhG;}7F%v=Nk)FkEpCMI#kbg!@=NnliZz*vQbFz5G*A;+1d>KUC3q32;4RVsNrAF( zQ7niR2kL^eIe|4oVmcTk3`+4u;GUT#hzo9{fYQ$`R`9s_EsosGyv$rspfOh5Vg-*9 zfxGW31tEb29v%t@X^jFAps*|Q2C;lV1gKYbizz9;2sC_E6b5nv4<v8J7ndX!mE2+~ z$+*RrlbHt^)`!N$EoM+-8dN9TVou6QEC!W^ker4@LL!DAEi#Hgsb?!F{heS0jqZP7 zVPO^hzyK$Bm{`?5Fu(~m22L*6(C!UR?gl3?@qvYz)q(K?1A_d@!@w^x)p-Wv9FB>; zQ+zM-DP7@H0+}NYCOVjIaB%f=c5zNn>E!R>@8E}Vl{@)*_&WGH`0nxw^~7E0Rl3Bh zw7_UV(M4XZE4*3_ZclmmdfcZvO>vuHI3sYP_Z07oJPKEM6dGJ_2#YrO+~pIV;5fyt zGq)!fG`?j3CO~$HT<4I!#34PyagN7=pgBHQWb`g_=wIQ`zW_#eg+-@W%<$RDw1w+} zu=Pb@>np<64L%P<<tIcfU|e8$QB?JcsA_}X4Gzv8_6devJRLlD`GkA&uJfs0;!|Ca zzM|%U`-RB#3k78t_*5_Qm0#g2?_lm=eu_;ENI}LGzKjm$4_piaLJb~w`9-D%&k&v? zIx%WW)J1-UEBp!|S8Icb4(1yi0{#45{Bv1nB+p4*;5;YmqJ-uZ3C)WfT30x<E`ZTj z5tQK<@MH=&m!OPzgT{wJ`TTPmg$=|Sq|tEH3J%^31la>Est^>`W*~Nz*qVVL_d^Xs zbX69BsuZXwf~vvR41_mv7?8%wk%w&%jYEQUB(`QCynMz86G$@<t50hfvOpyn*y$+E zKv0nc7DY7;+zdqa38>TptE=HIfj9ja7_vY`0a%=*W+2=Icr#F~1bet6xdChqm;f~c zYZ%}SK{f`4)$sVK<;znc=uJwDrl2C&dN5Ig)Nn%$J5(OLDTp;*Mw@~RkQqp5 zgP<r7R5OKuY6(z`LMjz-eNqIf$ErZH$Dnp0%4lPFW=Te1Kzz0@W^3*n$hZg)0jjA` z$|-o;rU5)mjH^8i>Z>CS>f)LRW<YPbp|pzOc@{KbjM~Bj4N5^hugP3w268HRbPQDY zuVe-{^}v<+El$t~Y<x+6ylXKiDJdu@K*9x7wO8puD_cV00&NEV12uv`BeM+*AJ`Z~ z#jcBJUJ}u~D58BuM7x6%t!22DX${+o${lJKH5{*KI9}v;yujgzS?t4ulSWO&q8L!n zB!E%|C^TWd2Q^}<42kqTqM63d$iPsP1TqnnF3?(eMadwE6i|1A<rYg~QF<|=DF<#W ztz<5$1u20y@Kz!>@W2f=)V5tw21q$LUJ(sDa2pV{VONw1QU_Xl0BI(I)^-%-fW$!E z;i6oS??DcR<O?JcoD^=!;thu)P(uF#iusFJ8(;#gtWJ!e)fI4(2YVZg3DNq3w8X$| zFhqNZiFJ6ky$nyJUq~+ez`#(==*V<|PxT`M1HTi~7chyf9R^VkZmJbs;VbH3{=h@6 zmYCRROAK5K!-&zA*l0@(lon9iUZ}j$mKX!U4hLdTj-r;BCuU2`3{>L|ot7B3sd7)q z&<v<$R#XA1ib-sTA%zOG9cBj#6i^Fr2(_n>e1T|Bxr01HxII-=18U3Efrxq#K~aOo zv#6bcfx!f1b0f$wP{RnNg%9f6f}-fN6V)eDYlvC`f@OjeF|`V6FoTLL2JqNAb*5H9 zeI~G(SZqd}T1DztgU0u3SdgYxnb)vjn_6AT>R03l3TW`WsuPIi3>w_zfMg5AG7|7O zWh+P?G|~(nzAXazw#XHfX+R?hNG(@LFXk3(8Bt&WybI$RpY2)H1)3>jWMC*>1s<{m z&5mNunL_75uM4VO5>&e=sBuM5V*|$p4$F%imRC3|8yr8dF>vyt%$$PEq0!7Kj#Wvc zi4l42GsTIqs1tY619ze+O?tjXpp_y$lqNm$r|dtlF>vvLhP^>E_ry)w6H0p9M$19a zA_UMR4!D$LKw4^_Lfq0A4wRWYQ0)RXDTRfog*BYuWi9y1Ue+~i*vei_cBHcR<=_AR z|Gxy;T|}T1z9j%&@CVwB;hR_in)HNBM8Oxi;46})prZ*$MY1o{q$1Gj`z4^faRpDY z3@v1@3o2g{RK6&vdPPumMZ^UTvx^*NS2)Z-<uMP4K&;>=dO6EoPVOFw2}ToCW(dv^ zo2a{>;sU3^MNWe&oCXa}5IMsM%HXxZItwx`aOz*=)W5>1-{5oul#ibBi{oATg2+Xh zDz`+Dwyx-<<rkHM_paPx%`43<s03Ghw*+$&OA2!GOL8)k^a?6LtXphQnz<mS<d$@9 zK~8*0etu4NW=XMLE=YlKie6$xYO!(3Ep{mX78_K6slfP_2$G43uzg8IpebY&%aZeR z@{5XZu|a8;<oukXTg=6k#kcstld5|8&^cA`B;zg4!UD+hr(#HL3~DUg;(@4ytWYlk zE$#u&AcFT<fY*Y97xET?#tOk}=)jAZz*BkP;UsVq3|vWri%W32Sjhlx&4P!desS33 z=BJeAq}mlNVPIeY<-Fo9Mh1ot%#4hTHyD&Jz|aE*p$jl{gF*fRD!Rd-aRCRq!Ju~m z72RM^xPXdoFmN@1;SC1S3ovwpLGA)7LUs?tVVG(@@USzAd|-eRQoJB8oKO)1ap8oK zAczYm^h7{hIAJ6U;=&0BMz{<Eh-GF5l7<s?j5Z)XoN!=*GZ{cE2PTjvuud>xfX0Qp KkpYDTjw=ApGLaPk literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/robot_stuff/__pycache__/joint_as_hom_mat.cpython-310.pyc b/python/ur_simple_control/visualize/robot_stuff/__pycache__/joint_as_hom_mat.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5ba205c8b7b413f27c4deb1fe4e9770e874c7c6e GIT binary patch literal 3448 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 literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/robot_stuff/__pycache__/joint_as_hom_mat.cpython-311.pyc b/python/ur_simple_control/visualize/robot_stuff/__pycache__/joint_as_hom_mat.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..936a5cf11707e3e29e66b708b2fad513c0bec527 GIT binary patch literal 7553 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 literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/robot_stuff/__pycache__/utils.cpython-310.pyc b/python/ur_simple_control/visualize/robot_stuff/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4c46e6f66c03ec78ccfa78f5c4ec2a045d0df4e9 GIT binary patch literal 1481 zcmd1j<>g{vU|=XnO-VImVPJR+;vi!t1_lNP1_p*=69xu`6owSW9EK>y6s8pB7KSLM z6qXd$7KSM16t)!h7KSL66pj?m7KSL+6s{ER7KSLc6vki%O`ew^^Zb%QCV^NW%*?>R zz{0@5;0!X^ih+Tlgkb?=4Py<11j9ncTBaJN6h=vg6sBy3BIz2Y6lNqg3yht@8qAQw z7R;c@?pMUfz`&6HXXVFBzDWu8FG0MQAlpLRG?{KO<rUmwPE0D+WWL2*5TAOBtt7E1 zJ+<T(W9mwVB2ESdhF`_{8Tq-X`sw+_1^Fq7MPQy0f}2yCm!e-#lvt5jtY2CbU!0j+ zkdqppoS#=xl%JztP+5|ZpNB=FEVH;YF(<PsRlg`dDZeDXxTG{KO~14xGpAUupz;=1 zYEe;sQG7{iaS13Oia8h<82A`f7+Dxun2NX=7#L8(3*;M+GH`eafWs?`aRC!7#DW=C zGWuyU7lD-AV#~?QOUy~X#gdm_l&i^fi#IVjBQv!uH6=bhKQZSP3y1`J972Gi{}wMu zFg_)-xFj(zITd7mF~~{*#v(zGQ$QgKiZ&1iiL=6@PJw}ep@ty~jI$U87-|_ym=-XX zFlDhUV69<X$heR(n4yS=fq_Aj?G}4hVsd;*QRXei5QsxriVKTMig+0q7;Z7Aq?Twh z-C_j?)h$L}h|@r^Qv`OOOmbpQa%oOtNvdyRUS>gQPGVAKPG(7^XI@Hb1wSYefxOPg zD8yJKf$m^XS_5Hl*lA&La2E3d7La2>X%3Y3YM2(VE@Y5o2xib^@+$)QSCg$sh=GBj zNEk$bY=XMFxGd!sTVhdqZf2e)bCDQGRvhFVk(A8D^!&WUocR2-_~Nn@5QX6<Ik=yK za}#rNQj1H#esj%CPt7Y!%mLX|%mz*s0*pM2MKb9A0?B~_2kfsdg8oWjs%5U?C}GNC zhD1#*OE^QG6bl1LB#W)mn318Dm64%}r-VI)xtWoXA%(G)Ih-MqA%&rqwT?B96+)LY zmNS$vEr6KA2@17^j5SO(ObfU`Ay&hj!ZL?Bm_d^j5^ms(S)~x<@9h`rp%AJN?CIwo z>f;#X8R8nEhya?*x43gb@fx2AiqBhu#n1qWPlX1?E$-sXy!6tX#G=fSN=<HTu~8%m z3VG(tys}%|KACxrIq9yT9CnMfpeQr1q(}r5Iies!3Y;7fu~j4u5|sz(X7!EF%quGb z<wf)qC=HLTfYhS2{Gwb?M)6H8DauSP1|_*-Q2g*Q3NW%TGBFA;3Ndmp3NUgsF#Qu? z=3-=GWcyd70<xF=7HeK<Zb2nD2Y~ZAIER5#EjWt7%8)Ea<N*$w-29Z%oK!ndZY&06 RDh>t?W)5}^77i93Rsi=vN$3Co literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/robot_stuff/__pycache__/utils.cpython-311.pyc b/python/ur_simple_control/visualize/robot_stuff/__pycache__/utils.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac5e6c96318a30402f6390f79b59f8d9d753523f GIT binary patch literal 2594 zcmZ3^%ge>Uz`zhWS1r|$g@NHQhy%k+P{wB+1_p-d3@HpLj5!QZj44bl3{gxe%q<L2 z%qc7_3{fm8tSt;ttSM|Q3{h+;jKK_=>@PuD{gPoOfOsqn3=GT+3=E$ez^0Wjf&?Kr z3(l@#tYN6)VqjRt$iT1~E?Ub}!<51(2~o_zz>va}4Q3Zf)i9+nV-e@8VM<}aBF>b; z8qAQw26CZa5y)}re^!3H<eQXW{}N=`OOQ~An<mpOro4h%%!x_Gn#{MD3*u96v6Uni zrKgtMVoWUt`9VQJ;a7rwMt*LpetLd!L4HbN5twI$;O3O(rRWzFC01k>>z5YA7iZ=c z<fO(Y=jW9a<>%<<Cgx=pl;$Ls<QK)4Wmf4I<tOEr#21&8rlskZmSpA>>lIYq;z}(l z$}fs9Ni8lZ;$dK50EJ+&3j+f~1H)Yot{%3IvKh>sH5WN#u5idSINo6A=n!lv=}2v= zX{edOI74v`%Z$=lJTrJ!2(B<(BeH>YmCOp68~nl@B@g&TI!Zr-LJ)fdxj`ZbqyUN$ z5wrjlL{L!#g+1DW8CEj-X)+hFF)%RPV#~?QOUy~X#gdm_l&i^fi#IVjBQv!uH6=bh zKQZSP3y3TR8LpsE#0}EG3lfP>$t*5O%u7x!5@BFq0Erh%Ljr)Cx5u*IuFGzQ$^zMo zQhFD;^{;U2UtrNM;$>i9NCw3w%z3P^gx3T1R}DiJOojoOjqqa?3j;$fV+lwfRAm+@ z(!uN!P$Ym^3=COt_5vieP)P;`h8jlHWQ@uSW+>ukU|`T>yTzWBm>geHlzEFW1QJ#( z#f3#BMFOBuU`|Ob(PX;C3Qi!m7=2NKNG3TkCmEEAQhlN6DJe52v!v29FD132NQQxd z0c1n50XW2NaPao<&S0IxJBR-whkAqKU2fhU^QlHtET`K}vRxo}QCRtku<}K2l`GsT z7g$s<gAo)2ApCg+X~75&BnAdVFfKq6#T{VCc^*^-)G%QW=w%EH46EVBAo6`MgC>(- z5y&b{wjwbG28JSW5CKY$(9kU|OS#3CSd^ZdnWxEIBn6TMrLS8eDVd4s`FV*s@%d@- z#bqfV$`_XPA?Z^N9{9nzi8(o`#U<cKaLr6l%_~dHDN<x$U=U_tU?_IR9{kd>b1diE z&az!0cu`vWinR6$$1Bo$7g!{2aLZp%FuAT^eM!OkqJr%e1=}m!b{AOeFk=Li=)f_u zgAr6dBPV8LHb%k*1qd{>D2tI4rdnoB29zuTau3*S>@kI&C2CpnjFfShkOhh-uvsb$ zMbfpbC7{F%<})y)Fr#KBMurr|TINWG6oy*XI@UZ62wl!t&QQS|$xx1(nK___;IMZA zCrlohMt3=?{WVNAObfs@DY70A8&}$_VNPM0!yL?@$qGrK;OeMKA;{m`FVsUJR3X^Y z&pp(~F~~E-HAE2sG?{O4gDa2tOi(7jC0GnieDSH!gm;U(I5RK3G$*krv!qg!8(X?B zk^>hlnR#WmxP3D79COlLK{d@S)`FtUypkdbP&NSNq9S>40fopHMG7D(Rgi90-}ubD zvLcY5i$M{k00xk(Aq~$O0jWi4`9-;)65Kbnq$o4FxJZkEfdOPg@dXm|g^+NA_Xj2x zR*Aa;LQ^=W^G@QOk$6!+;fjDl2WtoG4^Vk#d4*fy0|OJQ1cJEB!Fz$v;39|N6%NA- z9ELZ<#5=eV@W+oIKR&QBu}b{-@#8BSgN)o9^QA^hESK9ZvfUtfQQ7#4vhhV}lPl6D zHxx}Tn0Z|{3%q0&c+o8Qidpa##gH3PsvlUHcr-pRKnPZiVo(9ISKd*U;h?OD6A$x2 zLqR8Y=0ohPPCU$-?6+9+N^=V;!G%1yMhDkw;DQ=lT!6!7B?CC8gG2HchfQvNN@-52 zU6C#W0|Tg5D^6ozVEDky$jEquLFfVuePCl{6#c*eC-@i{4L&fy2@WPk)ej7Cf`ggS T{sRM?;NoV~_`rZkfZYKAJBtKD literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/robot_stuff/__pycache__/webots_api_helper_funs.cpython-310.pyc b/python/ur_simple_control/visualize/robot_stuff/__pycache__/webots_api_helper_funs.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0a69e62d20ea45aceaf28eb99b3149a77c7d3f3 GIT binary patch literal 4122 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 C=nUWh literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/robot_stuff/__pycache__/webots_api_helper_funs.cpython-311.pyc b/python/ur_simple_control/visualize/robot_stuff/__pycache__/webots_api_helper_funs.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd4d62b162bb90fbf4b4b0b69abfcdf75bcc9425 GIT binary patch literal 11565 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 K%K$S490C9-rKdIk literal 0 HcmV?d00001 diff --git a/python/ur_simple_control/visualize/robot_stuff/drawing.py b/python/ur_simple_control/visualize/robot_stuff/drawing.py new file mode 100644 index 0000000..d2c85e2 --- /dev/null +++ b/python/ur_simple_control/visualize/robot_stuff/drawing.py @@ -0,0 +1,36 @@ +import numpy as np +import matplotlib.pyplot as plt +import mpl_toolkits.mplot3d.axes3d as p3 +from matplotlib.animation import FuncAnimation +import matplotlib.colors as colr + + +def drawOrientation(ax, orientation, t_v, avg_link_lenth): + # every line is drawn be specifing its x, y and z coordinates + # thus we will take each relevant vector and turn every one of its coordinates into a steps array + # and every vector will be translated accordingly + # first we need to take rot mat and p out of hom mat + steps = np.arange(0.0, 1.0, 0.1) * (avg_link_lenth / 2) + + # now we draw the orientation of the current frame + col = ['b', 'g', 'r'] + for i in range(0,3): + x = t_v[0] + orientation[i,0] * steps + y = t_v[1] + orientation[i,1] * steps + z = t_v[2] + orientation[i,2] * steps + ax.plot(x, y, z, color=col[i]) + + +def drawVector(ax, link, t_v, color_link): + # first let's draw the translation vector to the next frame + steps = np.arange(0.0, 1.0, 0.1) + x = t_v[0] + link[0] * steps + y = t_v[1] + link[1] * steps + z = t_v[2] + link[2] * steps + ax.plot(x, y, z, color=color_link) + + + +def drawPoint(ax, p, color_inside, marker): + point, = ax.plot([p[0]], [p[1]], [p[2]], markerfacecolor=color_inside, markeredgecolor=color_inside, marker=marker, markersize=5.5, alpha=0.9) + return point diff --git a/python/ur_simple_control/visualize/robot_stuff/drawing_for_anim.py b/python/ur_simple_control/visualize/robot_stuff/drawing_for_anim.py new file mode 100644 index 0000000..f40ad14 --- /dev/null +++ b/python/ur_simple_control/visualize/robot_stuff/drawing_for_anim.py @@ -0,0 +1,38 @@ +import numpy as np +import matplotlib.pyplot as plt +import mpl_toolkits.mplot3d.axes3d as p3 +from matplotlib.animation import FuncAnimation +import matplotlib.colors as colr + + +def drawOrientationAnim(ax, orientation, orientation_lines, t_v, avg_link_lenth): + # every line is drawn be specifing its x, y and z coordinates + # thus we will take each relevant vector and turn every one of its coordinates into a steps array + # and every vector will be translated accordingly + # first we need to take rot mat and p out of hom mat + steps = np.arange(0.0, 1.0, 0.1) * (avg_link_lenth / 2) + + # now we draw the orientation of the current frame + col = ['b', 'g', 'r'] + for i in range(0,3): + x = t_v[0] + orientation[i,0] * steps + y = t_v[1] + orientation[i,1] * steps + z = t_v[2] + orientation[i,2] * steps +# ax.plot(x, y, z, color=col[i]) + + orientation_lines[i].set_data(x, y) + orientation_lines[i].set_3d_properties(z) + +def drawVectorAnim(ax, link, link_line, t_v, color_link): + # first let's draw the translation vector to the next frame + steps = np.arange(0.0, 1.0, 0.1) + x = t_v[0] + link[0] * steps + y = t_v[1] + link[1] * steps + z = t_v[2] + link[2] * steps + #ax.plot(x, y, z, color=color_link) + link_line.set_data(x, y) + link_line.set_3d_properties(z) + + + + diff --git a/python/ur_simple_control/visualize/robot_stuff/follow_curve.py b/python/ur_simple_control/visualize/robot_stuff/follow_curve.py new file mode 100644 index 0000000..49feb20 --- /dev/null +++ b/python/ur_simple_control/visualize/robot_stuff/follow_curve.py @@ -0,0 +1,102 @@ +"""control_test controller.""" + +from robot_stuff.forw_kinm import * +from robot_stuff.inv_kinm import * +#from anim_func import * +import numpy as np +#import matplotlib.pyplot as plt +#import mpl_toolkits.mplot3d.axes3d as p3 +#from matplotlib.animation import FuncAnimation +#import matplotlib.colors as colr +import sys +import subprocess +import scipy.optimize +import random + + + +# angle in radians ofc +def turnAngleToPlusMinusPiRange(angle): + return np.arcsin(np.sin(angle)) +# let's trace out a circle +# th circle formula is: +# x = radius * cos(t) +# y = radius * sin(t) +# z = height +# the direction you are to move in in order to trace a circle, +# or any parametric curve for that matter is +# in the direction of the derivative at the given point +# the derivative w.r.t. t is, by coordinate: +# x = radius * -1 * sin(t) +# y = radius * cos(t) +# z = 0 +def goInACirleViaDerivative(radius, height, current_parameter): + return np.array([radius * -1 * np.sin(curve_parameter), radius * np.cos(curve_parameter), 0], dtype=float32) + + +# or just pass the next point to reach and use the vector to get there lel +def goInACirleViaPositionAroundZ(radius, height, current_parameter): + return np.array([radius * np.cos(curve_parameter), radius * np.sin(curve_parameter), height], dtype=np.float32) + +# here height is distance from yz plane +# but also from xy plane (we want it to be floating around axis parallel to x axis actually) +def goInACirleViaPositionAroundLiftedX(radius, height, x_0, current_parameter): + return np.array([x_0, radius * np.sin(curve_parameter), height + + radius * np.cos(curve_parameter)], dtype=np.float32) + + +def error_test(robot, t): + e = abs(t - r.p_e) + if e[0] < 0.001 and e[1] < 0.001 and e[2] < 0.001: + return True + else: + return False + +# let radius be 0.4 +def goInACircleViaJacobian(circle_param): + """ doing a fixed circle with axis around x axis because + i only need one circle and this is easy + arg: s - the current parameter in the parametric eq.""" + # R0c is the rotational matrix for the circle + # p is the vector to the circle center + + # need to have a hom. transf. mat. to circle center + # first 3 entries are rot. mat. and last one is translation vec. + # with added hom. mat. padding + circ_hom_mat = np.array([[0.0, 0.0, -1.0, 0.0], \ + [0.0, -1.0, 0.0, 0.0], + [1.0, 0.0, 0.0, 0.0], + [-0.59, 0.0, 0.59, 1.0]], dtype=np.float32) + center = circ_hom_mat[0:3, 3] + R0c = circ_hom_mat[0:3, 0:3] + + # shouldn't jacobian be a 3x3 mat? + circle_jacobian_circle = np.array([-1 * np.sin(circle_param), np.cos(circle_param), 0], dtype=np.float32) + circle_jacobian_base = R0c @ circle_jacobian_circle + + # now we need a radij-vector + # you need circle center coordinates and some point on the circle + # i can calculate p from current parameter value + # i do that in circle's frame + # and then i can transport it to base frame with my hom. transform + + point_on_c_circle = np.array([np.cos(circle_param), np.sin(circle_param), 0.0], dtype=np.float32) + point_on_c_circle = np.hstack((point_on_c_circle, 1)) + point_on_c_base = circ_hom_mat @ point_on_c_circle + + # need to make center hom. + center = np.hstack((center, 0)) + radij_vec = point_on_c_base - center + + radius = 0.4 + + s = radius * np.arctan2(radij_vec[1], radij_vec[0]) + + s = turnAngleToPlusMinusPiRange(s) + np.pi - s + es = s * 2 * np.pi * radius - s + + e = es * circle_jacobian_base + return e + + + diff --git a/python/ur_simple_control/visualize/robot_stuff/forw_kinm.py b/python/ur_simple_control/visualize/robot_stuff/forw_kinm.py new file mode 100644 index 0000000..0a54f80 --- /dev/null +++ b/python/ur_simple_control/visualize/robot_stuff/forw_kinm.py @@ -0,0 +1,544 @@ +import numpy as np +#import matplotlib.pyplot as plt +#import mpl_toolkits.mplot3d.axes3d as p3 +#from matplotlib.animation import FuncAnimation +#import matplotlib.colors as colr +from robot_stuff.webots_api_helper_funs import * +import scipy.linalg + +#from drawing import * +from robot_stuff.joint_as_hom_mat import * +from robot_stuff.drawing import * +from robot_stuff.drawing_for_anim import * + +""" +The Robot_raw class is a collection of joints which +together constitute the manipulator. +It has methods for calculating the Jacobian, +the velocity manipulability Jacobian and two +different methods for calculating a gradient +toward a better manipulability positions. + +The Jacobian is calculated from its geometric +derivation as described in Siciliano chapter 3, +and so is the velocity manipulability Jacobian, +as described in Geometry Aware Manipulability +Learning, Tracking and Transfer or in +Symbolic differentiation of the velocity mapping for a serial kinematic chain, +by Bruyninckx. Here i avoided the tensor +formulation as to my knowledge +numpy does not support tensor operations. + + +Calculations of the gradients are described in the +Singularity Avoidance Using Riemannian Geometry paper. + + +notes on code: +- we are doing only positions, not orientations +- dh parameters are read from a file +- there is no other way to import a robot + + +""" +# TODO +# TODO +# TODO +# TODO add the following flags: +# one for (not) drawing in matplotlib +# one for (not) using Webots API + + + +# add a drawing flag so that you can turn it on and off +# if sim is to be had, you must pass motors=[list_of_motors] +# and sensors=[list_of_sensors] +class Robot_raw: + #def __init__(self, clamp): + def __init__(self, ax=None, **kwargs): + try: + self.motors = kwargs["motors"] + self.sensors = kwargs["sensors"] + self.robot_name = kwargs["robot_name"] + self.sim = 1 + except KeyError: + if len(kwargs) > 1: + print(" if sim is to be had, you must pass \n \ + motors=[list_of_motors] \n \ + and sensors=[list_of_sensors] in named args syntax") + exit(1) + else: + self.sim = 0 + self.robot_name = "no_sim" + pass + #self.mode = 'training' + self.mode = 'eval' + self.clamp = 0 + #self.clamp = 1 + self.joints = [] + # TODO FIX READING FILES + if self.robot_name == "no_sim": + #fil = open('inverse_kinematics_gymified/envs/arms/ur10e_dh_parameters_from_the_ur_site', 'r') + #fil = open('envs/arms/ur10e_dh_parameters_from_the_ur_site', 'r') + #fil = open('arms/ur10e_dh_parameters_from_the_ur_site', 'r') + #fil = open('/chalmers/users/guberina/ActorCriticManipulationControl/inverse_kinematics_gymified/inverse_kinematics_gymified/envs/arms/ur10e_dh_parameters_from_the_ur_site', 'r') + #fil = open('./arms/ur10e_dh_parameters_from_the_ur_site', 'r') + fil = open('./arms/ur5e_dh', 'r') + #fil = open('/home/gospodar/chalmers/ADL/ActorCriticManipulationControl/inverse_kinematics_gymified/inverse_kinematics_gymified/envs/arms/kuka_lbw_iiwa_dh_params', 'r') + print("i'm using: testing_dh_parameters (which are UR10e params)") + if self.robot_name == "UR10e": + fil = open('arms/ur10e_dh_parameters_from_the_ur_site', 'r') + print('im using: ur10e_dh_parameters_from_the_ur_site') + if self.robot_name == "base_link": + fil = open('arms/kuka_lbw_iiwa_dh_params', 'r') +# fil = open('./da /lbr_iiwa_jos_jedan_dh', 'r') + print("i'm using: kuka_lbw_iiwa_dh_params") + if self.robot_name == "j2n6s300": + fil = open('arms/j2n6s300_dh_params', 'r') + print("i'm using: j2n6s300_dh_params") + if self.robot_name == "j2n6s300": + fil = open('arms/j2n6s300_dh_params', 'r') + print("i'm using: j2n6s300_dh_params") + if self.robot_name == "j2s7s300_link_base": + fil = open('arms/j2s7s300_dh_params', 'r') + print("i'm using: j2n6s300_dh_params") + + params = fil.read().split('\n') + params.pop() + for p in params: + p = p.split(';') + self.joints.append(Joint(float(p[0]), float(p[1]), float(p[2]), float(p[3]), self.clamp)) + + self.ndof = len(self.joints) + + fil.close() + self.jacobian = 0 + self.calcJacobian() + + +# below are some utility functions for drawing + def initDrawing(self, ax, color_link): + #initialize the plot for each line in order to animate 'em + # each joint equates to four lines: 3 for orientation and 1 for the link + # and each line has its x,y and z coordinate + # thus 1 joint is: [x_hat, y_hat, z_hat, p] + # and lines are a list of all of those + self.ax = ax + self.color_link = color_link + self.lines = [] + avg_link_lenth = 0 + for j in range(self.ndof): + x_hat = self.joints[j].HomMat[0:3,0] + y_hat = self.joints[j].HomMat[0:3,1] + z_hat = self.joints[j].HomMat[0:3,2] + p = self.joints[j].HomMat[0:3,3] + + #line_x, = self.ax.plot(np.array([]),np.array([]),np.array([]), 'r')#, animated=True) + #line_y, = self.ax.plot(np.array([]),np.array([]),np.array([]), 'g')#, animated=True) + #line_z, = self.ax.plot(np.array([]),np.array([]),np.array([]), 'b')#, animated=True) + #line_p, = self.ax.plot(np.array([]),np.array([]),np.array([]), self.color_link)#, animated=True) + line_x, = self.ax.plot(np.array([]),np.array([]),np.array([]), 'r', animated=True) + line_y, = self.ax.plot(np.array([]),np.array([]),np.array([]), 'g', animated=True) + line_z, = self.ax.plot(np.array([]),np.array([]),np.array([]), 'b', animated=True) + line_p, = self.ax.plot(np.array([]),np.array([]),np.array([]), self.color_link, animated=True) + + self.lines += [[line_x, line_y, line_z, line_p]] + avg_link_lenth += self.joints[j].d + self.joints[j].r + + self.avg_link_lenth = avg_link_lenth / self.ndof + + +# NOTE switched from ax as argument to self.ax + 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) + + +# needed only for drawing, and even that is optional + def saveConfToFile(self): + fil = open('robot_parameters', 'r') + params = fil.read().split('\n') + fil.close() + fil = open('robot_parameters', 'w') + params.pop() + back_to_string = '' + for i in range(len(params)): + params[i] = params[i].split(';') + params[i][1] = self.joints[i].theta + for j in range(4): #n of d-h params + back_to_string += str(params[i][j]) + if j != 3: + back_to_string += ';' + else: + back_to_string += '\n' + fil.write(back_to_string) + fil.close() + + +# the jacobian is calulated for the end effector in base frame coordinates +# TODO: the base frame ain't aligned with world coordinate frame!!! +# ==> the only offset is on the z axis tho +# ==> resolved by putting first joint's d parameter to height, there is no x nor y offset +# the simulation is controlled via motors +# we do calculations by reading from the sensors +# and we send the results to the motors + +# HERE WE ALSO CALCULATE THE MANIPULABILITY JACOBIAN BUT I WON'T RENAME + def calcJacobian(self): + z_is = [np.array([0,0,1], dtype=np.float32)] + p_is = [np.array([0,0,0], dtype=np.float32)] + toBase = np.array([[1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1]], dtype=np.float32) + self.p_e = np.array([0.,0.,0.], dtype=np.float32) + + for j in range(self.ndof): + toBase = toBase @ self.joints[j].HomMat + z_is.append(toBase[0:3, 2]) + p_is.append(toBase[0:3, 3]) + p_e = p_is[-1] + self.p_e = p_is[-1] + jac = np.array([0,0,0,0,0,1], dtype=np.float32).reshape(6,1) +# we doin only revolute joints still +# we'll put these into array cos i don't trust myself that much + j_os = [] + j_ps = [] + + for j in range(self.ndof): + j_p = np.cross(z_is[j], p_e - p_is[j]) + j_ps.append(j_p) + j_p = j_p.reshape(3,1) + j_o = z_is[j] + j_os.append(z_is[j]) + j_o = j_o.reshape(3,1) + j_i = np.vstack((j_p, j_o)) + jac = np.hstack((jac, j_i)) + self.jacobian = jac[0:6,1:] +# print("jacobian incoming") +# print(self.jacobian) + self.jac_tri = self.jacobian[0:3,:] + + # the manipulability elipsoid is J @ J.T + # and we want to know its derivative w.r.t. q (all joints) + # thus we calculate the derivative of a matrix w.r.t a vector + # the result is a 3rd order tensor ('cos you get 1 Jacobian for each q_i) + # with size 6 x n x n + # the calculations are described in Geometry-aware learning, tracking.., eq. 66-68 + # just figure out the shape of the tensor and it's all clear after that + # formulae straight from GAMLTT, last page, 66-68 + +# if self.mode == 'eval': + mjac = [] + mjac_tri = [] + # this first column is here so that we can stack in the for loop, it's removed later + mjac_j = np.array([0,0,0,0,0,1], dtype=np.float32).reshape(6,1) + mj_o = 0 + mj_p = 0 + # now we take a jth joint by which we will do the derivate + for j in range(self.ndof): + # and we do this for every ith joint + for i in range(self.ndof): + if j <= i: + mj_o = np.cross(j_os[j], j_os[i]).reshape(3,1) + mj_p = np.cross(j_os[j], j_ps[i]).reshape(3,1) + mj_i = np.vstack((mj_p, mj_o)) + mjac_j = np.hstack((mjac_j, mj_i)) + + else: + mj_o = np.array([0.,0.,0.], dtype=np.float32).reshape(3,1) + mj_p = np.cross(j_ps[j], j_os[i]).reshape(3,1) + mj_i = np.vstack((mj_p, mj_o)) + mj_i = -1 * mj_i + mjac_j = np.hstack((mjac_j, mj_i)) + + # with that we calculated J derivative w.r.t. joint j, so append that and move on + mjac_j = mjac_j[0:6,1:] +# print("mjac_j", j + 1) +# print(mjac_j) + mjac_j_tri = mjac_j[0:3,:] + mjac.append(mjac_j) +# print("unutar calcJac") +# print(mjac_j) +# print("unutar calcJac") + mjac_tri.append(mjac_j_tri) + mjac_j = np.array([0,0,0,0,0,1], dtype=np.float32).reshape(6,1) + self.mjac = mjac + self.mjac_tri = mjac_tri + +# now we that we have the manipulability jacobian, +# we can get the velocity manipulability jacobian + + + # implementation of maric formula 12 (rightmostpart) + def calcMToEGradient_kM(self): + # first let's calculate the manipulability elipsoid + M = self.jacobian @ self.jacobian.T + M = M[0:3, 0:3] + k = np.trace(M) +# k = 2 +# print(k) + k_log = np.log(k) + + # this is the derivative of the manipulability jacobian w.r.t. joint angles + # self.calcManipulabilityJacobian() + + # we need M^-1 which might not exist in which case we'll return a 0 + # needs to throw an error or something along those lines in production tho + try: + M_inv = np.linalg.inv(M) + except np.linalg.LinAlgError as e: + print("ROKNUH U SINGULARITET!!!!!!!!!!!") +# M_inv = np.eye(M.shape[1], M.shape[0]) + return np.array([0] * self.ndof) + + + # now we right-mul M_inv by each partial derivative and do a trace of that + resulting_coefs = [] + for i in range(self.ndof): + # we first need to calculate an appropriate element of the + # velocity manipulability ellipsoid + # J^x_i = mjac[i] @ J.T + J @ mjac[i].T + #M_der_by_q_i = self.mjac_tri[i] @ self.jac_tri.T + self.jac_tri @ self.mjac_tri[i].T + M_der_by_q_i = self.mjac[i] @ self.jacobian.T + self.jacobian @ self.mjac[i].T + M_der_by_q_i = M_der_by_q_i[0:3, 0:3] + resulting_coefs.append(-2 * k_log * np.trace(M_der_by_q_i @ M_inv)) + resulting_coefs = np.array(resulting_coefs) +# print(resulting_coefs) + return resulting_coefs + + + # let's actually strech toward the sphere sigma = kI + def calcMToEGradient_kI(self): + # first let's calculate the manipulability elipsoid + M = self.jacobian @ self.jacobian.T + M = M[0:3, 0:3] + k = np.trace(M) + sigma = k * np.eye(3) +# sigma = 10 * k * np.eye(3) + sigma_sqrt = scipy.linalg.fractional_matrix_power(sigma, -0.5) + Theta = sigma_sqrt @ M @ sigma_sqrt + + log_Theta = scipy.linalg.logm(Theta) + Theta_der_wrt_q_i = [] + # calc the M derivate wrt q_is and same for Theta + for i in range(self.ndof): + M_der_by_q_i = self.mjac[i] @ self.jacobian.T + self.jacobian @ self.mjac[i].T + M_der_by_q_i = M_der_by_q_i[0:3, 0:3] + Theta_der_wrt_q_i.append(sigma_sqrt @ M_der_by_q_i @ sigma_sqrt) + # now this is the E + E = np.array(Theta_der_wrt_q_i) + + # to compute the derivative we have to use the frechet derivative + # on the [[Theta, E], [0, Theta]] +# gradMtoE = [] + resulting_coefs = [] + for i in range(self.ndof): + mat_for_frechet = np.vstack((np.hstack((Theta, E[i])), \ + np.hstack((np.eye(3) - np.eye(3), Theta)))) + frechet_der = scipy.linalg.logm(mat_for_frechet) + der_theta_q_i = frechet_der[0:3, -3:] + resulting_coefs.append(2 * np.trace(der_theta_q_i @ log_Theta.T)) + + return np.array(resulting_coefs) + + + def calcManipMaxGrad(self): + M = self.jacobian @ self.jacobian.T + M = M[0:3, 0:3] + resulting_coefs = [] + print("pinv") + print(np.linalg.pinv(self.jacobian)) + for i in range(self.ndof): + A = self.mjac[i] @ np.linalg.pinv(self.jacobian) +# print("A", i) +# print(A) +# A = A[0:3, 0:3] + resulting_coefs.append(-1 * np.sqrt(np.linalg.det(M)) * np.trace(A)) + + return np.array(resulting_coefs) + + + + + + def drawState(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,))) + for j in range(self.ndof): + toBase.append(toBase[-1] @ self.joints[j].HomMat) + drawOrientation(self.ax, toBase[-1][0:3,0:3], toBase[-1][0:3,3]) + drawVector(self.ax, -1* ( toBase[-2][0:3,3] - toBase[-1][0:3,3] ), toBase[-2][0:3,3],self.color_link) + + + def updateJointsAndJacobian(self): + thetas = np.array(readJointState(self.sensors, dtype=np.float32)) +# potential clamping for joint rotation limits +# offset for j2n6s300 + if self.robot_name == "j2n6s300": + q1 = np.array([-0.01497, 1.5708, -1.5708, -1.5708, -3.14159, 0.0], dtype=np.float32) + thetas = thetas + q1 +# if self.robot_name == "base_link": +# thetas = thetas + np.array([np.pi, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], dtype=np.float32) + for i in range(len(thetas)): + if self.clamp == 1: + self.joints[i].rotate_numerically(clampTheta(thetas[i]), self.clamp) + else: + self.joints[i].rotate_numerically(thetas[i], self.clamp) + self.calcJacobian() + + # only here for plots, hence does not update jacobian + def setJoints(self, thetas): + for i in range(len(thetas)): + if self.clamp == 1: + self.joints[i].rotate_numerically(clampTheta(thetas[i]), self.clamp) + else: + self.joints[i].rotate_numerically(thetas[i], self.clamp) + + + + def forwardKinmViaPositions(self, thetas, extra_damping): +# potential clamping for joint rotation limits +# ==> ur10e does not have joint limits, but other robots might + # offset for j2n6s300 + if self.robot_name == "j2n6s300": + thetas = thetas + np.array([np.pi, np.pi, np.pi, \ + np.pi, 0.0, 1.57079633], dtype=np.float32) +# if self.robot_name == "base_link": +# thetas = thetas + np.array([np.pi, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) + for i in range(len(thetas)): + # clamp + if self.clamp == 1: + if self.sim == 1: + self.motors[i].setPosition(clampTheta(self.joints[i].theta + thetas[i])) + else: + self.joints[i].rotate_numerically(clampTheta(self.joints[i].theta + thetas[i] / extra_damping), self.clamp) + # no clamp + else: + if self.sim == 1: + self.motors[i].setPosition(self.joints[i].theta + thetas[i]) + else: + self.joints[i].rotate_numerically(thetas[i] + self.joints[i].theta / extra_damping, self.clamp) + + if self.sim == 1: + self.updateJointsAndJacobian() + else: + if self.mode == "eval": + self.calcJacobian() + + + + def forwardKinmNumericsOnlyDebug(self, thetas): +# if self.robot_name == "j2n6s300": +# thetas = thetas + np.array([np.pi, np.pi, np.pi, np.pi, 0.0, np.pi]) + for i in range(len(thetas)): + # clamp + if self.clamp == 1: + if self.sim == 1: + self.motors[i].setPosition(clampTheta(thetas[i])) +# self.updateJointsAndJacobian() + else: + self.joints[i].rotate_numerically(clampTheta( thetas[i]), self.clamp) + self.calcJacobian() + # no clamp + else: + if self.sim == 1: +# self.motors[i].setPosition(thetas[i]) + self.updateJointsAndJacobian() + else: + self.joints[i].rotate_numerically(thetas[i] , self.clamp) + self.calcJacobian() + + + def forwardKinmNumericsOnlyDebug2(self, thetas): + print("======================================") +# if self.robot_name == "j2n6s300": +# thetas = thetas + np.array([np.pi, np.pi /2, -np.pi/2, \ +# 0.0, 0.0, 1.57079633]) + for i in range(self.ndof): + if self.clamp == 1: + self.joints[i].rotate_numerically(clampTheta(thetas[i]), self.clamp) + else: + # print("----------- +", thetas[i]) + self.joints[i].rotate_numerically(thetas[i], self.clamp) + # print("after:", self.joints[i].theta) + # print("") + print("") + print("") + print("") + self.calcJacobian() +# print(self.jacobian) +# print(self.mjac) + + + +# in webots, i get this for free with a position sensor +# but since such a device is not available in reality. +# thus, the gps device in webots shall be used to measure accuracy. + def eePositionAfterForwKinm(self, thetas): + joints2 = self.joints + for i in range(len(thetas)): + joints2[i].rotate(joints2[i].theta + thetas[i]) + + toBase = np.array([[1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1]], dtype=np.float32) + + for j in range(len(joints2)): + toBase = toBase @ joints2[j].HomMat + p_e = toBase[0:3,3] + return p_e + + + def bruteAnimForwKinm(self, ax, thetas): + shots = np.linspace(0,1,20) + for shot in shots: + for i in range(len(thetas)): + self.joints[i].rotate(self.joints[i].theta + thetas[i] * shot) + self.drawState(ax, 'bisque') + self.calcJacobian() + self.drawState(ax, 'b') + + + # remake this into the FuncAnimation update function + # divide each angle from the current to desired angle in the same number of steps + # then for each joint and each of its steps calculate the new position, + # and append it in the appropriate "line" list +# there is no set_data() for 3dim, so we use set_3_properties() for the z axis +def forwardKinAnim(frame, r, thetas, n_of_frames, ax): + thetas_sliced = [] +# print("frame") + # print(frame) + for j in range(len(r.joints)): + # i have no idea why but the sum of frame(i)/n_of_frames adds up to 0.5 + # we need it to add to to 1 so that in total the manipulator rotates by the specified angles + thetas_sliced.append(thetas[j] * (2 * frame / n_of_frames)) + r.forwardKinm(thetas_sliced) + toBase = [np.array([[1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1]], dtype=np.float32)] + x_hat_dat =[[0],[0],[0]] + y_hat_dat =[[0],[0],[0]] + z_hat_dat =[[0],[0],[0]] + p_dat = [[0],[0],[0]] + for j in range(len(r.joints)): + toBase.append(toBase[-1] @ r.joints[j].HomMat) + orientation = toBase[-1][0:3,0:3] + x_hat = orientation[0:3,0] + toBase[-1][0:3,3] + y_hat = orientation[0:3,1] + toBase[-1][0:3,3] + z_hat = orientation[0:3,2] + toBase[-1][0:3,3] + p = (-1* ( toBase[-2][0:3,3] - toBase[-1][0:3,3] ) + toBase[-2][0:3,3]) + for i in range(3): + x_hat_dat[i].append(x_hat[i]) + y_hat_dat[i].append(y_hat[i]) + z_hat_dat[i].append(z_hat[i]) + p_dat[i].append(p[i]) +# r.lines[j][0].set_data(x_hat_dat[0], x_hat_dat[1]) +# r.lines[j][0].set_3d_properties(x_hat_dat[2]) +# r.lines[j][1].set_data(y_hat_dat[0], y_hat_dat[1]) +# r.lines[j][1].set_3d_properties(y_hat_dat[2]) +# r.lines[j][2].set_data(z_hat_dat[0], z_hat_dat[1]) +# r.lines[j][2].set_3d_properties(z_hat_dat[2]) + r.lines[j][3].set_data(p_dat[0], p_dat[1]) + r.lines[j][3].set_3d_properties(p_dat[2]) +# print(frame / frame_length) + if frame == 1: + r.drawState(ax, 'r') + diff --git a/python/ur_simple_control/visualize/robot_stuff/inv_kinm.py b/python/ur_simple_control/visualize/robot_stuff/inv_kinm.py new file mode 100644 index 0000000..2906bfd --- /dev/null +++ b/python/ur_simple_control/visualize/robot_stuff/inv_kinm.py @@ -0,0 +1,434 @@ +from robot_stuff.forw_kinm import * +#from anim_func import * +import numpy as np +import matplotlib.pyplot as plt +import mpl_toolkits.mplot3d.axes3d as p3 +from matplotlib.animation import FuncAnimation +import matplotlib.colors as colr +import sys +import scipy.optimize +from qpsolvers import solve_qp +from qpsolvers import dense_solvers +from scipy.linalg import sqrtm + + + +# hardcoded for all joints +# of course each joint can have its own limit +def clampVelocity(del_thet): + for indeks in range(len(del_thet)): + if del_thet[indeks] > 3.0: + del_thet[indeks] = 3.0 + + if del_thet[indeks] < -3.0: + del_thet[indeks] = -3.0 + return del_thet +# r is the Robot_raw +# t is the target position + + + +def invKinm_Jac_T(r, t): + e = t - r.p_e + num = np.dot(e, r.jac_tri @ r.jac_tri.T @ e) + den = np.dot(r.jac_tri @ r.jac_tri.T @ e, r.jac_tri @ r.jac_tri.T @ e) + alpha = num / den + del_thet = alpha * r.jac_tri.T @ e + +# clamping for joint rotation limits + del_thet = clampVelocity(del_thet) + +# if you want a damping constant other than alpha +# del_thet = 0.011 * r.jac_tri.T @ e + + return del_thet + + +# using the nullspace for the comfort function +# when doing manipulability, use the nullspace then go to vec_toward_greater_manip +def invKinm_PseudoInv(r, t): + e = t - r.p_e + + psedo_inv = np.linalg.pinv(r.jac_tri) + del_thet = psedo_inv @ e +# we can add any nulspace vector to del_thet +# and given the constraints, we should implement some sort of a comfort function +# the min and max theta for each angle are hardcoded, but that will be changed +# they are hardcoded to +/- pi # 3/4 with center at 0 +# thus for all i q_iM - q_im = pi * 6/4 +# the added q_0 must be left multiplyed by (np.eye(n) - np.linalg.pinv(r.jac_tri) @ r.jac_tri) +# we take into account the current theta (punish more if closer to the limit) +# the formula is 3.57 in siciliano + theta_for_limits = [] + for k in range(r.ndof): + theta_for_limits.append( (-1/r.ndof) * (r.joints[k].theta / (np.pi * 1.5))) + theta_for_limits = np.array(theta_for_limits, dtype=np.float32) + + del_thet += (np.eye(r.ndof) - psedo_inv @ r.jac_tri) @ theta_for_limits + + del_thet = clampVelocity(del_thet) + + return del_thet + +# what is this, i don't know +def invKinm_PseudoInv_half(r, t): + e = t - r.p_e + + #psedo_inv = np.linalg.pinv(r.jac_tri) + #psedo_inv = r.jac_tri.T @ sqrtm(r.jac_tri @ r.jac_tri.T) + psedo_inv = r.jac_tri.T @ np.linalg.inv(sqrtm(r.jac_tri @ r.jac_tri.T)) + #psedo_inv = sqrtm(r.jac_tri.T @ r.jac_tri) @ r.jac_tri.T + del_thet = psedo_inv @ e +# theta_for_limits = [] +# for k in range(r.ndof): +# theta_for_limits.append( (-1/r.ndof) * (r.joints[k].theta / (np.pi * 1.5))) +# theta_for_limits = np.array(theta_for_limits, dtype=np.float32) +# +# del_thet += (np.eye(r.ndof) - psedo_inv @ r.jac_tri) @ theta_for_limits + + del_thet = clampVelocity(del_thet) + + return del_thet + + + + +# using the nullspace singularity avoidance +def invKinmSingAvoidance_PseudoInv(r, e): +# e = t - r.p_e + + psedo_inv = np.linalg.pinv(r.jac_tri) + del_thet = psedo_inv @ e +# we can add any nulspace vector to del_thet +# and given the constraints, we should implement some sort of a comfort function +# the min and max theta for each angle are hardcoded, but that will be changed +# they are hardcoded to +/- pi # 3/4 with center at 0 +# thus for all i q_iM - q_im = pi * 6/4 +# the added q_0 must be left multiplyed by (np.eye(n) - np.linalg.pinv(r.jac_tri) @ r.jac_tri) +# we take into account the current theta (punish more if closer to the limit) +# the formula is 3.57 in siciliano + gradMtoE = r.calcMToEGradient_kM() +# print(gradMtoE) + + del_thet += (np.eye(r.ndof) - psedo_inv @ r.jac_tri) @ gradMtoE + + del_thet = clampVelocity(del_thet) + + return del_thet + + + + +def invKinm_dampedSquares(r, t): + e = t - r.p_e +# e = np.array([0.0,0.0,-1.0], dtype=np.float32) +# e = np.array([0.0,1.0,0.0], dtype=np.float32) +# e = np.array([1.0,0.0,0.0], dtype=np.float32) + lamda = 0.3 + iden = np.array([[1.,0.,0.], [0.,1.,0.], [0.,0.,1.]], dtype=np.float32) + + del_thet = r.jac_tri.T @ np.linalg.inv(r.jac_tri @ r.jac_tri.T + lamda**2 * iden) @ e + + del_thet = clampVelocity(del_thet) + + # let's try to use the calculation which uses svd + # the derivation is in the iksurvey section 6 + # the final equation is (11) and it calculates all left of e in above del_thet + + # something is wrong here and i have no idea what +# m = 3 +# n = len(r.jac_tri[0,:]) +# svdd = np.zeros((n, m)) +# svd = np.linalg.svd(r.jac_tri) # the output is a list of 3 matrices: U, D and V +# # important note: D is not returned as a diagonal matrix, but as the list of diagonal entries +# for s in range(m): # 3 is the maximum rank of jacobian +# svdd = svdd + (svd[1][s] / (svd[1][s] ** 2 + lamda ** 2)) * svd[2][:,s].reshape(n,1) @ svd[0][:,s].reshape(1, m) + + +# del_thet = svdd @ e +# del_thet = clampVelocity(del_thet) + + return del_thet + + +def invKinmQP(r, t): + P = np.eye(r.ndof, dtype="double") + q = np.array([0] * r.ndof, dtype="double") # should be q imo + #G = np.eye(r.ndof, dtype="double") + G = None + e = t - r.p_e +# e = np.array([0.0, 1.0, 0.0]) + b = np.array(e, dtype="double") + A = np.array(r.jac_tri, dtype="double") + #lb = np.array([-3] * r.ndof, dtype="double") + lb = None + #ub = np.array([3] * r.ndof, dtype="double") + ub = None + #h = ub + h = None + + + del_thet = solve_qp(P, q, G, h, A, b, lb, ub, solver="ecos") + #del_thet = solve_qp(P, q, G, h, A, b, lb, ub, solver="quadprog") + + return del_thet + + + + + +# qp formulation, solved with scipy +def invKinmGradDesc(r, t): + + def getEEPos(thetas, r, t): + p_e = r.eePositionAfterForwKinm(thetas) + e = t - p_e + error = np.sqrt(np.dot(e,e)) + return error + + def toOptim(thetas): + #return np.sqrt(np.dot(thetas, thetas)) + return np.dot(thetas, thetas) + e = t - r.p_e + lb = [] + ub = [] + + def constraint(r, e): + # jac_tri @ del_thet must be equal to e + # when doing manipulability optimization it will be e + vec_toward_greater_manip + return scipy.optimize.LinearConstraint(r.jac_tri, e, e) + + + for bo in range(len(r.joints)): + lb.append(-3.0) + ub.append(3.0) + bounds = scipy.optimize.Bounds(lb, ub) + + error = np.sqrt(np.dot(e,e)) + thetas_start = [] + for th in range(r.ndof): + thetas_start.append(r.joints[th].theta) + thetas_start = np.array(thetas_start, dtype=np.float32) + + lin_constraint = constraint(r, e) + if (r.clamp == 1): + res = scipy.optimize.minimize(toOptim, thetas_start, method='SLSQP', constraints=lin_constraint, bounds=bounds) + else: + res = scipy.optimize.minimize(toOptim, thetas_start, method='SLSQP', constraints=lin_constraint) +# res = scipy.optimize.minimize(getEEPos, thetas_start, args=(r,t), method='SLSQP', constraints=lin_constraint, bounds=bounds) +# res = scipy.optimize.minimize(toOptim, thetas_start, method='CG', bounds=bounds) + # without constraints it returns some crazy big numbres like 10**300 or sth + # so something is seriously wrong there + del_thet = [] + for bla in range(len(res.x)): + del_thet.append(float(res.x[bla])) +# del_thet.append(res.x[bla] - 0.01) +# for bla in range(len(res.x)): +# del_thet.append(float(res.x[bla])) +# del_thet[bla] += 0.01 +# print(del_thet[bla]) +# print("del_thet") +# print(del_thet) + +# del_thet = np.array(del_thet, dtype=np.float32) + del_thet = clampVelocity(del_thet) + return del_thet + + + +############################################################### +# IK with singularity avoidance via QP +############################################################### + +# qp formulation, solved with scipy +def invKinmSingAvoidanceWithQP_kM(r, t): + + def getEEPos(thetas, r, t): + p_e = r.eePositionAfterForwKinm(thetas) + e = t - p_e + error = np.sqrt(np.dot(e,e)) + return error + + # E is shere toward which the manipulability elipsoid M should move + # the calculation is defined as a method in the robot_raw class + def toOptim(thetas, r): + # alpha is a coef to select the amount of moving toward E +# aplha = 3.03 + grad_to_E = r.calcMToEGradient_kM() +# return np.dot(thetas, thetas) #+ coef_to_E @ thetas + return np.dot(thetas, thetas) + np.dot(grad_to_E, thetas) + e = t - r.p_e + lb = [] + ub = [] + def constraint(r, e): + # jac_tri @ del_thet must be equal to e + # when doing manipulability optimization it will be e + vec_toward_greater_manip + return scipy.optimize.LinearConstraint(r.jac_tri, e, e) + + + for bo in range(r.ndof): + lb.append(-3.0) + ub.append(3.0) + bounds = scipy.optimize.Bounds(lb, ub) + + error = np.sqrt(np.dot(e,e)) + thetas_start = [] + for th in range(r.ndof): + thetas_start.append(r.joints[th].theta) + thetas_start = np.array(thetas_start, dtype=np.float32) + + lin_constraint = constraint(r, e) + if (r.clamp == 1): + res = scipy.optimize.minimize(toOptim, thetas_start, args=(r), method='SLSQP', constraints=lin_constraint, bounds=bounds) + else: + res = scipy.optimize.minimize(toOptim, thetas_start, args=(r), method='SLSQP', constraints=lin_constraint) +# res = scipy.optimize.minimize(toOptim, thetas_start, args=(r), method='CG', constraints=lin_constraint) +# res = scipy.optimize.minimize(getEEPos, thetas_start, args=(r,t), method='SLSQP', constraints=lin_constraint, bounds=bounds) +# res = scipy.optimize.minimize(toOptim, thetas_start, method='CG', bounds=bounds) + # without constraints it returns some crazy big numbres like 10**300 or sth + # so something is seriously wrong there + del_thet = [] + for bla in range(len(res.x)): + del_thet.append(float(res.x[bla])) +# del_thet.append(res.x[bla] - 0.01) +# for bla in range(len(res.x)): +# del_thet.append(float(res.x[bla])) +# del_thet[bla] += 0.01 +# print(del_thet[bla]) +# print("del_thet") +# print(del_thet) + +# del_thet = np.array(del_thet, dtype=np.float32) +# print(del_thet) + del_thet = clampVelocity(del_thet) + return del_thet + + + +def invKinmSingAvoidanceWithQP_kI(r, t): + + def getEEPos(thetas, r, t): + p_e = r.eePositionAfterForwKinm(thetas) + e = t - p_e + error = np.sqrt(np.dot(e,e)) + return error + + # E is shere toward which the manipulability elipsoid M should move + # the calculation is defined as a method in the robot_raw class + def toOptim(thetas, r): + # alpha is a coef to select the amount of moving toward E +# aplha = 3.03 + grad_to_E = r.calcMToEGradient_kI() +# return np.dot(thetas, thetas) #+ coef_to_E @ thetas + return np.dot(thetas, thetas) + np.dot(grad_to_E, thetas) + e = t - r.p_e + lb = [] + ub = [] + def constraint(r, e): + # jac_tri @ del_thet must be equal to e + # when doing manipulability optimization it will be e + vec_toward_greater_manip + return scipy.optimize.LinearConstraint(r.jac_tri, e, e) + + + for bo in range(r.ndof): + lb.append(-3.0) + ub.append(3.0) + bounds = scipy.optimize.Bounds(lb, ub) + + error = np.sqrt(np.dot(e,e)) + thetas_start = [] + for th in range(r.ndof): + thetas_start.append(r.joints[th].theta) + thetas_start = np.array(thetas_start, dtype=np.float32) + + lin_constraint = constraint(r, e) + if (r.clamp == 1): + res = scipy.optimize.minimize(toOptim, thetas_start, args=(r), method='SLSQP', constraints=lin_constraint, bounds=bounds) + else: + res = scipy.optimize.minimize(toOptim, thetas_start, args=(r), method='SLSQP', constraints=lin_constraint) +# res = scipy.optimize.minimize(toOptim, thetas_start, args=(r), method='CG', constraints=lin_constraint) +# res = scipy.optimize.minimize(getEEPos, thetas_start, args=(r,t), method='SLSQP', constraints=lin_constraint, bounds=bounds) +# res = scipy.optimize.minimize(toOptim, thetas_start, method='CG', bounds=bounds) + # without constraints it returns some crazy big numbres like 10**300 or sth + # so something is seriously wrong there + del_thet = [] + for bla in range(len(res.x)): + del_thet.append(float(res.x[bla])) +# del_thet.append(res.x[bla] - 0.01) +# for bla in range(len(res.x)): +# del_thet.append(float(res.x[bla])) +# del_thet[bla] += 0.01 +# print(del_thet[bla]) +# print("del_thet") +# print(del_thet) + +# del_thet = np.array(del_thet, dtype=np.float32) +# print(del_thet) + del_thet = clampVelocity(del_thet) + return del_thet + + + +def invKinmQPSingAvoidE_kI(r, t): + P = np.eye(r.ndof, dtype="double") +# q = 0.5 * np.array(r.calcMToEGradient_kI(), dtype="double") + q = np.array(r.calcMToEGradient_kI(), dtype="double") +# G = np.eye(r.ndof, dtype="double") +# G = [] + G = None + e = t - r.p_e + b = np.array(e, dtype="double") + A = np.array(r.jac_tri, dtype="double") + #lb = np.array([-3] * r.ndof, dtype="double") + lb = None + #ub = np.array([3] * r.ndof, dtype="double") + ub = None +# h = ub + h = None + +# del_thet = solve_qp(P, q, G, h, A, b, lb, ub, solver="quadprog") + del_thet = solve_qp(P, q, G, h, A, b, lb, ub, solver="ecos") + + return del_thet + + + +def invKinmQPSingAvoidE_kM(r, t): + P = np.eye(r.ndof, dtype="double") +# q = 0.5 * np.array(r.calcMToEGradient_kM(), dtype="double") + q = np.array(r.calcMToEGradient_kM(), dtype="double") + #G = np.eye(r.ndof, dtype="double") + G = None + e = t - r.p_e +# e = np.array([0.0, 1.0, 0.0]) + b = np.array(e, dtype="double") + A = np.array(r.jac_tri, dtype="double") + #lb = np.array([-3] * r.ndof, dtype="double") + #ub = np.array([3] * r.ndof, dtype="double") + lb = None + ub = None + #h = ub + h = None + + + del_thet = solve_qp(P, q, G, h, A, b, lb, ub, solver="ecos") + + return del_thet + + + +def invKinmQPSingAvoidManipMax(r, t): + P = np.eye(r.ndof, dtype="double") + q = np.array(r.calcManipMaxGrad(), dtype="double") + G = None + e = t - r.p_e + b = np.array(e, dtype="double") + A = np.array(r.jac_tri, dtype="double") + lb = np.array([-3] * r.ndof, dtype="double") + ub = np.array([3] * r.ndof, dtype="double") + h = None + + del_thet = solve_qp(P, q, G, h, A, b, lb, ub, solver="ecos") + + return del_thet + diff --git a/python/ur_simple_control/visualize/robot_stuff/joint_as_hom_mat.py b/python/ur_simple_control/visualize/robot_stuff/joint_as_hom_mat.py new file mode 100644 index 0000000..66233e1 --- /dev/null +++ b/python/ur_simple_control/visualize/robot_stuff/joint_as_hom_mat.py @@ -0,0 +1,146 @@ +import numpy as np + +""" +Here we formalize the robot as a series of rigid bodies (links) +connected with joints. Each joint is endowed with a coordinate frame +which is derived via the Denavit-Hartenberg parameters. +Here all joints are revolute, meaning rotating, and rotations are +reprezented as rotation matrices. +Rotating matrices and translation vectors are combined +into homogenous matrices and then a frame change amounts +to matrix multiplication. +""" + +# deriving the homogeneus transformation matrix +# as a series of transformations between two coordinate frames +# detailed description: Wikipedia article on Denavit-Hartenberg parameters + +# this function is wrong!!!!!!!!!!!!!1 +def DHtoHomMat(d, theta, r, alpha): + ct = np.cos(theta) + st = np.sin(theta) + ca = np.cos(alpha) + sa = np.sin(alpha) + + # the d: translation along i^th z-axis until the common normal + trans_z_n_prev = np.array([[1,0,0], [0,1,0], [0,0,1]], dtype=np.float32) + trans_z_n_prev = np.hstack((trans_z_n_prev, np.array([0,0,d], dtype=np.float32).reshape(3,1))) + trans_z_n_prev = np.vstack((trans_z_n_prev, np.array([0,0,0,1], dtype=np.float32))) + + # the theta: rotation about i^th z-axis until i^th x-axis coincides with the common normal + # this is the parameter we get to control + rot_z_n_prev = np.array([[ct,-1*st,0], [st,ct,0], [0,0,1]], dtype=np.float32) + rot_z_n_prev= np.hstack((rot_z_n_prev, np.array([0,0,1], dtype=np.float32).reshape(3,1))) + rot_z_n_prev= np.vstack((rot_z_n_prev, np.array([0,0,0,1], dtype=np.float32))) + + # the r: traslation along the i^th x-axis, now common normal, until the origin of i+1^th frame + trans_x_n = np.array([[1,0,0], [0,1,0], [0,0,1]], dtype=np.float32) + trans_x_n = np.hstack((trans_x_n, np.array([r,0,0], dtype=np.float32).reshape(3,1))) + trans_x_n = np.vstack((trans_x_n, np.array([0,0,0,1], dtype=np.float32))) + + # the a: rotation about common normal so that z-axis aling + rot_x_n = np.array([[1,0,0], [0,ca,-1*sa], [0,sa,ca]], dtype=np.float32) + rot_x_n = np.hstack((rot_x_n, np.array([0,0,0], dtype=np.float32).reshape(3,1))) + rot_x_n = np.vstack((rot_x_n, np.array([0,0,0,1], dtype=np.float32))) + + return trans_z_n_prev @ rot_z_n_prev @ trans_x_n @ rot_x_n + +# the above, but multiplied into the final form (less calculation needed, hence better) +# also correct +def createDHMat(d, theta, r, alpha): + ct = np.cos(theta) + st = np.sin(theta) + ca = np.cos(alpha) + sa = np.sin(alpha) + DHMat = np.array([ [ct, -1 * st * ca, st * sa, r * ct], \ + [st, ct * ca, -1 * ct * sa, r * st], \ + [0, sa, ca, d], \ + [0, 0, 0, 1] ], dtype=np.float32) + return DHMat + + +def createModifiedDHMat(d, theta, r, alpha): + ct = np.cos(theta) + st = np.sin(theta) + ca = np.cos(alpha) + sa = np.sin(alpha) + MDHMat = np.array( [[ct, -1* st, 0, r], \ + [st * ca, ct * ca, -1* sa, -d * sa], \ + [st * sa, ct * sa, ca, d * ca], \ + [0, 0, 0, 1]], dtype=np.float32) + return MDHMat + + +# clamping of all joints to rotate only by 7/8 of the cirle +# of course this can be changed to other limits as needed +# however, it is hardcoded here because it's good enough for demonstrative purposes +# the ur arms all do not have joint limits! +def clampTheta(theta): + if theta > np.pi * 7/4: + theta = np.pi * 7/4 + if theta < -1 * (np.pi * 7/4): + theta = -1 * (np.pi * 7/4) + return theta + +# if clamp is == 0 then don't clamp and if it is 1 then do clamp the angles +# this class does not reprezent a geometric entity, but rather the coordinate system +# in which kinematics is defined for a particular joint +# and that coordinate system is defined via the D-H parameters +class Joint: + def __init__(self, d, theta, r, alpha, clamp): + self.clamp = clamp +# potential clamping for joint rotation limits + if clamp == 1: + self.theta = clampTheta(theta) + else: + self.theta = theta + self.d = d + self.r = r + self.alpha = alpha + self.ct = np.cos(theta) + self.st = np.sin(theta) + self.ca = np.cos(alpha) + self.sa = np.sin(alpha) + self.HomMat = createDHMat(self.d, self.theta, self.r, self.alpha) + + +# we simply recalculate the homogenous transformation matrix for the given joint +# and that equates to it being rotated +# the timely rotation is simply the rotation broken into discete parts +######################################################################## +# this is fundamentaly different from how the physics simulation works! +# there rotation equates to setting positions/velocities to individual motors, +# which then do the moving. +# there you can only SENSE via SENSORS what the result of the rotation is +# in this brach we essentially just do numerics! + def updateDHMat(self): + self.ct = np.cos(self.theta) + self.st = np.sin(self.theta) + self.HomMat = np.array([ [self.ct, -1 * self.st * self.ca, self.st * self.sa, self.r * self.ct], \ + [self.st, self.ct * self.ca, -1 * self.ct * self.sa, self.r * self.st], \ + [0, self.sa, self.ca, self.d], \ + [0, 0, 0, 1] ], dtype=np.float32) + + + def rotate_numerically(self,theta, clamp): +# potential clamping for joint rotation limits + if self.clamp == 1: + self.theta = clampTheta(theta) + else: + self.theta = theta % (2 * np.pi) + # self.theta = theta + #self.HomMat = createDHMat(self.d, self.theta, self.r, self.alpha) + self.updateDHMat() + + def rotate_with_MDH(self, theta, clamp): +# potential clamping for joint rotation limits + if self.clamp == 1: + self.theta = clampTheta(theta) + else: + self.theta = theta % (2 * np.pi) + #self.theta = theta + self.HomMat = createModifiedDHMat(self.d, self.theta, self.r, self.alpha) + + + def __repr__(self): + return str(self.HomMat) diff --git a/python/ur_simple_control/visualize/robot_stuff/utils.py b/python/ur_simple_control/visualize/robot_stuff/utils.py new file mode 100644 index 0000000..e359695 --- /dev/null +++ b/python/ur_simple_control/visualize/robot_stuff/utils.py @@ -0,0 +1,36 @@ +import numpy as np + + +def error_test(p_e, target): + e = np.abs(target - p_e) + if e[0] < 0.002 and e[1] < 0.002 and e[2] < 0.002: + return True + else: + return False + +def goal_distance(achieved_goal, goal): + return np.linalg.norm(goal - achieved_goal) + + +def calculateManipulabilityIndex(robot): + M = robot.jac_tri @ robot.jac_tri.T + return np.sqrt(np.linalg.det(M)) + +def calculateSmallestManipEigenval(robot): + M = robot.jac_tri @ robot.jac_tri.T + diagonal_of_svd_of_M = np.linalg.svd(M)[1] + return diagonal_of_svd_of_M[diagonal_of_svd_of_M.argmin()] + +def calculatePerformanceMetrics(robot): + M = robot.jac_tri @ robot.jac_tri.T + diagonal_of_svd_of_M = np.linalg.svd(M)[1] + singularity = 0 + try: + M_inv = np.linalg.inv(M) + except np.linalg.LinAlgError as e: + print("ROKNUH U SINGULARITET!!!!!!!!!!!") + singularity = 1 + return {'manip_index': np.sqrt(np.linalg.det(M)), + 'smallest_eigenval': diagonal_of_svd_of_M[diagonal_of_svd_of_M.argmin()], + 'singularity':singularity } + diff --git a/python/ur_simple_control/visualize/robot_stuff/webots_api_helper_funs.py b/python/ur_simple_control/visualize/robot_stuff/webots_api_helper_funs.py new file mode 100644 index 0000000..8299d4b --- /dev/null +++ b/python/ur_simple_control/visualize/robot_stuff/webots_api_helper_funs.py @@ -0,0 +1,191 @@ +#########################################################3 +# for now only for ur10e, later write out for other robots +#########################################################3 +import numpy as np + + +def getAllMotors(robot): + motors = [] + motors.append(robot.getMotor('shoulder_pan_joint')) + motors.append(robot.getMotor('shoulder_lift_joint')) + motors.append(robot.getMotor('elbow_joint')) + motors.append(robot.getMotor('wrist_1_joint')) + motors.append(robot.getMotor('wrist_2_joint')) + motors.append(robot.getMotor('wrist_3_joint')) + print("imported motors") + return motors + + +# for validation purposes, we must plot this circle +def drawCircle(radius, height, robot): + display = robot.getDisplay("display") +# display.setColor(0xFFFFF) + display.setColor(0xF8FF04) + display.setColor(0xF8FF04) + display.setColor(0xFF0022) + display.setColor(0xFF0022) + display.setColor(0x00FFE6) + display.setColor(0x00FFE6) + display.drawOval(100,100,60,60) + display.drawOval(100,100,60,60) + display.drawOval(100,100,59,59) + display.drawOval(100,100,59,59) + print("drew the circle on the screen") + +def setMotorSpeeds(motors, speeds): + for i in range(len(motors)): + motors[i].setVelocity(speeds[i]) + + + +def initializeMotorsForVelocity(motors): + speeds = [] + for motor in motors: + motor.setPosition(float('inf')) + speeds.append(0) + + + setMotorSpeeds(motors, speeds) + + + +def initializeMotorsForPosition(motors): + speeds = [] + for motor in motors: +# motor.setVelocity(float('inf')) + speeds.append(0) + + + setMotorSpeeds(motors, speeds) + print("did motor init") + + +def getAndInitAllSensors(robot): + sensors = [] + sensors.append(robot.getPositionSensor('shoulder_pan_joint_sensor')) + sensors.append(robot.getPositionSensor('shoulder_lift_joint_sensor')) + sensors.append(robot.getPositionSensor('elbow_joint_sensor')) + sensors.append(robot.getPositionSensor('wrist_1_joint_sensor')) + sensors.append(robot.getPositionSensor('wrist_2_joint_sensor')) + sensors.append(robot.getPositionSensor('wrist_3_joint_sensor')) + + # now we must specify how often do sensors get read in miliseconds + # i've put 10ms since this seems like it should work smoothly, + # but ofc you should play with this value later on + for sensor in sensors: + sensor.enable(10) + + print("imported and inited sensors") + return sensors + +def readJointState(sensors): + joint_positions = [] + for sensor in sensors: + joint_positions.append(sensor.getValue()) + return joint_positions + + + + + +def getAllMotorsJaco(robot): + motors = [] + motors.append(robot.getMotor('j2n6s300_joint_1')) + motors.append(robot.getMotor('j2n6s300_joint_2')) + motors.append(robot.getMotor('j2n6s300_joint_3')) + motors.append(robot.getMotor('j2n6s300_joint_4')) + motors.append(robot.getMotor('j2n6s300_joint_5')) + motors.append(robot.getMotor('j2n6s300_joint_6')) +# motors.append(robot.getMotor('j2n6s300_joint7')) + print("imported motors") + return motors + + +def getAndInitAllSensorsJaco(robot): + sensors = [] + sensors.append(robot.getPositionSensor('j2n6s300_joint_1_sensor')) + sensors.append(robot.getPositionSensor('j2n6s300_joint_2_sensor')) + sensors.append(robot.getPositionSensor('j2n6s300_joint_3_sensor')) + sensors.append(robot.getPositionSensor('j2n6s300_joint_4_sensor')) + sensors.append(robot.getPositionSensor('j2n6s300_joint_5_sensor')) + sensors.append(robot.getPositionSensor('j2n6s300_joint_6_sensor')) +# sensors.append(robot.getPositionSensor('j2n6s300_joint7_sensor')) + + # now we must specify how often do sensors get read in miliseconds + # i've put 10ms since this seems like it should work smoothly, + # but ofc you should play with this value later on + for sensor in sensors: + sensor.enable(10) + + print("imported and inited sensors") + return sensors + + + + +def getAllMotorsKuka(robot): + motors = [] + motors.append(robot.getMotor('joint_a1')) + motors.append(robot.getMotor('joint_a2')) + motors.append(robot.getMotor('joint_a3')) + motors.append(robot.getMotor('joint_a4')) + motors.append(robot.getMotor('joint_a5')) + motors.append(robot.getMotor('joint_a6')) + motors.append(robot.getMotor('joint_a7')) + print("imported motors") + return motors + + +def getAndInitAllSensorsKuka(robot): + sensors = [] + sensors.append(robot.getPositionSensor('joint_a1_sensor')) + sensors.append(robot.getPositionSensor('joint_a2_sensor')) + sensors.append(robot.getPositionSensor('joint_a3_sensor')) + sensors.append(robot.getPositionSensor('joint_a4_sensor')) + sensors.append(robot.getPositionSensor('joint_a5_sensor')) + sensors.append(robot.getPositionSensor('joint_a6_sensor')) + sensors.append(robot.getPositionSensor('joint_a7_sensor')) + + # now we must specify how often do sensors get read in miliseconds + # i've put 10ms since this seems like it should work smoothly, + # but ofc you should play with this value later on + for sensor in sensors: + sensor.enable(10) + + print("imported and inited sensors") + return sensors + + + +def getAllMotorsJaco7(robot): + motors = [] + motors.append(robot.getMotor('j2s7s300_joint_1')) + motors.append(robot.getMotor('j2s7s300_joint_2')) + motors.append(robot.getMotor('j2s7s300_joint_3')) + motors.append(robot.getMotor('j2s7s300_joint_4')) + motors.append(robot.getMotor('j2s7s300_joint_5')) + motors.append(robot.getMotor('j2s7s300_joint_6')) + motors.append(robot.getMotor('j2s7s300_joint_7')) + print("imported motors") + return motors + + +def getAndInitAllSensorsJaco7(robot): + sensors = [] + sensors.append(robot.getPositionSensor('j2s7s300_joint_1_sensor')) + sensors.append(robot.getPositionSensor('j2s7s300_joint_2_sensor')) + sensors.append(robot.getPositionSensor('j2s7s300_joint_3_sensor')) + sensors.append(robot.getPositionSensor('j2s7s300_joint_4_sensor')) + sensors.append(robot.getPositionSensor('j2s7s300_joint_5_sensor')) + sensors.append(robot.getPositionSensor('j2s7s300_joint_6_sensor')) + sensors.append(robot.getPositionSensor('j2s7s300_joint_7_sensor')) + + # now we must specify how often do sensors get read in miliseconds + # i've put 10ms since this seems like it should work smoothly, + # but ofc you should play with this value later on + for sensor in sensors: + sensor.enable(10) + + print("imported and inited sensors") + return sensors + -- GitLab