323 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			323 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|  | import argparse | ||
|  | import time | ||
|  | import warnings | ||
|  | 
 | ||
|  | import numpy as np | ||
|  | import placo | ||
|  | from placo_utils.visualization import ( | ||
|  |     footsteps_viz, | ||
|  |     frame_viz, | ||
|  |     line_viz, | ||
|  |     robot_frame_viz, | ||
|  |     robot_viz, | ||
|  | ) | ||
|  | 
 | ||
|  | from mini_bdx.bdx_mujoco_server import BDXMujocoServer | ||
|  | from mini_bdx.hwi import HWI | ||
|  | 
 | ||
|  | warnings.filterwarnings("ignore") | ||
|  | 
 | ||
|  | parser = argparse.ArgumentParser(description="Process some integers.") | ||
|  | parser.add_argument( | ||
|  |     "-p", "--pybullet", action="store_true", help="PyBullet visualization" | ||
|  | ) | ||
|  | parser.add_argument( | ||
|  |     "-m", "--meshcat", action="store_true", help="MeshCat visualization" | ||
|  | ) | ||
|  | parser.add_argument("-r", "--robot", action="store_true", help="run on real robot") | ||
|  | parser.add_argument("--mujoco", action="store_true", help="Mujoco visualization") | ||
|  | args = parser.parse_args() | ||
|  | 
 | ||
|  | DT = 0.01 | ||
|  | REFINE = 10 | ||
|  | model_filename = "../../mini_bdx/robots/bdx/robot.urdf" | ||
|  | 
 | ||
|  | # Loading the robot | ||
|  | robot = placo.HumanoidRobot(model_filename) | ||
|  | robot.set_joint_limits("left_knee", -2, -0.01) | ||
|  | robot.set_joint_limits("right_knee", -2, -0.01) | ||
|  | 
 | ||
|  | # Walk parameters - if double_support_ratio is not set to 0, should be greater than replan_frequency | ||
|  | parameters = placo.HumanoidParameters() | ||
|  | 
 | ||
|  | parameters.double_support_ratio = 0.2  # Ratio of double support (0.0 to 1.0) | ||
|  | parameters.startend_double_support_ratio = ( | ||
|  |     1.5  # Ratio duration of supports for starting and stopping walk | ||
|  | ) | ||
|  | parameters.planned_timesteps = 48  # Number of timesteps planned ahead | ||
|  | parameters.replan_timesteps = 10  # Replanning each n timesteps | ||
|  | # parameters.zmp_reference_weight = 1e-6 | ||
|  | 
 | ||
|  | # Posture parameters | ||
|  | parameters.walk_com_height = 0.15  # Constant height for the CoM [m] | ||
|  | parameters.walk_foot_height = 0.006  # Height of foot rising while walking [m] | ||
|  | parameters.walk_trunk_pitch = np.deg2rad(10)  # Trunk pitch angle [rad] | ||
|  | parameters.walk_foot_rise_ratio = ( | ||
|  |     0.2  # Time ratio for the foot swing plateau (0.0 to 1.0) | ||
|  | ) | ||
|  | 
 | ||
|  | # Timing parameters | ||
|  | # parameters.single_support_duration = 1 / ( | ||
|  | #     0.5 * np.sqrt(9.81 / parameters.walk_com_height) | ||
|  | # )  # Constant height for the CoM [m]  # Duration of single support phase [s] | ||
|  | parameters.single_support_duration = 0.2  # Duration of single support phase [s] | ||
|  | parameters.single_support_timesteps = ( | ||
|  |     10  # Number of planning timesteps per single support phase | ||
|  | ) | ||
|  | 
 | ||
|  | # Feet parameters | ||
|  | parameters.foot_length = 0.06  # Foot length [m] | ||
|  | parameters.foot_width = 0.006  # Foot width [m] | ||
|  | parameters.feet_spacing = 0.12  # Lateral feet spacing [m] | ||
|  | parameters.zmp_margin = 0.0  # ZMP margin [m] | ||
|  | parameters.foot_zmp_target_x = 0.0  # Reference target ZMP position in the foot [m] | ||
|  | parameters.foot_zmp_target_y = 0.0  # Reference target ZMP position in the foot [m] | ||
|  | 
 | ||
|  | # Limit parameters | ||
|  | parameters.walk_max_dtheta = 1  # Maximum dtheta per step [rad] | ||
|  | parameters.walk_max_dy = 0.1  # Maximum dy per step [m] | ||
|  | parameters.walk_max_dx_forward = 0.08  # Maximum dx per step forward [m] | ||
|  | parameters.walk_max_dx_backward = 0.03  # Maximum dx per step backward [m] | ||
|  | 
 | ||
