diff --git a/python/examples/__init__.py b/python/examples/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/python/examples/cart_pulling.py b/python/examples/cart_pulling/cart_pulling.py similarity index 100% rename from python/examples/cart_pulling.py rename to python/examples/cart_pulling/cart_pulling.py diff --git a/python/examples/cart_pulling_notes.md b/python/examples/cart_pulling/cart_pulling_notes.md similarity index 100% rename from python/examples/cart_pulling_notes.md rename to python/examples/cart_pulling/cart_pulling_notes.md diff --git a/python/examples/clik.py b/python/examples/cartesian_space/clik.py similarity index 100% rename from python/examples/clik.py rename to python/examples/cartesian_space/clik.py diff --git a/python/examples/crocoddyl_mpc.py b/python/examples/crocoddyl_mpc.py deleted file mode 100644 index e6921bbb54557324fad8f035b6481488b67b90e0..0000000000000000000000000000000000000000 --- a/python/examples/crocoddyl_mpc.py +++ /dev/null @@ -1,84 +0,0 @@ -import numpy as np -from smc import getMinimalArgParser, getRobotFromArgs -from smc.control.optimal_control.crocoddyl_optimal_control import ( - createCrocoIKOCP, - solveCrocoOCP, -) -from smc.control.optimal_control.util import get_OCP_args -from smc.control.optimal_control.crocoddyl_mpc import CrocoIKMPC -from smc.control.cartesian_space import getClikArgs -import pinocchio as pin -import importlib.util -import argcomplete - - -def get_args(): - parser = getMinimalArgParser() - parser = get_OCP_args(parser) - parser = getClikArgs(parser) # literally just for goal error - argcomplete.autocomplete(parser) - args = parser.parse_args() - return args - - -if __name__ == "__main__": - if importlib.util.find_spec("mim_solvers"): - import mim_solvers - - args = get_args() - robot = getRobotFromArgs(args) - # TODO: put this back for nicer demos - # Mgoal = robot.defineGoalPointCLI() - Mgoal = pin.SE3.Random() - if robot.robot_name == "yumi": - Mgoal.rotation = np.eye(3) - Mgoal_transform = pin.SE3.Identity() - Mgoal_transform.translation[1] = 0.1 - Mgoal_left = Mgoal_transform.act(Mgoal) - Mgoal_right = Mgoal_transform.inverse().act(Mgoal) - Mgoal = (Mgoal_left, Mgoal_right) - - robot.Mgoal = Mgoal.copy() - if args.visualizer: - # TODO document this somewhere - robot.visualizer_manager.sendCommand({"Mgoal": Mgoal}) - # 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 - # starting state - x0 = np.concatenate([robot.q, robot.v]) - problem = createCrocoIKOCP(args, robot, x0, Mgoal) - - # NOTE: this might be useful if we solve with a large time horizon, - # lower frequency, and then follow the predicted trajectory with velocity p-control - # this shouldn't really depend on x0 but i can't be bothered - # reference, solver = solveCrocoOCP(args, robot, problem, x0) - # if args.solver == "boxfddp": - # log = solver.getCallbacks()[1] - # crocoddyl.plotOCSolution(log.xs, log.us, figIndex=1, show=True) - # if args.solver == "csqp": - # log = solver.getCallbacks()[1] - # mim_solvers.plotOCSolution(log.xs, log.us, figIndex=1, show=True) - - # 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 - # followKinematicJointTrajP(args, robot, reference) - - CrocoIKMPC(args, robot, Mgoal) - - print("final position:") - print(robot.getT_w_e()) - - if args.real: - robot.stopRobot() - - if args.visualizer: - robot.killManipulatorVisualizer() - - if args.save_log: - robot._log_manager.saveLog() - robot._log_manager.plotAllControlLoops() - - # loop_manager.stopHandler(None, None) diff --git a/python/examples/drawing_from_input_drawing.py b/python/examples/drawing/drawing_from_input_drawing.py similarity index 100% rename from python/examples/drawing_from_input_drawing.py rename to python/examples/drawing/drawing_from_input_drawing.py diff --git a/python/examples/plane_pose.pickle_save b/python/examples/drawing/plane_pose.pickle_save similarity index 100% rename from python/examples/plane_pose.pickle_save rename to python/examples/drawing/plane_pose.pickle_save diff --git a/python/examples/wiping_path.csv_save b/python/examples/drawing/wiping_path.csv_save similarity index 100% rename from python/examples/wiping_path.csv_save rename to python/examples/drawing/wiping_path.csv_save diff --git a/python/examples/point_impedance_control.py b/python/examples/impedance/point_impedance_control.py similarity index 100% rename from python/examples/point_impedance_control.py rename to python/examples/impedance/point_impedance_control.py diff --git a/python/examples/joint_trajectory.csv b/python/examples/joint_trajectory.csv deleted file mode 100644 index 179460bd485474e866e136d7685823fdfb2f1e01..0000000000000000000000000000000000000000 --- a/python/examples/joint_trajectory.csv +++ /dev/null @@ -1,187 +0,0 @@ -0.00000,0.53253,-1.30334,-1.52085,-1.05683,2.47804,-0.62074 -0.08065,0.53252,-1.30337,-1.52080,-1.05684,2.47805,-0.62074 -0.16129,0.53453,-1.30974,-1.50563,-1.06203,2.47638,-0.61634 -0.24194,0.54595,-1.31977,-1.47230,-1.07385,2.46769,-0.60187 -0.32258,0.55266,-1.32414,-1.45596,-1.07980,2.46260,-0.59417 -0.40323,0.56163,-1.32845,-1.43722,-1.08672,2.45579,-0.58450 -0.48387,0.56504,-1.32991,-1.43048,-1.08923,2.45320,-0.58091 -0.56452,0.57036,-1.33149,-1.42126,-1.09265,2.44914,-0.57542 -0.64516,0.57777,-1.33292,-1.40990,-1.09687,2.44348,-0.56795 -0.72581,0.58637,-1.33341,-1.39890,-1.10092,2.43687,-0.55944 -0.80645,0.59562,-1.33387,-1.38741,-1.10518,2.42973,-0.55048 -0.88710,0.60467,-1.33393,-1.37703,-1.10904,2.42271,-0.54187 -0.96774,0.61330,-1.33305,-1.36893,-1.11201,2.41599,-0.53381 -1.04839,0.61886,-1.33212,-1.36441,-1.11364,2.41163,-0.52866 -1.12903,0.62256,-1.33100,-1.36231,-1.11435,2.40872,-0.52526 -1.20968,0.62513,-1.32995,-1.36136,-1.11463,2.40670,-0.52292 -1.29032,0.62947,-1.32740,-1.36109,-1.11455,2.40328,-0.51898 -1.37097,0.63595,-1.32285,-1.36208,-1.11383,2.39815,-0.51313 -1.45161,0.64183,-1.31805,-1.36422,-1.11266,2.39349,-0.50788 -1.53226,0.64623,-1.31412,-1.36648,-1.11151,2.38998,-0.50398 -1.61290,0.65053,-1.30980,-1.36955,-1.11001,2.38655,-0.50019 -1.69355,0.65600,-1.30380,-1.37439,-1.10773,2.38218,-0.49540 -1.77419,0.65985,-1.29884,-1.37912,-1.10555,2.37909,-0.49206 -1.85484,0.66525,-1.29033,-1.38849,-1.10135,2.37474,-0.48738 -1.93548,0.66732,-1.28692,-1.39235,-1.09962,2.37307,-0.48560 -2.01613,0.66955,-1.28287,-1.39718,-1.09748,2.37127,-0.48368 -2.09677,0.67181,-1.27808,-1.40327,-1.09480,2.36944,-0.48174 -2.17742,0.67563,-1.26385,-1.42413,-1.08582,2.36632,-0.47841 -2.25806,0.67728,-1.24987,-1.44660,-1.07627,2.36495,-0.47690 -2.33871,0.67695,-1.23938,-1.46488,-1.06860,2.36518,-0.47708 -2.41935,0.67529,-1.22718,-1.48725,-1.05933,2.36649,-0.47835 -2.50000,0.67136,-1.21267,-1.51552,-1.04781,2.36961,-0.48150 -2.58065,0.66755,-1.20228,-1.53659,-1.03934,2.37264,-0.48462 -2.66129,0.66488,-1.19618,-1.54932,-1.03428,2.37476,-0.48682 -2.74194,0.66309,-1.19252,-1.55713,-1.03119,2.37619,-0.48830 -2.82258,0.66108,-1.18910,-1.56473,-1.02821,2.37779,-0.48998 -2.90323,0.65850,-1.18505,-1.57391,-1.02464,2.37984,-0.49215 -2.98387,0.65621,-1.18188,-1.58134,-1.02177,2.38166,-0.49409 -3.06452,0.65273,-1.17767,-1.59160,-1.01784,2.38442,-0.49705 -3.14516,0.64717,-1.17222,-1.60590,-1.01245,2.38884,-0.50183 -3.22581,0.64224,-1.16836,-1.61697,-1.00835,2.39274,-0.50612 -3.30645,0.63673,-1.16503,-1.62777,-1.00441,2.39709,-0.51096 -3.38710,0.62993,-1.16190,-1.63951,-1.00022,2.40245,-0.51702 -3.46774,0.62148,-1.15930,-1.65207,-0.99587,2.40909,-0.52464 -3.54839,0.61533,-1.15783,-1.66057,-0.99299,2.41390,-0.53026 -3.62903,0.60983,-1.15685,-1.66768,-0.99064,2.41820,-0.53535 -3.70968,0.60149,-1.15640,-1.67688,-0.98771,2.42468,-0.54316 -3.79032,0.58864,-1.15810,-1.68750,-0.98462,2.43462,-0.55544 -3.87097,0.57937,-1.16053,-1.69343,-0.98311,2.44175,-0.56449 -3.95161,0.56884,-1.16450,-1.69843,-0.98209,2.44979,-0.57495 -4.03226,0.55160,-1.17429,-1.70202,-0.98225,2.46284,-0.59255 -4.11290,0.54299,-1.17938,-1.70369,-0.98246,2.46931,-0.60156 -4.19355,0.53290,-1.18552,-1.70559,-0.98279,2.47684,-0.61230 -4.27419,0.52106,-1.19308,-1.70758,-0.98333,2.48559,-0.62515 -4.35484,0.51236,-1.19860,-1.70931,-0.98369,2.49198,-0.63480 -4.43548,0.50632,-1.20249,-1.71052,-0.98397,2.49638,-0.64158 -4.51613,0.49999,-1.20671,-1.71166,-0.98433,2.50098,-0.64878 -4.59677,0.49309,-1.21128,-1.71308,-0.98468,2.50595,-0.65673 -4.67742,0.48695,-1.21552,-1.71420,-0.98507,2.51036,-0.66390 -4.75806,0.48267,-1.21851,-1.71498,-0.98535,2.51341,-0.66894 -4.83871,0.47825,-1.22142,-1.71609,-0.98555,2.51655,-0.67418 -4.91935,0.47286,-1.22476,-1.71786,-0.98565,2.52037,-0.68065 -5.00000,0.46388,-1.22867,-1.72353,-0.98491,2.52667,-0.69155 -5.08065,0.45727,-1.23095,-1.72875,-0.98405,2.53128,-0.69971 -5.16129,0.45393,-1.23198,-1.73161,-0.98355,2.53358,-0.70386 -5.24194,0.45040,-1.23267,-1.73526,-0.98282,2.53602,-0.70828 -5.32258,0.44743,-1.23312,-1.73856,-0.98214,2.53806,-0.71202 -5.40323,0.44312,-1.23344,-1.74387,-0.98097,2.54101,-0.71749 -5.48387,0.42071,-1.22869,-1.78190,-0.97171,2.55610,-0.74660 -5.56452,0.41356,-1.22662,-1.79498,-0.96866,2.56083,-0.75618 -5.64516,0.40657,-1.22419,-1.80841,-0.96555,2.56541,-0.76565 -5.72581,0.39830,-1.22042,-1.82567,-0.96157,2.57077,-0.77703 -5.80645,0.39668,-1.21962,-1.82915,-0.96079,2.57182,-0.77929 -5.88710,0.39558,-1.21903,-1.83157,-0.96024,2.57253,-0.78083 -5.96774,0.39506,-1.21876,-1.83270,-0.95998,2.57286,-0.78155 -6.04839,0.39400,-1.21815,-1.83510,-0.95944,2.57354,-0.78303 -6.12903,0.39395,-1.21812,-1.83519,-0.95942,2.57357,-0.78309 -6.20968,0.39393,-1.21811,-1.83524,-0.95941,2.57358,-0.78312 -6.29032,0.39392,-1.21809,-1.83529,-0.95940,2.57359,-0.78314 -6.37097,0.39390,-1.21808,-1.83533,-0.95939,2.57361,-0.78317 -6.45161,0.39388,-1.21807,-1.83537,-0.95938,2.57362,-0.78319 -6.53226,0.39387,-1.21806,-1.83540,-0.95937,2.57362,-0.78321 -6.61290,0.39386,-1.21805,-1.83544,-0.95936,2.57363,-0.78323 -6.69355,0.39384,-1.21804,-1.83547,-0.95936,2.57364,-0.78324 -6.77419,0.39384,-1.21803,-1.83549,-0.95935,2.57364,-0.78325 -6.85484,0.39384,-1.21803,-1.83550,-0.95935,2.57365,-0.78325 -6.93548,0.39384,-1.21803,-1.83549,-0.95935,2.57364,-0.78325 -7.01613,0.39384,-1.21804,-1.83546,-0.95936,2.57364,-0.78324 -7.09677,0.39386,-1.21806,-1.83541,-0.95937,2.57363,-0.78323 -7.17742,0.39561,-1.22187,-1.82734,-0.96160,2.57252,-0.78081 -7.25806,0.39777,-1.22730,-1.81630,-0.96471,2.57114,-0.77782 -7.33871,0.39941,-1.23309,-1.80547,-0.96788,2.57010,-0.77560 -7.41935,0.40055,-1.23808,-1.79648,-0.97058,2.56937,-0.77405 -7.50000,0.40098,-1.24049,-1.79230,-0.97186,2.56909,-0.77346 -7.58065,0.40173,-1.24659,-1.78220,-0.97503,2.56862,-0.77248 -7.66129,0.40204,-1.25576,-1.76813,-0.97961,2.56843,-0.77211 -7.74194,0.40195,-1.26246,-1.75827,-0.98292,2.56850,-0.77228 -7.82258,0.40199,-1.27002,-1.74687,-0.98677,2.56850,-0.77229 -7.90323,0.40205,-1.27504,-1.73918,-0.98940,2.56846,-0.77224 -7.98387,0.40287,-1.28373,-1.72475,-0.99426,2.56795,-0.77119 -8.06452,0.40408,-1.29249,-1.70950,-0.99941,2.56718,-0.76960 -8.14516,0.40521,-1.30089,-1.69485,-1.00444,2.56646,-0.76813 -8.22581,0.40654,-1.30992,-1.67882,-1.00998,2.56561,-0.76638 -8.30645,0.40962,-1.32552,-1.64971,-1.02012,2.56364,-0.76234 -8.38710,0.41177,-1.33339,-1.63392,-1.02567,2.56224,-0.75950 -8.46774,0.41319,-1.33808,-1.62427,-1.02908,2.56132,-0.75763 -8.54839,0.41494,-1.34358,-1.61278,-1.03315,2.56018,-0.75533 -8.62903,0.41620,-1.34790,-1.60393,-1.03633,2.55936,-0.75369 -8.70968,0.42136,-1.36182,-1.57351,-1.04722,2.55598,-0.74695 -8.79032,0.42246,-1.36447,-1.56748,-1.04941,2.55525,-0.74551 -8.87097,0.42545,-1.37157,-1.55127,-1.05531,2.55327,-0.74164 -8.95161,0.42921,-1.37948,-1.53248,-1.06219,2.55077,-0.73679 -9.03226,0.43285,-1.38644,-1.51537,-1.06849,2.54834,-0.73214 -9.11290,0.43638,-1.39227,-1.50025,-1.07408,2.54597,-0.72763 -9.19355,0.44474,-1.40602,-1.46444,-1.08748,2.54032,-0.71710 -9.27419,0.44878,-1.41255,-1.44726,-1.09401,2.53757,-0.71209 -9.35484,0.45180,-1.41705,-1.43504,-1.09869,2.53550,-0.70835 -9.43548,0.45453,-1.42093,-1.42429,-1.10281,2.53363,-0.70499 -9.51613,0.45691,-1.42425,-1.41502,-1.10639,2.53199,-0.70208 -9.59677,0.46020,-1.42840,-1.40294,-1.11104,2.52972,-0.69807 -9.67742,0.46317,-1.43192,-1.39244,-1.11511,2.52767,-0.69448 -9.75806,0.46666,-1.43588,-1.38038,-1.11979,2.52524,-0.69027 -9.83871,0.46982,-1.43913,-1.37004,-1.12381,2.52304,-0.68649 -9.91935,0.47222,-1.44125,-1.36280,-1.12662,2.52136,-0.68363 -10.00000,0.47620,-1.44443,-1.35136,-1.13106,2.51856,-0.67891 -10.08065,0.47890,-1.44620,-1.34428,-1.13380,2.51665,-0.67572 -10.16129,0.48215,-1.44822,-1.33597,-1.13702,2.51436,-0.67191 -10.24194,0.48553,-1.45008,-1.32776,-1.14019,2.51196,-0.66796 -10.32258,0.48945,-1.45229,-1.31818,-1.14392,2.50917,-0.66342 -10.40323,0.49282,-1.45420,-1.30995,-1.14713,2.50676,-0.65954 -10.48387,0.49568,-1.45577,-1.30307,-1.14982,2.50471,-0.65628 -10.56452,0.49803,-1.45700,-1.29754,-1.15199,2.50302,-0.65360 -10.64516,0.50042,-1.45818,-1.29205,-1.15414,2.50130,-0.65090 -10.72581,0.50391,-1.45999,-1.28388,-1.15736,2.49878,-0.64696 -10.80645,0.50862,-1.46248,-1.27282,-1.16174,2.49537,-0.64169 -10.88710,0.51348,-1.46490,-1.26171,-1.16616,2.49184,-0.63630 -10.96774,0.51984,-1.46766,-1.24796,-1.17163,2.48719,-0.62933 -11.04839,0.52395,-1.46912,-1.23968,-1.17492,2.48417,-0.62485 -11.12903,0.52786,-1.47061,-1.23168,-1.17813,2.48129,-0.62064 -11.20968,0.53059,-1.47157,-1.22624,-1.18031,2.47928,-0.61771 -11.29032,0.53221,-1.47201,-1.22326,-1.18150,2.47808,-0.61597 -11.37097,0.53371,-1.47237,-1.22059,-1.18256,2.47697,-0.61438 -11.45161,0.53585,-1.47290,-1.21674,-1.18409,2.47538,-0.61211 -11.53226,0.53753,-1.47321,-1.21392,-1.18521,2.47413,-0.61032 -11.61290,0.54076,-1.47324,-1.20952,-1.18692,2.47173,-0.60691 -11.69355,0.54449,-1.47270,-1.20550,-1.18843,2.46894,-0.60300 -11.77419,0.54792,-1.47168,-1.20276,-1.18941,2.46637,-0.59941 -11.85484,0.55091,-1.47013,-1.20158,-1.18973,2.46412,-0.59629 -11.93548,0.55316,-1.46869,-1.20121,-1.18974,2.46243,-0.59396 -12.01613,0.55555,-1.46673,-1.20157,-1.18942,2.46063,-0.59149 -12.09677,0.55806,-1.46398,-1.20323,-1.18852,2.45873,-0.58889 -12.17742,0.56098,-1.46002,-1.20652,-1.18687,2.45651,-0.58588 -12.25806,0.56399,-1.45497,-1.21168,-1.18439,2.45422,-0.58278 -12.33871,0.56669,-1.44956,-1.21791,-1.18145,2.45217,-0.58002 -12.41935,0.56885,-1.44496,-1.22338,-1.17888,2.45051,-0.57780 -12.50000,0.57041,-1.44118,-1.22815,-1.17666,2.44931,-0.57620 -12.58065,0.57179,-1.43762,-1.23278,-1.17452,2.44825,-0.57479 -12.66129,0.57285,-1.43467,-1.23672,-1.17270,2.44744,-0.57370 -12.74194,0.57364,-1.43226,-1.24003,-1.17118,2.44683,-0.57289 -12.82258,0.57444,-1.42980,-1.24342,-1.16963,2.44621,-0.57207 -12.90323,0.57549,-1.42607,-1.24877,-1.16719,2.44540,-0.57099 -12.98387,0.57729,-1.41634,-1.26384,-1.16042,2.44400,-0.56911 -13.06452,0.57807,-1.41113,-1.27213,-1.15671,2.44339,-0.56828 -13.14516,0.57856,-1.40557,-1.28138,-1.15261,2.44300,-0.56774 -13.22581,0.57879,-1.39758,-1.29523,-1.14651,2.44281,-0.56743 -13.30645,0.57853,-1.39012,-1.30871,-1.14063,2.44298,-0.56760 -13.38710,0.57808,-1.38369,-1.32057,-1.13548,2.44331,-0.56797 -13.46774,0.57703,-1.37429,-1.33830,-1.12786,2.44409,-0.56890 -13.54839,0.57641,-1.36900,-1.34830,-1.12357,2.44455,-0.56945 -13.62903,0.57521,-1.36105,-1.36361,-1.11706,2.44545,-0.57056 -13.70968,0.57255,-1.35067,-1.38480,-1.10817,2.44745,-0.57309 -13.79032,0.56969,-1.34283,-1.40177,-1.10114,2.44960,-0.57584 -13.87097,0.56703,-1.33701,-1.41501,-1.09572,2.45161,-0.57845 -13.95161,0.56487,-1.33281,-1.42482,-1.09173,2.45324,-0.58057 -14.03226,0.56089,-1.32626,-1.44084,-1.08531,2.45624,-0.58453 -14.11290,0.55835,-1.32261,-1.45013,-1.08162,2.45814,-0.58707 -14.19355,0.55612,-1.31967,-1.45784,-1.07858,2.45982,-0.58932 -14.27419,0.55381,-1.31685,-1.46544,-1.07561,2.46155,-0.59166 -14.35484,0.55192,-1.31465,-1.47146,-1.07327,2.46297,-0.59359 -14.43548,0.55048,-1.31305,-1.47593,-1.07154,2.46404,-0.59505 -14.51613,0.54952,-1.31200,-1.47888,-1.07041,2.46476,-0.59604 -14.59677,0.54806,-1.31044,-1.48330,-1.06871,2.46586,-0.59754 -14.67742,0.54804,-1.31042,-1.48336,-1.06868,2.46587,-0.59756 -14.75806,0.54756,-1.30992,-1.48479,-1.06814,2.46623,-0.59805 -14.83871,0.54706,-1.30940,-1.48628,-1.06757,2.46660,-0.59857 -14.91935,0.54656,-1.30888,-1.48776,-1.06700,2.46698,-0.59908 -15.00000,0.54654,-1.30886,-1.48782,-1.06698,2.46699,-0.59911 diff --git a/python/examples/comparing_logs_example.py b/python/examples/log_analysis/comparing_logs_example.py similarity index 100% rename from python/examples/comparing_logs_example.py rename to python/examples/log_analysis/comparing_logs_example.py diff --git a/python/examples/log_analysis_example.py b/python/examples/log_analysis/log_analysis_example.py similarity index 100% rename from python/examples/log_analysis_example.py rename to python/examples/log_analysis/log_analysis_example.py diff --git a/python/examples/force_control_test.py b/python/examples/old_or_experimental/force_control/force_control_test.py similarity index 100% rename from python/examples/force_control_test.py rename to python/examples/old_or_experimental/force_control/force_control_test.py diff --git a/python/examples/old_or_experimental/force_mode_api.py b/python/examples/old_or_experimental/force_control/force_mode_api.py similarity index 100% rename from python/examples/old_or_experimental/force_mode_api.py rename to python/examples/old_or_experimental/force_control/force_mode_api.py diff --git a/python/examples/old_or_experimental/forcemode_example.py b/python/examples/old_or_experimental/force_control/forcemode_example.py similarity index 100% rename from python/examples/old_or_experimental/forcemode_example.py rename to python/examples/old_or_experimental/force_control/forcemode_example.py diff --git a/python/examples/point_force_control.py b/python/examples/old_or_experimental/force_control/point_force_control.py similarity index 100% rename from python/examples/point_force_control.py rename to python/examples/old_or_experimental/force_control/point_force_control.py diff --git a/python/examples/test_crocoddyl_opt_ctrl.py b/python/examples/old_or_experimental/test_crocoddyl_opt_ctrl.py similarity index 100% rename from python/examples/test_crocoddyl_opt_ctrl.py rename to python/examples/old_or_experimental/test_crocoddyl_opt_ctrl.py diff --git a/python/examples/casadi_ocp_collision_avoidance.py b/python/examples/optimal_control/casadi_ocp_collision_avoidance.py similarity index 100% rename from python/examples/casadi_ocp_collision_avoidance.py rename to python/examples/optimal_control/casadi_ocp_collision_avoidance.py diff --git a/python/examples/optimal_control/croco_ik_mpc.py b/python/examples/optimal_control/croco_ik_mpc.py new file mode 100644 index 0000000000000000000000000000000000000000..5fc1ff99b65dc8efe9ab17237b92bbad79e67607 --- /dev/null +++ b/python/examples/optimal_control/croco_ik_mpc.py @@ -0,0 +1,52 @@ +import numpy as np +from smc import getMinimalArgParser, getRobotFromArgs +from smc.control.optimal_control.croco_mpc_point_to_point import ( + CrocoIKMPC, +) +from smc.control.optimal_control.util import get_OCP_args +from smc.control.cartesian_space import getClikArgs +import pinocchio as pin +import argcomplete + + +def get_args(): + parser = getMinimalArgParser() + parser = get_OCP_args(parser) + parser = getClikArgs(parser) # literally just for goal error + argcomplete.autocomplete(parser) + args = parser.parse_args() + return args + + +if __name__ == "__main__": + args = get_args() + robot = getRobotFromArgs(args) + # TODO: put this back for nicer demos + # Mgoal = defineGoalPointCLI() + T_w_goal = pin.SE3.Random() + if robot.robot_name == "yumi": + T_w_goal.rotation = np.eye(3) + T_w_goal_transform = pin.SE3.Identity() + T_w_goal_transform.translation[1] = 0.1 + Mgoal_left = T_w_goal_transform.act(T_w_goal) + Mgoal_right = T_w_goal_transform.inverse().act(T_w_goal) + T_w_goal = (Mgoal_left, Mgoal_right) + + if args.visualizer: + # TODO: document this somewhere + robot.visualizer_manager.sendCommand({"Mgoal": T_w_goal}) + + CrocoIKMPC(args, robot, T_w_goal) + + print("final position:") + print(robot.T_w_e) + + if args.real: + robot.stopRobot() + + if args.visualizer: + robot.killManipulatorVisualizer() + + if args.save_log: + robot._log_manager.saveLog() + robot._log_manager.plotAllControlLoops() diff --git a/python/examples/crocoddyl_ocp_clik.py b/python/examples/optimal_control/croco_ik_ocp.py similarity index 85% rename from python/examples/crocoddyl_ocp_clik.py rename to python/examples/optimal_control/croco_ik_ocp.py index 32bef5cdce5cb68a873471b79826cd30a5c423af..9ca52152057d71bb23f3708c28afa3fb00be8af9 100644 --- a/python/examples/crocoddyl_ocp_clik.py +++ b/python/examples/optimal_control/croco_ik_ocp.py @@ -1,15 +1,13 @@ # PYTHON_ARGCOMPLETE_OK from smc.control.optimal_control.util import get_OCP_args from smc import getMinimalArgParser, getRobotFromArgs -from smc.control.optimal_control import ( - createCrocoIKOCP, - solveCrocoOCP, +from smc.control.optimal_control.point_to_point_croco_ocp import ( + SingleArmIKOCP, ) from smc.control.joint_space import followKinematicJointTrajP import pinocchio as pin import numpy as np -import crocoddyl import argcomplete @@ -26,11 +24,11 @@ if __name__ == "__main__": robot = getRobotFromArgs(args) # TODO: put this back for nicer demos # Mgoal = robot.defineGoalPointCLI() - Mgoal = pin.SE3.Random() + T_w_goal = pin.SE3.Random() if args.visualizer: # TODO document this somewhere - robot.visualizer_manager.sendCommand({"Mgoal": Mgoal}) + robot.visualizer_manager.sendCommand({"Mgoal": T_w_goal}) # create and solve the optimal control problem of # getting from current to goal end-effector position. @@ -38,9 +36,9 @@ if __name__ == "__main__": # while solver is a crocoddyl object containing a lot more information # starting state x0 = np.concatenate([robot.q, robot.v]) - problem = createCrocoIKOCP(args, robot, x0, Mgoal) # this shouldn't really depend on x0 but i can't be bothered - reference, solver = solveCrocoOCP(args, robot, problem, x0) + ocp = SingleArmIKOCP(args, robot, x0, T_w_goal) + reference = ocp.solveOCP(x0) # NOTE: IF YOU PLOT SOMETHING OTHER THAN REAL-TIME PLOTTING FIRST IT BREAKS EVERYTHING # if args.solver == "boxfddp": diff --git a/python/examples/path_following_mpc.py b/python/examples/optimal_control/path_following_mpc.py similarity index 100% rename from python/examples/path_following_mpc.py rename to python/examples/optimal_control/path_following_mpc.py diff --git a/python/examples/path_in_pixels.csv b/python/examples/path_in_pixels.csv deleted file mode 100644 index c28926c91fc239e78ed6a53c7f3efb934cedcff4..0000000000000000000000000000000000000000 --- a/python/examples/path_in_pixels.csv +++ /dev/null @@ -1,46 +0,0 @@ -0.28846,0.66239 -0.29140,0.66108 -0.29287,0.66108 -0.29434,0.66108 -0.29581,0.66108 -0.31638,0.65586 -0.35166,0.65455 -0.37370,0.65194 -0.44278,0.65194 -0.46483,0.64803 -0.52215,0.64672 -0.52362,0.64541 -0.52656,0.64411 -0.52803,0.64411 -0.52950,0.64280 -0.53097,0.63367 -0.52950,0.62714 -0.52509,0.62061 -0.51921,0.61278 -0.50451,0.60364 -0.48834,0.59189 -0.46483,0.57231 -0.37076,0.50965 -0.32520,0.49268 -0.25171,0.46396 -0.23848,0.45090 -0.21644,0.43263 -0.20909,0.42741 -0.20615,0.42610 -0.20468,0.42479 -0.20321,0.42349 -0.20321,0.42349 -0.21056,0.41957 -0.23408,0.41566 -0.30168,0.41305 -0.32961,0.41174 -0.40310,0.40913 -0.42221,0.40652 -0.48394,0.40130 -0.54273,0.39607 -0.54860,0.39346 -0.55448,0.39216 -0.55595,0.39216 -0.55742,0.39216 -0.55889,0.39216 -0.55889,0.39216 diff --git a/python/examples/pin_contact3d.py b/python/examples/pinocchio_math_understanding/pin_contact3d.py similarity index 100% rename from python/examples/pin_contact3d.py rename to python/examples/pinocchio_math_understanding/pin_contact3d.py diff --git a/python/examples/dummy_cmds_pub.py b/python/examples/ros/dummy_cmds_pub.py similarity index 100% rename from python/examples/dummy_cmds_pub.py rename to python/examples/ros/dummy_cmds_pub.py diff --git a/python/examples/ros2_clik.py b/python/examples/ros/ros2_clik.py similarity index 100% rename from python/examples/ros2_clik.py rename to python/examples/ros/ros2_clik.py diff --git a/python/examples/smc_node.py b/python/examples/ros/smc_node.py similarity index 100% rename from python/examples/smc_node.py rename to python/examples/ros/smc_node.py diff --git a/python/examples/smc_yumi_challenge.py b/python/examples/ros/smc_yumi_challenge.py similarity index 100% rename from python/examples/smc_yumi_challenge.py rename to python/examples/ros/smc_yumi_challenge.py diff --git a/python/examples/camera_no_lag.py b/python/examples/vision/camera_no_lag.py similarity index 100% rename from python/examples/camera_no_lag.py rename to python/examples/vision/camera_no_lag.py diff --git a/python/smc/control/optimal_control/__init__.py b/python/smc/control/optimal_control/__init__.py index 927eb07a605fa1c2d0e49f6b93c899affdd8bfc0..bd3883b08e418e5c1f5c1efb957bd0d1189b5dbc 100644 --- a/python/smc/control/optimal_control/__init__.py +++ b/python/smc/control/optimal_control/__init__.py @@ -6,5 +6,7 @@ import importlib.util # print("you need to have pinocchio version 3.0.0 or greater to use pinocchio.casadi!") # exit() # from .create_pinocchio_casadi_ocp import * -from .crocoddyl_mpc import * -from .crocoddyl_optimal_control import * +from .path_following_croco_ocp import * +from .point_to_point_croco_ocp import * +from .croco_mpc_path_following import * +from .croco_mpc_point_to_point import * diff --git a/python/smc/control/optimal_control/abstract_croco_ocp.py b/python/smc/control/optimal_control/abstract_croco_ocp.py new file mode 100644 index 0000000000000000000000000000000000000000..867d3517e9464ef477ccf5719e1d2b10b066045b --- /dev/null +++ b/python/smc/control/optimal_control/abstract_croco_ocp.py @@ -0,0 +1,286 @@ +# TODO: make a bundle method which solves and immediately follows the traj. +from smc.control.joint_space.joint_space_trajectory_following import ( + followKinematicJointTrajP, +) +from smc.robots.robotmanager_abstract import AbstractRobotManager + +import numpy as np +import crocoddyl +from argparse import Namespace +import time +import importlib.util +import abc +from typing import Any + +if importlib.util.find_spec("mim_solvers"): + try: + import mim_solvers + except ModuleNotFoundError: + print( + "mim_solvers installation is broken, rebuild and reinstall it if you want it" + ) + + +class CrocoOCP(abc.ABC): + """ + CrocoOCP + ---------- + General info: + - torque inputs are assumed - we already solve for state, which includes velocity commands, and we send that if the robot accepts vel cmds + State model : StateMultibody + I'm not even sure what else is there to choose lol. + Actuation model : ActuatioModelFull + We do underactuation via a constraint at the moment. This is solely + because coding up a proper underactuated model is annoying, + and dealing with this is a TODO for later. Having that said, + input constraints are necessary either way. + # TODO: consider ActuationModelFloatingBaseTpl for heron + # TODO: create a different actuation model (or whatever) + # for the mobile base - basically just remove the y movement in the base + # and update the corresponding derivates to 0 + # there's python examples for this, ex. acrobot. + # you might want to implement the entire action model too idk what's really necessary here + Action model : DifferentialActionModelFreeFwdDynamics + We need to create an action model for running and terminal knots. The + forward dynamics (computed using ABA) are implemented + inside DifferentialActionModelFreeFwdDynamics. + """ + + def __init__( + self, args: Namespace, robot: AbstractRobotManager, x0: np.ndarray, goal + ): + # TODO: declare attributes here + self.args = args + self.solver_name = args.solver + self.robot = robot + self.x0 = x0 + self.constructCrocoCostModels() + self.constructRegulationCosts() + self.constructStateLimits() + self.constructConstraints() + self.constructTaskObjectiveFunction(goal) + self.constructRunningModels() + self.problem = crocoddyl.ShootingProblem( + self.x0, self.runningModels, self.terminalModel + ) + self.createSolver() + + def constructCrocoCostModels(self): + self.state = crocoddyl.StateMultibody(self.robot.model) + # NOTE: atm we just set effort level in that direction to 0 + self.actuation = crocoddyl.ActuationModelFull(self.state) + + # we will be summing 4 different costs + # first 3 are for tracking, state and control regulation + self.runningCostModels = [] + for _ in range(self.args.n_knots): + self.runningCostModels.append(crocoddyl.CostModelSum(self.state)) + self.terminalCostModel = crocoddyl.CostModelSum(self.state) + + def constructRegulationCosts(self): + # cost 1) u residual (actuator cost) + self.uResidual = crocoddyl.ResidualModelControl(self.state, self.state.nv) + self.uRegCost = crocoddyl.CostModelResidual(self.state, self.uResidual) + # cost 2) x residual (overall amount of movement) + self.xResidual = crocoddyl.ResidualModelState( + self.state, self.x0, self.state.nv + ) + self.xRegCost = crocoddyl.CostModelResidual(self.state, self.xResidual) + + # put these costs into the running costs + for i in range(self.args.n_knots): + self.runningCostModels[i].addCost("xReg", self.xRegCost, 1e-3) + self.runningCostModels[i].addCost("uReg", self.uRegCost, 1e-3) + # and add the terminal cost, which is the distance to the goal + # NOTE: shouldn't there be a final velocity = 0 here? + self.terminalCostModel.addCost("uReg", self.uRegCost, 1e3) + + def constructStateLimits(self) -> None: + """ + constructStateConstraints + ------------------------- + """ + # the 4th cost is for defining bounds via costs + # NOTE: could have gotten the same info from state.lb and state.ub. + # the first state is unlimited there idk what that means really, + # but the arm's base isn't doing a full rotation anyway, let alone 2 or more + self.xlb = np.concatenate( + [self.robot.model.lowerPositionLimit, -1 * self.robot.model.velocityLimit] + ) + self.xub = np.concatenate( + [self.robot.model.upperPositionLimit, self.robot.model.velocityLimit] + ) + + # NOTE: in an ideal universe this is handled elsewhere + # we have no limits on the mobile base. + # the mobile base is a planar joint. + # since it is represented as [x,y,cos(theta),sin(theta)], there is no point + # to limiting cos(theta),sin(theta) even if we wanted limits, + # because we would then limit theta, or limit ct and st jointly. + # in any event, xlb and xub are 1 number too long -- + # the residual has to be [x,y,theta] because it is in the tangent space - + # the difference between two points on a manifold in pinocchio is defined + # as the velocity which if parallel transported for 1 unit of "time" + # from one to point to the other. + # point activation input and the residual need to be of the same length obviously, + # and this should be 2 * model.nv the way things are defined here. + + # TODO: replace this with subclass or whatever call + # to check if the robot has mobilebase interface + if ( + self.robot.robot_name == "heron" + or self.robot.robot_name == "heronros" + or self.robot.robot_name == "mirros" + or self.robot.robot_name == "yumi" + ): + self.xlb = self.xlb[1:] + self.xub = self.xub[1:] + + def constructConstraints(self) -> None: + if self.solver_name == "boxfddp": + self.stateConstraintsViaBarriersInObjectiveFunction() + if self.solver_name == "csqp": + self.boxInputConstraintsAsActualConstraints() + + # NOTE: used by BoxFDDP + def stateConstraintsViaBarriersInObjectiveFunction(self) -> None: + bounds = crocoddyl.ActivationBounds(self.xlb, self.xub, 1.0) + xLimitResidual = crocoddyl.ResidualModelState( + self.state, self.x0, self.state.nv + ) + xLimitActivation = crocoddyl.ActivationModelQuadraticBarrier(bounds) + + limitCost = crocoddyl.CostModelResidual( + self.state, xLimitActivation, xLimitResidual + ) + for i in range(self.args.n_knots): + self.runningCostModels[i].addCost("limitCost", limitCost, 1e3) + self.terminalCostModel.addCost("limitCost", limitCost, 1e3) + + # NOTE: used by csqp + def boxInputConstraintsAsActualConstraints(self) -> None: + # ConstraintModelManager just stores constraints + self.constraints = crocoddyl.ConstraintModelManager( + self.state, self.robot.model.nv + ) + u_constraint = crocoddyl.ConstraintModelResidual( + self.state, + self.uResidual, + -1 * self.robot.model.effortLimit * 0.1, + self.robot.model.effortLimit * 0.1, + ) + self.constraints.addConstraint("u_box_constraint", u_constraint) + x_constraint = crocoddyl.ConstraintModelResidual( + self.state, self.xResidual, self.xlb, self.xub + ) + self.constraints.addConstraint("x_box_constraint", x_constraint) + + def constructRunningModels(self) -> None: + self.runningModels = [] + if self.solver_name == "boxfddp": + self.constructRunningModelsWithoutExplicitConstraints() + if self.solver_name == "csqp": + self.constructRunningModelsWithExplicitConstraints() + + # NOTE: has input constraints, but reads them from model.effortlimit + # or whatever else + def constructRunningModelsWithoutExplicitConstraints(self) -> None: + for i in range(self.args.n_knots): + runningModel = crocoddyl.IntegratedActionModelEuler( + crocoddyl.DifferentialActionModelFreeInvDynamics( + self.state, self.actuation, self.runningCostModels[i] + ), + self.args.ocp_dt, + ) + runningModel.u_lb = -1 * self.robot.model.effortLimit * 0.1 + runningModel.u_ub = self.robot.model.effortLimit * 0.1 + self.runningModels.append(runningModel) + self.terminalModel = crocoddyl.IntegratedActionModelEuler( + crocoddyl.DifferentialActionModelFreeInvDynamics( + self.state, self.actuation, self.terminalCostModel + ), + 0.0, + ) + self.terminalModel.u_lb = -1 * self.robot.model.effortLimit * 0.1 + self.terminalModel.u_ub = self.robot.model.effortLimit * 0.1 + + def constructRunningModelsWithExplicitConstraints(self) -> None: + for i in range(self.args.n_knots): + runningModel = crocoddyl.IntegratedActionModelEuler( + crocoddyl.DifferentialActionModelFreeInvDynamics( + self.state, + self.actuation, + self.runningCostModels[i], + self.constraints, + ), + self.args.ocp_dt, + ) + self.runningModels.append(runningModel) + self.terminalModel = crocoddyl.IntegratedActionModelEuler( + crocoddyl.DifferentialActionModelFreeInvDynamics( + self.state, self.actuation, self.terminalCostModel, self.constraints + ), + 0.0, + ) + + + @abc.abstractmethod + def constructTaskObjectiveFunction(self, goal) -> None: ... + + def getSolver(self) -> Any: + return self.solver + + def createSolver(self) -> None: + if self.solver_name == "boxfddp": + self.createCrocoSolver() + if self.solver_name == "csqp": + self.createMimSolver() + + # NOTE: used by boxfddp + def createCrocoSolver(self) -> None: + # just for reference can try + # solver = crocoddyl.SolverIpopt(problem) + # TODO: select other solvers from arguments + self.solver = crocoddyl.SolverBoxFDDP(self.problem) + # TODO: make these callbacks optional + self.solver.setCallbacks( + [crocoddyl.CallbackVerbose(), crocoddyl.CallbackLogger()] + ) + + # NOTE: used by csqp + def createMimSolver(self) -> None: + # TODO try out the following solvers from mim_solvers: + # - csqp + # - stagewise qp + # and the solver + # both of these have generic inequalities you can put in. + # and both are basically QP versions of iLQR if i'm not wrong + # (i have no idea tho) + # solver = mim_solvers.SolverSQP(problem) + # TODO: select other solvers from arguments + self.solver = mim_solvers.SolverCSQP(self.problem) + # TODO: make these callbacks optional + self.solver.setCallbacks( + [mim_solvers.CallbackVerbose(), mim_solvers.CallbackLogger()] + ) + + # this shouldn't really depend on x0 but i can't be bothered + def solveOCP(self, x0: np.ndarray) -> tuple[dict, Any]: + # run solve + # NOTE: there are some possible parameters here that I'm not using + xs = [x0] * (self.solver.problem.T + 1) + us = self.solver.problem.quasiStatic([x0] * self.solver.problem.T) + + start = time.time() + self.solver.solve(xs, us, 500, False, 1e-9) + end = time.time() + print("solved in:", end - start, "seconds") + + # solver.solve() + # get reference (state trajectory) + # we aren't using controls because we only have velocity inputs + xs = np.array(self.solver.xs) + qs = xs[:, : self.robot.model.nq] + vs = xs[:, self.robot.model.nq :] + reference = {"qs": qs, "vs": vs, "dt": self.args.ocp_dt} + return reference diff --git a/python/smc/control/optimal_control/crocoddyl_mpc.py b/python/smc/control/optimal_control/croco_mpc_path_following.py similarity index 82% rename from python/smc/control/optimal_control/crocoddyl_mpc.py rename to python/smc/control/optimal_control/croco_mpc_path_following.py index b415f3b1d55eeace1ac4f2339214994aad27aab9..7e63a890ca0ee3d8ad5b1a5b124eaea70f9c2a65 100644 --- a/python/smc/control/optimal_control/crocoddyl_mpc.py +++ b/python/smc/control/optimal_control/croco_mpc_path_following.py @@ -1,13 +1,11 @@ from smc.robots.interfaces.single_arm_interface import SingleArmInterface from smc.control.control_loop_manager import ControlLoopManager from smc.multiprocessing.process_manager import ProcessManager -from smc.control.optimal_control.crocoddyl_optimal_control import ( - createCrocoIKOCP, - createCrocoEEPathFollowingOCP, - createBaseAndEEPathFollowingOCP, +from smc.control.optimal_control.path_following_croco_ocp import ( + CrocoEEPathFollowingOCP, + BaseAndEEPathFollowingOCP, ) -import pinocchio as pin import crocoddyl import numpy as np from functools import partial @@ -15,7 +13,12 @@ import types import importlib.util if importlib.util.find_spec("mim_solvers"): - import mim_solvers + try: + import mim_solvers + except ModuleNotFoundError: + print( + "mim_solvers installation is broken, rebuild and reinstall it if you want it" + ) # TODO: put others here too if importlib.util.find_spec("shapely"): @@ -25,92 +28,13 @@ if importlib.util.find_spec("shapely"): path2D_to_SE3, ) -def CrocoIKMPCControlLoop(args, robot: SingleArmInterface, solver, T_w_goal, i, past_data): - """ - CrocoIKMPCControlLoop - --------------------- - """ - breakFlag = False - log_item = {} - save_past_dict = {} - - # check for goal - if robot.robot_name == "yumi": - goal_left, goal_right = goal - SEerror = robot.T_w_e.actInv(T_w_goal) - err_vector = pin.log6(SEerror).vector - if np.linalg.norm(err_vector) < robot.args.goal_error: - # print("Convergence achieved, reached destionation!") - breakFlag = True - - # set initial state from sensor - x0 = np.concatenate([robot.q, robot.v]) - solver.problem.x0 = x0 - # warmstart solver with previous solution - xs_init = list(solver.xs[1:]) + [solver.xs[-1]] - xs_init[0] = x0 - us_init = list(solver.us[1:]) + [solver.us[-1]] - - solver.solve(xs_init, us_init, args.max_solver_iter) - xs = np.array(solver.xs) - us = np.array(solver.us) - vel_cmds = xs[1, robot.model.nq :] - robot.sendVelocityCommand(vel_cmds) - - log_item["qs"] = x0[: robot.model.nq] - log_item["dqs"] = x0[robot.model.nv :] - log_item["dqs_cmd"] = vel_cmds - log_item["u_tau"] = us[0] - - return breakFlag, save_past_dict, log_item - - -def CrocoIKMPC(args, robot, T_w_goal, run=True): - """ - IKMPC - ----- - run mpc for a point-to-point inverse kinematics. - note that the actual problem is solved on - a dynamics level, and velocities we command - are actually extracted from the state x(q,dq) - """ - x0 = np.concatenate([robot.q, robot.v]) - problem = createCrocoIKOCP(args, robot, x0, T_w_goal) - if args.solver == "boxfddp": - solver = crocoddyl.SolverBoxFDDP(problem) - if args.solver == "csqp": - solver = mim_solvers.SolverCSQP(problem) - - # technically should be done in controlloop because now - # it's solved 2 times before the first command, - # but we don't have time for details rn - xs_init = [x0] * (solver.problem.T + 1) - us_init = solver.problem.quasiStatic([x0] * solver.problem.T) - solver.solve(xs_init, us_init, args.max_solver_iter) - - controlLoop = partial(CrocoIKMPCControlLoop, args, robot, solver, T_w_goal) - log_item = { - "qs": np.zeros(robot.model.nq), - "dqs": np.zeros(robot.model.nq), - "dqs_cmd": np.zeros(robot.model.nv), # we're assuming full actuation here - "u_tau": np.zeros(robot.model.nv), - } - save_past_dict = {} - loop_manager = ControlLoopManager( - robot, controlLoop, args, save_past_dict, log_item - ) - if run: - loop_manager.run() - else: - return loop_manager - def CrocoEndEffectorPathFollowingMPCControlLoop( args, robot: SingleArmInterface, solver: crocoddyl.SolverBoxFDDP, path_planner: ProcessManager, - i, + t, past_data, ): """ @@ -134,7 +58,7 @@ def CrocoEndEffectorPathFollowingMPCControlLoop( # if it's the same 2D path if type(path_planner) == types.FunctionType: - pathSE3 = path_planner(T_w_e, i) + pathSE3 = path_planner(T_w_e, t) else: path_planner.sendCommand(p) data = path_planner.getData() @@ -162,7 +86,7 @@ def CrocoEndEffectorPathFollowingMPCControlLoop( # TODO: EVIL AND HAS TO BE REMOVED FROM HERE if args.visualize_manipulator: - if i % 20 == 0: + if t % 20 == 0: robot.visualizer_manager.sendCommand({"frame_path": pathSE3}) x0 = np.concatenate([robot.q, robot.v]) diff --git a/python/smc/control/optimal_control/croco_mpc_point_to_point.py b/python/smc/control/optimal_control/croco_mpc_point_to_point.py new file mode 100644 index 0000000000000000000000000000000000000000..1f643a5b38837c0daf12a9d3c8914fd1365e9af2 --- /dev/null +++ b/python/smc/control/optimal_control/croco_mpc_point_to_point.py @@ -0,0 +1,92 @@ +from smc.robots.interfaces.single_arm_interface import SingleArmInterface +from smc.control.control_loop_manager import ControlLoopManager +from smc.multiprocessing.process_manager import ProcessManager +from smc.control.optimal_control.point_to_point_croco_ocp import SingleArmIKOCP + +import pinocchio as pin +import crocoddyl +import numpy as np +from functools import partial +import importlib.util + +if importlib.util.find_spec("mim_solvers"): + try: + import mim_solvers + except ModuleNotFoundError: + print( + "mim_solvers installation is broken, rebuild and reinstall it if you want it" + ) + + +def CrocoIKMPCControlLoop(args, robot: SingleArmInterface, solver, T_w_goal, _, __): + """ + CrocoIKMPCControlLoop + --------------------- + """ + breakFlag = False + log_item = {} + save_past_dict = {} + + SEerror = robot.T_w_e.actInv(T_w_goal) + err_vector = pin.log6(SEerror).vector + if np.linalg.norm(err_vector) < robot.args.goal_error: + # print("Convergence achieved, reached destionation!") + breakFlag = True + + # set initial state from sensor + x0 = np.concatenate([robot.q, robot.v]) + solver.problem.x0 = x0 + # warmstart solver with previous solution + xs_init = list(solver.xs[1:]) + [solver.xs[-1]] + xs_init[0] = x0 + us_init = list(solver.us[1:]) + [solver.us[-1]] + + solver.solve(xs_init, us_init, args.max_solver_iter) + xs = np.array(solver.xs) + us = np.array(solver.us) + vel_cmds = xs[1, robot.model.nq :] + robot.sendVelocityCommand(vel_cmds) + + log_item["qs"] = x0[: robot.model.nq] + log_item["dqs"] = x0[robot.model.nv :] + log_item["dqs_cmd"] = vel_cmds + log_item["u_tau"] = us[0] + + return breakFlag, save_past_dict, log_item + + +def CrocoIKMPC(args, robot, T_w_goal, run=True): + """ + IKMPC + ----- + run mpc for a point-to-point inverse kinematics. + note that the actual problem is solved on + a dynamics level, and velocities we command + are actually extracted from the state x(q,dq) + """ + x0 = np.concatenate([robot.q, robot.v]) + ocp = SingleArmIKOCP(args, robot, x0, T_w_goal) + solver = ocp.getSolver() + + # technically should be done in controlloop because now + # it's solved 2 times before the first command, + # but we don't have time for details rn + xs_init = [x0] * (solver.problem.T + 1) + us_init = solver.problem.quasiStatic([x0] * solver.problem.T) + solver.solve(xs_init, us_init, args.max_solver_iter) + + controlLoop = partial(CrocoIKMPCControlLoop, args, robot, solver, T_w_goal) + log_item = { + "qs": np.zeros(robot.model.nq), + "dqs": np.zeros(robot.model.nq), + "dqs_cmd": np.zeros(robot.model.nv), # we're assuming full actuation here + "u_tau": np.zeros(robot.model.nv), + } + save_past_dict = {} + loop_manager = ControlLoopManager( + robot, controlLoop, args, save_past_dict, log_item + ) + if run: + loop_manager.run() + else: + return loop_manager diff --git a/python/smc/control/optimal_control/crocoddyl_optimal_control.py b/python/smc/control/optimal_control/crocoddyl_optimal_control.py deleted file mode 100644 index 2b01dee7fd147461c8a1db17414457f8c778ead1..0000000000000000000000000000000000000000 --- a/python/smc/control/optimal_control/crocoddyl_optimal_control.py +++ /dev/null @@ -1,556 +0,0 @@ -from smc.control.joint_space.joint_space_trajectory_following import ( - followKinematicJointTrajP, -) -from smc.visualization.plotters import plotFromDict -from smc import getMinimalArgParser -from smc.robots.robotmanager_abstract import AbstractRobotManager -from smc.robots.interfaces.single_arm_interface import SingleArmInterface -from smc.robots.interfaces.dual_arm_interface import DualArmInterface - -import numpy as np -import pinocchio as pin -import crocoddyl -from argparse import Namespace -import example_robot_data -import time -import importlib.util -import abc - -if importlib.util.find_spec("mim_solvers"): - import mim_solvers - - -class CrocoOCP(abc.ABC): - """ - CrocoOCP - ---------- - General info: - - torque inputs are assumed - we already solve for state, which includes velocity commands, and we send that if the robot accepts vel cmds - State model : StateMultibody - I'm not even sure what else is there to choose lol. - Actuation model : ActuatioModelFull - We do underactuation via a constraint at the moment. This is solely - because coding up a proper underactuated model is annoying, - and dealing with this is a TODO for later. Having that said, - input constraints are necessary either way. - # TODO: consider ActuationModelFloatingBaseTpl for heron - # TODO: create a different actuation model (or whatever) - # for the mobile base - basically just remove the y movement in the base - # and update the corresponding derivates to 0 - # there's python examples for this, ex. acrobot. - # you might want to implement the entire action model too idk what's really necessary here - Action model : DifferentialActionModelFreeFwdDynamics - We need to create an action model for running and terminal knots. The - forward dynamics (computed using ABA) are implemented - inside DifferentialActionModelFreeFwdDynamics. - """ - - def __init__( - self, args: Namespace, robot: AbstractRobotManager, x0: np.ndarray, solver: str - ): - # TODO: declare attributes here - self.x0 = x0 - self.constructCrocoModels() - self.constructRegulationCosts() - self.constructStateConstraints() - self.constructInputConstraints(solver) - self.problem = crocoddyl.ShootingProblem( - self.x0, [self.runningModel] * args.n_knots, self.terminalModel - ) - - def constructCrocoModels(self): - self.state = crocoddyl.StateMultibody(self.robot.model) - # NOTE: atm we just set effort level in that direction to 0 - self.actuation = crocoddyl.ActuationModelFull(self.state) - - # we will be summing 4 different costs - # first 3 are for tracking, state and control regulation - self.runningCostModel = crocoddyl.CostModelSum(self.state) - self.terminalCostModel = crocoddyl.CostModelSum(self.state) - - def constructRegulationCosts(self): - # cost 1) u residual (actuator cost) - self.uResidual = crocoddyl.ResidualModelControl(self.state, self.state.nv) - self.uRegCost = crocoddyl.CostModelResidual(self.state, self.uResidual) - # cost 2) x residual (overall amount of movement) - self.xResidual = crocoddyl.ResidualModelState( - self.state, self.x0, self.state.nv - ) - self.xRegCost = crocoddyl.CostModelResidual(self.state, self.xResidual) - - # put these costs into the running costs - self.runningCostModel.addCost("xReg", self.xRegCost, 1e-3) - self.runningCostModel.addCost("uReg", self.uRegCost, 1e-3) - # and add the terminal cost, which is the distance to the goal - # NOTE: shouldn't there be a final velocity = 0 here? - self.terminalCostModel.addCost("uReg", self.uRegCost, 1e3) - - def constructStateConstraints(self) -> None: - """ - constructStateConstraints - ------------------------- - """ - # the 4th cost is for defining bounds via costs - # NOTE: could have gotten the same info from state.lb and state.ub. - # the first state is unlimited there idk what that means really, - # but the arm's base isn't doing a full rotation anyway, let alone 2 or more - xlb = np.concatenate( - [self.robot.model.lowerPositionLimit, -1 * self.robot.model.velocityLimit] - ) - xub = np.concatenate( - [self.robot.model.upperPositionLimit, self.robot.model.velocityLimit] - ) - - # NOTE: in an ideal universe this is handled elsewhere - # we have no limits on the mobile base. - # the mobile base is a planar joint. - # since it is represented as [x,y,cos(theta),sin(theta)], there is no point - # to limiting cos(theta),sin(theta) even if we wanted limits, - # because we would then limit theta, or limit ct and st jointly. - # in any event, xlb and xub are 1 number too long -- - # the residual has to be [x,y,theta] because it is in the tangent space - - # the difference between two points on a manifold in pinocchio is defined - # as the velocity which if parallel transported for 1 unit of "time" - # from one to point to the other. - # point activation input and the residual need to be of the same length obviously, - # and this should be 2 * model.nv the way things are defined here. - - # TODO: replace this with subclass or whatever call - # to check if the robot has mobilebase interface - if ( - self.robot.robot_name == "heron" - or self.robot.robot_name == "heronros" - or self.robot.robot_name == "mirros" - or self.robot.robot_name == "yumi" - ): - xlb = xlb[1:] - xub = xub[1:] - - def constructInputConstraints(self) -> None: - if solver == "boxFDDP": - self.inputConstraintsViaBarriersInObjectiveFunction() - if solver == "csqp": - self.boxInputConstraintsAsActualConstraints() - - # NOTE: used by BoxFDDP - def inputConstraintsViaBarriersInObjectiveFunction(self) -> None: - bounds = crocoddyl.ActivationBounds(xlb, xub, 1.0) - xLimitResidual = crocoddyl.ResidualModelState( - self.state, self.x0, self.state.nv - ) - xLimitActivation = crocoddyl.ActivationModelQuadraticBarrier(bounds) - - limitCost = crocoddyl.CostModelResidual( - self.state, xLimitActivation, xLimitResidual - ) - self.runningCostModel.addCost("limitCost", limitCost, 1e3) - self.terminalCostModel.addCost("limitCost", limitCost, 1e3) - - self.runningModel = crocoddyl.IntegratedActionModelEuler( - crocoddyl.DifferentialActionModelFreeInvDynamics( - self.state, self.actuation, self.runningCostModel - ), - args.ocp_dt, - ) - self.runningModel.u_lb = -1 * self.robot.model.effortLimit * 0.1 - self.runningModel.u_ub = self.robot.model.effortLimit * 0.1 - self.terminalModel = crocoddyl.IntegratedActionModelEuler( - crocoddyl.DifferentialActionModelFreeInvDynamics( - self.state, self.actuation, self.terminalCostModel - ), - 0.0, - ) - self.terminalModel.u_lb = -1 * self.robot.model.effortLimit * 0.1 - self.terminalModel.u_ub = self.robot.model.effortLimit * 0.1 - - # NOTE: used by csqp - def boxInputConstraintsAsActualConstraints(self) -> None: - # ConstraintModelManager just stores constraints - constraints = crocoddyl.ConstraintModelManager(self.state, self.robot.model.nv) - u_constraint = crocoddyl.ConstraintModelResidual( - self.state, - self.uResidual, - -1 * self.robot.model.effortLimit * 0.1, - self.robot.model.effortLimit * 0.1, - ) - constraints.addConstraint("u_box_constraint", u_constraint) - x_constraint = crocoddyl.ConstraintModelResidual( - self.state, self.xResidual, xlb, xub - ) - constraints.addConstraint("x_box_constraint", x_constraint) - self.runningModel = crocoddyl.IntegratedActionModelEuler( - crocoddyl.DifferentialActionModelFreeInvDynamics( - self.state, self.actuation, self.runningCostModel, constraints - ), - args.ocp_dt, - ) - self.terminalModel = crocoddyl.IntegratedActionModelEuler( - crocoddyl.DifferentialActionModelFreeInvDynamics( - self.state, self.actuation, self.terminalCostModel, constraints - ), - 0.0, - ) - - @abc.abstractmethod - def constructTaskObjectiveFunction(self, goal) -> None: ... - - # NOTE: used by boxfddp - def createCrocoSolver(self, args, robot, x0): - # just for reference can try - # solver = crocoddyl.SolverIpopt(problem) - self.solver = crocoddyl.SolverBoxFDDP(self.problem) - # TODO: make these callbacks optional - self.solver.setCallbacks( - [crocoddyl.CallbackVerbose(), crocoddyl.CallbackLogger()] - ) - - # NOTE: used by csqp - def createMimSolver(self, args, robot, x0): - # TODO try out the following solvers from mim_solvers: - # - csqp - # - stagewise qp - # and the solver - # both of these have generic inequalities you can put in. - # and both are basically QP versions of iLQR if i'm not wrong - # (i have no idea tho) - # solver = mim_solvers.SolverSQP(problem) - self.solver = mim_solvers.SolverCSQP(self.problem) - # TODO: make these callbacks optional - self.solver.setCallbacks( - [mim_solvers.CallbackVerbose(), mim_solvers.CallbackLogger()] - ) - - # this shouldn't really depend on x0 but i can't be bothered - def solveOCP(self, args, robot, x0): - # run solve - # NOTE: there are some possible parameters here that I'm not using - xs = [x0] * (self.solver.problem.T + 1) - us = self.solver.problem.quasiStatic([x0] * self.solver.problem.T) - - start = time.time() - solver.solve(xs, us, 500, False, 1e-9) - end = time.time() - print("solved in:", end - start, "seconds") - - # solver.solve() - # get reference (state trajectory) - # we aren't using controls because we only have velocity inputs - xs = np.array(solver.xs) - qs = xs[:, : robot.model.nq] - vs = xs[:, robot.model.nq :] - reference = {"qs": qs, "vs": vs, "dt": args.ocp_dt} - return reference, solver - - -class SingleArmIKOCP(CrocoOCP): - def __init__( - args: Namespace, robot: SingleArmInterface, x0: np.ndarray, T_w_goal: pin.SE3 - ): - super().__init__(args, robot, x0) - self.constructTaskObjectiveFunction(T_w_goal) - - def constructTaskObjectiveFunction(self, T_w_goal): - # cost 3) distance to goal -> SE(3) error - # TODO: make this follow a path. - # to do so, you need to implement a residualmodel for that in crocoddyl. - # there's an example of exending crocoddyl in colmpc repo - # (look at that to see how to compile, make python bindings etc) - framePlacementResidual = crocoddyl.ResidualModelFramePlacement( - self.state, - # TODO: check if this is the frame we actually want (ee tip) - # the id is an integer and that's what api wants - robot.model.getFrameId("tool0"), - T_w_goal.copy(), - self.state.nv, - ) - goalTrackingCost = crocoddyl.CostModelResidual( - self.state, framePlacementResidual - ) - # 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( - self.state, - robot.model.getFrameId("tool0"), - pin.Motion(np.zeros(6)), - pin.ReferenceFrame.WORLD, - ) - frameVelocityCost = crocoddyl.CostModelResidual( - self.state, frameVelocityResidual - ) - self.runningCostModel.addCost("gripperPose", goalTrackingCost, 1e2) - self.terminalCostModel.addCost("gripperPose", goalTrackingCost, 1e2) - self.terminalCostModel.addCost("velFinal", frameVelocityCost, 1e3) - - -def DualArmIKOCP(CrocoOCP): - def __init__( - args: Namespace, robot: DualArmInterface, x0: np.ndarray, goal: pin.SE3 - ): - super().__init__(args, robot, x0) - - def constructTaskObjectiveFunction(self, goal): - T_w_lgoal, T_w_rgoal = goal - framePlacementResidual_l = crocoddyl.ResidualModelFramePlacement( - self.state, - self.robot.model.l_ee_frame_id, - T_w_lgoal.copy(), - self.state.nv, - ) - framePlacementResidual_r = crocoddyl.ResidualModelFramePlacement( - self.state, - self.robot.model.r_ee_frame_id, - T_w_rgoal.copy(), - self.state.nv, - ) - goalTrackingCost_l = crocoddyl.CostModelResidual( - self.state, framePlacementResidual_l - ) - goalTrackingCost_r = crocoddyl.CostModelResidual( - self.state, framePlacementResidual_r - ) - frameVelocityResidual_l = crocoddyl.ResidualModelFrameVelocity( - self.state, - self.robot.model.l_ee_frame_id, - pin.Motion(np.zeros(6)), - pin.ReferenceFrame.WORLD, - ) - frameVelocityResidual_r = crocoddyl.ResidualModelFrameVelocity( - self.state, - self.robot.model.r_ee_frame_id, - pin.Motion(np.zeros(6)), - pin.ReferenceFrame.WORLD, - ) - frameVelocityCost_l = crocoddyl.CostModelResidual( - self.state, frameVelocityResidual_l - ) - frameVelocityCost_r = crocoddyl.CostModelResidual( - self.state, frameVelocityResidual_r - ) - self.runningCostModel.addCost("gripperPose_l", goalTrackingCost_l, 1e2) - self.runningCostModel.addCost("gripperPose_r", goalTrackingCost_r, 1e2) - self.terminalCostModel.addCost("gripperPose_l", goalTrackingCost_l, 1e2) - self.terminalCostModel.addCost("gripperPose_r", goalTrackingCost_r, 1e2) - self.terminalCostModel.addCost("velFinal_l", frameVelocityCost_l, 1e3) - self.terminalCostModel.addCost("velFinal_r", frameVelocityCost_r, 1e3) - - -class CrocoEEPathFollowingOCP(CrocoOCP): - """ - createCrocoEEPathFollowingOCP - ------------------------------- - creates a path following problem with a single end-effector reference. - it is instantiated to just to stay at the current position. - NOTE: the path MUST be time indexed with the SAME time used between the knots - """ - - def __init__(self, args: Namespace, robot: SingleArmInterface, x0: np.ndarray): - super().__init__(self, args, robot, x0) - - def constructTaskObjectiveFunction(self, goal): - T_w_e = robot.T_w_e - path = [T_w_e] * args.n_knots - - # TODO: you need to find a way to make this mesh - # with the generic construction. - # the only difference is that we construct SLIGHTLY DIFFERENT running models here - # instead of multiplying the same one - def constructPathFollowingRunningModels(self): - runningModels = [] - for i in range(args.n_knots): - runningCostModel = crocoddyl.CostModelSum(state) - runningCostModel.addCost("xReg", xRegCost, 1e-3) - runningCostModel.addCost("uReg", uRegCost, 1e-3) - if args.solver == "boxfddp": - runningCostModel.addCost("limitCost", limitCost, 1e3) - framePlacementResidual = crocoddyl.ResidualModelFramePlacement( - state, - robot.model.getFrameId("tool0"), - path[i], # this better be done with the same time steps as the knots - # NOTE: this should be done outside of this function to clarity - state.nv, - ) - goalTrackingCost = crocoddyl.CostModelResidual( - state, framePlacementResidual - ) - runningCostModel.addCost("gripperPose" + str(i), goalTrackingCost, 1e2) - # runningCostModel.addCost("gripperPose", goalTrackingCost, 1e2) - - if args.solver == "boxfddp": - runningModel = crocoddyl.IntegratedActionModelEuler( - crocoddyl.DifferentialActionModelFreeInvDynamics( - state, actuation, runningCostModel - ), - args.ocp_dt, - ) - runningModel.u_lb = -1 * robot.model.effortLimit * 0.1 - runningModel.u_ub = robot.model.effortLimit * 0.1 - if args.solver == "csqp": - runningModel = crocoddyl.IntegratedActionModelEuler( - crocoddyl.DifferentialActionModelFreeInvDynamics( - state, actuation, runningCostModel, constraints - ), - args.ocp_dt, - ) - runningModels.append(runningModel) - - # terminal model - if args.solver == "boxfddp": - terminalModel = crocoddyl.IntegratedActionModelEuler( - crocoddyl.DifferentialActionModelFreeInvDynamics( - state, actuation, terminalCostModel - ), - 0.0, - ) - terminalModel.u_lb = -1 * robot.model.effortLimit * 0.1 - terminalModel.u_ub = robot.model.effortLimit * 0.1 - if args.solver == "csqp": - terminalModel = crocoddyl.IntegratedActionModelEuler( - crocoddyl.DifferentialActionModelFreeInvDynamics( - state, actuation, terminalCostModel, constraints - ), - 0.0, - ) - - terminalCostModel.addCost( - "gripperPose" + str(args.n_knots), goalTrackingCost, 1e2 - ) - # terminalCostModel.addCost("gripperPose", goalTrackingCost, 1e2) - - # now we define the problem - problem = crocoddyl.ShootingProblem(x0, runningModels, terminalModel) - return problem - - -def createBaseAndEEPathFollowingOCP(args, robot: AbstractRobotManager, x0): - """ - createBaseAndEEPathFollowingOCP - ------------------------------- - creates a path following problem. - it is instantiated to just to stay at the current position. - NOTE: the path MUST be time indexed with the SAME time used between the knots - """ - if robot.robot_name != "yumi": - T_w_e = robot.getT_w_e() - path_ee = [T_w_e] * args.n_knots - else: - T_w_e_left, T_w_e_right = robot.getT_w_e() - path_ee = [T_w_e_left] * args.n_knots - # TODO: have a different reference for each arm - path_base = [np.append(x0[:2], 0.0)] * args.n_knots - - # TODO: MAKE A RUNNING MODEL FUNCTION HERE WHICH - # PLAYS BALL WITH GENERAL CONSTRUCTION - runningModels = [] - for i in range(args.n_knots): - runningCostModel = crocoddyl.CostModelSum(state) - runningCostModel.addCost("xReg", xRegCost, 1e-3) - runningCostModel.addCost("uReg", uRegCost, 1e-3) - if args.solver == "boxfddp": - runningCostModel.addCost("limitCost", limitCost, 1e3) - - ########################## - # single arm reference # - ########################## - if robot.robot_name != "yumi": - eePoseResidual = crocoddyl.ResidualModelFramePlacement( - state, - robot.model.getFrameId("tool0"), - path_ee[i], # this better be done with the same time steps as the knots - # NOTE: this should be done outside of this function to clarity - state.nv, - ) - eeTrackingCost = crocoddyl.CostModelResidual(state, eePoseResidual) - runningCostModel.addCost( - "ee_pose" + str(i), eeTrackingCost, args.ee_pose_cost - ) - ######################### - # dual arm references # - ######################### - else: - l_eePoseResidual = crocoddyl.ResidualModelFramePlacement( - state, - robot.model.l_ee_frame_id, - # MASSIVE TODO: actually put in reference for left arm - path_ee[i], - state.nv, - ) - l_eeTrackingCost = crocoddyl.CostModelResidual(state, l_eePoseResidual) - runningCostModel.addCost( - "l_ee_pose" + str(i), l_eeTrackingCost, args.ee_pose_cost - ) - r_eePoseResidual = crocoddyl.ResidualModelFramePlacement( - state, - robot.model.r_ee_frame_id, - # MASSIVE TODO: actually put in reference for left arm - path_ee[i], - state.nv, - ) - r_eeTrackingCost = crocoddyl.CostModelResidual(state, r_eePoseResidual) - runningCostModel.addCost( - "r_ee_pose" + str(i), r_eeTrackingCost, args.ee_pose_cost - ) - - baseTranslationResidual = crocoddyl.ResidualModelFrameTranslation( - state, robot.model.getFrameId("mobile_base"), path_base[i], state.nv - ) - baseTrackingCost = crocoddyl.CostModelResidual(state, baseTranslationResidual) - runningCostModel.addCost( - "base_translation" + str(i), baseTrackingCost, args.base_translation_cost - ) - - if args.solver == "boxfddp": - runningModel = crocoddyl.IntegratedActionModelEuler( - crocoddyl.DifferentialActionModelFreeInvDynamics( - state, actuation, runningCostModel - ), - args.ocp_dt, - ) - runningModel.u_lb = -1 * robot.model.effortLimit * 0.1 - runningModel.u_ub = robot.model.effortLimit * 0.1 - if args.solver == "csqp": - runningModel = crocoddyl.IntegratedActionModelEuler( - crocoddyl.DifferentialActionModelFreeInvDynamics( - state, actuation, runningCostModel, constraints - ), - args.ocp_dt, - ) - runningModels.append(runningModel) - - # terminal model - if args.solver == "boxfddp": - terminalModel = crocoddyl.IntegratedActionModelEuler( - crocoddyl.DifferentialActionModelFreeInvDynamics( - state, actuation, terminalCostModel - ), - 0.0, - ) - terminalModel.u_lb = -1 * robot.model.effortLimit * 0.1 - terminalModel.u_ub = robot.model.effortLimit * 0.1 - if args.solver == "csqp": - terminalModel = crocoddyl.IntegratedActionModelEuler( - crocoddyl.DifferentialActionModelFreeInvDynamics( - state, actuation, terminalCostModel, constraints - ), - 0.0, - ) - - if robot.robot_name != "yumi": - terminalCostModel.addCost( - "ee_pose" + str(args.n_knots), eeTrackingCost, args.ee_pose_cost - ) - else: - terminalCostModel.addCost( - "l_ee_pose" + str(args.n_knots), l_eeTrackingCost, args.ee_pose_cost - ) - terminalCostModel.addCost( - "r_ee_pose" + str(args.n_knots), r_eeTrackingCost, args.ee_pose_cost - ) - terminalCostModel.addCost( - "base_translation" + str(args.n_knots), - baseTrackingCost, - args.base_translation_cost, - ) - - # now we define the problem - problem = crocoddyl.ShootingProblem(x0, runningModels, terminalModel) - return problem diff --git a/python/smc/control/optimal_control/path_following_croco_ocp.py b/python/smc/control/optimal_control/path_following_croco_ocp.py new file mode 100644 index 0000000000000000000000000000000000000000..26fdae9f5eb727b0404740447c660133da41c0aa --- /dev/null +++ b/python/smc/control/optimal_control/path_following_croco_ocp.py @@ -0,0 +1,163 @@ +# TODO: make a bundle method which solves and immediately follows the traj. +from smc.control.optimal_control.abstract_croco_ocp import CrocoOCP +from smc.robots.robotmanager_abstract import AbstractRobotManager +from smc.robots.interfaces.single_arm_interface import SingleArmInterface +from smc.robots.interfaces.dual_arm_interface import DualArmInterface + +import numpy as np +import crocoddyl +from argparse import Namespace + + +class CrocoEEPathFollowingOCP(CrocoOCP): + """ + createCrocoEEPathFollowingOCP + ------------------------------- + creates a path following problem with a single end-effector reference. + it is instantiated to just to stay at the current position. + NOTE: the path MUST be time indexed with the SAME time used between the knots + """ + + def __init__(self, args: Namespace, robot: SingleArmInterface, x0: np.ndarray): + goal = None + super().__init__(args, robot, x0, goal) + + def constructTaskObjectiveFunction(self, goal): + T_w_e = self.robot.T_w_e + path = [T_w_e] * self.args.n_knots + + for i in range(self.args.n_knots): + framePlacementResidual = crocoddyl.ResidualModelFramePlacement( + self.state, + self.robot.ee_frame_id, + path[i], # this better be done with the same time steps as the knots + # NOTE: this should be done outside of this function to clarity + self.state.nv, + ) + goalTrackingCost = crocoddyl.CostModelResidual( + self.state, framePlacementResidual + ) + self.runningCostModels[i].addCost( + "gripperPose" + str(i), goalTrackingCost, 1e2 + ) + # runningCostModel.addCost("gripperPose", goalTrackingCost, 1e2) + + self.terminalCostModel.addCost( + "gripperPose" + str(self.args.n_knots), goalTrackingCost, 1e2 + ) + # terminalCostModel.addCost("gripperPose", goalTrackingCost, 1e2) + + +class BaseAndEEPathFollowingOCP(CrocoOCP): + """ + createBaseAndEEPathFollowingOCP + ------------------------------- + creates a path following problem. + it is instantiated to just to stay at the current position. + NOTE: the path MUST be time indexed with the SAME time used between the knots + """ + + def __init__(self, args, robot: AbstractRobotManager, x0): + goal = None + super().__init__(args, robot, x0, goal) + + def constructTaskObjectiveFunction(self, goal) -> None: + T_w_e = self.robot.T_w_e + path_ee = [T_w_e] * self.args.n_knots + path_base = [np.append(self.x0[:2], 0.0)] * self.args.n_knots + + for i in range(self.args.n_knots): + eePoseResidual = crocoddyl.ResidualModelFramePlacement( + self.state, + self.robot.model.ee_frame_id, + path_ee[i], # this better be done with the same time steps as the knots + # NOTE: this should be done outside of this function to clarity + self.state.nv, + ) + eeTrackingCost = crocoddyl.CostModelResidual(self.state, eePoseResidual) + self.runningCostModels[i].addCost( + "ee_pose" + str(i), eeTrackingCost, self.args.ee_pose_cost + ) + + baseTranslationResidual = crocoddyl.ResidualModelFrameTranslation( + self.state, self.robot.model.base_frame_id, path_base[i], self.state.nv + ) + baseTrackingCost = crocoddyl.CostModelResidual( + self.state, baseTranslationResidual + ) + self.runningCostModels[i].addCost( + "base_translation" + str(i), + baseTrackingCost, + self.args.base_translation_cost, + ) + + self.terminalCostModel.addCost( + "ee_pose" + str(self.args.n_knots), eeTrackingCost, self.args.ee_pose_cost + ) + self.terminalCostModel.addCost( + "base_translation" + str(self.args.n_knots), + baseTrackingCost, + self.args.base_translation_cost, + ) + + +class BaseAndDualArmEEPathFollowingOCP(CrocoOCP): + def __init__(self, args, robot: DualArmInterface, x0): + goal = None + super().__init__(args, robot, x0, goal) + + def constructTaskObjectiveFunction(self, goal) -> None: + T_w_e_left, T_w_e_right = self.robot.getT_w_e + path_ee_left = [T_w_e_left] * self.args.n_knots + path_ee_right = [T_w_e_right] * self.args.n_knots + path_base = [np.append(self.x0[:2], 0.0)] * self.args.n_knots + + for i in range(self.args.n_knots): + l_eePoseResidual = crocoddyl.ResidualModelFramePlacement( + self.state, + self.robot.model.l_ee_frame_id, + # MASSIVE TODO: actually put in reference for left arm + path_ee_left[i], + self.state.nv, + ) + l_eeTrackingCost = crocoddyl.CostModelResidual(self.state, l_eePoseResidual) + self.runningCostModels[i].addCost( + "l_ee_pose" + str(i), l_eeTrackingCost, self.args.ee_pose_cost + ) + r_eePoseResidual = crocoddyl.ResidualModelFramePlacement( + self.state, + self.robot.model.r_ee_frame_id, + # MASSIVE TODO: actually put in reference for left arm + path_ee_right[i], + self.state.nv, + ) + r_eeTrackingCost = crocoddyl.CostModelResidual(self.state, r_eePoseResidual) + self.runningCostModels[i].addCost( + "r_ee_pose" + str(i), r_eeTrackingCost, self.args.ee_pose_cost + ) + baseTranslationResidual = crocoddyl.ResidualModelFrameTranslation( + self.state, self.robot.model.base_frame_id, path_base[i], self.state.nv + ) + baseTrackingCost = crocoddyl.CostModelResidual( + self.state, baseTranslationResidual + ) + self.runningCostModels[i].addCost( + "base_translation" + str(i), + baseTrackingCost, + self.args.base_translation_cost, + ) + self.terminalCostModel.addCost( + "l_ee_pose" + str(self.args.n_knots), + l_eeTrackingCost, + self.args.ee_pose_cost, + ) + self.terminalCostModel.addCost( + "r_ee_pose" + str(self.args.n_knots), + r_eeTrackingCost, + self.args.ee_pose_cost, + ) + self.terminalCostModel.addCost( + "base_translation" + str(self.args.n_knots), + baseTrackingCost, + self.args.base_translation_cost, + ) diff --git a/python/smc/control/optimal_control/point_to_point_croco_ocp.py b/python/smc/control/optimal_control/point_to_point_croco_ocp.py new file mode 100644 index 0000000000000000000000000000000000000000..39c6e19bc4c3c2901c5ce751e6c286c2aa109ba5 --- /dev/null +++ b/python/smc/control/optimal_control/point_to_point_croco_ocp.py @@ -0,0 +1,107 @@ +from smc.control.optimal_control.abstract_croco_ocp import CrocoOCP +from smc.robots.interfaces.single_arm_interface import SingleArmInterface +from smc.robots.interfaces.dual_arm_interface import DualArmInterface + +import numpy as np +import pinocchio as pin +import crocoddyl +from argparse import Namespace + + +class SingleArmIKOCP(CrocoOCP): + def __init__( + self, + args: Namespace, + robot: SingleArmInterface, + x0: np.ndarray, + T_w_goal: pin.SE3, + ): + super().__init__(args, robot, x0, T_w_goal) + self.constructTaskObjectiveFunction(T_w_goal) + + def constructTaskObjectiveFunction(self, goal): + T_w_goal = goal + # cost 3) distance to goal -> SE(3) error + # TODO: make this follow a path. + # to do so, you need to implement a residualmodel for that in crocoddyl. + # there's an example of exending crocoddyl in colmpc repo + # (look at that to see how to compile, make python bindings etc) + framePlacementResidual = crocoddyl.ResidualModelFramePlacement( + self.state, + # TODO: check if this is the frame we actually want (ee tip) + # the id is an integer and that's what api wants + self.robot.model.getFrameId("tool0"), + T_w_goal.copy(), + self.state.nv, + ) + goalTrackingCost = crocoddyl.CostModelResidual( + self.state, framePlacementResidual + ) + # 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( + self.state, + self.robot.model.getFrameId("tool0"), + pin.Motion(np.zeros(6)), + pin.ReferenceFrame.WORLD, + ) + frameVelocityCost = crocoddyl.CostModelResidual( + self.state, frameVelocityResidual + ) + for i in range(self.args.n_knots): + self.runningCostModels[i].addCost("gripperPose", goalTrackingCost, 1e2) + self.terminalCostModel.addCost("gripperPose", goalTrackingCost, 1e2) + self.terminalCostModel.addCost("velFinal", frameVelocityCost, 1e3) + + +class DualArmIKOCP(CrocoOCP): + def __init__( + self, args: Namespace, robot: DualArmInterface, x0: np.ndarray, goal: pin.SE3 + ): + super().__init__(args, robot, x0, goal) + + def constructTaskObjectiveFunction(self, goal): + T_w_lgoal, T_w_rgoal = goal + framePlacementResidual_l = crocoddyl.ResidualModelFramePlacement( + self.state, + self.robot.model.l_ee_frame_id, + T_w_lgoal.copy(), + self.state.nv, + ) + framePlacementResidual_r = crocoddyl.ResidualModelFramePlacement( + self.state, + self.robot.model.r_ee_frame_id, + T_w_rgoal.copy(), + self.state.nv, + ) + goalTrackingCost_l = crocoddyl.CostModelResidual( + self.state, framePlacementResidual_l + ) + goalTrackingCost_r = crocoddyl.CostModelResidual( + self.state, framePlacementResidual_r + ) + frameVelocityResidual_l = crocoddyl.ResidualModelFrameVelocity( + self.state, + self.robot.model.l_ee_frame_id, + pin.Motion(np.zeros(6)), + pin.ReferenceFrame.WORLD, + ) + frameVelocityResidual_r = crocoddyl.ResidualModelFrameVelocity( + self.state, + self.robot.model.r_ee_frame_id, + pin.Motion(np.zeros(6)), + pin.ReferenceFrame.WORLD, + ) + frameVelocityCost_l = crocoddyl.CostModelResidual( + self.state, frameVelocityResidual_l + ) + frameVelocityCost_r = crocoddyl.CostModelResidual( + self.state, frameVelocityResidual_r + ) + for i in range(self.args.n_knots): + self.runningCostModels[i].addCost("gripperPose_l", goalTrackingCost_l, 1e2) + self.runningCostModels[i].addCost("gripperPose_r", goalTrackingCost_r, 1e2) + self.terminalCostModel.addCost("gripperPose_l", goalTrackingCost_l, 1e2) + self.terminalCostModel.addCost("gripperPose_r", goalTrackingCost_r, 1e2) + self.terminalCostModel.addCost("velFinal_l", frameVelocityCost_l, 1e3) + self.terminalCostModel.addCost("velFinal_r", frameVelocityCost_r, 1e3) diff --git a/python/smc/robots/implementations/ur5e.py b/python/smc/robots/implementations/ur5e.py index 5d7f652cc10cb866bc9367b26353664560d19d39..ba9768a545ac675a39896b902be43a411e64203c 100644 --- a/python/smc/robots/implementations/ur5e.py +++ b/python/smc/robots/implementations/ur5e.py @@ -262,7 +262,7 @@ def get_model(): model.jointPlacements[5] = wrist_2_se3 model.jointPlacements[6] = wrist_3_se3 # TODO: fix where the fingers end up by setting a better position here (or maybe not here idk) - # model = pin.buildReducedModel(model, [7, 8], np.zeros(model.nq)) + model = pin.buildReducedModel(model, [7, 8], np.zeros(model.nq)) data = pin.Data(model) return model, collision_model, visual_model, data