diff --git a/python/examples/camera_no_lag.py b/python/examples/camera_no_lag.py index 41c1406e9e45e6353af6b99cbb2714cc04143bed..180c52e8a8b151d4e6b08870ace21b8c42655415 100644 --- a/python/examples/camera_no_lag.py +++ b/python/examples/camera_no_lag.py @@ -1,7 +1,8 @@ import cv2 -#camera = cv2.VideoCapture(0) -camera = cv2.VideoCapture("http://192.168.219.153:8080/video") +camera = cv2.VideoCapture(0) +# if you have ip_webcam set-up you can do this (but you have put in the correct ip) +#camera = cv2.VideoCapture("http://192.168.219.153:8080/video") while True: (grabbed, frame) = camera.read() diff --git a/python/examples/clik.py b/python/examples/clik.py index dd6791f7ec2e601110679a3063a064957da7c576..99867e0bf4a61c04763b211d86f5d006bd960c72 100644 --- a/python/examples/clik.py +++ b/python/examples/clik.py @@ -4,24 +4,7 @@ import argparse from functools import partial from ur_simple_control.managers import getMinimalArgParser, ControlLoopManager, RobotManager from ur_simple_control.clik.clik import getClikArgs, getClikController, controlLoopClik, moveL, compliantMoveL -# TODO write this in managers and automate name generation - -""" -Every imaginable magic number, flag and setting is put in here. -This way the rest of the code is clean. -If you want to know what these various arguments do, just grep -though the code to find them (but replace '-' with '_' in multi-word arguments). -All the sane defaults are here, and you can change any number of them as an argument -when running your program. If you want to change a drastic amount of them, and -not have to run a super long commands, just copy-paste this function and put your -own parameters as default ones. -NOTE1: the args object obtained from args = parser.get_args() -is pushed all around the rest of the code (because it's tidy), so don't change their names. -NOTE2: that you need to copy-paste and add aguments you need -to the script you will be writing. This is kept here -only as a convenient reference point. -""" def get_args(): parser = getMinimalArgParser() parser.description = 'Run closed loop inverse kinematics \ @@ -33,17 +16,12 @@ def get_args(): if __name__ == "__main__": args = get_args() robot = RobotManager(args) - #Mgoal = robot.defineGoalPointCLI() - #compliantMoveL(args, robot, Mgoal) - #robot.closeGripper() - time.sleep(6.0) + Mgoal = robot.defineGoalPointCLI() + compliantMoveL(args, robot, Mgoal) robot.closeGripper() - #robot.openGripper() + robot.openGripper() if not args.pinocchio_only: - print("stopping via freedrive lel") - robot.rtde_control.freedriveMode() - time.sleep(0.5) - robot.rtde_control.endFreedriveMode() + robot.stopRobot() if args.visualize_manipulator: robot.killManipulatorVisualizer() diff --git a/python/examples/comparing_logs_example.py b/python/examples/comparing_logs_example.py new file mode 100644 index 0000000000000000000000000000000000000000..c59097852b909e385717e42dec7a4779de0de1a0 --- /dev/null +++ b/python/examples/comparing_logs_example.py @@ -0,0 +1,27 @@ +from ur_simple_control.visualize.manipulator_comparison_visualizer import getLogComparisonArgs, ManipulatorComparisonManager +import time + +args = getLogComparisonArgs() +cmp_manager = ManipulatorComparisonManager(args) + +print(""" +this code animates 2 manipulator and can run an animated +plot along side it with the same timing. +you're supposed to select what you want to plot yourself. +the comparison manager has no idea how many control loops +you have nor what's logged in them, apart from the fact +that everything has to have qs to visualize the manipulators. +here we're assuming you have 1 control loop per log, +and that the same things are logged. +""") + +key = list(cmp_manager.logm1.loop_logs.keys())[0] +cmp_manager.createRunningPlot(cmp_manager.logm1.loop_logs[key], 0, len(cmp_manager.logm1.loop_logs[key]['qs'])) +cmp_manager.createRunningPlot(cmp_manager.logm2.loop_logs[key], 0, len(cmp_manager.logm2.loop_logs[key]['qs'])) +cmp_manager.visualizeWholeRuns() +cmp_manager.manipulator_visualizer_cmd_queue.put("befree") +print("main done") +time.sleep(0.1) +cmp_manager.manipulator_visualizer_process.terminate() +if args.debug_prints: + print("terminated manipulator_visualizer_process") diff --git a/python/examples/crocoddyl_ocp_clik.py b/python/examples/crocoddyl_ocp_clik.py new file mode 100644 index 0000000000000000000000000000000000000000..b8e3940cbf38901fe5f7baa9633da7f8f4a26da7 --- /dev/null +++ b/python/examples/crocoddyl_ocp_clik.py @@ -0,0 +1,45 @@ +import numpy as np +import time +import argparse +from functools import partial +from ur_simple_control.managers import getMinimalArgParser, ControlLoopManager, RobotManager +from ur_simple_control.optimal_control.crocoddyl_optimal_control import get_OCP_args, CrocoIKOCP +from ur_simple_control.basics.basics import jointTrajFollowingPD +from ur_simple_control.util.logging_utils import LogManager +import pinocchio as pin + + +def get_args(): + parser = getMinimalArgParser() + parser = get_OCP_args(parser) + args = parser.parse_args() + return args + + +if __name__ == "__main__": + args = get_args() + robot = RobotManager(args) + Mgoal = robot.defineGoalPointCLI() + # create and solve the optimal control problem of + # getting from current to goal end-effector position. + # reference is position and velocity reference (as a dictionary), + # while solver is a crocoddyl object containing a lot more information + reference, solver = CrocoIKOCP(args, robot, Mgoal) + # we need a way to follow the reference trajectory, + # both because there can be disturbances, + # and because it is sampled at a much lower frequency + jointTrajFollowingPD(args, robot, reference) + + print("final position:") + print(robot.getT_w_e()) + + if not args.pinocchio_only: + robot.stopRobot() + + if args.visualize_manipulator: + robot.killManipulatorVisualizer() + + if args.save_log: + robot.log_manager.saveLog() + #loop_manager.stopHandler(None, None) + diff --git a/python/examples/drawing_from_input_drawing.py b/python/examples/drawing_from_input_drawing.py index f602be7223fc3d95390bb3de8d029e3fe73cc0d4..fa8caa18c3b1e0fc08b4d435b656d67e4ad1113c 100644 --- a/python/examples/drawing_from_input_drawing.py +++ b/python/examples/drawing_from_input_drawing.py @@ -1,9 +1,3 @@ -#, this is the main file for the drawing your drawings with a dmp with force feedback -# TODO: -# 2. delete the unnecessary comments -# 8. add some code to pick up the marker from a prespecified location -# 10. write documentation as you go along - import pinocchio as pin import numpy as np import matplotlib.pyplot as plt @@ -65,6 +59,7 @@ def getArgs(): # go and pick up the marker # marker position: +# NOTE: this function does not really work (needs more points) """ R = -0.73032 -0.682357 0.0319574 @@ -283,9 +278,9 @@ def controlLoopWriting(dmp, tc, controller, robot, i, past_data): # log what you said you'd log # TODO fix the q6 situation (hide this) log_item['qs'] = q[:6].reshape((6,)) - log_item['dmp_poss'] = dmp.pos.reshape((6,)) - log_item['dqs'] = dq.reshape((6,)) - log_item['dmp_vels'] = dmp.vel.reshape((6,)) + log_item['dmp_qs'] = dmp.pos.reshape((6,)) + log_item['vs'] = dq.reshape((6,)) + log_item['dmp_vs'] = dmp.vel.reshape((6,)) log_item['wrench'] = wrench.reshape((6,)) log_item['tau'] = tau.reshape((6,)) @@ -332,7 +327,7 @@ if __name__ == "__main__": print(translation_vector) exit() else: - # TODO: save this somewhere obviously + # TODO: save this somewhere obviously (side file) # also make it prettier if possible print("using predefined values") #q_init = np.array([1.4545, -1.7905, -1.1806, -1.0959, 1.6858, -0.1259, 0.0, 0.0]) diff --git a/python/examples/oc_for_ik.py b/python/examples/oc_for_ik.py deleted file mode 100644 index da2a7e98ace1ece389969b960e333372ee10027a..0000000000000000000000000000000000000000 --- a/python/examples/oc_for_ik.py +++ /dev/null @@ -1,39 +0,0 @@ -import numpy as np -import time -import argparse -from functools import partial -from ur_simple_control.managers import getMinimalArgParser, ControlLoopManager, RobotManager -# TODO: implement -from ur_simple_control.optimal_control.optimal_control import IKOCP -# TODO: implement -from ur_simple_control.basics.basics import jointTrajFollowingPID -from ur_simple_control.util.logging_utils import saveLog -import pinocchio as pin - - -def get_args(): - parser = getMinimalArgParser() - -if __name__ == "__main__": - args = get_args() - robot = RobotManager(args) - #Mgoal = robot.defineGoalPointCLI() - Mgoal = pin.SE3.Identity() - Mgoal.translation = np.array([0.3,0.3,0.3]) - traj = IKOCP(robot, Mgoal) - exit() - - #log_dict, final_iteration = compliantMoveL(args, robot, Mgoal) - if not args.pinocchio_only: - print("stopping via freedrive lel") - robot.rtde_control.freedriveMode() - time.sleep(0.5) - robot.rtde_control.endFreedriveMode() - - if args.visualize_manipulator: - robot.killManipulatorVisualizer() - - if args.save_log: - saveLog(log_dict, final_iteration, args) - #loop_manager.stopHandler(None, None) - diff --git a/python/ur_simple_control/__pycache__/managers.cpython-312.pyc b/python/ur_simple_control/__pycache__/managers.cpython-312.pyc index c40c23c490c43a9b9001a93bc44c2fce0a62027b..b59fcce0499fab94884093e38ecf948933a2717a 100644 Binary files a/python/ur_simple_control/__pycache__/managers.cpython-312.pyc and b/python/ur_simple_control/__pycache__/managers.cpython-312.pyc differ diff --git a/python/ur_simple_control/basics/__pycache__/basics.cpython-312.pyc b/python/ur_simple_control/basics/__pycache__/basics.cpython-312.pyc index 45e09e3e1cb3ddfb458602f0e07a7f0aa3eee846..e1ec0d0b99d4fb96948b9ab5c2c7409a97300de5 100644 Binary files a/python/ur_simple_control/basics/__pycache__/basics.cpython-312.pyc and b/python/ur_simple_control/basics/__pycache__/basics.cpython-312.pyc differ diff --git a/python/ur_simple_control/basics/basics.py b/python/ur_simple_control/basics/basics.py index 8fd4b848a21b2b630645a4ddcc4eb220b5cbc727..aeb95345ce0ab563d438da95c9d10e2bc1e0b472 100644 --- a/python/ur_simple_control/basics/basics.py +++ b/python/ur_simple_control/basics/basics.py @@ -126,16 +126,17 @@ def jointTrajFollowingPDControlLoop(stop_at_final : bool, robot: RobotManager, r Kd = 0.5 # feedforward feedback - v_cmd = v_ref + Kp * error_q + Kd * error_v + v_cmd = v_ref + Kp * error_q #+ Kd * error_v qd_cmd = v_cmd[:6] robot.sendQd(v_cmd) - log_item['error_q'] = error_q - log_item['error_v'] = error_v - log_item['q'] = q - log_item['v'] = v - log_item['reference_q'] = q_ref - log_item['reference_v'] = v_ref + log_item['error_qs'] = error_q + log_item['error_vs'] = error_v + log_item['qs'] = q + log_item['vs'] = v + log_item['vs_cmd'] = v_cmd + log_item['reference_qs'] = q_ref + log_item['reference_vs'] = v_ref return breakFlag, {}, log_item @@ -143,12 +144,13 @@ def jointTrajFollowingPD(args, robot, reference): # we're not using any past data or logging, hence the empty arguments controlLoop = partial(jointTrajFollowingPDControlLoop, args.stop_at_final, robot, reference) log_item = { - 'error_q' : np.zeros(robot.model.nq), - 'error_v' : np.zeros(robot.model.nv), - 'q' : np.zeros(robot.model.nq), - 'v' : np.zeros(robot.model.nv), - 'reference_q' : np.zeros(robot.model.nq), - 'reference_v' : np.zeros(robot.model.nv) + 'error_qs' : np.zeros(robot.model.nq), + 'error_vs' : np.zeros(robot.model.nv), + 'qs' : np.zeros(robot.model.nq), + 'vs' : np.zeros(robot.model.nv), + 'vs_cmd' : np.zeros(robot.model.nv), + 'reference_qs' : np.zeros(robot.model.nq), + 'reference_vs' : np.zeros(robot.model.nv) } loop_manager = ControlLoopManager(robot, controlLoop, args, {}, log_item) loop_manager.run() diff --git a/python/ur_simple_control/dmp/__pycache__/dmp.cpython-312.pyc b/python/ur_simple_control/dmp/__pycache__/dmp.cpython-312.pyc index 55668f2b64c34f7d4c980310fea151591c9ef526..4e6126a9fbe330cb962c4cf2245ed8776e8eb631 100644 Binary files a/python/ur_simple_control/dmp/__pycache__/dmp.cpython-312.pyc and b/python/ur_simple_control/dmp/__pycache__/dmp.cpython-312.pyc differ diff --git a/python/ur_simple_control/managers.py b/python/ur_simple_control/managers.py index eb33ff86b2a7a8a06a9ab71eff293aec8f4f337c..dfffff597f4062b3daf7d7f7ffaec21bea628a1c 100644 --- a/python/ur_simple_control/managers.py +++ b/python/ur_simple_control/managers.py @@ -100,9 +100,13 @@ def getMinimalArgParser(): very useful and convenient when running simulation before running on real", \ default=False) parser.add_argument('--acceleration', type=float, \ - help="robot's joints acceleration. scalar positive constant, max 1.7, and default 0.4. \ + help="robot's joints acceleration. scalar positive constant, max 1.7, and default 0.3. \ BE CAREFUL WITH THIS. the urscript doc says this is 'lead axis acceleration'.\ TODO: check what this means", default=0.3) + parser.add_argument('--max-qd', type=float, \ + help="robot's joint velocities [rad/s]. scalar positive constant, max 3.14, and default 0.5. \ + BE CAREFUL WITH THIS. also note that wrist joints can go to 6.28 rad, but here \ + everything is clipped to this one number.", default=0.5) parser.add_argument('--debug-prints', action=argparse.BooleanOptionalAction, \ help="print some debug info", default=False) parser.add_argument('--save-log', action=argparse.BooleanOptionalAction, \ @@ -341,7 +345,7 @@ class ControlLoopManager: print("putting it to freedrive for good measure too") self.robot_manager.rtde_control.freedriveMode() - if args.save_log: + if self.args.save_log: self.robot_manager.log_manager.saveLog() # set kill command and join visualization processes # TODO: actually send them a SIGINT and a SIGKILL if necessary @@ -470,7 +474,9 @@ class RobotManager: # we're clipping joint velocities with this. # if your controllers are not what you expect, you might be commanding a very high velocity, # which is clipped, resulting in unexpected movement. - self.max_qd = 0.5 * args.speed_slider + self.MAX_QD = 3.14 + assert args.max_qd <= self.MAX_QD and args.max_qd > 0.0 + self.max_qd = args.max_qd self.gripper = None if (self.args.gripper != "none") and not self.pinocchio_only: @@ -929,3 +935,9 @@ class RobotManager: if self.args.debug_prints: print("terminated manipulator_visualizer_process") + def stopRobot(self): + if not self.args.pinocchio_only: + print("stopping via freedrive lel") + self.rtde_control.freedriveMode() + time.sleep(0.5) + self.rtde_control.endFreedriveMode() diff --git a/python/ur_simple_control/optimal_control/optimal_control.py b/python/ur_simple_control/optimal_control/crocoddyl_optimal_control.py similarity index 90% rename from python/ur_simple_control/optimal_control/optimal_control.py rename to python/ur_simple_control/optimal_control/crocoddyl_optimal_control.py index 4da6ba3fd504a8ce2f896bfcfef30e6fb798db3f..152e0f9218da9b6edb1464dd7027143c9dad7415 100644 --- a/python/ur_simple_control/optimal_control/optimal_control.py +++ b/python/ur_simple_control/optimal_control/crocoddyl_optimal_control.py @@ -22,7 +22,7 @@ def get_OCP_args(parser : argparse.ArgumentParser): # TODO: use fddp and incorporate torque (i.e. velocity constraints) # --> those will correspond to max_qd and acceleration attributes in robotmanager -def IKOCP(args, robot : RobotManager, goal : pin.SE3): +def CrocoIKOCP(args, robot : RobotManager, goal : pin.SE3): # create torque bounds which correspond to percentage # of maximum allowed acceleration # NOTE: idk if this makes any sense, but let's go for it @@ -41,11 +41,13 @@ def IKOCP(args, robot : RobotManager, goal : pin.SE3): # first 3 are for tracking, state and control regulation runningCostModel = crocoddyl.CostModelSum(state) terminalCostModel = crocoddyl.CostModelSum(state) + # cost 1) u residual (actuator cost) uResidual = crocoddyl.ResidualModelControl(state, state.nv) + uRegCost = crocoddyl.CostModelResidual(state, uResidual) + # cost 2) x residual (overall amount of movement) xResidual = crocoddyl.ResidualModelState(state, x0, state.nv) xRegCost = crocoddyl.CostModelResidual(state, xResidual) - uRegCost = crocoddyl.CostModelResidual(state, uResidual) - # cost 1) distance to goal -> SE(3) error + # cost 3) distance to goal -> SE(3) error framePlacementResidual = crocoddyl.ResidualModelFramePlacement( state, # TODO: check if this is the frame we actually want (ee tip) @@ -54,10 +56,14 @@ def IKOCP(args, robot : RobotManager, goal : pin.SE3): goal.copy(), state.nv) goalTrackingCost = crocoddyl.CostModelResidual(state, framePlacementResidual) - # cost 2) u residual (actuator cost) - uResidual = crocoddyl.ResidualModelControl(state, state.nv) - # cost 3) x residual (overall amount of movement) - xResidual = crocoddyl.ResidualModelState(state, x0, state.nv) + # cost 4) final ee velocity is 0 (can't directly formulate joint velocity, + # because that's a part of the state, and we don't know final joint positions) + frameVelocityResidual = crocoddyl.ResidualModelFrameVelocity( + state, + robot.model.getFrameId("tool0"), + pin.Motion(np.zeros(6)), + pin.ReferenceFrame.WORLD) + frameVelocityCost = crocoddyl.CostModelResidual(state, frameVelocityResidual) # put these costs into the running costs runningCostModel.addCost("gripperPose", goalTrackingCost, 1e2) runningCostModel.addCost("xReg", xRegCost, 1e-3) @@ -66,6 +72,7 @@ def IKOCP(args, robot : RobotManager, goal : pin.SE3): # NOTE: shouldn't there be a final velocity = 0 here? terminalCostModel.addCost("gripperPose", goalTrackingCost, 1e2) terminalCostModel.addCost("uReg", uRegCost, 1e3) + terminalCostModel.addCost("velFinal", frameVelocityCost, 1e3) # the 4th cost is for defining bounds via costs # NOTE: could have gotten the same info from state.lb and state.ub. @@ -153,7 +160,7 @@ if __name__ == "__main__": robot.q = pin.randomConfiguration(robot.model) goal = pin.SE3.Random() goal.translation = np.random.random(3) * 0.4 - reference, solver = IKOCP(args, robot, goal) + reference, solver = CrocoIKOCP(args, robot, goal) # we only work within -pi - pi (or 0-2pi idk anymore) #reference['qs'] = reference['qs'] % (2*np.pi) - np.pi log = solver.getCallbacks()[1] diff --git a/python/ur_simple_control/util/__pycache__/logging_utils.cpython-312.pyc b/python/ur_simple_control/util/__pycache__/logging_utils.cpython-312.pyc index f47591d995636eee4dd219f3004ed75c1e0574b0..fe80f5e9d71c11753cadb1118869190383545e4c 100644 Binary files a/python/ur_simple_control/util/__pycache__/logging_utils.cpython-312.pyc and b/python/ur_simple_control/util/__pycache__/logging_utils.cpython-312.pyc differ diff --git a/python/ur_simple_control/visualize/__pycache__/visualize.cpython-312.pyc b/python/ur_simple_control/visualize/__pycache__/visualize.cpython-312.pyc index 240ee89c04d2fca703a08f0ec3f80304f4b696c7..cdb303e8174d784085e917dde458ea4c3b6b14d9 100644 Binary files a/python/ur_simple_control/visualize/__pycache__/visualize.cpython-312.pyc and b/python/ur_simple_control/visualize/__pycache__/visualize.cpython-312.pyc differ diff --git a/python/ur_simple_control/visualize/manipulator_comparison_visualizer.py b/python/ur_simple_control/visualize/manipulator_comparison_visualizer.py index 6682326d87da63d4aff4d17667a11075916bab25..9537c1573b4cd6ace510792eeb22e1f23c9d07f1 100644 --- a/python/ur_simple_control/visualize/manipulator_comparison_visualizer.py +++ b/python/ur_simple_control/visualize/manipulator_comparison_visualizer.py @@ -76,34 +76,36 @@ class ManipulatorComparisonManager: self.manipulator_visualizer_ack_queue.get() ########################################### - # in case you will want log plotter too # + # in case you will want log plotters too # ########################################### - self.log_plotter = False - self.log_plotter_cmd_queue = None - self.log_plotter_ack_queue = None - self.log_plotter_process = None + self.log_plotters = [] # NOTE i assume what you want to plot is a time-indexed with # the same frequency that the control loops run in. # if it's not then it's pointless to have a running plot anyway. + # TODO: put these in a list so that we can have multiple plots at the same time + class RunningPlotter: + def __init__(self, args, log, log_plotter_time_start, log_plotter_time_stop): + self.time_start = log_plotter_time_start + self.time_stop = log_plotter_time_stop + self.cmd_queue = Queue() + self.ack_queue = Queue() + self.process = Process(target=logPlotter, + args=(args, log, self.cmd_queue, + self.ack_queue)) + self.process.start() + self.ack_queue.get() + def createRunningPlot(self, log, log_plotter_time_start, log_plotter_time_stop): - self.log_plotter = True - self.log_plotter_time_start = log_plotter_time_start - self.log_plotter_time_stop = log_plotter_time_stop - self.log_plotter_cmd_queue = Queue() - self.log_plotter_ack_queue = Queue() - self.log_plotter_process = Process(target=logPlotter, - args=(args, log, self.log_plotter_cmd_queue, - self.log_plotter_ack_queue)) - self.log_plotter_process.start() - self.log_plotter_ack_queue.get() + self.log_plotters.append(self.RunningPlotter(self.args, log, log_plotter_time_start, log_plotter_time_stop)) def updateViz(self, q1, q2, time_index): self.manipulator_visualizer_cmd_queue.put((q1, q2)) - if self.log_plotter and (time_index >= self.log_plotter_time_start) and (time_index < self.log_plotter_time_stop): - self.log_plotter_cmd_queue.put(time_index - self.log_plotter_time_start) - self.log_plotter_ack_queue.get() + for log_plotter in self.log_plotters: + if (time_index >= log_plotter.time_start) and (time_index < log_plotter.time_stop): + log_plotter.cmd_queue.put(time_index - log_plotter.time_start) + log_plotter.ack_queue.get() self.manipulator_visualizer_ack_queue.get() # NOTE: this uses slightly fancy python to make it bareable to code diff --git a/python/ur_simple_control/visualize/visualize.py b/python/ur_simple_control/visualize/visualize.py index 9890be02c686b6795c4ef457de2f777797ec3314..6d1cfde6bbbbe4a4a7780e4fc7713f79819658d6 100644 --- a/python/ur_simple_control/visualize/visualize.py +++ b/python/ur_simple_control/visualize/visualize.py @@ -107,17 +107,13 @@ def realTimePlotter(args, queue): colors = plt.cm.jet(np.linspace(0, 1, log_item[data_key].shape[0])) ax = fig.add_subplot(int(subplot_col_row + str(i + 1))) # some hacks, i'll have to standardize things somehow - if data_key == 'qs': + if 'qs' in data_key: ax.set_ylim(bottom=-6.14, top=6.14) - if data_key == 'dmp_poss': - ax.set_ylim(bottom=-6.14, top=6.14) - if data_key == 'dqs': - ax.set_ylim(bottom=-1.7, top=1.7) - if data_key == 'dmp_vels': - ax.set_ylim(bottom=-1.7, top=1.7) + if 'vs' in data_key: + ax.set_ylim(bottom=-3.14, top=3.14) if 'wrench' in data_key: ax.set_ylim(bottom=-20.0, top=20.0) - if data_key == 'tau': + if 'tau' in data_key: ax.set_ylim(bottom=-2.0, top=2.0) axes_and_updating_artists[data_key] = AxisAndArtists(ax, {}) for j in range(log_item[data_key].shape[0]): @@ -302,7 +298,13 @@ def manipulatorComparisonVisualizer(args, model, collision_model, visual_model, viz.viewer.window.server_proc.wait() - +# TODO: this has to be a class so that we can access +# the canvas in the onclik event function +# because that can be a method of that class and +# get arguments that way. +# but otherwise i can't pass arguments to that. +# even if i could with partial, i can't return them, +# so no cigar from that def logPlotter(args, log, cmd_queue, ack_queue): """ logPlotter @@ -353,16 +355,30 @@ def logPlotter(args, log, cmd_queue, ack_queue): axes_and_updating_artists[data_key] = AxisAndArtists(ax, point_in_time_line) axes_and_updating_artists[data_key].ax.legend(loc='upper left') - # need to call it once + # need to call it once to start, more if something breaks canvas.draw() canvas.flush_events() background = canvas.copy_from_bbox(fig.bbox) + # we need to have an event that triggers redrawing + #cid = fig.canvas.mpl_connect('button_press_event', onclick) + def onEvent(event): + print("drawing") + canvas.draw() + canvas.flush_events() + background = canvas.copy_from_bbox(fig.bbox) + print("copied canvas") + + cid = fig.canvas.mpl_connect('button_press_event', onEvent) + + ack_queue.put("ready") if args.debug_prints: print("LOG_PLOTTER: FULLY ONLINE") try: + counter = 0 while True: + counter += 1 time_index = cmd_queue.get() if time_index == "befree": if args.debug_prints: @@ -372,8 +388,18 @@ def logPlotter(args, log, cmd_queue, ack_queue): for data_key in log: axes_and_updating_artists[data_key].artists.set_xdata([time_index]) axes_and_updating_artists[data_key].ax.draw_artist(axes_and_updating_artists[data_key].artists) - canvas.blit(fig.bbox) - canvas.flush_events() + # NOTE: this is stupid, i just want to catch the resize event + if not (counter % 50 == 0): + #if True: + print(counter) + canvas.blit(fig.bbox) + canvas.flush_events() + else: + print("drawing") + canvas.draw() + canvas.flush_events() + background = canvas.copy_from_bbox(fig.bbox) + print("copied canvas") ack_queue.put("ready") except KeyboardInterrupt: if args.debug_prints: