Mujoco KDL Wrapper  0.2.2
MuJoCo + KDL bridge for robot kinematics and dynamics
Loading...
Searching...
No Matches
ex_achd_pick_place.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""ACHD Cartesian pick-place example using PyKDL directly.
3
4Ported from src/examples/ex_achd_pick_place.cpp. Uses the Vereshchagin
5acceleration-constrained hybrid-dynamics solver plus RNEA for computed torques.
6An Env on_reset hook re-homes the arm, re-poses the cube, and opens the gripper.
7"""
8
9from __future__ import annotations
10
11import argparse
12
13import PyKDL as kdl
14import mj_kdl_wrapper as mjk
15
16HOME = [0.0, 0.2618, 3.1416, -2.2689, 0.0, 0.9599, 1.5708]
17SURFACE_Z = 0.70
18KP_LIN, KD_LIN = 160.0, 30.0
19KP_ROT, KD_ROT = 100.0, 35.0
20BETA_LIN_MAX, BETA_ROT_MAX, TAU_MAX = 100.0, 70.0, 59.0
21CUBE_START = [0.40, 0.0, SURFACE_Z + 0.02]
22
23
24class ResetRequested(Exception):
25 """Raised when the simulate UI reset is detected, to restart the sequence."""
26
27
28def build_env() -> tuple[mjk.Env, mjk.Robot]:
29 table = mjk.SceneObject()
30 table.name = "table"
31 table.mjcf_path = mjk.menagerie.asset_path("table.xml", env_var="MJ_KDL_TABLE")
32 table.pos = [0.0, 0.0, SURFACE_Z]
33 table.fixed = True
34
35 cube = mjk.SceneObject()
36 cube.name = "cube"
37 cube.shape = mjk.Shape.BOX
38 cube.size = [0.02, 0.02, 0.02]
39 cube.pos = CUBE_START[:]
40 cube.rgba = [0.1, 0.35, 1.0, 1.0]
41 cube.mass = 0.1
42 cube.condim = mjk.Condim.Torsional
43 cube.friction = [0.8, 0.02, 0.001]
44
45 attach = mjk.AttachmentSpec()
46 attach.mjcf_path = mjk.menagerie.asset_path("robotiq_2f85/2f85.xml", env_var="MJ_KDL_GRIPPER")
47 attach.attach_to = mjk.AttachTarget(mjk.AttachKind.Site, "pinch_site")
48 attach.prefix = "g_"
49
50 spec = mjk.SceneSpec()
51 spec.timestep = 0.002
52 spec.add_floor = True
53 spec.add_skybox = True
54 spec.objects = [table, cube]
55 robot_spec = mjk.RobotSpec()
56 robot_spec.path = mjk.menagerie.model_path("kinova_gen3", env_var="MJ_KDL_MODEL")
57 robot_spec.pos = [0.0, 0.0, SURFACE_Z]
58 robot_spec.attachments = [attach]
59 spec.robots = [robot_spec]
60
61 env = mjk.Env.build(spec)
62 tool = mjk.ToolFrameSpec()
63 tool.tool_body = "g_base"
64 tool.tcp_site = "g_pinch"
65 robot = env.create_robot("base_link", "bracelet_link", tool=tool)
66 return env, robot
67
68
69def jnt(values: list[float]) -> kdl.JntArray:
70 out = kdl.JntArray(len(values))
71 for i, v in enumerate(values):
72 out[i] = v
73 return out
74
75
76def clamp_abs(value: float, limit: float) -> float:
77 return max(-limit, min(limit, value))
78
79
80def smoothstep(value: float) -> float:
81 t = max(0.0, min(1.0, value))
82 return t * t * (3.0 - 2.0 * t)
83
84
85def alpha_identity() -> kdl.Jacobian:
86 alpha = kdl.Jacobian(6)
87 for i in range(6):
88 alpha[i, i] = 1.0
89 return alpha
90
91
92def achd_step(robot, chain, fk, achd, rnea, alpha, target, err_prev, first_pid) -> None:
93 robot.update()
94 n = robot.n_joints
95 q = jnt(robot.jnt_pos_msr)
96 qd = jnt(robot.jnt_vel_msr)
97
98 current = kdl.Frame()
99 fk.JntToCart(q, current)
100 err = kdl.diff(current, target)
101 dt = 0.002
102 e = [err.vel.x(), err.vel.y(), err.vel.z(), err.rot.x(), err.rot.y(), err.rot.z()]
103 if first_pid[0]:
104 err_prev[:] = e
105 first_pid[0] = False
106 de = [(e[i] - err_prev[i]) / dt for i in range(6)]
107 err_prev[:] = e
108
109 beta = kdl.JntArray(6)
110 beta[0] = clamp_abs(KP_LIN * e[0] + KD_LIN * de[0], BETA_LIN_MAX)
111 beta[1] = clamp_abs(KP_LIN * e[1] + KD_LIN * de[1], BETA_LIN_MAX)
112 beta[2] = clamp_abs(KP_LIN * e[2] + KD_LIN * de[2], BETA_LIN_MAX)
113 beta[3] = clamp_abs(KP_ROT * e[3] + KD_ROT * de[3], BETA_ROT_MAX)
114 beta[4] = clamp_abs(KP_ROT * e[4] + KD_ROT * de[4], BETA_ROT_MAX)
115 beta[5] = clamp_abs(KP_ROT * e[5] + KD_ROT * de[5], BETA_ROT_MAX)
116
117 qdd = kdl.JntArray(n)
118 ff = kdl.JntArray(n)
119 constraint_tau = kdl.JntArray(n)
120 f_ext = [kdl.Wrench.Zero() for _ in range(chain.getNrOfSegments())]
121 if achd.CartToJnt(q, qd, qdd, alpha, beta, f_ext, ff, constraint_tau) < 0:
122 raise RuntimeError("PyKDL ACHD failed")
123
124 tau = kdl.JntArray(n)
125 if rnea.CartToJnt(q, qd, qdd, f_ext, tau) < 0:
126 raise RuntimeError("PyKDL RNEA failed")
127 robot.jnt_trq_cmd = [clamp_abs(tau[i], TAU_MAX) for i in range(n)]
128
129
130def step_once(env, robot, viewer, state) -> bool:
131 ok = viewer.step() if viewer is not None else robot.step()
132 if not ok:
133 return False
134 if viewer is not None and env.time() < state["prev"] - 1e-6:
135 env.reset()
136 state["prev"] = env.time()
137 raise ResetRequested()
138 state["prev"] = env.time()
139 return True
140
141
142def run_phase(env, robot, chain, fk, achd, rnea, alpha, phase, viewer, state) -> bool:
143 print(f"State: {phase['name']}")
144 start = robot.fk_frame()
145 t0 = env.time()
146 err_prev = [0.0] * 6
147 first_pid = [True]
148 while env.time() - t0 < phase["duration"]:
149 t = smoothstep((env.time() - t0) / phase["duration"])
150 target = kdl.addDelta(start, kdl.diff(start, phase["target"]), t)
151 achd_step(robot, chain, fk, achd, rnea, alpha, target, err_prev, first_pid)
152 if env.has_actuator("g_fingers_actuator"):
153 env.set_actuator_ctrl("g_fingers_actuator", phase["gripper"])
154 if viewer is not None and not viewer.is_running():
155 return False
156 if not step_once(env, robot, viewer, state):
157 return False
158 return True
159
160
161def main() -> int:
162 parser = argparse.ArgumentParser()
163 parser.add_argument("--gui", action="store_true")
164 args = parser.parse_args()
165
166 env, robot = build_env()
167 try:
168 chain = robot.kdl_chain()
169 fk = kdl.ChainFkSolverPos_recursive(chain)
170 achd = kdl.ChainHdSolver_Vereshchagin_Fixed_Joint(
171 chain, kdl.Twist(kdl.Vector(0.0, 0.0, 9.81), kdl.Vector.Zero()), 6
172 )
173 rnea = kdl.ChainIdSolver_RNE(chain, kdl.Vector(0.0, 0.0, -9.81))
174 alpha = alpha_identity()
175
176 robot.ctrl_mode = mjk.CtrlMode.TORQUE
177
178 def on_reset(ctx):
179 robot.set_joint_pos(HOME, call_forward=False)
180 env.set_body_pose("cube", CUBE_START)
181 if env.has_actuator("g_fingers_actuator"):
182 env.set_actuator_ctrl("g_fingers_actuator", 0.0)
183
184 env.on_reset = on_reset
185 env.reset()
186
187 robot.update()
188 grasp_rot = robot.fk_frame().M
189
190 def target(x: float, y: float, z: float) -> kdl.Frame:
191 return kdl.Frame(grasp_rot, kdl.Vector(x, y, z))
192
193 phases = [
194 {"name": "HOME", "target": robot.fk_frame(), "duration": 0.8, "gripper": 0.0},
195 {"name": "PICK_ABOVE", "target": target(0.40, 0.0, 0.24), "duration": 2.0, "gripper": 0.0},
196 {"name": "PICK", "target": target(0.40, 0.0, 0.04), "duration": 1.8, "gripper": 0.0},
197 {"name": "CLOSE", "target": target(0.40, 0.0, 0.04), "duration": 1.0, "gripper": 255.0},
198 {"name": "LIFT", "target": target(0.40, 0.0, 0.34), "duration": 1.6, "gripper": 255.0},
199 {"name": "PLACE_ABOVE", "target": target(0.40, 0.24, 0.24), "duration": 1.8, "gripper": 255.0},
200 {"name": "PLACE", "target": target(0.40, 0.24, 0.04), "duration": 1.8, "gripper": 255.0},
201 {"name": "OPEN", "target": target(0.40, 0.24, 0.04), "duration": 0.8, "gripper": 0.0},
202 {"name": "RETREAT", "target": target(0.40, 0.24, 0.24), "duration": 1.2, "gripper": 0.0},
203 ]
204
205 state = {"prev": env.time()}
206 if args.gui:
207 viewer = mjk.SimulateViewer.open(robot, "ex_achd_pick_place.py")
208 try:
209 while viewer.is_running():
210 try:
211 for phase in phases:
212 if not run_phase(env, robot, chain, fk, achd, rnea, alpha, phase, viewer, state):
213 raise StopIteration
214 break
215 except ResetRequested:
216 continue
217 except StopIteration:
218 break
219 finally:
220 viewer.close()
221 else:
222 for phase in phases:
223 if not run_phase(env, robot, chain, fk, achd, rnea, alpha, phase, None, state):
224 break
225 cube = env.body_frame("cube")
226 print(f"cube final position: {[round(v, 3) for v in [cube.p.x(), cube.p.y(), cube.p.z()]]}")
227 finally:
228 env.close()
229 return 0
230
231
232if __name__ == "__main__":
233 raise SystemExit(main())
kdl.Jacobian alpha_identity()
float smoothstep(float value)
kdl.JntArray jnt(list[float] values)
bool step_once(env, robot, viewer, state)
float clamp_abs(float value, float limit)
None achd_step(robot, chain, fk, achd, rnea, alpha, target, err_prev, first_pid)
bool run_phase(env, robot, chain, fk, achd, rnea, alpha, phase, viewer, state)
tuple[mjk.Env, mjk.Robot] build_env()