|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +import numpy as np |
| 4 | +import time |
| 5 | + |
| 6 | +from juliacall import Main as jl, convert as jlconvert |
| 7 | + |
| 8 | + |
| 9 | +def init(): |
| 10 | + """ |
| 11 | + Initialize the Julia environment, load the TORA.jl package, and create a robot model. |
| 12 | + """ |
| 13 | + # Load package dependencies |
| 14 | + jl.seval("using MeshCat") |
| 15 | + jl.seval("using TORA") |
| 16 | + |
| 17 | + # Call the greet() function from the TORA.jl package |
| 18 | + jl.TORA.greet() |
| 19 | + |
| 20 | + jl_vis = jl.MeshCat.Visualizer() |
| 21 | + # jl.open(jl_vis) # NOT WORKING |
| 22 | + |
| 23 | + jl_robot = jl.TORA.create_robot_franka("panda_arm", jl_vis) |
| 24 | + |
| 25 | + return jl_robot |
| 26 | + |
| 27 | + |
| 28 | +def prepare_initial_guess(jl_problem, jl_robot, py_q): |
| 29 | + # Start by assuming zero positions, velocities, and torques for all the knots of the trajectory |
| 30 | + py_initial_qs = np.zeros((jl_problem.num_knots, jl_robot.n_q)) |
| 31 | + py_initial_vs = np.zeros((jl_problem.num_knots, jl_robot.n_v)) |
| 32 | + py_initial_τs = np.zeros((jl_problem.num_knots, jl_robot.n_τ)) |
| 33 | + |
| 34 | + # To improve the initial guess for the robot joint positions, we can take |
| 35 | + # the current robot configuration and simply repeat it for all the knots |
| 36 | + py_initial_qs = np.tile(py_q, (jl_problem.num_knots, 1)) |
| 37 | + |
| 38 | + # Concatenate the initial guess |
| 39 | + py_initial_guess = np.concatenate((py_initial_qs, py_initial_vs, py_initial_τs), axis=1) |
| 40 | + |
| 41 | + # Flatten the numpy array |
| 42 | + py_initial_guess = py_initial_guess.flatten() |
| 43 | + |
| 44 | + # Truncate the torques at the last knot of the trajectory from the initial guess |
| 45 | + py_initial_guess = py_initial_guess[: -jl_robot.n_τ] |
| 46 | + |
| 47 | + # Convert the numpy array to a Julia array |
| 48 | + jl_initial_guess = jlconvert(jl.Vector[jl.Float64], py_initial_guess) |
| 49 | + |
| 50 | + return jl_initial_guess |
| 51 | + |
| 52 | + |
| 53 | +def main(): |
| 54 | + jl.println("Hello from Julia!") |
| 55 | + |
| 56 | + jl_robot = init() |
| 57 | + |
| 58 | + trajectory_discretisation = 100 # in Hertz |
| 59 | + trajectory_duration = 2.0 # in seconds |
| 60 | + |
| 61 | + trajectory_num_knots = int(trajectory_discretisation * trajectory_duration) + 1 # in units (number of knots) |
| 62 | + trajectory_dt = 1 / trajectory_discretisation # in seconds |
| 63 | + |
| 64 | + # Options for the Ipopt numerical solver |
| 65 | + ipopt_options = jl.Dict() |
| 66 | + ipopt_options["hessian_approximation"] = "limited-memory" # L-BFGS |
| 67 | + ipopt_options["max_cpu_time"] = 2.0 # in seconds |
| 68 | + ipopt_options["mu_strategy"] = "adaptive" # monotone, adaptive, quality-function |
| 69 | + # ipopt_options["acceptable_compl_inf_tol"] = 0.1 |
| 70 | + # ipopt_options["acceptable_constr_viol_tol"] = 0.1 |
| 71 | + # ipopt_options["acceptable_dual_inf_tol"] = 1.0 |
| 72 | + ipopt_options["acceptable_iter"] = 1 |
| 73 | + ipopt_options["acceptable_tol"] = 1.0 |
| 74 | + # ipopt_options["print_level"] = 3 # default: 5 |
| 75 | + |
| 76 | + # Starting configuration of the robot |
| 77 | + py_q_start = [0, 0, 0, -np.pi / 2, 0, np.pi, 0] |
| 78 | + |
| 79 | + done = False |
| 80 | + |
| 81 | + while not done: |
| 82 | + # Create a problem instance |
| 83 | + jl_problem = jl.TORA.Problem(jl_robot, trajectory_num_knots, trajectory_dt) |
| 84 | + |
| 85 | + # Get the current robot state |
| 86 | + # (but for this example, let's use a nominal configuration and random velocities) |
| 87 | + py_q = py_q_start |
| 88 | + py_v = np.random.rand(jl_robot.n_v) |
| 89 | + |
| 90 | + # Constrain the initial joint positions and velocities to the current state of the robot |
| 91 | + jl.TORA.fix_joint_positions_b(jl_problem, jl_robot, 1, py_q) |
| 92 | + jl.TORA.fix_joint_velocities_b(jl_problem, jl_robot, 1, py_v) |
| 93 | + |
| 94 | + # Constrain the final joint velocities to zero |
| 95 | + jl.TORA.fix_joint_velocities_b(jl_problem, jl_robot, jl_problem.num_knots, np.zeros(jl_robot.n_v)) |
| 96 | + |
| 97 | + # Define the end-effector position that we want to achieve |
| 98 | + py_eff_pos = [0.5, 0.2, 0.1] |
| 99 | + |
| 100 | + # Constrain the end-effector position at the last knot of the trajectory |
| 101 | + jl.TORA.constrain_ee_position_b(jl_problem, jl_problem.num_knots, py_eff_pos) |
| 102 | + |
| 103 | + # Show a summary of the problem instance (this can be commented out) |
| 104 | + jl.TORA.show_problem_info(jl_problem) |
| 105 | + |
| 106 | + # Prepare the initial guess for the solver |
| 107 | + jl_initial_guess = prepare_initial_guess(jl_problem, jl_robot, py_q) |
| 108 | + |
| 109 | + jl_cpu_time, jl_x, jl_solver_log = jl.TORA.solve_with_ipopt( |
| 110 | + jl_problem, |
| 111 | + jl_robot, |
| 112 | + initial_guess=jl_initial_guess, |
| 113 | + user_options=ipopt_options, |
| 114 | + use_inv_dyn=True, |
| 115 | + minimise_τ=False, |
| 116 | + ) |
| 117 | + |
| 118 | + # Unpack the solution `x` into joint positions, velocities, and torques |
| 119 | + jl_qs, jl_vs, jl_τs = jl.TORA.unpack_x(jl_x, jl_robot, jl_problem) |
| 120 | + |
| 121 | + # Convert the Julia arrays to numpy arrays |
| 122 | + py_qs = np.array(jl_qs) |
| 123 | + py_vs = np.array(jl_vs) |
| 124 | + py_τs = np.array(jl_τs) |
| 125 | + |
| 126 | + # Do something with the result (e.g., control the real robot) |
| 127 | + # (...) |
| 128 | + |
| 129 | + # Sleep for 1 second before the next iteration |
| 130 | + time.sleep(1) |
| 131 | + |
| 132 | + |
| 133 | +if __name__ == "__main__": |
| 134 | + main() |
0 commit comments