diff --git a/docs/simulation.md b/docs/simulation.md index 4240b99b01a77caba043e44e9d38fc3c030278b8..321d6e7a4c652c38da46704cea1a494bf4ba37a1 100644 --- a/docs/simulation.md +++ b/docs/simulation.md @@ -1,19 +1,4 @@ ## numerical simulation ----------------------- -- use the --pinocchio-only argument to numerically integrate the dynamics - without sending any commands -- if you want to simulate external disturbances, put them into the kinematics/dynamics equations (create a new control loop with this) - - - -## using ur_sim (BROKEN ATM) ------------------------------ -1. install docker (sudo apt-get install docker) -2. have a look at docker documentation to have an idea of what it is and for any additional information - as it's very well made -3. navigate to the the simulation folder of this repo -4. run "docker build -t myursim -f Dockerfile ." to crete the image -5. to run the image, run "docker run --rm -it --net=host myursim" . the --rm flag removes the previous container, -it attaches the terminal to the containter, and --net=host makes the image share the localhost connection with your OS. -6. go to http://127.0.1.1:6080/vnc.html?host=127.0.1.1&port=6080, go to the installation tab and change the settings (IP addresses) of the external_control urcap in the URCaps tab. change the Host IP to 127.0.0.1 -7. go to the program tab, and put external control as a part of the program (the only part after main) -8. now run your program as if it was running on real hardware use the --simulation flag, but NOTE: that there is no gripper in the simulation +- use the --no-real (it's there by default) argument to numerically integrate the kinematics (or dynamics in the future) +- if you want to simulate external disturbances, or simulate sensors like wrenches, you will need to copy-paste and then modify the source code for the simulated robot you are working on. Alternatively simply manually add the disturbances into robot's PRIVATE variables in your control loop diff --git a/examples/data/from_writing_to_handover.pickle_save b/examples/data/from_writing_to_handover.pickle_save deleted file mode 100644 index 7baa1bd1a4c14461433e70e54ce43c97165a6877..0000000000000000000000000000000000000000 Binary files a/examples/data/from_writing_to_handover.pickle_save and /dev/null differ diff --git a/examples/data/fts.png b/examples/data/fts.png deleted file mode 100644 index 1264575745d49472df2edfd19549127f77304aa1..0000000000000000000000000000000000000000 Binary files a/examples/data/fts.png and /dev/null differ diff --git a/examples/data/latest_run b/examples/data/latest_run deleted file mode 100644 index af940e618540c5817f2e410a3e97e7af366131c1..0000000000000000000000000000000000000000 Binary files a/examples/data/latest_run and /dev/null differ diff --git a/examples/data/latest_run_0 b/examples/data/latest_run_0 deleted file mode 100644 index 161ff8a1bba90f9074b9539cdc38a3aa1bdd49e3..0000000000000000000000000000000000000000 Binary files a/examples/data/latest_run_0 and /dev/null differ diff --git a/examples/drawing/drawing_from_input_drawing.py b/examples/drawing/drawing_from_input_drawing.py index a6cd2d9e666ba1ce4e454e59e0e16275e48dd06e..413c641a074e0faf1b2688cc59b891ebc15b748b 100644 --- a/examples/drawing/drawing_from_input_drawing.py +++ b/examples/drawing/drawing_from_input_drawing.py @@ -1,3 +1,4 @@ +# TODO: write error handlers around file loading from utils import getArgsForDrawing from get_marker import getMarker from find_marker_offset import findMarkerOffset diff --git a/examples/graz/clik_m.py b/examples/graz/clik_m.py deleted file mode 100644 index fec7a24e168a9223899e3fc2e21a1b8853a20413..0000000000000000000000000000000000000000 --- a/examples/graz/clik_m.py +++ /dev/null @@ -1,596 +0,0 @@ -import pinocchio as pin -import numpy as np -import copy -import argparse -from functools import partial -from smc.managers import ControlLoopManager, RobotManager -import time -from qpsolvers import solve_qp -import argparse - -# CHECK THESE OTHER -from spatialmath.base import rotz, skew, r2q, tr2angvec - -def getClikArgs(parser): - """ - getClikArgs - ------------ - Every clik-related magic number, flag and setting is put in here. - This way the rest of the code is clean. - Force filtering is considered a core part of these control loops - because it's not necessarily the same as elsewhere. - If you want to know what these various arguments do, just grep - though the code to find them (but replace '-' with '_' in multi-word arguments). - All the sane defaults are here, and you can change any number of them as an argument - when running your program. If you want to change a drastic amount of them, and - not have to run a super long commands, just copy-paste this function and put your - own parameters as default ones. - """ - #parser = argparse.ArgumentParser(description='Run closed loop inverse kinematics \ - # of various kinds. Make sure you know what the goal is before you run!', - # formatter_class=argparse.ArgumentDefaultsHelpFormatter) - - #################### - # clik arguments # - #################### - parser.add_argument('--goal-error', type=float, \ - help="the final position error you are happy with", default=1e-2) - parser.add_argument('--tikhonov-damp', type=float, \ - help="damping scalar in tikhonov regularization", default=1e-3) - # TODO add the rest - parser.add_argument('--clik-controller', type=str, \ - help="select which click algorithm you want", \ - default='dampedPseudoinverse', choices=['dampedPseudoinverse', 'jacobianTranspose', 'invKinmQP']) - - ########################################### - # force sensing and feedback parameters # - ########################################### - parser.add_argument('--alpha', type=float, \ - help="force feedback proportional coefficient", \ - default=0.01) - parser.add_argument('--beta', type=float, \ - help="low-pass filter beta parameter", \ - default=0.01) - - ############################### - # path following parameters # - ############################### - parser.add_argument('--max-init-clik-iterations', type=int, \ - help="number of max clik iterations to get to the first point", default=10000) - parser.add_argument('--max-running-clik-iterations', type=int, \ - help="number of max clik iterations between path points", default=1000) - - return parser - -####################################################################### -# controllers # -####################################################################### -""" -controllers general ------------------------ -really trully just the equation you are running. -ideally, everything else is a placeholder, -meaning you can swap these at will. -if a controller has some additional arguments, -those are put in via functools.partial in getClikController, -so that you don't have to worry about how your controller is handled in -the actual control loop after you've put it in getClikController, -which constructs the controller from an argument to the file. -""" - -def dampedPseudoinverse(tikhonov_damp, J, err_vector): - qd = J.T @ np.linalg.inv(J @ J.T + np.eye(J.shape[0], J.shape[0]) * tikhonov_damp) @ err_vector - return qd - -def jacobianTranspose(J, err_vector): - qd = J.T @ err_vector - return qd - -def invKinmQP(J, err_vector): - # maybe a lower precision dtype is equally good, but faster? - P = np.eye(J.shape[1], dtype="double") - # TODO: why is q all 0? - q = np.array([0] * J.shape[1], dtype="double") - G = None - # TODO: extend for orientation as well - b = err_vector#[:3] - A = J#[:3] - # TODO: you probably want limits here - lb = None - ub = None - h = None - #qd = solve_qp(P, q, G, h, A, b, lb, ub, solver="ecos") - qd = solve_qp(P, q, G, h, A, b, lb, ub, solver="quadprog") - return qd - -def getClikController(args): - """ - getClikController - ----------------- - A string argument is used to select one of these. - It's a bit ugly, bit totally functional and OK solution. - we want all of theme to accept the same arguments, i.e. the jacobian and the error vector. - if they have extra stuff, just map it in the beginning with partial - NOTE: this could be changed to something else if it proves inappropriate later - TODO: write out other algorithms - """ - if args.clik_controller == "dampedPseudoinverse": - return partial(dampedPseudoinverse, args.tikhonov_damp) - if args.clik_controller == "jacobianTranspose": - return jacobianTranspose - # TODO implement and add in the rest - #if controller_name == "invKinmQPSingAvoidE_kI": - # return invKinmQPSingAvoidE_kI - #if controller_name == "invKinm_PseudoInv": - # return invKinm_PseudoInv - #if controller_name == "invKinm_PseudoInv_half": - # return invKinm_PseudoInv_half - if args.clik_controller == "invKinmQP": - return invKinmQP - #if controller_name == "invKinmQPSingAvoidE_kI": - # return invKinmQPSingAvoidE_kI - #if controller_name == "invKinmQPSingAvoidE_kM": - # return invKinmQPSingAvoidE_kM - #if controller_name == "invKinmQPSingAvoidManipMax": - # return invKinmQPSingAvoidManipMax - - # default - return partial(dampedPseudoinverse, args.tikhonov_damp) - - -def controlLoopClik(robot : RobotManager, clik_controller, i, past_data): - """ - controlLoopClik - --------------- - generic control loop for clik (handling error to final point etc). - in some version of the universe this could be extended to a generic - point-to-point motion control loop. - """ - breakFlag = False - log_item = {} - q = robot.getQ() - T_w_e = robot.getT_w_e() - # first check whether we're at the goal - SEerror = T_w_e.actInv(robot.Mgoal) - err_vector = pin.log6(SEerror).vector - if np.linalg.norm(err_vector) < robot.args.goal_error: -# print("Convergence achieved, reached destionation!") - breakFlag = True - #J = pin.computeJointJacobian(robot.model, robot.data, q, robot.JOINT_ID) - J = pin.computeFrameJacobian(robot.model, robot.data, q, robot.ee_frame_id) - # compute the joint velocities based on controller you passed - qd = clik_controller(J, err_vector) - robot.sendQd(qd) - - log_item['qs'] = q.reshape((robot.model.nq,)) - log_item['dqs'] = robot.getQd().reshape((robot.model.nq,)) - # we're not saving here, but need to respect the API, - # hence the empty dict - return breakFlag, {}, log_item - - -def controlLoop_WholeBodyClik(robot : RobotManager, clik_controller, i, past_data): - """ - controlLoop_WholeBodyClik - --------------- - generic control loop for clik for mobile robot - """ - th = 10^-3 - if e_norm > th: # trajectory_planner.has_next_position() and elapsed_time > max_time_limit - rospy.loginfo_once(f"Target {np.array2string(trajectory_planner.pf, precision=3)} raggiunto") - break - # breakFlag = False # CHECK the exit conditions - - log_item = {} - q = robot.getQ() # it should contain both manipulator and base variables - qb = q[:2] - qm = q[2:] - T_w_e = robot.getT_w_e().homogeneous() - - # TODO: Handle the Desired trajectory - ''' - pos_d = trajectory_planner.next_position_sample() - linear_vel_d = trajectory_planner.next_linear_velocity_sample() - - orient_d = trajectory_planner.next_orient_sample() - angular_vel_d = trajectory_planner.next_angular_velocity_sample() - ''' - - # position error - ep = pos_d - T_w_e[:3,3] - # orientation error - e_angle, e_axis = tr2angvec(orient_d @ T_w_e[:3, :3].T) - eo = np.sin(e_angle) * e_axis - - # error norm - e_norm = np.linalg.norm(np.concatenate((ep,eo))) - - L = L_matrix(orient_d, current_orientation) - - cmd_EE_twist = np.r_[ - linear_vel_d + Kp @ ep, - inv(L) @ (L.T @ angular_vel_d + Ko @ eo) - ] - - J = robot.whole_body_jacobian(---) # INSERT input parameters - - ## Weight the sub-Jacobians - dof_base = 2 - dof_manipulator = 6 - - W_base = np.eye(dof_base) - W_arm = np.eye(dof_manipulator) - - W = np.block([ - [W_base , np.zeros((dof_base, dof_manipulator))], - [np.zeros((dof_manipulator, dof_base)), W_arm ] - ]) - - W_inv = np.linalg.inv(W) - J_T = J.T - - J_pinv_weighted = W_inv @ J_T @ inv(J @ W_inv @ J_T) - - qd = clik_controller(J_pinv_weighted, err_vector) - robot.sendQd(qd) - - log_item['qs'] = q.reshape((robot.model.nq,)) - log_item['dqs'] = robot.getQd().reshape((robot.model.nq,)) - # we're not saving here, but need to respect the API, - # hence the empty dict - return e_norm, {}, log_item - - -def moveUntilContactControlLoop(args, robot : RobotManager, speed, clik_controller, i, past_data): - """ - moveUntilContactControlLoop - --------------- - generic control loop for clik (handling error to final point etc). - in some version of the universe this could be extended to a generic - point-to-point motion control loop. - """ - breakFlag = False - # know where you are, i.e. do forward kinematics - log_item = {} - q = robot.getQ() - # break if wrench is nonzero basically - #wrench = robot.getWrench() - # you're already giving the speed in the EE i.e. body frame - # so it only makes sense to have the wrench in the same frame - #wrench = robot._getWrenchInEE() - wrench = robot.getWrench() - # and furthermore it's a reasonable assumption that you'll hit the thing - # in the direction you're going in. - # thus we only care about wrenches in those direction coordinates - mask = speed != 0.0 - # NOTE: contact getting force is a magic number - # it is a 100% empirical, with the goal being that it's just above noise. - # so far it's worked fine, and it's pretty soft too. - if np.linalg.norm(wrench[mask]) > args.contact_detecting_force: - print("hit with", np.linalg.norm(wrench[mask])) - breakFlag = True - if (args.pinocchio_only) and (i > 500): - print("let's say you hit something lule") - breakFlag = True - # pin.computeJointJacobian is much different than the C++ version lel - J = pin.computeJointJacobian(robot.model, robot.data, q, robot.JOINT_ID) - # compute the joint velocities. - qd = clik_controller(J, speed) - robot.sendQd(qd) - log_item['wrench'] = wrench.reshape((6,)) - return breakFlag, {}, log_item - -def moveUntilContact(args, robot, speed): - """ - moveUntilContact - ----- - does clik until it feels something with the f/t sensor - """ - assert type(speed) == np.ndarray - clik_controller = getClikController(args) - controlLoop = partial(moveUntilContactControlLoop, args, robot, speed, clik_controller) - # we're not using any past data or logging, hence the empty arguments - log_item = {'wrench' : np.zeros(6)} - loop_manager = ControlLoopManager(robot, controlLoop, args, {}, log_item) - loop_manager.run() - # TODO: remove, this isn't doing anything - time.sleep(0.01) - print("Collision detected!!") - -def moveL(args, robot : RobotManager, goal_point): - """ - moveL - ----- - does moveL. - send a SE3 object as goal point. - if you don't care about rotation, make it np.zeros((3,3)) - """ - #assert type(goal_point) == pin.pinocchio_pywrap.SE3 - robot.Mgoal = copy.deepcopy(goal_point) - clik_controller = getClikController(args) - controlLoop = partial(controlLoopClik, robot, clik_controller) - # we're not using any past data or logging, hence the empty arguments - log_item = { - 'qs' : np.zeros(robot.model.nq), - 'dqs' : np.zeros(robot.model.nq), - } - loop_manager = ControlLoopManager(robot, controlLoop, args, {}, log_item) - loop_manager.run() - -# TODO: implement -def moveLFollowingLine(args, robot, goal_point): - """ - moveLFollowingLine - ------------------ - make a path from current to goal position, i.e. - just a straight line between them. - the question is what TODO with orientations. - i suppose it makes sense to have one function that enforces/assumes - that the start and end positions have the same orientation. - then another version goes in a line and linearly updates the orientation - as it goes - """ - pass - -# TODO: implement -def cartesianPathFollowing(args, robot, path): - pass - -def controlLoopCompliantClik(args, robot : RobotManager, i, past_data): - """ - controlLoopClik - --------------- - generic control loop for clik (handling error to final point etc). - in some version of the universe this could be extended to a generic - point-to-point motion control loop. - """ - breakFlag = False - log_item = {} - save_past_dict = {} - # know where you are, i.e. do forward kinematics - q = robot.getQ() - T_w_e = robot.getT_w_e() - wrench = robot.getWrench() - # we need to overcome noise if we want to converge - if np.linalg.norm(wrench) < args.minimum_detectable_force_norm: - wrench = np.zeros(6) - save_past_dict['wrench'] = copy.deepcopy(wrench) - wrench = args.beta * wrench + (1 - args.beta) * past_data['wrench'][-1] - #mapping = np.zeros((6,6)) - #mapping[0:3, 0:3] = T_w_e.rotation - #mapping[3:6, 3:6] = T_w_e.rotation - #wrench = mapping.T @ wrench - #Z = np.diag(np.array([1.0, 1.0, 2.0, 1.0, 1.0, 1.0])) - Z = np.diag(np.array([1.0, 1.0, 1.0, 1.0, 1.0, 1.0])) - wrench = Z @ wrench - #pin.forwardKinematics(robot.model, robot.data, q) - # first check whether we're at the goal - SEerror = T_w_e.actInv(robot.Mgoal) - err_vector = pin.log6(SEerror).vector - if np.linalg.norm(err_vector) < robot.args.goal_error: -# print("Convergence achieved, reached destionation!") - breakFlag = True - # pin.computeJointJacobian is much different than the C++ version lel - J = pin.computeJointJacobian(robot.model, robot.data, q, robot.JOINT_ID) - # compute the joint velocities. - # just plug and play different ones - qd = J.T @ np.linalg.inv(J @ J.T + np.eye(J.shape[0], J.shape[0]) * args.tikhonov_damp) @ err_vector - tau = J.T @ wrench - #tau = tau[:6].reshape((6,1)) - qd += args.alpha * tau - robot.sendQd(qd) - - log_item['qs'] = q.reshape((robot.model.nq,)) - # get actual velocity, not the commanded one. - # although that would be fun to compare i guess - # TODO: have both, but call the compute control signal like it should be - log_item['dqs'] = robot.getQd().reshape((robot.model.nq,)) - log_item['wrench'] = wrench.reshape((6,)) - log_item['tau'] = tau.reshape((robot.model.nq,)) - # we're not saving here, but need to respect the API, - # hence the empty dict - return breakFlag, save_past_dict, log_item - -# add a threshold for the wrench -def compliantMoveL(args, robot, goal_point): - """ - compliantMoveL - ----- - does compliantMoveL - a moveL, but with compliance achieved - through f/t feedback - send a SE3 object as goal point. - if you don't care about rotation, make it np.zeros((3,3)) - """ -# assert type(goal_point) == pin.pinocchio_pywrap.SE3 - robot.Mgoal = copy.deepcopy(goal_point) - clik_controller = getClikController(args) - controlLoop = partial(controlLoopCompliantClik, args, robot) - # we're not using any past data or logging, hence the empty arguments - log_item = { - 'qs' : np.zeros(robot.model.nq), - 'wrench' : np.zeros(6), - 'tau' : np.zeros(robot.model.nq), - 'dqs' : np.zeros(robot.model.nq), - } - save_past_dict = { - 'wrench': np.zeros(6), - } - loop_manager = ControlLoopManager(robot, controlLoop, args, save_past_dict, log_item) - loop_manager.run() - - -def clikCartesianPathIntoJointPath(args, robot, path, \ - clikController, q_init, plane_pose): - """ - clikCartesianPathIntoJointPath - ------------------------------ - functionality - ------------ - Follows a provided Cartesian path, - creates a joint space trajectory for it. - Output format and timing works only for what the dmp code expects - because it's only used there, - and I never gave that code a lift-up it needs. - - return - ------ - - joint_space_trajectory to follow the given path. - - arguments - ---------- - - path: cartesian path given in task frame - """ - # we don't know how many there will be, so a linked list is - # clearly the best data structure here (instert is o(1) still, - # and we aren't pressed on time when turning it into an array later) - qs = [] - # let's use the functions we already have. TODO so - # we need to create a new RobotManager with arguments for simulation, - # otherwise weird things will happen. - # we keep all the other args intact - sim_args = copy.deepcopy(args) - sim_args.pinocchio_only = True - sim_args.fast_simulation = True - sim_args.real_time_plotting = False - sim_args.visualize_manipulator = False - sim_args.save_log = False # we're not using sim robot outside of this - sim_args.max_iterations = 10000 # more than enough - sim_robot = RobotManager(sim_args) - sim_robot.q = q_init.copy() - sim_robot._step() - for pose in path: - moveL(sim_args, sim_robot, pose) - # loop logs is a dict, dict keys list preserves input order - new_q = sim_robot.q.copy() - robot.updateViz(sim_robot.q, sim_robot.T_w_e) - time.sleep(0.05) - qs.append(new_q[:6]) - # plot this on the real robot - - ############################################## - # save the obtained joint-space trajectory # - ############################################## - qs = np.array(qs) - # we're putting a dmp over this so we already have the timing ready - # TODO: make this general, you don't want to depend on other random - # arguments (make this one traj_time, then put tau0 = traj_time there - t = np.linspace(0, args.tau0, len(qs)).reshape((len(qs),1)) - joint_trajectory = np.hstack((t, qs)) - # TODO handle saving more consistently/intentionally - # (although this definitely works right now and isn't bad, just mid) - # os.makedir -p a data dir and save there, this is ugly - np.savetxt("./joint_trajectory.csv", joint_trajectory, delimiter=',', fmt='%.5f') - return joint_trajectory - - - def whole_body_jacobian(robot : RobotManager, clik_controller, i, past_data): - # Whole-body jacobian calculation - I_p = np.array([1,0,0], dtype=np.float64).reshape(-1,1) - I_o = np.array([0,0,1], dtype=np.float64).reshape(-1,1) - O_3x1 = np.zeros((3, 1), dtype=np.float64) - - q = robot.getQ() # it should contain both base and manipulator/dual arm joint variables - qb = q[:2] - qm = q[2:] - Rb = rotz(qb[2]) - - if robot_name == ur - T_w_b = robot.data.oMi[1].homogeneous() # mobile base frame wrt world frame - T_w_e = robot.getT_w_e().homogeneous() # ee frame wrt world frame - - T_b_e = np.linalg.inv(T_w_b) @ T_w_e - - pos_EE_b = T_b_e[:3, 3] - - J_m = pin.computeFrameJacobian(robot.model, robot.data, qm, robot.ee_frame_id) - - JP_m = J_m[:3] - JO_m = J_m[3:] - - S = (-skew(Rb@pos_EE_b)[:, 2]).reshape(-1,1) # select 3rd column - - J = np.block([ - [I_p , S , Rb@JP_m], - [O_3x1, I_o , JO_m] - ]) - else - """ - FIX - """ - T_b_a = robot.getT_w_a().homogeneous() # transformation matrix between absolute frame and mobile base frame - pos_a_b = T_b_a[:3, 3] # absolute frame origin - - J_m1 = pin.computeFrameJacobian(robot.model, robot.data, qm, robot.ee_frame_id) # Manipulator 1 Jacobian matrix - J_m2 = pin.computeFrameJacobian(robot.model, robot.data, qm, robot.ee_frame_id) # Manipulator 2 Jacobian matrix - - JP_m1 = J_m1[:3] - JO_m1 = J_m1[3:] - JP_m2 = J_m2[:3] - JO_m2 = J_m2[3:] - - S = (-skew(Rb@pos_a_b)[:, 2]).reshape(-1,1) # select 3rd column - - J_b = np.block([ - [I_p , S ], - [O_3x1, I_o] - ]) - - T = 0.5 * np.block([ - [np.identity(6), np.identity(6)] - ]) - - O_3x6 = np.zeros((3, 6), dtype=np.float64) - - J_m = T @ np.block([ - [Rb@JP_m1, O_3x6], - [JO_m1, O_3x6], - [O_3x6, Rb@ JP_m2], - [O_3x6, JO_m2] - ]) - - J = np.block([[J_b,J_m]]) - - return J - - # L-matrix calculation for angle-axis error - def L_matrix(orient_d, current_orientation): - '''Calcola la matrice L, usata nel calcolo dell'errore di orientamento''' - nd = orient_d[:, 0] # Normal vector (x-axis) - ad = orient_d[:, 1] # Approach vector (y-axis) - sd = orient_d[:, 2] # Sliding vector (z-axis) - - ne = current_orientation[:, 0] # Normal vector (x-axis) - ae = current_orientation[:, 1] # Approach vector (y-axis) - se = current_orientation[:, 2] # Sliding vector (z-axis) - - L = -0.5 * (skew(nd) @ skew(ne) + skew(sd) @ skew(se) + skew(ad) @ skew(ae)) - return L - - def traj(v,type): - ''' - Calculates the time law coeffs - The v-vector should contain: - - "cubic": pi, vi, pf, vf, tf; - - "quintic": pi, vi, ai, pf, vf, af, tf; - ''' - if type == "cubic" - t_end = v(end) - np.array([[np.zeros((1,3)), 1], - [np.zeros((1,2)), 1, 0], - t_end**np.array([3:-1:0]), - np.array([3:-1:0])* t_end ** np.array([2:-1:-1]) - ]) - # TO BE CONTINUED - - return L - - -if __name__ == "__main__": - args = get_args() - robot = RobotManager(args) - Mgoal = robot.defineGoalPointCLI() - clik_controller = getClikController(args) - controlLoop = partial(controlLoopClik, robot, clik_controller) - # we're not using any past data or logging, hence the empty arguments - loop_manager = ControlLoopManager(robot, controlLoop, args, {}, {}) - loop_manager.run() diff --git a/examples/impedance/point_impedance_control.py b/examples/impedance/point_impedance_control.py index 7d15a642bec474b559283e0df0ef5ce18e5215b9..9ba534dddb43ca3cefad1957974dc46ae0339f13 100644 --- a/examples/impedance/point_impedance_control.py +++ b/examples/impedance/point_impedance_control.py @@ -31,14 +31,8 @@ def getArgs(): return args -# feedforward velocity, feedback position and force for impedance -def controller(): - pass - - # control loop to be passed to ControlLoopManager def JointSpacePointImpedanceControlLoop( - controller: Any, q_init: np.ndarray, args: argparse.Namespace, robot: ForceTorqueOnSingleArmWrist, @@ -51,7 +45,7 @@ def JointSpacePointImpedanceControlLoop( log_item = {} q = robot.q wrench = robot.wrench - log_item["wrench_raw"] = wrench.reshape((6,)) + log_item["wrench_raw"] = wrench # deepcopy for good coding practise (and correctness here) save_past_dict["wrench"] = copy.deepcopy(wrench) # rolling average @@ -71,20 +65,18 @@ def JointSpacePointImpedanceControlLoop( J = robot.getJacobian() tau = J.T @ wrench - tau = tau[:6].reshape((6, 1)) # compute control law: # - feedback the position # kv is not needed if we're running velocity control - v_cmd = args.kp * (q_init.reshape((-1, 1)) - q.reshape((-1, 1))) + args.alpha * tau + # pin.difference is here for the mobile base case where nq =/= nv + # output is what takes you from q to q_init (i.e. q_init \circminus q)) + v_cmd = args.kp * pin.difference(robot.model, q, q_init) + args.alpha * tau # immediatelly stop if something weird happened (some non-convergence) - if np.isnan(v_cmd[0]): - breakFlag = True - v_cmd = np.zeros(robot.nv) robot.sendVelocityCommand(v_cmd) log_item["qs"] = robot.q log_item["dqs"] = robot.v - log_item["wrench_used"] = wrench.reshape((6,)) + log_item["wrench_used"] = wrench return breakFlag, save_past_dict, log_item @@ -163,9 +155,7 @@ if __name__ == "__main__": T_w_einit = robot.T_w_e if not args.cartesian_space_impedance: - controlLoop = partial( - JointSpacePointImpedanceControlLoop, controller, q_init, args, robot - ) + controlLoop = partial(JointSpacePointImpedanceControlLoop, q_init, args, robot) else: controlLoop = partial( controlLoopCartesianPointImpedance, ik_solver, T_w_einit, args, robot @@ -176,12 +166,12 @@ if __name__ == "__main__": ) loop_manager.run() - if not args.pinocchio_only: + if args.real: robot.stopRobot() if args.save_log: robot._log_manager.saveLog() robot._log_manager.plotAllControlLoops() - if args.visualize_manipulator: + if args.visualizer: robot.killManipulatorVisualizer() diff --git a/examples/impedance/test_by_running.sh b/examples/impedance/test_by_running.sh new file mode 100755 index 0000000000000000000000000000000000000000..6018bd51846664a3e1e972db2229640ecbc98bd4 --- /dev/null +++ b/examples/impedance/test_by_running.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# joint-space point impedance ur5e +runnable="point_impedance_control.py --robot=ur5e --no-cartesian-space-impedance --ctrl-freq=-1 --no-visualizer --no-plotter --max-iterations=2000" +echo $runnable +python $runnable + +# joint-space point impedance heron +runnable="point_impedance_control.py --robot=heron --no-cartesian-space-impedance --ctrl-freq=-1 --no-visualizer --no-plotter --max-iterations=2000" +echo $runnable +python $runnable + +# cartesian-space point impedance ur5e +runnable="point_impedance_control.py --robot=ur5e --cartesian-space-impedance --ctrl-freq=-1 --no-visualizer --no-plotter --max-iterations=2000" +echo $runnable +python $runnable + +# cartesian-space point impedance heron +runnable="point_impedance_control.py --robot=heron --cartesian-space-impedance --ctrl-freq=-1 --no-visualizer --no-plotter --max-iterations=2000" +echo $runnable +python $runnable diff --git a/examples/path6d_from_path2d.py b/examples/math_understanding/path6d_from_path2d.py similarity index 100% rename from examples/path6d_from_path2d.py rename to examples/math_understanding/path6d_from_path2d.py diff --git a/examples/pinocchio_math_understanding/pin_contact3d.py b/examples/math_understanding/pin_contact3d.py similarity index 100% rename from examples/pinocchio_math_understanding/pin_contact3d.py rename to examples/math_understanding/pin_contact3d.py diff --git a/examples/pinocchio_math_understanding/slerp.py b/examples/math_understanding/slerp.py similarity index 100% rename from examples/pinocchio_math_understanding/slerp.py rename to examples/math_understanding/slerp.py diff --git a/examples/graz/cartesian_pose_command_server.py b/examples/networked_clik_client/cartesian_pose_command_server.py similarity index 96% rename from examples/graz/cartesian_pose_command_server.py rename to examples/networked_clik_client/cartesian_pose_command_server.py index 8349916faccc471345173177826850aee4d174ad..f9a13ca7a1ecb47c1090b844c880e9609aeb4a13 100644 --- a/examples/graz/cartesian_pose_command_server.py +++ b/examples/networked_clik_client/cartesian_pose_command_server.py @@ -19,10 +19,6 @@ def getArgs(): def sendCircleGoalReceiveWrench(freq, i): - save_past_dict = {} - log_item = {} - breakFlag = False - # create command to go in a circle radius = 0.6 t = (i / freq) / 6 diff --git a/examples/graz/clik_client.py b/examples/networked_clik_client/clik_client.py similarity index 100% rename from examples/graz/clik_client.py rename to examples/networked_clik_client/clik_client.py diff --git a/examples/graz/joint_copier_client.py b/examples/networked_clik_client/joint_copier_client.py similarity index 100% rename from examples/graz/joint_copier_client.py rename to examples/networked_clik_client/joint_copier_client.py diff --git a/examples/graz/message_specs_pb2.py b/examples/networked_clik_client/message_specs_pb2.py similarity index 100% rename from examples/graz/message_specs_pb2.py rename to examples/networked_clik_client/message_specs_pb2.py diff --git a/examples/graz/point_impedance_server.py b/examples/networked_clik_client/point_impedance_server.py similarity index 100% rename from examples/graz/point_impedance_server.py rename to examples/networked_clik_client/point_impedance_server.py diff --git a/examples/graz/protobuf_ros1_bridge.py b/examples/networked_clik_client/protobuf_ros1_bridge.py similarity index 100% rename from examples/graz/protobuf_ros1_bridge.py rename to examples/networked_clik_client/protobuf_ros1_bridge.py diff --git a/examples/graz/protobuf_to_ros1.py b/examples/networked_clik_client/protobuf_to_ros1.py similarity index 100% rename from examples/graz/protobuf_to_ros1.py rename to examples/networked_clik_client/protobuf_to_ros1.py diff --git a/examples/graz/ros1_to_protobuf.py b/examples/networked_clik_client/ros1_to_protobuf.py similarity index 100% rename from examples/graz/ros1_to_protobuf.py rename to examples/networked_clik_client/ros1_to_protobuf.py diff --git a/examples/graz/technical_report/technical_report.aux b/examples/networked_clik_client/technical_report/technical_report.aux similarity index 100% rename from examples/graz/technical_report/technical_report.aux rename to examples/networked_clik_client/technical_report/technical_report.aux diff --git a/examples/graz/technical_report/technical_report.log b/examples/networked_clik_client/technical_report/technical_report.log similarity index 100% rename from examples/graz/technical_report/technical_report.log rename to examples/networked_clik_client/technical_report/technical_report.log diff --git a/examples/graz/technical_report/technical_report.pdf b/examples/networked_clik_client/technical_report/technical_report.pdf similarity index 100% rename from examples/graz/technical_report/technical_report.pdf rename to examples/networked_clik_client/technical_report/technical_report.pdf diff --git a/examples/graz/technical_report/technical_report.synctex.gz b/examples/networked_clik_client/technical_report/technical_report.synctex.gz similarity index 100% rename from examples/graz/technical_report/technical_report.synctex.gz rename to examples/networked_clik_client/technical_report/technical_report.synctex.gz diff --git a/examples/graz/technical_report/technical_report.tex b/examples/networked_clik_client/technical_report/technical_report.tex similarity index 100% rename from examples/graz/technical_report/technical_report.tex rename to examples/networked_clik_client/technical_report/technical_report.tex diff --git a/examples/graz/to_install_on_ubuntu18.md b/examples/networked_clik_client/to_install_on_ubuntu18.md similarity index 100% rename from examples/graz/to_install_on_ubuntu18.md rename to examples/networked_clik_client/to_install_on_ubuntu18.md diff --git a/examples/graz/traiettoria.m b/examples/networked_clik_client/traiettoria.m similarity index 100% rename from examples/graz/traiettoria.m rename to examples/networked_clik_client/traiettoria.m diff --git a/examples/planar_pushing_via_visual_servoing/TODO b/examples/planar_pushing_via_visual_servoing/TODO new file mode 100644 index 0000000000000000000000000000000000000000..2ef360ecbbcba1e389a86687f704526a5771846c --- /dev/null +++ b/examples/planar_pushing_via_visual_servoing/TODO @@ -0,0 +1 @@ +refactor and merge student project on this topic diff --git a/examples/pushing_via_friction_cones.py b/examples/planar_pushing_via_visual_servoing/pushing_via_friction_cones.py similarity index 100% rename from examples/pushing_via_friction_cones.py rename to examples/planar_pushing_via_visual_servoing/pushing_via_friction_cones.py diff --git a/examples/vision/test_by_running.sh b/examples/vision/test_by_running.sh new file mode 100755 index 0000000000000000000000000000000000000000..ecbac4db149b5b2105f290f44e8cfe6fd309702f --- /dev/null +++ b/examples/vision/test_by_running.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# NOTE: it works if: +# 1) a window pops up with opencv camera feed +# 2) the plotter produces random result (it's random test data representing camera processing output) +runnable="./vision/camera_no_lag.py --max-iterations=1500 --no-visualize-manipulator" +echo $runnable +python $runnable