diff --git a/python/examples/clik.py b/python/examples/clik.py index 651e8c31f9412d4d2de12bc44778cf3c6b01d68c..622e719e90fb1756a205d959c659555605caadf5 100644 --- a/python/examples/clik.py +++ b/python/examples/clik.py @@ -39,8 +39,9 @@ def get_args(): help="whether you want to visualize the manipulator and workspace with meshcat", default=False) parser.add_argument('--real-time-plotting', action=argparse.BooleanOptionalAction, help="whether you want to have some real-time matplotlib graphs (parts of log_dict you select)", default=False) - parser.add_argument('--gripper', action=argparse.BooleanOptionalAction, \ - help="whether you're using the gripper", default=False) + parser.add_argument('--gripper', type=str, \ + help="gripper you're using (no gripper is the default)", + default="none", choices=['none', 'robotiq', 'onrobot']) parser.add_argument('--goal-error', type=float, \ help="the final position error you are happy with", default=1e-2) parser.add_argument('--max-iterations', type=int, \ diff --git a/python/examples/drawing_from_input_drawing.py b/python/examples/drawing_from_input_drawing.py index 2bf8e0f4efc9e18467a79152882be5b973c28fbd..7c30f64ff7502ca661f7b37d898a17d8a1815f38 100644 --- a/python/examples/drawing_from_input_drawing.py +++ b/python/examples/drawing_from_input_drawing.py @@ -50,8 +50,9 @@ def getArgs(): help="whether you want to visualize the manipulator and workspace with meshcat", default=False) parser.add_argument('--real-time-plotting', action=argparse.BooleanOptionalAction, help="whether you want to have some real-time matplotlib graphs (parts of log_dict you select)", default=False) - parser.add_argument('--gripper', action=argparse.BooleanOptionalAction, \ - help="whether you're using the gripper", default=False) + parser.add_argument('--gripper', type=str, \ + help="gripper you're using (no gripper is the default)", + default="none", choices=['none', 'robotiq', 'onrobot']) parser.add_argument('--acceleration', type=float, \ help="robot's joints acceleration. scalar positive constant, \ max 1.7, and default 0.4. \ diff --git a/python/examples/oc_for_ik.py b/python/examples/oc_for_ik.py new file mode 100644 index 0000000000000000000000000000000000000000..36ce607997405ea29e755c6422305b9c337f15b5 --- /dev/null +++ b/python/examples/oc_for_ik.py @@ -0,0 +1,112 @@ +import numpy as np +import time +import argparse +from functools import partial +from ur_simple_control.managers import 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 + + +""" +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 = 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) + parser.add_argument('--simulation', action=argparse.BooleanOptionalAction, + help="whether you are running the UR simulator", default=False) + parser.add_argument('--robot-ip', type=str, + help="robot's ip address (only needed if running on the real robot)", \ + default="192.168.1.102") + parser.add_argument('--pinocchio-only', action=argparse.BooleanOptionalAction, + help="whether you want to just integrate with pinocchio", default=False) + parser.add_argument('--visualize-manipulator', action=argparse.BooleanOptionalAction, + help="whether you want to visualize the manipulator and workspace with meshcat", default=False) + parser.add_argument('--real-time-plotting', action=argparse.BooleanOptionalAction, + help="whether you want to have some real-time matplotlib graphs (parts of log_dict you select)", default=False) + parser.add_argument('--gripper', action=argparse.BooleanOptionalAction, \ + help="whether you're using the gripper", default=False) + parser.add_argument('--goal-error', type=float, \ + help="the final position error you are happy with", default=1e-2) + parser.add_argument('--max-iterations', type=int, \ + help="maximum allowable iteration number (it runs at 500Hz)", default=100000) + parser.add_argument('--acceleration', type=float, \ + help="robot's joints acceleration. scalar positive constant, max 1.7, and default 0.4. \ + BE CAREFUL WITH THIS. the urscript doc says this is 'lead axis acceleration'.\ + TODO: check what this means", default=0.3) + parser.add_argument('--speed-slider', type=float,\ + help="cap robot's speed with the speed slider \ + to something between 0 and 1, 0.5 by default \ + BE CAREFUL WITH THIS.", default=0.5) + parser.add_argument('--tikhonov-damp', type=float, \ + help="damping scalar in tikhonov regularization", default=1e-3) + parser.add_argument('--minimum-detectable-force-norm', type=float, \ + help="we need to disregard noise to converge despite filtering. \ + a quick fix is to zero all forces of norm below this argument threshold.", + default=3.0) + # TODO add the rest + parser.add_argument('--clik-controller', type=str, \ + help="select which click algorithm you want", \ + default='dampedPseudoinverse', choices=['dampedPseudoinverse', 'jacobianTranspose', 'invKinmQP']) + # maybe you want to scale the control signal + parser.add_argument('--controller-speed-scaling', type=float, \ + default='1.0', help='not actually_used atm') + parser.add_argument('--debug-prints', action=argparse.BooleanOptionalAction, \ + help="print some debug info", default=False) + parser.add_argument('--save-log', action=argparse.BooleanOptionalAction, \ + help="save log data folder in whatever directory you called this in. if it doesn't exists there, it's saved to /tmp/data", default=False) + 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) + parser.add_argument('--past-window-size', type=int, \ + help="how many timesteps of past data you want to save", default=5) + + args = parser.parse_args() + if args.gripper and args.simulation: + raise NotImplementedError('Did not figure out how to put the gripper in \ + the simulation yet, sorry :/ . You can have only 1 these flags right now') + return args + +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/examples/point_impedance_control.py b/python/examples/point_impedance_control.py index 58ac4d710d83d7e85e691c37973e8ce7ca263a0c..8167bcc184d9bd23c8bbdb040cb87fcdea60a5f5 100644 --- a/python/examples/point_impedance_control.py +++ b/python/examples/point_impedance_control.py @@ -44,8 +44,9 @@ def getArgs(): help="whether you want to visualize the manipulator and workspace with meshcat", default=False) parser.add_argument('--real-time-plotting', action=argparse.BooleanOptionalAction, help="whether you want to have some real-time matplotlib graphs (parts of log_dict you select)", default=False) - parser.add_argument('--gripper', action=argparse.BooleanOptionalAction, \ - help="whether you're using the gripper", default=False) + parser.add_argument('--gripper', type=str, \ + help="gripper you're using (no gripper is the default)", + default="none", choices=['none', 'robotiq', 'onrobot']) parser.add_argument('--acceleration', type=float, \ help="robot's joints acceleration. scalar positive constant, \ max 1.7, and default 0.4. \ diff --git a/python/ur_simple_control.egg-info/SOURCES.txt b/python/ur_simple_control.egg-info/SOURCES.txt index 958962fe06fb5f61114fbec7342e08ea2b3b8c16..8ed94185351c47000095ce13d61fc99f3f3b3f99 100644 --- a/python/ur_simple_control.egg-info/SOURCES.txt +++ b/python/ur_simple_control.egg-info/SOURCES.txt @@ -1,10 +1,271 @@ LICENSE.txt README.md +joint_trajectory.csv +path_in_pixels.csv setup.py +convenience_tool_box/check_tcp_payload +convenience_tool_box/currents.png +convenience_tool_box/frame_validation.py +convenience_tool_box/freedrive.py +convenience_tool_box/ft_readings.py +convenience_tool_box/fts.png +convenience_tool_box/jog_example +convenience_tool_box/measuring_stick.py +convenience_tool_box/notes.md +convenience_tool_box/open_close_gripper.py +convenience_tool_box/taus.png +convenience_tool_box/__pycache__/give_me_the_calibrated_model.cpython-310.pyc +convenience_tool_box/__pycache__/robotiq_gripper.cpython-310.pyc +examples/clik.py +examples/drawing_from_input_drawing.py +examples/joint_trajectory.csv +examples/path_in_pixels.csv +examples/planar_dragging_via_top_contact_force.py +examples/point_impedance_control.py +examples/pushing_via_friction_cones.py +examples/test_crocoddyl_opt_ctrl.py +examples/test_gripper.py +examples/test_movej.py +examples/__pycache__/robotiq_gripper.cpython-310.pyc +examples/data/clik_run_001.pickle +examples/data/clik_run_001_args.pickle +examples/data/fts.png +examples/data/joint_trajectory.csv +examples/data/path_in_pixels.csv +examples/old_or_experimental/clik_old.py +examples/old_or_experimental/force_mode_api.py +examples/old_or_experimental/forcemode_example.py +examples/old_or_experimental/test_traj_w_movej.py +examples/old_or_experimental/test_traj_w_speedj.py +initial_python_solution/main.py +initial_python_solution/make_run.py +initial_python_solution/manipulator_visual_motion_analyzer.py +initial_python_solution/__pycache__/make_run.cpython-310.pyc +initial_python_solution/__pycache__/make_run.cpython-311.pyc +initial_python_solution/arms/another_debug_dh +initial_python_solution/arms/j2n6s300_dh_params +initial_python_solution/arms/j2s7s300_dh_params +initial_python_solution/arms/kinova_jaco_params +initial_python_solution/arms/kuka_lbw_iiwa_dh_params +initial_python_solution/arms/lbr_iiwa_jos_jedan_dh +initial_python_solution/arms/robot_parameters2 +initial_python_solution/arms/testing_dh_parameters +initial_python_solution/arms/ur10e_dh_parameters_from_the_ur_site +initial_python_solution/arms/ur5e_dh +initial_python_solution/example_code/blit_in_tk.py +initial_python_solution/example_code/blit_test.py +initial_python_solution/example_code/dark_background_example.py +initial_python_solution/example_code/ellipse.py +initial_python_solution/example_code/embedding_in_tk_sgskip.py +initial_python_solution/example_code/manager_anim.py +initial_python_solution/example_code/run_classic_ik_algs.py +initial_python_solution/robot_stuff/InverseKinematics.py +initial_python_solution/robot_stuff/drawing.py +initial_python_solution/robot_stuff/drawing_for_anim.py +initial_python_solution/robot_stuff/follow_curve.py +initial_python_solution/robot_stuff/forw_kinm.py +initial_python_solution/robot_stuff/inv_kinm.py +initial_python_solution/robot_stuff/joint_as_hom_mat.py +initial_python_solution/robot_stuff/utils.py +initial_python_solution/robot_stuff/webots_api_helper_funs.py +initial_python_solution/robot_stuff/__pycache__/InverseKinematics.cpython-310.pyc +initial_python_solution/robot_stuff/__pycache__/InverseKinematics.cpython-311.pyc +initial_python_solution/robot_stuff/__pycache__/drawing.cpython-310.pyc +initial_python_solution/robot_stuff/__pycache__/drawing.cpython-311.pyc +initial_python_solution/robot_stuff/__pycache__/drawing_for_anim.cpython-310.pyc +initial_python_solution/robot_stuff/__pycache__/drawing_for_anim.cpython-311.pyc +initial_python_solution/robot_stuff/__pycache__/follow_curve.cpython-310.pyc +initial_python_solution/robot_stuff/__pycache__/follow_curve.cpython-311.pyc +initial_python_solution/robot_stuff/__pycache__/forw_kinm.cpython-310.pyc +initial_python_solution/robot_stuff/__pycache__/forw_kinm.cpython-311.pyc +initial_python_solution/robot_stuff/__pycache__/inv_kinm.cpython-310.pyc +initial_python_solution/robot_stuff/__pycache__/inv_kinm.cpython-311.pyc +initial_python_solution/robot_stuff/__pycache__/joint_as_hom_mat.cpython-310.pyc +initial_python_solution/robot_stuff/__pycache__/joint_as_hom_mat.cpython-311.pyc +initial_python_solution/robot_stuff/__pycache__/utils.cpython-310.pyc +initial_python_solution/robot_stuff/__pycache__/utils.cpython-311.pyc +initial_python_solution/robot_stuff/__pycache__/webots_api_helper_funs.cpython-310.pyc +initial_python_solution/robot_stuff/__pycache__/webots_api_helper_funs.cpython-311.pyc ur_simple_control/__init__.py ur_simple_control/managers.py ur_simple_control.egg-info/PKG-INFO ur_simple_control.egg-info/SOURCES.txt ur_simple_control.egg-info/dependency_links.txt ur_simple_control.egg-info/not-zip-safe -ur_simple_control.egg-info/top_level.txt \ No newline at end of file +ur_simple_control.egg-info/top_level.txt +ur_simple_control/__pycache__/__init__.cpython-310.pyc +ur_simple_control/__pycache__/__init__.cpython-311.pyc +ur_simple_control/__pycache__/__init__.cpython-312.pyc +ur_simple_control/__pycache__/managers.cpython-310.pyc +ur_simple_control/__pycache__/managers.cpython-311.pyc +ur_simple_control/__pycache__/managers.cpython-312.pyc +ur_simple_control/basics/__init__.py +ur_simple_control/basics/basics.py +ur_simple_control/basics/__pycache__/__init__.cpython-310.pyc +ur_simple_control/basics/__pycache__/__init__.cpython-311.pyc +ur_simple_control/basics/__pycache__/__init__.cpython-312.pyc +ur_simple_control/basics/__pycache__/basics.cpython-310.pyc +ur_simple_control/basics/__pycache__/basics.cpython-311.pyc +ur_simple_control/basics/__pycache__/basics.cpython-312.pyc +ur_simple_control/clik/__init__.py +ur_simple_control/clik/clik_point_to_point.py +ur_simple_control/clik/clik_trajectory_following.py +ur_simple_control/clik/__pycache__/__init__.cpython-310.pyc +ur_simple_control/clik/__pycache__/__init__.cpython-311.pyc +ur_simple_control/clik/__pycache__/__init__.cpython-312.pyc +ur_simple_control/clik/__pycache__/clik_point_to_point.cpython-310.pyc +ur_simple_control/clik/__pycache__/clik_point_to_point.cpython-311.pyc +ur_simple_control/clik/__pycache__/clik_point_to_point.cpython-312.pyc +ur_simple_control/clik/__pycache__/clik_trajectory_following.cpython-310.pyc +ur_simple_control/clik/__pycache__/clik_trajectory_following.cpython-311.pyc +ur_simple_control/clik/__pycache__/clik_trajectory_following.cpython-312.pyc +ur_simple_control/dmp/__init__.py +ur_simple_control/dmp/dmp.py +ur_simple_control/dmp/notes.md +ur_simple_control/dmp/__pycache__/__init__.cpython-310.pyc +ur_simple_control/dmp/__pycache__/__init__.cpython-311.pyc +ur_simple_control/dmp/__pycache__/__init__.cpython-312.pyc +ur_simple_control/dmp/__pycache__/create_dmp.cpython-310.pyc +ur_simple_control/dmp/__pycache__/dmp.cpython-310.pyc +ur_simple_control/dmp/__pycache__/dmp.cpython-311.pyc +ur_simple_control/dmp/__pycache__/dmp.cpython-312.pyc +ur_simple_control/dmp/__pycache__/robotiq_gripper.cpython-310.pyc +ur_simple_control/dmp/__pycache__/temporal_coupling.cpython-310.pyc +ur_simple_control/dmp/trajectories/new_traj.csv +ur_simple_control/dmp/trajectories/path_in_pixels.csv +ur_simple_control/dmp/trajectories/ur10_omega_trajectory.csv +ur_simple_control/robot_descriptions/__init__.py +ur_simple_control/robot_descriptions/my_robot_calibration.yaml +ur_simple_control/robot_descriptions/__pycache__/__init__.cpython-310.pyc +ur_simple_control/robot_descriptions/__pycache__/__init__.cpython-311.pyc +ur_simple_control/robot_descriptions/__pycache__/__init__.cpython-312.pyc +ur_simple_control/robot_descriptions/config/__init__.py +ur_simple_control/robot_descriptions/config/ur5e/__init__.py +ur_simple_control/robot_descriptions/config/ur5e/default_kinematics.yaml +ur_simple_control/robot_descriptions/config/ur5e/joint_limits.yaml +ur_simple_control/robot_descriptions/config/ur5e/physical_parameters.yaml +ur_simple_control/robot_descriptions/config/ur5e/visual_parameters.yaml +ur_simple_control/robot_descriptions/meshes/__init__.py +ur_simple_control/robot_descriptions/meshes/robotiq_85_coupler.stl +ur_simple_control/robot_descriptions/meshes/ur5e/__init__.py +ur_simple_control/robot_descriptions/meshes/ur5e/robotiq_85_coupler.stl +ur_simple_control/robot_descriptions/meshes/ur5e/collision/__init__.py +ur_simple_control/robot_descriptions/meshes/ur5e/collision/base.stl +ur_simple_control/robot_descriptions/meshes/ur5e/collision/finger_1-collision.dae +ur_simple_control/robot_descriptions/meshes/ur5e/collision/finger_2-collision.dae +ur_simple_control/robot_descriptions/meshes/ur5e/collision/forearm.stl +ur_simple_control/robot_descriptions/meshes/ur5e/collision/hand-e-collision.dae +ur_simple_control/robot_descriptions/meshes/ur5e/collision/shoulder.stl +ur_simple_control/robot_descriptions/meshes/ur5e/collision/upperarm.stl +ur_simple_control/robot_descriptions/meshes/ur5e/collision/wrist1.stl +ur_simple_control/robot_descriptions/meshes/ur5e/collision/wrist2.stl +ur_simple_control/robot_descriptions/meshes/ur5e/collision/wrist3.stl +ur_simple_control/robot_descriptions/meshes/ur5e/visual/__init__.py +ur_simple_control/robot_descriptions/meshes/ur5e/visual/base.dae +ur_simple_control/robot_descriptions/meshes/ur5e/visual/finger_1.dae +ur_simple_control/robot_descriptions/meshes/ur5e/visual/finger_2.dae +ur_simple_control/robot_descriptions/meshes/ur5e/visual/forearm.dae +ur_simple_control/robot_descriptions/meshes/ur5e/visual/hand-e.dae +ur_simple_control/robot_descriptions/meshes/ur5e/visual/shoulder.dae +ur_simple_control/robot_descriptions/meshes/ur5e/visual/upperarm.dae +ur_simple_control/robot_descriptions/meshes/ur5e/visual/wrist1.dae +ur_simple_control/robot_descriptions/meshes/ur5e/visual/wrist2.dae +ur_simple_control/robot_descriptions/meshes/ur5e/visual/wrist3.dae +ur_simple_control/robot_descriptions/urdf/__init__.py +ur_simple_control/robot_descriptions/urdf/ur5e_with_robotiq_hande.urdf +ur_simple_control/robot_descriptions/urdf/ur5e_with_robotiq_hande_FIXED_PATHS.urdf +ur_simple_control/robot_descriptions/urdf/__pycache__/__init__.cpython-310.pyc +ur_simple_control/robot_descriptions/urdf/__pycache__/__init__.cpython-311.pyc +ur_simple_control/robot_descriptions/urdf/__pycache__/__init__.cpython-312.pyc +ur_simple_control/util/__init__.py +ur_simple_control/util/calib_board_hacks.py +ur_simple_control/util/default_args.py +ur_simple_control/util/draw_path.py +ur_simple_control/util/freedrive.py +ur_simple_control/util/ft_calibration.py +ur_simple_control/util/get_model.py +ur_simple_control/util/logging_utils.py +ur_simple_control/util/path_in_pixels.csv +ur_simple_control/util/__pycache__/__init__.cpython-310.pyc +ur_simple_control/util/__pycache__/__init__.cpython-311.pyc +ur_simple_control/util/__pycache__/__init__.cpython-312.pyc +ur_simple_control/util/__pycache__/boilerplate_wrapper.cpython-310.pyc +ur_simple_control/util/__pycache__/calib_board_hacks.cpython-310.pyc +ur_simple_control/util/__pycache__/calib_board_hacks.cpython-311.pyc +ur_simple_control/util/__pycache__/calib_board_hacks.cpython-312.pyc +ur_simple_control/util/__pycache__/draw_path.cpython-310.pyc +ur_simple_control/util/__pycache__/draw_path.cpython-311.pyc +ur_simple_control/util/__pycache__/draw_path.cpython-312.pyc +ur_simple_control/util/__pycache__/freedrive.cpython-310.pyc +ur_simple_control/util/__pycache__/freedrive.cpython-311.pyc +ur_simple_control/util/__pycache__/freedrive.cpython-312.pyc +ur_simple_control/util/__pycache__/get_model.cpython-310.pyc +ur_simple_control/util/__pycache__/get_model.cpython-311.pyc +ur_simple_control/util/__pycache__/get_model.cpython-312.pyc +ur_simple_control/util/__pycache__/logging_utils.cpython-310.pyc +ur_simple_control/util/__pycache__/logging_utils.cpython-311.pyc +ur_simple_control/util/__pycache__/logging_utils.cpython-312.pyc +ur_simple_control/util/__pycache__/robotiq_gripper.cpython-310.pyc +ur_simple_control/util/__pycache__/robotiq_gripper.cpython-311.pyc +ur_simple_control/util/__pycache__/robotiq_gripper.cpython-312.pyc +ur_simple_control/visualize/__init__.py +ur_simple_control/visualize/main.py +ur_simple_control/visualize/make_run.py +ur_simple_control/visualize/manipulator_visual_motion_analyzer.py +ur_simple_control/visualize/visualize.py +ur_simple_control/visualize/__pycache__/__init__.cpython-310.pyc +ur_simple_control/visualize/__pycache__/__init__.cpython-311.pyc +ur_simple_control/visualize/__pycache__/__init__.cpython-312.pyc +ur_simple_control/visualize/__pycache__/make_run.cpython-310.pyc +ur_simple_control/visualize/__pycache__/make_run.cpython-311.pyc +ur_simple_control/visualize/__pycache__/make_run.cpython-312.pyc +ur_simple_control/visualize/__pycache__/pin_to_vedo_plot_util.cpython-311.pyc +ur_simple_control/visualize/__pycache__/visualize.cpython-310.pyc +ur_simple_control/visualize/__pycache__/visualize.cpython-311.pyc +ur_simple_control/visualize/__pycache__/visualize.cpython-312.pyc +ur_simple_control/visualize/arms/another_debug_dh +ur_simple_control/visualize/arms/j2n6s300_dh_params +ur_simple_control/visualize/arms/j2s7s300_dh_params +ur_simple_control/visualize/arms/kinova_jaco_params +ur_simple_control/visualize/arms/kuka_lbw_iiwa_dh_params +ur_simple_control/visualize/arms/lbr_iiwa_jos_jedan_dh +ur_simple_control/visualize/arms/robot_parameters2 +ur_simple_control/visualize/arms/testing_dh_parameters +ur_simple_control/visualize/arms/ur10e_dh_parameters_from_the_ur_site +ur_simple_control/visualize/arms/ur5e_dh +ur_simple_control/visualize/robot_stuff/InverseKinematics.py +ur_simple_control/visualize/robot_stuff/drawing.py +ur_simple_control/visualize/robot_stuff/drawing_for_anim.py +ur_simple_control/visualize/robot_stuff/follow_curve.py +ur_simple_control/visualize/robot_stuff/forw_kinm.py +ur_simple_control/visualize/robot_stuff/inv_kinm.py +ur_simple_control/visualize/robot_stuff/joint_as_hom_mat.py +ur_simple_control/visualize/robot_stuff/utils.py +ur_simple_control/visualize/robot_stuff/webots_api_helper_funs.py +ur_simple_control/visualize/robot_stuff/__pycache__/InverseKinematics.cpython-310.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/InverseKinematics.cpython-311.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/InverseKinematics.cpython-312.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/drawing.cpython-310.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/drawing.cpython-311.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/drawing.cpython-312.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/drawing_for_anim.cpython-310.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/drawing_for_anim.cpython-311.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/drawing_for_anim.cpython-312.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/follow_curve.cpython-310.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/follow_curve.cpython-311.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/follow_curve.cpython-312.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/forw_kinm.cpython-310.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/forw_kinm.cpython-311.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/forw_kinm.cpython-312.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/inv_kinm.cpython-310.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/inv_kinm.cpython-311.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/inv_kinm.cpython-312.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/joint_as_hom_mat.cpython-310.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/joint_as_hom_mat.cpython-311.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/joint_as_hom_mat.cpython-312.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/utils.cpython-310.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/utils.cpython-311.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/utils.cpython-312.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/webots_api_helper_funs.cpython-310.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/webots_api_helper_funs.cpython-311.pyc +ur_simple_control/visualize/robot_stuff/__pycache__/webots_api_helper_funs.cpython-312.pyc \ No newline at end of file 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 03f3f9a372b4970f16339d4e5aedf8edfc824b6a..a907dd1917993fe0c168898bc788a4aa7abd34c3 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 6b9f414014d44f5bc2e2f0e13977cc6ad689afd3..d81beb36ff7ec21e6520d05bfab108ca4915d6ed 100644 --- a/python/ur_simple_control/basics/basics.py +++ b/python/ur_simple_control/basics/basics.py @@ -10,14 +10,12 @@ from functools import partial from ur_simple_control.managers import ControlLoopManager, RobotManager import time -""" -moveJControlLoop ---------------- -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. -""" def moveJControlLoop(q_desired, robot, i, past_data): + """ + moveJControlLoop + --------------- + most basic P control for joint space point-to-point motion, actual control loop. + """ breakFlag = False save_past_dict = {} # you don't even need forward kinematics for this lmao @@ -48,6 +46,12 @@ def moveJControlLoop(q_desired, robot, i, past_data): # MOVEL works just fine, so apply whatever's missing for there here # and that's it. def moveJ(args, robot, q_desired): + """ + moveJ + --------------- + most basic P control for joint space point-to-point motion. + just starts the control loop without any logging. + """ assert type(q_desired) == np.ndarray controlLoop = partial(moveJControlLoop, q_desired, robot) # we're not using any past data or logging, hence the empty arguments @@ -57,3 +61,11 @@ def moveJ(args, robot, q_desired): #time.sleep(0.01) if args.debug_prints: print("MoveJ done: convergence achieved, reached destionation!") + + +def jointTrajFollowingPIDControlLoop(): + pass + + +def jointTrajFollowingPID(): + pass diff --git a/python/ur_simple_control/clik/__pycache__/clik_point_to_point.cpython-312.pyc b/python/ur_simple_control/clik/__pycache__/clik_point_to_point.cpython-312.pyc index a300f6887915c8835819f530df29d28673262313..1d1e7ce98035d5c0df25852e9620c0fba4c5593c 100644 Binary files a/python/ur_simple_control/clik/__pycache__/clik_point_to_point.cpython-312.pyc and b/python/ur_simple_control/clik/__pycache__/clik_point_to_point.cpython-312.pyc 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 514613736a71a71285d7aaa8b68468299ef04269..83ab327900f78d78898072c0ca5c86934b3b426d 100644 --- a/python/ur_simple_control/clik/clik_point_to_point.py +++ b/python/ur_simple_control/clik/clik_point_to_point.py @@ -7,22 +7,22 @@ from ur_simple_control.managers import ControlLoopManager, RobotManager import time from qpsolvers import solve_qp -""" -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(): + """ + 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. + """ 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) @@ -117,17 +117,17 @@ def invKinmQP(J, err_vector): qd = solve_qp(P, q, G, h, A, b, lb, ub, solver="quadprog") return qd -""" -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 -""" 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": @@ -155,14 +155,14 @@ def getClikController(args): # modularity yo # past data to comply with the API # TODO make this a kwarg or whatever to be more lax with this -""" -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. -""" def controlLoopClik(robot, 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 = {} # know where you are, i.e. do forward kinematics @@ -200,14 +200,14 @@ def controlLoopClik(robot, clik_controller, i, past_data): return breakFlag, {}, log_item -""" -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. -""" def moveUntilContactControlLoop(contact_detecting_force, speed, robot, 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 q = robot.getQ() @@ -233,12 +233,12 @@ def moveUntilContactControlLoop(contact_detecting_force, speed, robot, clik_cont robot.sendQd(qd) return breakFlag, {}, {} -""" -moveUntilContact ------ -does clik until it feels something with the f/t sensor -""" 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) # TODO: just make the manager pass the robot or something, this is weird man @@ -250,14 +250,14 @@ def moveUntilContact(args, robot, speed): time.sleep(0.01) print("Collision detected!!") -""" -moveL ------ -does moveL. -send a SE3 object as goal point. -if you don't care about rotation, make it np.zeros((3,3)) -""" def moveL(args, robot, 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) @@ -274,14 +274,14 @@ def moveL(args, robot, goal_point): print("MoveL done: convergence achieved, reached destionation!") -""" -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. -""" 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 = {} @@ -328,16 +328,17 @@ def controlLoopCompliantClik(args, robot : RobotManager, i, past_data): # we're not saving here, but need to respect the API, # hence the empty dict return breakFlag, save_past_dict, log_item -""" -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)) -""" + # 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) diff --git a/python/ur_simple_control/clik/clik_trajectory_following.py b/python/ur_simple_control/clik/clik_trajectory_following.py index 621be7dc9234cc8bc663b6f0d9c5bb9ccc6bf797..eae999f69fee0c5a9a82fc99adfac03976054ae1 100644 --- a/python/ur_simple_control/clik/clik_trajectory_following.py +++ b/python/ur_simple_control/clik/clik_trajectory_following.py @@ -14,23 +14,23 @@ from ur_simple_control.managers import ControlLoopManager ####################################################################### # map the pixels to a 3D plane # ####################################################################### -""" -map2DPathTo3DPlane --------------------- -TODO: THINK AND FINALIZE THE FRAME -TODO: WRITE PRINT ABOUT THE FRAME TO THE USER -assumptions: -- origin in top-left corner (natual for western latin script writing) -- x goes right (from TCP) -- z will go away from the board -- y just completes the right-hand frame -TODO: RIGHT NOW we don't have a right-handed frame lmao, change that where it should be -NOTE: this function as well be in the util or drawing file, but whatever for now, it will be done - once it will actually be needed elsewhere -Returns a 3D path appropriately scaled, and placed into the first quadrant -of the x-y plane of the body-frame (TODO: what is the body frame if we're general?) -""" def map2DPathTo3DPlane(path_2D, width, height): + """ + map2DPathTo3DPlane + -------------------- + TODO: THINK AND FINALIZE THE FRAME + TODO: WRITE PRINT ABOUT THE FRAME TO THE USER + assumptions: + - origin in top-left corner (natual for western latin script writing) + - x goes right (from TCP) + - z will go away from the board + - y just completes the right-hand frame + TODO: RIGHT NOW we don't have a right-handed frame lmao, change that where it should be + NOTE: this function as well be in the util or drawing file, but whatever for now, it will be done + once it will actually be needed elsewhere + Returns a 3D path appropriately scaled, and placed into the first quadrant + of the x-y plane of the body-frame (TODO: what is the body frame if we're general?) + """ z = np.zeros((len(path_2D),1)) path_3D = np.hstack((path_2D,z)) # scale the path to m @@ -43,43 +43,43 @@ def map2DPathTo3DPlane(path_2D, width, height): return path_3D -""" -clikCartesianPathIntoJointPath ------------------------------- -functionality ------------- -Follows a provided Cartesian path, -creates a joint space trajectory for it. +def clikCartesianPathIntoJointPath(path, args, robot, \ + """ + clikCartesianPathIntoJointPath + ------------------------------ + functionality + ------------ + Follows a provided Cartesian path, + creates a joint space trajectory for it. -return ------- -- joint_space_trajectory to follow the given path. + return + ------ + - joint_space_trajectory to follow the given path. -arguments ----------- -- path: - --> cartesian path given in task frame -TODO: write asserts for these arguments -- args: - --> all the magic numbers used here better be here -- clikController: - --> clik controller you're using -- robot: - --> RobotManager instance (needed for forward kinematics etc) -TODO: maybe it's better design to expect path in body frame idk - the good thing here is you're forced to think about frames. -TODO: make this a kwarg with a neural transform as default. -- transf_body_task_frame: - --> A transformation from the body frame to task frame. - --> It is assumed that the path was provided in the task frame - because 9/10 times that's more convenient, - and you can just pass a neural transform if you're not using it. -- q_init: - --> starting point. - --> you can movej to it before executing the trajectory, - so this makes perfect sense to ensure correctness -""" -def clikCartesianPathIntoJointPath(path, args, robot, \ + arguments + ---------- + - path: + --> cartesian path given in task frame + TODO: write asserts for these arguments + - args: + --> all the magic numbers used here better be here + - clikController: + --> clik controller you're using + - robot: + --> RobotManager instance (needed for forward kinematics etc) + TODO: maybe it's better design to expect path in body frame idk + the good thing here is you're forced to think about frames. + TODO: make this a kwarg with a neural transform as default. + - transf_body_task_frame: + --> A transformation from the body frame to task frame. + --> It is assumed that the path was provided in the task frame + because 9/10 times that's more convenient, + and you can just pass a neural transform if you're not using it. + - q_init: + --> starting point. + --> you can movej to it before executing the trajectory, + so this makes perfect sense to ensure correctness + """ clikController, q_init, R, p): transf_body_to_task_frame = pin.SE3(R, p) diff --git a/python/ur_simple_control/managers.py b/python/ur_simple_control/managers.py index bdd49bb1703b2195177a47ef3bc27a6fec1175f0..9a7908a6066af4add6177116ecfa59f66034543e 100644 --- a/python/ur_simple_control/managers.py +++ b/python/ur_simple_control/managers.py @@ -13,7 +13,8 @@ import time from rtde_control import RTDEControlInterface from rtde_receive import RTDEReceiveInterface from rtde_io import RTDEIOInterface -from ur_simple_control.util.robotiq_gripper import RobotiqGripper +from ur_simple_control.util.grippers.robotiq.robotiq_gripper import RobotiqGripper +from ur_simple_control.util.grippers.on_robot.twofg import TWOFG import copy import signal from ur_simple_control.util.get_model import get_model @@ -57,49 +58,49 @@ There is an interface to a physics simulator. Functions for torque controlled robots exist. """ -""" -ControlLoopManager -------------------- -Slightly fancier programming (passing a function as argument and using functools.partial) -to get a wrapper around the control loop. -In other words, it's the book-keeping around the actual control loop. -It's a class because it keeps non-directly-control-loop-related parameters -like max_iterations, what data to save etc. -NOTE: you give this the ready-made control loop. -if it has arguments, bake them in with functools.partial. -Handles short-term data saving and logging. -Details on this are given below. - -Short term data saving: - - it's a dictionaries of deques (initialized here), because deque is the most convenient class - for removing the first element and appending a last (it is just a linked list under the hood of course). - - it's a dictionary for modularity's sake, because this way you can save whatever you want - - and it will just work based on dictionary keys. - - it is the user's resposibility to make sure they're providing correct data. - - --> TODO but make an assert for the keys at least - - in the c++ imlementation, make the user write their own struct or something. - - since this is python, you need to give me initial values to infer types. - - you need to provide initial values to populate the deque to start anyway. - -Logging data (for analysis and plotting): - - it can only be handled here because the control loop itself only cares about the present/ - a small time-window around it. - - saves it all in a dictionary of ndarrays (initialized here), returns that after a run - TODO: it's provided by the user now, make it actually initialize here!!! - - you need to specify which keys you're using to do the initialization - - later, the controlLoop needs to return what's to be save in a small temporary dict. - - NOTE: this is of course a somewhat redundant solution, but it's the simplest - and most flexible way of doing it. - it probably will be done some other way down the line, but it works and is not - a priority right now - -Other info: -- relies on RobotManager to handle all the magic numbers - that are not truly only control loop related +class ControlLoopManager: + """ + ControlLoopManager + ------------------- + Slightly fancier programming (passing a function as argument and using functools.partial) + to get a wrapper around the control loop. + In other words, it's the book-keeping around the actual control loop. + It's a class because it keeps non-directly-control-loop-related parameters + like max_iterations, what data to save etc. + NOTE: you give this the ready-made control loop. + if it has arguments, bake them in with functools.partial. + Handles short-term data saving and logging. + Details on this are given below. + + Short term data saving: + - it's a dictionaries of deques (initialized here), because deque is the most convenient class + for removing the first element and appending a last (it is just a linked list under the hood of course). + - it's a dictionary for modularity's sake, because this way you can save whatever you want + - and it will just work based on dictionary keys. + - it is the user's resposibility to make sure they're providing correct data. + - --> TODO but make an assert for the keys at least + - in the c++ imlementation, make the user write their own struct or something. + - since this is python, you need to give me initial values to infer types. + - you need to provide initial values to populate the deque to start anyway. + + Logging data (for analysis and plotting): + - it can only be handled here because the control loop itself only cares about the present/ + a small time-window around it. + - saves it all in a dictionary of ndarrays (initialized here), returns that after a run + TODO: it's provided by the user now, make it actually initialize here!!! + - you need to specify which keys you're using to do the initialization + - later, the controlLoop needs to return what's to be save in a small temporary dict. + - NOTE: this is of course a somewhat redundant solution, but it's the simplest + and most flexible way of doing it. + it probably will be done some other way down the line, but it works and is not + a priority right now + + Other info: + - relies on RobotManager to handle all the magic numbers + that are not truly only control loop related + """ -""" -class ControlLoopManager: def __init__(self, robot_manager, controlLoop, args, save_past_item, log_item): signal.signal(signal.SIGINT, self.stopHandler) self.max_iterations = args.max_iterations @@ -145,15 +146,15 @@ class ControlLoopManager: print("CONTROL_LOOP_MANAGER: i managed to put initializing log_item to queue") - """ - run - --- - do timing to run at 500Hz. - also handle the number of iterations. - it's the controlLoop's responsibility to break if it achieved it's goals. - this is done via the breakFlag - """ def run(self): + """ + run + --- + do timing to run at 500Hz. + also handle the number of iterations. + it's the controlLoop's responsibility to break if it achieved it's goals. + this is done via the breakFlag + """ self.final_iteration = 0 for i in range(self.max_iterations): start = time.time() @@ -242,16 +243,16 @@ class ControlLoopManager: return self.log_dict, self.final_iteration - """ - stopHandler - ----------- - upon receiving SIGINT it sends zeros for speed commands to - stop the robot. - NOTE: apparently this isn't enough, - nor does stopJ do anything, so it goes to freedriveMode - and then exits it, which actually stops ur robots at least. - """ def stopHandler(self, signum, frame): + """ + stopHandler + ----------- + upon receiving SIGINT it sends zeros for speed commands to + stop the robot. + NOTE: apparently this isn't enough, + nor does stopJ do anything, so it goes to freedriveMode + and then exits it, which actually stops ur robots at least. + """ print('sending 300 speedjs full of zeros and exiting') for i in range(300): vel_cmd = np.zeros(6) @@ -305,30 +306,31 @@ class ControlLoopManager: exit() -""" -robotmanager: ---------------- -- design goal: rely on pinocchio as much as possible while - concealing obvious bookkeeping -- right now it is assumed you're running this on UR5e so some - magic numbers are just put to it. - this will be extended once there's a need for it. -- at this stage it's just a boilerplate reduction class - but the idea is to inherit it for more complicated things - with many steps, like dmp. - or just showe additional things in, this is python after all -- you write your controller separately, - and then drop it into this - there is a wrapper function you put - around the control loop which handles timing so that you - actually run at 500Hz and not more. -- this is probably not the most new-user friendly solution, - but it's designed for fastest idea to implementation rate. -- if this was a real programming language, all of these would really be private variables. - as it currently stands, "private" functions have the '_' prefix - while the public getters don't have a prefix. -- TODO: write out default arguments needed here as well -""" class RobotManager: + """ + RobotManager: + --------------- + - design goal: rely on pinocchio as much as possible while + concealing obvious bookkeeping + - right now it is assumed you're running this on UR5e so some + magic numbers are just put to it. + this will be extended once there's a need for it. + - at this stage it's just a boilerplate reduction class + but the idea is to inherit it for more complicated things + with many steps, like dmp. + or just showe additional things in, this is python after all + - you write your controller separately, + and then drop it into this - there is a wrapper function you put + around the control loop which handles timing so that you + actually run at 500Hz and not more. + - this is probably not the most new-user friendly solution, + but it's designed for fastest idea to implementation rate. + - if this was a real programming language, all of these would really be private variables. + as it currently stands, "private" functions have the '_' prefix + while the public getters don't have a prefix. + - TODO: write out default arguments needed here as well + """ + # just pass all of the arguments here and store them as is # so as to minimize the amount of lines. # might be changed later if that seems more appropriate @@ -336,7 +338,6 @@ class RobotManager: self.args = args self.pinocchio_only = args.pinocchio_only self.simulation = args.simulation - self.gripper_flag = args.gripper # load model # collision and visual models are none if args.visualize == False self.model, self.collision_model, self.visual_model, self.data = \ @@ -391,10 +392,16 @@ class RobotManager: # TODO: make general - if self.gripper_flag and not self.pinocchio_only: - self.gripper = RobotiqGripper() - self.gripper.connect(args.robot_ip, 63352) - self.gripper.activate() + self.gripper = None + if (self.args.gripper != "none") and not self.pinocchio_only: + if self.args.gripper == "robotiq": + self.gripper = RobotiqGripper() + self.gripper.connect(args.robot_ip, 63352) + self.gripper.activate() + if self.args.gripper == "onrobot": + device = OnRobotDevice() + device.getCB() + self.gripper = TWOFG(device) # also TODO: figure out how to best solve the gripper_velocity problem # NOTE: you need to initialize differently for other types of joints @@ -419,7 +426,7 @@ class RobotManager: # and you need to minus them to have usable readings. # we provide this with calibrateFT self.wrench_offset = self.calibrateFT() - if self.gripper_flag: + if self.args.gripper == "robotiq": self.gripper.connect("192.168.1.102", 63352) # this is a blocking call # this isn't actually needed and it's annoying @@ -468,17 +475,17 @@ class RobotManager: - """ - calibrateFT - ----------- - Read from the f/t sensor a bit, average the results - and return the result. - This can be used to offset the bias of the f/t sensor. - NOTE: this is not an ideal solution. - ALSO TODO: test whether the offset changes when - the manipulator is in different poses. - """ def calibrateFT(self): + """ + calibrateFT + ----------- + Read from the f/t sensor a bit, average the results + and return the result. + This can be used to offset the bias of the f/t sensor. + NOTE: this is not an ideal solution. + ALSO TODO: test whether the offset changes when + the manipulator is in different poses. + """ ft_readings = [] print("Will read from f/t sensors for a some number of seconds") print("and give you the average.") @@ -504,28 +511,28 @@ class RobotManager: print(self.wrench_offset) return self.wrench_offset.copy() - """ - _step - ---- - - the idea is to update everything that should be updated - on a step-by-step basis - - the actual problem this is solving is that you're not calling - forwardKinematics, an expensive call, more than once per step. - - within the TODO is to make all (necessary) variable private - so that you can rest assured that everything is handled the way - it's supposed to be handled. then have getters for these - private variables which return deepcopies of whatever you need. - that way the computations done in the control loop - can't mess up other things. this is important if you want - to switch between controllers during operation and have a completely - painless transition between them. - TODO: make the getQ, getQd and the rest here do the actual communication, - and make these functions private. - then have the deepcopy getters public. - also TODO: make ifs for the simulation etc. - this is less ifs overall right. - """ def _step(self): + """ + _step + ---- + - the idea is to update everything that should be updated + on a step-by-step basis + - the actual problem this is solving is that you're not calling + forwardKinematics, an expensive call, more than once per step. + - within the TODO is to make all (necessary) variable private + so that you can rest assured that everything is handled the way + it's supposed to be handled. then have getters for these + private variables which return deepcopies of whatever you need. + that way the computations done in the control loop + can't mess up other things. this is important if you want + to switch between controllers during operation and have a completely + painless transition between them. + TODO: make the getQ, getQd and the rest here do the actual communication, + and make these functions private. + then have the deepcopy getters public. + also TODO: make ifs for the simulation etc. + this is less ifs overall right. + """ self._getQ() self._getQd() #self._getWrench() @@ -544,31 +551,31 @@ class RobotManager: # TODO NOTE: you'll want to do the additional math for # torque controlled robots here, but it's ok as is rn - """ - setSpeedSlider - --------------- - update in all places - """ def setSpeedSlider(self, value): + """ + setSpeedSlider + --------------- + update in all places + """ assert value <= 1.0 and value > 0.0 if not self.args.pinocchio_only: self.rtde_io.setSpeedSlider(value) self.speed_slider = value - """ - _getQ - ----- - NOTE: private function for use in _step(), use the getter getQ() - urdf treats gripper as two prismatic joints, - but they do not affect the overall movement - of the robot, so we add or remove 2 items to the joint list. - also, the gripper is controlled separately so we'd need to do this somehow anyway - NOTE: this gripper_past_pos thing is not working atm, but i'll keep it here as a TODO - """ def _getQ(self): + """ + _getQ + ----- + NOTE: private function for use in _step(), use the getter getQ() + urdf treats gripper as two prismatic joints, + but they do not affect the overall movement + of the robot, so we add or remove 2 items to the joint list. + also, the gripper is controlled separately so we'd need to do this somehow anyway + NOTE: this gripper_past_pos thing is not working atm, but i'll keep it here as a TODO + """ if not self.pinocchio_only: q = self.rtde_receive.getActualQ() - if self.args.gripper: + if self.args.gripper == "robotiq": # TODO: make it work or remove it #self.gripper_past_pos = self.gripper_pos # this is pointless by itself @@ -587,19 +594,19 @@ class RobotManager: q = np.array(q) self.q = q - """ - _getT_w_e - ----- - NOTE: private function, use the getT_w_e() getter - urdf treats gripper as two prismatic joints, - but they do not affect the overall movement - of the robot, so we add or remove 2 items to the joint list. - also, the gripper is controlled separately so we'd need to do this somehow anyway - NOTE: this gripper_past_pos thing is not working atm, but i'll keep it here as a TODO. - NOTE: don't use this if use called _step() because it repeats forwardKinematics - """ # TODO remove evil hack def _getT_w_e(self, q_given=None): + """ + _getT_w_e + ----- + NOTE: private function, use the getT_w_e() getter + urdf treats gripper as two prismatic joints, + but they do not affect the overall movement + of the robot, so we add or remove 2 items to the joint list. + also, the gripper is controlled separately so we'd need to do this somehow anyway + NOTE: this gripper_past_pos thing is not working atm, but i'll keep it here as a TODO. + NOTE: don't use this if use called _step() because it repeats forwardKinematics + """ test = True try: test = q_given.all() == None @@ -635,17 +642,17 @@ class RobotManager: # TODO probably remove deepcopy self.T_w_e = self.data.oMi[self.JOINT_ID] - """ - _getQd - ----- - NOTE: private function, use the _getQd() getter - same note as _getQ. - TODO NOTE: atm there's no way to get current gripper velocity. - this means you'll probably want to read current positions and then finite-difference - to get the velocity. - as it stands right now, we'll just pass zeros in because I don't need this ATM - """ def _getQd(self): + """ + _getQd + ----- + NOTE: private function, use the _getQd() getter + same note as _getQ. + TODO NOTE: atm there's no way to get current gripper velocity. + this means you'll probably want to read current positions and then finite-difference + to get the velocity. + as it stands right now, we'll just pass zeros in because I don't need this ATM + """ if not self.pinocchio_only: qd = self.rtde_receive.getActualQd() if self.args.gripper: @@ -669,15 +676,15 @@ class RobotManager: qd = np.array(qd) self.v_q = qd - """ - _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 _getWrenchRaw(self): + """ + _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) + """ if not self.pinocchio_only: wrench = np.array(self.rtde_receive.getActualTCPForce()) else: @@ -706,15 +713,15 @@ class RobotManager: mapping[3:6, 3:6] = self.T_w_e.rotation self.wrench = mapping.T @ self.wrench - """ - sendQd - ----- - 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 sendQd(self, qd): + """ + sendQd + ----- + 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) + """ # we're hiding the extra 2 prismatic joint shenanigans from the control writer # because there you shouldn't need to know this anyway qd_cmd = qd[:6] @@ -751,21 +758,21 @@ class RobotManager: # utility functions # ####################################################################### - """ - defineGoalPointCLI - ------------------ - NOTE: this assume _step has not been called because it's run before the controlLoop - --> best way to handle the goal is to tell the user where the gripper is - in both UR tcp frame and with pinocchio and have them - manually input it when running. - this way you force the thinking before the moving, - but you also get to view and analyze the information first - TODO get the visual thing you did in ivc project with sliders also. - it's just text input for now because it's totally usable, just not superb. - but also you do want to have both options. obviously you go for the sliders - in the case you're visualizing, makes no sense otherwise. - """ def defineGoalPointCLI(self): + """ + defineGoalPointCLI + ------------------ + NOTE: this assume _step has not been called because it's run before the controlLoop + --> best way to handle the goal is to tell the user where the gripper is + in both UR tcp frame and with pinocchio and have them + manually input it when running. + this way you force the thinking before the moving, + but you also get to view and analyze the information first + TODO get the visual thing you did in ivc project with sliders also. + it's just text input for now because it's totally usable, just not superb. + but also you do want to have both options. obviously you go for the sliders + in the case you're visualizing, makes no sense otherwise. + """ self._getQ() q = self.getQ() # define goal @@ -806,18 +813,18 @@ class RobotManager: self.Mgoal = Mgoal return Mgoal - """ - killManipulatorVisualizer - --------------------------- - if you're using the manipulator visualizer, you want to start it only once. - because you start the meshcat server, initialize the manipulator and then - do any subsequent changes with that server. there's no point in restarting. - but this means you have to kill it manually, because the ControlLoopManager - can't nor should know whether this is the last control loop you're running - - RobotManager has to handle the meshcat server. - and in this case the user needs to say when the tasks are done. - """ def killManipulatorVisualizer(self): + """ + killManipulatorVisualizer + --------------------------- + if you're using the manipulator visualizer, you want to start it only once. + because you start the meshcat server, initialize the manipulator and then + do any subsequent changes with that server. there's no point in restarting. + but this means you have to kill it manually, because the ControlLoopManager + can't nor should know whether this is the last control loop you're running - + RobotManager has to handle the meshcat server. + and in this case the user needs to say when the tasks are done. + """ if self.args.debug_prints: print("i am putting befree in plotter_queue to stop the manipulator visualizer") # putting this command tells our process to kill the meshcat zmq server process diff --git a/python/ur_simple_control/optimal_control/notes.md b/python/ur_simple_control/optimal_control/notes.md new file mode 100644 index 0000000000000000000000000000000000000000..83489752a753b32e17450d09c79a0fe301bf164e --- /dev/null +++ b/python/ur_simple_control/optimal_control/notes.md @@ -0,0 +1,8 @@ +## goal +----------- +use crocoddyl to compute a whole trajectory in advance. +the trajectory can be followed with some other controller, +with controls here being feed-forward or whatever + + + diff --git a/python/ur_simple_control/optimal_control/optimal_control.py b/python/ur_simple_control/optimal_control/optimal_control.py new file mode 100644 index 0000000000000000000000000000000000000000..f5c724907b1a93c627849802192acfec8fb18c9c --- /dev/null +++ b/python/ur_simple_control/optimal_control/optimal_control.py @@ -0,0 +1,7 @@ +import numpy as np +import pinocchio as pin +import crocoddyl +from ur_simple_control.managers import ControlLoopManager, RobotManager + +def IKOCP(robot : RobotManager, goal : pin.SE3): + state = crocoddyl.StateMultibody(robot.model) diff --git a/python/ur_simple_control/util/grippers/abstract_gripper.py b/python/ur_simple_control/util/grippers/abstract_gripper.py new file mode 100644 index 0000000000000000000000000000000000000000..b8672d2969b9fe18aa594704c918e26f2e85d872 --- /dev/null +++ b/python/ur_simple_control/util/grippers/abstract_gripper.py @@ -0,0 +1,32 @@ +from abc import ABC + +class AbstractGripper(ABC): + """ + AbstractGripper + ------------------ + Abstract gripper class enforcing all grippers to have the same API toward RobotManager. + The point of this is to avoid having too many ifs in RobotManager + which reduce its readability, while achieving the same effect + of moving the stupid hardware bookkeeping out of sight. + Bookkeeping here refers to various grippers using different interfaces and differently + named functions just to do the same thing - move the gripper to a certain position + with a certain speed and certain final gripping force. + There are also the classic expected open(), close(), isGripping() quality-of-life functions. + """ + + + def move(self, position, speed=None, gripping_force=None): + pass + + def open(self): + pass + + def close(self): + pass + +# def setVelocity(self): +# pass +# +# def setGrippingForce(self): +# pass +# diff --git a/python/ur_simple_control/util/grippers/on_robot/twofg.py b/python/ur_simple_control/util/grippers/on_robot/twofg.py new file mode 100644 index 0000000000000000000000000000000000000000..e4b11514d6c111244f2a417deae674ab3a126d64 --- /dev/null +++ b/python/ur_simple_control/util/grippers/on_robot/twofg.py @@ -0,0 +1,281 @@ +import time +import numpy as np +import xmlrpc.client +from ur_simple_control.util.grippers.abstract_gripper import AbstractGripper + + +class OnRobotDevice: + ''' + Generic OnRobot device object + ''' + cb = None + + def __init__(self, Global_cbip='192.168.1.1'): + #try to get Computebox IP address + try: + self.Global_cbip = Global_cbip + except NameError: + print("Global_cbip is not defined!") + + def getCB(self): + try: + self.cb = xmlrpc.client.ServerProxy( + "http://" + str(self.Global_cbip) + ":41414/") + return self.cb + except TimeoutError: + print("Connection to ComputeBox failed!") + + +''' +XML-RPC library for controlling OnRobot devcies from Doosan robots + +Global_cbip holds the IP address of the compute box, needs to be defined by the end user +''' + + +class TWOFG(AbstractGripper): + ''' + This class is for handling the 2FG device + ''' + cb = None + + def __init__(self, dev): + self.cb = dev.getCB() + # Device ID + self.TWOFG_ID = 0xC0 + + # Connection + self.CONN_ERR = -2 # Connection failure + self.RET_OK = 0 # Okay + self.RET_FAIL = -1 # Error + + # arguments moved to these parameters because you almost certainly won't need this + self.t_index = 0 + self.wait_for_grip = False + self.speed = 10 # [m/s] + self.gripping_force = 20 # [N] + self.max_width = self.get_max_exposition() + self.min_width = self.get_min_exposition() + + def isConnected(self): + ''' + Returns with True if 2FG device is connected, False otherwise + + @param MOVED TO PROPERTY t_index: The position of the device (0 for single, 1 for dual primary, 2 for dual secondary) + @return: True if connected, False otherwise + @rtype: bool + ''' + try: + IsTwoFG = self.cb.cb_is_device_connected(self.t_index, self.TWOFG_ID) + except TimeoutError: + IsTwoFG = False + + if IsTwoFG is False: + print("No 2FG device connected on the given instance") + return False + else: + return True + + def isBusy(self): + ''' + Gets if the gripper is busy or not + + @param MOVED TO PROPERTY t_index: The position of the device (0 for single, 1 for dual primary, 2 for dual secondary) + @type t_index: int + + @rtype: bool + @return: True if busy, False otherwise + ''' + if self.isConnected() is False: + return self.CONN_ERR + return self.cb.twofg_get_busy(self.t_index) + + def setWaitForGrip(self, wait_for_grip : bool): + self.wait_for_grip = wait_for_grip + + def open(self): + self.move(self.max_width) + + def close(self): + self.move(self.min_width) + + def setSpeed(speed : float): + self.speed = speed + + def setGrippingForce(gripping_force : float): + self.gripping_force = gripping_force + + def isGripped(self): + ''' + Gets if the gripper is gripping or not + + @param MOVED TO PROPERTY t_index: The position of the device (0 for single, 1 for dual primary, 2 for dual secondary) + @type t_index: int + + @rtype: bool + @return: True if gripped, False otherwise + ''' + if self.isConnected() is False: + return self.CONN_ERR + return self.cb.twofg_get_grip_detected() + + def getStatus(self): + ''' + Gets the status of the gripper + + @param MOVED TO PROPERTY t_index: The position of the device (0 for single, 1 for dual primary, 2 for dual secondary) + @type t_index: int + + @rtype: int + @return: Status code of the device + ''' + if self.isConnected() is False: + return self.CONN_ERR + status = self.cb.twofg_get_status(self.t_index) + return status + + def get_exposition(self): + ''' + Returns with current external width + + @param MOVE TO PROPERTY: t_index: The position of the device (0 for single, 1 for dual primary, 2 for dual secondary) + @return: External width in mm + @rtype: float + ''' + if self.isConnected() is False: + return self.CONN_ERR + extWidth = self.cb.twofg_get_external_width(self.t_index) + return extWidth + + def get_min_exposition(self): + ''' + Returns with current minimum external width + + @param MOVE TO PROPERTY: t_index: The position of the device (0 for single, 1 for dual primary, 2 for dual secondary) + @return: Minimum external width in mm + @rtype: float + ''' + if self.isConnected() is False: + return self.CONN_ERR + extMinWidth = self.cb.twofg_get_min_external_width(self.t_index) + return extMinWidth + + def get_max_exposition(self): + ''' + Returns with current maximum external width + + @param MOVED TO PROPERTY t_index: The position of the device (0 for single, 1 for dual primary, 2 for dual secondary) + @return: Maximum external width in mm + @rtype: float + ''' + if self.isConnected() is False: + return self.CONN_ERR + extMaxWidth = self.cb.twofg_get_max_external_width(self.t_index) + return extMaxWidth + + def get_force(self): + ''' + Returns with current force + + @param MOVED TO PROPERTY t_index: The position of the device (0 for single, 1 for dual primary, 2 for dual secondary) + @return: Force in N + @rtype: float + ''' + if self.isConnected() is False: + return self.CONN_ERR + currForce = self.cb.twofg_get_force(self.t_index) + return currForce + + def stop(self): + ''' + Stop the grippers movement + + @param MOVED TO PROPERTY t_index: The position of the device (0 for single, 1 for dual primary, 2 for dual secondary) + @type t_index: int + ''' + if self.isConnected() is False: + return self.CONN_ERR + self.cb.twofg_stop(self.t_index) + + def move(self, position, speed=None, gripping_force=None): + ''' + Makes an external grip with the gripper to the desired position + + @param MOVED TO PROPERTY t_index: The position of the device (0 for single, 1 for dual primary, 2 for dual secondary) + @param position: The width to move the gripper to in mm's + @type position: float + @param gripping_force: The force to move the gripper width in N + @type gripping_force: float + @param speed: The speed of the gripper in % + @type speed: int + @type self.wait_for_grip: bool + @param self.wait_for_grip: wait for the grip to end or not? + ''' + + if self.isConnected() is False: + return self.CONN_ERR + + if speed == None: + speed = self.speed + + if gripping_force == None: + gripping_force = self.gripping_force + + #Sanity check + #self.max_width = self.get_max_exposition() + #self.min_width = self.get_min_exposition() + if position > self.max_width or position < self.min_width: + print("Invalid 2FG width parameter, " + str(self.max_width)+" - "+str(self.min_width) +" is valid only") + return self.RET_FAIL + + if gripping_force > 140 or gripping_force < 20: + print("Invalid 2FG force parameter, 20-140 is valid only") + return self.RET_FAIL + + if speed > 100 or speed < 10: + print("Invalid 2FG speed parameter, 10-100 is valid only") + return self.RET_FAIL + + self.cb.twofg_grip_external(self.t_index, float(position), int(gripping_force), int(speed)) + + if self.wait_for_grip: + tim_cnt = 0 + fbusy = self.isBusy() + while (fbusy): + time.sleep(0.1) + fbusy = self.isBusy() + tim_cnt += 1 + if tim_cnt > 30: + print("2FG external grip command timeout") + break + else: + #Grip detection + grip_tim = 0 + gripped = self.isGripped() + while (not gripped): + time.sleep(0.1) + gripped = self.isGripped() + grip_tim += 1 + if grip_tim > 20: + print("2FG external grip detection timeout") + break + else: + return self.RET_OK + return self.RET_FAIL + return self.RET_FAIL + else: + return self.RET_OK + + +if __name__ == '__main__': + device = OnRobotDevice() + device.getCB() + gripper_2FG7 = TWOFG(device) + #gripper_2FG7.grip(0, position=37.0) + #gripper_2FG7.grip(0, position=37.0) + gripper_2FG7.move(10.0) + time.sleep(1.0) + gripper_2FG7.close() + time.sleep(1.0) + gripper_2FG7.open() + print("Connection check: ", gripper_2FG7.isConnected()) diff --git a/python/ur_simple_control/util/robotiq_gripper.py b/python/ur_simple_control/util/grippers/robotiq/robotiq_gripper.py similarity index 95% rename from python/ur_simple_control/util/robotiq_gripper.py rename to python/ur_simple_control/util/grippers/robotiq/robotiq_gripper.py index 53efd879267a9ee28a58ecbebdcd008cc1450ac6..977db0c09042be89a0a6c348b36dcdcbcc33d2fb 100644 --- a/python/ur_simple_control/util/robotiq_gripper.py +++ b/python/ur_simple_control/util/grippers/robotiq/robotiq_gripper.py @@ -5,8 +5,10 @@ import threading import time from enum import Enum from typing import Union, Tuple, OrderedDict +from ur_simple_control.util.grippers.abstract_gripper import AbstractGripper -class RobotiqGripper: + +class RobotiqGripper(AbstractGripper): """ Communicates with the gripper directly, via socket with string commands, leveraging string names for variables. """ @@ -281,13 +283,13 @@ class RobotiqGripper: if log: print(f"Gripper auto-calibrated to [{self.get_min_position()}, {self.get_max_position()}]") - def move(self, position: int, speed: int, force: int) -> Tuple[bool, int]: + def move(self, position: int, speed=255, gripping_force=0) -> Tuple[bool, int]: """ Sends commands to start moving towards the given position, with the - specified speed and force. + specified speed and gripping_force. :param position: Position to move to [min_position, max_position] :param speed: Speed to move at [min_speed, max_speed] - :param force: Force to use [min_force, max_force] + :param gripping_force: Force to use [min_force, max_force] :return: A tuple with a bool indicating whether the action it was successfully sent, and an integer with the actual position that was requested, after being adjusted to the min/max calibrated range. """ @@ -297,12 +299,18 @@ class RobotiqGripper: clip_pos = clip_val(self._min_position, position, self._max_position) clip_spe = clip_val(self._min_speed, speed, self._max_speed) - clip_for = clip_val(self._min_force, force, self._max_force) + clip_for = clip_val(self._min_force, gripping_force, self._max_force) - # moves to the given position with the given speed and force + # moves to the given position with the given speed and gripping_force var_dict = OrderedDict([(self.POS, clip_pos), (self.SPE, clip_spe), (self.FOR, clip_for), (self.GTO, 1)]) return self._set_vars(var_dict), clip_pos + def open(self): + self.move(0, 255, 255) + + def close(self): + self.move(255, 255, 0) + def move_and_wait_for_pos(self, position: int, speed: int, force: int) -> Tuple[int, ObjectStatus]: # noqa """ Sends commands to start moving towards the given position, with the