|  | # Creating the kinematics solver | ||
|  | solver = placo.KinematicsSolver(robot) | ||
|  | solver.enable_velocity_limits(True) | ||
|  | # solver.enable_joint_limits(False) | ||
|  | robot.set_velocity_limits(12.0) | ||
|  | solver.dt = DT / REFINE | ||
|  | 
 | ||
|  | # Creating the walk QP tasks | ||
|  | tasks = placo.WalkTasks() | ||
|  | # tasks.trunk_mode = True | ||
|  | # tasks.com_x = 0.04 | ||
|  | tasks.initialize_tasks(solver, robot) | ||
|  | tasks.left_foot_task.orientation().mask.set_axises("yz", "local") | ||
|  | tasks.right_foot_task.orientation().mask.set_axises("yz", "local") | ||
|  | # tasks.trunk_orientation_task.configure("trunk_orientation", "soft", 1e-4) | ||
|  | # tasks.left_foot_task.orientation().configure("left_foot_orientation", "soft", 1e-6) | ||
|  | # tasks.right_foot_task.orientation().configure("right_foot_orientation", "soft", 1e-6) | ||
|  | 
 | ||
|  | # Creating a joint task to assign DoF values for upper body | ||
|  | elbow = -50 * np.pi / 180 | ||
|  | shoulder_roll = 0 * np.pi / 180 | ||
|  | shoulder_pitch = 20 * np.pi / 180 | ||
|  | joints_task = solver.add_joints_task() | ||
|  | joints_task.set_joints( | ||
|  |     { | ||
|  |         # "left_shoulder_roll": shoulder_roll, | ||
|  |         # "left_shoulder_pitch": shoulder_pitch, | ||
|  |         # "left_elbow": elbow, | ||
|  |         # "right_shoulder_roll": -shoulder_roll, | ||
|  |         # "right_shoulder_pitch": shoulder_pitch, | ||
|  |         # "right_elbow": elbow, | ||
|  |         "head_pitch": 0.0, | ||
|  |         "head_yaw": 0.0, | ||
|  |         "neck_pitch": 0.0, | ||
|  |         "left_antenna": 0.0, | ||
|  |         "right_antenna": 0.0, | ||
|  |         # "left_ankle_roll": 0.0, | ||
|  |         # "right_ankle_roll": 0.0 | ||
|  |     } | ||
|  | ) | ||
|  | joints_task.configure("joints", "soft", 1.0) | ||
|  | 
 | ||
|  | # cam = solver.add_centroidal_momentum_task(np.array([0., 0., 0.])) | ||
|  | # cam.mask.set_axises("x", "custom") | ||
|  | # cam.mask.R_custom_world = robot.get_T_world_frame("trunk")[:3, :3].T | ||
|  | # cam.configure("cam", "soft", 1e-3) | ||
|  | 
 | ||
|  | # Placing the robot in the initial position | ||
|  | print("Placing the robot in the initial position...") | ||
|  | tasks.reach_initial_pose( | ||
|  |     np.eye(4), | ||
|  |     parameters.feet_spacing, | ||
|  |     parameters.walk_com_height, | ||
|  |     parameters.walk_trunk_pitch, | ||
|  | ) | ||
|  | print("Initial position reached") | ||
|  | 
 | ||
|  | 
 | ||
|  | # Creating the FootstepsPlanner | ||
|  | repetitive_footsteps_planner = placo.FootstepsPlannerRepetitive(parameters) | ||
|  | d_x = 0.1 | ||
|  | d_y = 0.0 | ||
|  | d_theta = 0.0 | ||
|  | nb_steps = 5 | ||
|  | repetitive_footsteps_planner.configure(d_x, d_y, d_theta, nb_steps) | ||
|  | 
 | ||
|  | # Planning footsteps | ||
|  | T_world_left = placo.flatten_on_floor(robot.get_T_world_left()) | ||
|  | T_world_right = placo.flatten_on_floor(robot.get_T_world_right()) | ||
|  | footsteps = repetitive_footsteps_planner.plan( | ||
|  |     placo.HumanoidRobot_Side.left, T_world_left, T_world_right | ||
|  | ) | ||
|  | 
 | ||
|  | supports = placo.FootstepsPlanner.make_supports( | ||
|  |     footsteps, True, parameters.has_double_support(), True | ||
|  | ) | ||
|  | 
 | ||
