diff --git a/python/.README.md.swp b/python/.README.md.swp deleted file mode 100644 index a6c83f7581c07662841f1005cf20654e377c8946..0000000000000000000000000000000000000000 Binary files a/python/.README.md.swp and /dev/null differ diff --git a/python/examples/.drawing_from_input_drawing.py.swp b/python/examples/.drawing_from_input_drawing.py.swp index 777277a1dd36d736b472c96862d313480e100de7..cb6dfeb6f1e8d357a11c2c00ba7b286da2049046 100644 Binary files a/python/examples/.drawing_from_input_drawing.py.swp and b/python/examples/.drawing_from_input_drawing.py.swp differ diff --git a/python/examples/drawing_from_input_drawing.py b/python/examples/drawing_from_input_drawing.py index f1b661034f4fc7cf04a45b13386bcc2e09905469..14e04c89b2a5debe9193adf70e6bb001950289de 100644 --- a/python/examples/drawing_from_input_drawing.py +++ b/python/examples/drawing_from_input_drawing.py @@ -31,7 +31,7 @@ import matplotlib.pyplot as plt import copy from ur_simple_control.util.get_model import get_model from ur_simple_control.visualize.visualize import plotFromDict -from ur_simple_control.clik.clik_point_to_point import getController +from ur_simple_control.clik.clik_point_to_point import getClikController ####################################################################### # arguments # @@ -56,8 +56,10 @@ def get_args(): parser.add_argument('--gripper', action=argparse.BooleanOptionalAction, \ help="whether you're using the gripper", default=True) # not applicable here, but leaving it in the case it becomes applicable -# parser.add_argument('--goal-error', type=float, \ -# help="the final position error you are happy with", default=1e-3) +# it's also in the robot manager even though it shouldn't be + parser.add_argument('--goal-error', type=float, \ + help="the final position error you are happy with. NOTE: not used here", \ + default=1e-3) parser.add_argument('--max-iterations', type=int, \ help="maximum allowable iteration number (it runs at 500Hz)", default=100000) parser.add_argument('--acceleration', type=float, \ @@ -122,28 +124,26 @@ def get_args(): # control loop # ####################################################################### -def controlLoopWriting(robot, controller, i): +# feedforward velocity, feedback position and force for impedance +def controller(): + pass + +def controlLoopWriting(robot, dmp, controller, i): # dmp - dmp.step(dt) + dmp.step(robot.dt) # temporal coupling - tau = dmp.tau + tc.update(dmp, dt) * dt + tau = dmp.tau + tc.update(dmp, robot.dt) * robot.dt dmp.set_tau(tau) - q = rtde_receive.getActualQ() - # TODO and NOTE the weight, TCP and inertial matrix needs to be set on the robot - # you already found an API in rtde_control for this, just put it in initialization - # under using/not-using gripper parameters + q = robot.getQ() # TODO: document the mathematics with one-line comments - wrench = np.array(rtde_receive.getActualTCPForce()) + wrench = robot.getWrench() # rolling average # TODO: try doing a low-pass filter instead + # TODO handle past data in some nice way from here or elsewhere? wrench_avg[i % 5] = wrench wrench = np.average(wrench_avg, axis=0) # pinocchio model is 8-dof because it treats the gripper # as two prismatic joints - q6 = np.array(copy.deepcopy(q)) - q.append(0.0) - q.append(0.0) - q = np.array(q) pin.forwardKinematics(model, data, q) # calculate joint torques based on the force-torque sensor # TODO look into UR code/api for estimating the same @@ -179,7 +179,7 @@ def controlLoopWriting(robot, controller, i): if __name__ == "__main__": args = parser.get_args() - controller = getController(args) + clik_controller = getController(args) robot = RobotManager(args) plot_dict = { 'qs' : np.zeros((args.max_iterations, 6)), @@ -202,8 +202,17 @@ if __name__ == "__main__": if not args.temporal_coupling: tc = NoTC() else: - # TODO learn the math already # TODO test whether this works (it should, but test it) - v_max = np.ones(6) * 0.5 * speed_slider - a_max = np.ones(6) * 1.7 + # test the interplay between this and the speed slider + v_max_ndarray = np.ones(6) * robot.max_qd + # test the interplay between this and the speed slider + # args.acceleration is the actual maximum you're using + a_max_ndarray = np.ones(6) * args.acceleration tc = TCVelAccConstrained(gamma_nominal, gamma_a, v_max, a_max, eps_tc) + + # TODO and NOTE the weight, TCP and inertial matrix needs to be set on the robot + # you already found an API in rtde_control for this, just put it in initialization + # under using/not-using gripper parameters + # ALSO NOTE: to use this you need to change the version inclusions in + # ur_rtde due to a bug there in the current ur_rtde + robot firmware version + # (the bug is it works with the firmware verion, but ur_rtde thinks it doesn't) diff --git a/python/ur_simple_control/.managers.py.swp b/python/ur_simple_control/.managers.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..a43d7d58d3891f18e11ae16742e2ed16edc12893 Binary files /dev/null and b/python/ur_simple_control/.managers.py.swp differ diff --git a/python/ur_simple_control/clik/.clik_point_to_point.py.swp b/python/ur_simple_control/clik/.clik_point_to_point.py.swp index f448d0aca693a2d3a204c1725ca5bc5a7dd673cc..57c33bd5da6b047cba444428a0e241de82cfc793 100644 Binary files a/python/ur_simple_control/clik/.clik_point_to_point.py.swp and b/python/ur_simple_control/clik/.clik_point_to_point.py.swp differ diff --git a/python/ur_simple_control/clik/clik_point_to_point.py b/python/ur_simple_control/clik/clik_point_to_point.py index a4600275c410d48344cbac8580a9f6c49ac35a2e..54614006e1a9cffd01466c1c0095577a98815eae 100644 --- a/python/ur_simple_control/clik/clik_point_to_point.py +++ b/python/ur_simple_control/clik/clik_point_to_point.py @@ -3,7 +3,7 @@ import numpy as np import copy import argparse from functools import partial -from ur_simple_control.util.boilerplate_wrapper import ControlLoopManager, RobotManager +from ur_simple_control.managers import ControlLoopManager, RobotManager """ Every imaginable magic number, flag and setting is put in here. @@ -44,7 +44,7 @@ def get_args(): parser.add_argument('--tikhonov-damp', type=float, \ help="damping scalar in tiknohov regularization", default=1e-2) # TODO add the rest - parser.add_argument('--controller', type=str, \ + parser.add_argument('--clik-controller', type=str, \ help="select which click algorithm you want", \ default='dampedPseudoinverse', choices=['dampedPseudoinverse', 'jacobianTranspose']) # maybe you want to scale the control signal @@ -76,10 +76,10 @@ if they have extra stuff, just map it in the beginning with partial NOTE: this could be changed to something else if it proves inappropriate later TODO: write out other algorithms """ -def getController(args): - if args.controller == "dampedPseudoinverse": +def getClikController(args): + if args.clik_controller == "dampedPseudoinverse": return partial(dampedPseudoinverse, args.tikhonov_damp) - if args.controller == "jacobianTranspose": + if args.clik_controller == "jacobianTranspose": return jacobianTranspose # TODO implement and add in the rest #if controller_name == "invKinmQPSingAvoidE_kI": @@ -102,7 +102,7 @@ def getController(args): # modularity yo -def controlLoopClik(robot, controller, i): +def controlLoopClik(robot, clik_controller, i): breakFlag = False # know where you are, i.e. do forward kinematics q = robot.getQ() @@ -117,7 +117,7 @@ def controlLoopClik(robot, controller, i): J = pin.computeJointJacobian(robot.model, robot.data, q, robot.JOINT_ID) # compute the joint velocities. # just plug and play different ones - qd = controller(J, err_vector) + qd = clik_controller(J, err_vector) robot.sendQd(qd) # we do the printing here because controlLoopManager should be general. # and these prints are click specific (although i'm not 100% happy with the arrangement) @@ -134,7 +134,7 @@ if __name__ == "__main__": args = get_args() robot = RobotManager(args) Mgoal = robot.defineGoalPoint() - controller = getController(args) - controlLoop = partial(controlLoopClik, robot, controller) + clik_controller = getClikController(args) + controlLoop = partial(controlLoopClik, robot, clik_controller) loop_manager = ControlLoopManager(robot, controlLoop, args) loop_manager.run() diff --git a/python/ur_simple_control/boilerplate_wrapper.py b/python/ur_simple_control/managers.py similarity index 86% rename from python/ur_simple_control/boilerplate_wrapper.py rename to python/ur_simple_control/managers.py index 87dce863f6e4e27557d3ba32d721ddeaac19f45f..e9b7f5882d22cb4d1433f049926ff7f411436e1b 100644 --- a/python/ur_simple_control/boilerplate_wrapper.py +++ b/python/ur_simple_control/managers.py @@ -15,6 +15,38 @@ from ur_simple_control.util.get_model import get_model from ur_simple_control.util.robotiq_gripper import RobotiqGripper import argparse +""" +general notes +--------------- +The first design principle of this library is to minimize the time needed +to go from a control algorithm on paper to the same control algorithm +running on the real robot. The second design principle is to have +the code as simple as possible. In particular, this pertains to avoiding +overly complex abstractions and having the code as concise as possible. +The user is expected to read and understand the entire codebase because +changes will have to accomodate it to their specific project. +Target users are control engineers. +The final design choices are made to accommodate these sometimes opposing goals +to the best of the author's ability. + +This file contains a robot manager and a control loop manager. +The point of these managers is to handle: + - boiler plate code around the control loop which is always the same + - have all the various parameters neatly organized and in one central location + - hide the annoying if-elses of different APIs required + for the real robot and various simulations with single commands + that just do exactly what you want them to do + +current state +------------- +Everything is UR specific. + +long term vision +------------------- +Cut out the robot-specific parts out of the manager classes, +and create child classes for particular robots. +""" + """ ControlLoopManager ------------------- @@ -154,7 +186,6 @@ class RobotManager: self.rtde_receive = RTDEReceiveInterface("127.0.0.1") self.rtde_io = RTDEIOInterface("127.0.0.1") - # ur specific magic numbers self.n_joints = 6 # last joint because pinocchio adds base frame as 0th joint. @@ -167,7 +198,7 @@ class RobotManager: # and i'm not clipping it, you're fixing it assert args.acceleration <= 1.7 and args.acceleration > 0.0 # we're not saying it's qdd because it isn't directly (apparently) - # TODO: check that + # TODO: check that again self.acceleration = args.acceleration if not args.pinocchio_only: self.rtde_io.setSpeedSlider(args.speed_slider) @@ -175,6 +206,7 @@ class RobotManager: # TODO: these are almost certainly higher # maybe you want to have them high and cap the rest with speed slider? # idk really, but it's better to have this low and burried for safety and robot longevity reasons + # TODO: self.max_qdd = 1.7 # NOTE: i had this thing at 2 in other code self.max_qd = 0.5 @@ -212,6 +244,22 @@ class RobotManager: return self.q return q + """ + getWrench + ----- + different things need to be send depending on whether you're running a simulation, + you're on a real robot, you're running some new simulator bla bla. this is handled + here because this things depend on the arguments which are manager here (hence the + class name RobotManager) + """ + def getWrench(self): + if not self.pinocchio_only: + wrench = np.array(self.rtde_receive.getActualTCPForce()) + else: + raise NotImplementedError("Don't have time to implement this right now.") + + return wrench + """ sendQd ----- @@ -235,6 +283,7 @@ class RobotManager: self.q = pin.integrate(self.model, self.q, qd * self.dt) + """ defineGoalPoint ------------------