|  | # Creating the pattern generator and making an initial plan | ||
|  | walk = placo.WalkPatternGenerator(robot, parameters) | ||
|  | trajectory = walk.plan(supports, robot.com_world(), 0.0) | ||
|  | 
 | ||
|  | if args.pybullet: | ||
|  |     # Loading the PyBullet simulation | ||
|  |     import pybullet as p | ||
|  |     from onshape_to_robot.simulation import Simulation | ||
|  | 
 | ||
|  |     sim = Simulation(model_filename, realTime=True, dt=DT, ignore_self_collisions=True) | ||
|  | elif args.meshcat: | ||
|  |     # Starting Meshcat viewer | ||
|  |     viz = robot_viz(robot) | ||
|  |     footsteps_viz(trajectory.get_supports()) | ||
|  | elif args.robot: | ||
|  |     hwi = HWI() | ||
|  |     hwi.turn_on() | ||
|  |     time.sleep(2) | ||
|  | elif args.mujoco: | ||
|  |     time_since_last_right_contact = 0.0 | ||
|  |     time_since_last_left_contact = 0.0 | ||
|  |     bdx_mujoco_server = BDXMujocoServer( | ||
|  |         model_path="../../mini_bdx/robots/bdx", gravity_on=True | ||
|  |     ) | ||
|  |     bdx_mujoco_server.start() | ||
|  | else: | ||
|  |     print("No visualization selected, use either -p or -m") | ||
|  |     exit() | ||
|  | 
 | ||
|  | # Timestamps | ||
|  | start_t = time.time() | ||
|  | initial_delay = -1.0 | ||
|  | t = initial_delay | ||
|  | last_display = time.time() | ||
|  | last_replan = 0 | ||
|  | petage_de_gueule = False | ||
|  | got_input = False | ||
|  | while True: | ||
|  |     if got_input: | ||
|  |         repetitive_footsteps_planner.configure(d_x, d_y, d_theta, nb_steps) | ||
|  |         got_input = False | ||
|  | 
 | ||
|  |     # Invoking the IK QP solver | ||
|  |     for k in range(REFINE): | ||
|  |         # Updating the QP tasks from planned trajectory | ||
|  |         if not petage_de_gueule: | ||
|  |             tasks.update_tasks_from_trajectory(trajectory, t - DT + k * DT / REFINE) | ||
|  | 
 | ||
|  |         robot.update_kinematics() | ||
|  |         qd_sol = solver.solve(True) | ||
|  |     # solver.dump_status() | ||
|  | 
 | ||
|  |     # Ensuring the robot is kinematically placed on the floor on the proper foot to avoid integration drifts | ||
|  |     # if not trajectory.support_is_both(t): | ||
|  |     # robot.update_support_side(str(trajectory.support_side(t))) | ||
|  |     # robot.ensure_on_floor() | ||
|  | 
 | ||
|  |     # If enough time elapsed and we can replan, do the replanning | ||
|  |     if ( | ||
|  |         t - last_replan > parameters.replan_timesteps * parameters.dt() | ||
|  |         and walk.can_replan_supports(trajectory, t) | ||
|  |     ): | ||
|  |         last_replan = t | ||
|  | 
 | ||
|  |         # Replanning footsteps from current trajectory | ||
|  |         supports = walk.replan_supports(repetitive_footsteps_planner, trajectory, t) | ||
|  | 
 | ||
|  |         # Replanning CoM trajectory, yielding a new trajectory we can switch to | ||
|  |         trajectory = walk.replan(supports, trajectory, t) | ||
|  | 
 | ||
|  |         if args.meshcat: | ||
|  |             # Drawing footsteps | ||
|  |             footsteps_viz(supports) | ||
|  | 
 | ||
|  |             # Drawing planned CoM trajectory on the ground | ||
|  |             coms = [ | ||
|  |                 [*trajectory.get_p_world_CoM(t)[:2], 0.0] | ||
|  |                 for t in np.linspace(trajectory.t_start, trajectory.t_end, 100) | ||
|  |             ] | ||
|  |             line_viz("CoM_trajectory", np.array(coms), 0xFFAA00) | ||
|  | 
 | ||
|  |     # During the warmup phase, the robot is enforced to stay in the initial position | ||
|  |     if args.pybullet: | ||
|  |         if t < -2: | ||
|  |             T_left_origin = sim.transformation("origin", "left_foot_frame") | ||
|  |             T_world_left = sim.poseToMatrix(([0.0, 0.0, 0.05], [0.0, 0.0, 0.0, 1.0])) | ||
|  |             T_world_origin = T_world_left @ T_left_origin | ||
|  | 
 | ||
|  |             sim.setRobotPose(*sim.matrixToPose(T_world_origin)) | ||
|  | 
 | ||
|  |         joints = {joint: robot.get_joint(joint) for joint in sim.getJoints()} | ||
|  |         applied = sim.setJoints(joints) | ||
|  |         sim.tick() | ||
|  | 
 | ||
|  |     # Updating meshcat display periodically | ||
|  |     elif args.meshcat: | ||
|  |         if time.time() - last_display > 0.01: | ||
|  |             last_display = time.time() | ||
|  |             viz.display(robot.state.q) | ||
|  | 
 | ||
|  |             # frame_viz("left_foot_target", trajectory.get_T_world_left(t)) | ||
|  |             # frame_viz("right_foot_target", trajectory.get_T_world_right(t)) | ||
|  |             # robot_frame_viz(robot, "left_foot") | ||
|  |             # robot_frame_viz(robot, "right_foot") | ||
|  | 
 | ||
|  |             T_world_trunk = np.eye(4) | ||
|  |             T_world_trunk[:3, :3] = trajectory.get_R_world_trunk(t) | ||
|  |             T_world_trunk[:3, 3] = trajectory.get_p_world_CoM(t) | ||
|  |             frame_viz("trunk_target", T_world_trunk) | ||
|  |             # footsteps_viz(trajectory.get_supports()) | ||
|  | 
 | ||
|  |     if args.robot or args.mujoco: | ||
|  |         angles = { | ||
|  |             "right_hip_yaw": robot.get_joint("right_hip_yaw"), | ||
|  |             "right_hip_roll": robot.get_joint("right_hip_roll"), | ||
|  |             "right_hip_pitch": robot.get_joint("right_hip_pitch"), | ||
|  |             "right_knee": robot.get_joint("right_knee"), | ||
|  |             "right_ankle": robot.get_joint("right_ankle"), | ||
|  |             "left_hip_yaw": robot.get_joint("left_hip_yaw"), | ||
|  |             "left_hip_roll": robot.get_joint("left_hip_roll"), | ||
|  |             "left_hip_pitch": robot.get_joint("left_hip_pitch"), | ||
|  |             "left_knee": robot.get_joint("left_knee"), | ||
|  |             "left_ankle": robot.get_joint("left_ankle"), | ||
|  |             "neck_pitch": robot.get_joint("neck_pitch"), | ||
|  |             "head_pitch": robot.get_joint("head_pitch"), | ||
|  |             "head_yaw": robot.get_joint("head_yaw"), | ||
|  |         } | ||
|  |         if args.robot: | ||
|  |             hwi.set_position_all(angles) | ||
|  |         elif args.mujoco: | ||
|  |             right_contact, left_contact = bdx_mujoco_server.get_feet_contact() | ||
|  |             if left_contact: | ||
|  |                 time_since_last_left_contact = 0.0 | ||
|  |             if right_contact: | ||
|  |                 time_since_last_right_contact = 0.0 | ||
|  |             # print("time since last left contact :", time_since_last_left_contact) | ||
|  |             # print("time since last right contact :", time_since_last_right_contact) | ||
|  |             bdx_mujoco_server.send_action(list(angles.values())) | ||
|  | 
 | ||
|  |         if ( | ||
|  |             time_since_last_left_contact > parameters.single_support_duration | ||
|  |             or time_since_last_right_contact > parameters.single_support_duration | ||
|  |         ): | ||
|  |             petage_de_gueule = True | ||
|  |             # print("pétage de gueule") | ||
|  |         else: | ||
|  |             petage_de_gueule = False | ||
|  | 
 | ||
|  |         time_since_last_left_contact += DT | ||
|  |         time_since_last_right_contact += DT | ||
|  | 
 | ||
|  |         if bdx_mujoco_server.key_pressed is not None: | ||
|  |             got_input = True | ||
|  |             d_x = 0.05 | ||
|  |         else: | ||
|  |             got_input = True | ||
|  |             d_x = 0 | ||
|  |             d_y = 0 | ||
|  |             d_theta = 0 | ||
|  | 
 | ||
|  |     t += DT | ||
|  |     # print(t) | ||
|  |     while time.time() < start_t + t: | ||
|  |         time.sleep(1e-5) |