39 table = mjk.SceneObject()
41 table.mjcf_path = str(mjk.ASSETS_DIR /
"table.xml")
42 table.pos = [0.0, 0.0, SURFACE_Z]
44 cube = mjk.SceneObject()
46 cube.shape = mjk.Shape.BOX
47 cube.size = [CUBE_HS, CUBE_HS, CUBE_HS]
48 cube.pos = CUBE_START[:]
49 cube.rgba = [0.1, 0.35, 1.0, 1.0]
51 cube.condim = mjk.Condim.Torsional
52 cube.friction = [0.8, 0.02, 0.001]
53 gripper = mjk.AttachmentSpec()
54 gripper.mjcf_path = str(mjk.ASSETS_DIR /
"robotiq_2f85/2f85.xml")
55 gripper.attach_to = mjk.AttachTarget(mjk.AttachKind.Site,
"pinch_site")
57 robot_spec = mjk.RobotSpec()
58 robot_spec.path = str(mjk.ASSETS_DIR /
"kinova_gen3/gen3.xml")
59 robot_spec.pos = [0.0, 0.0, SURFACE_Z]
60 robot_spec.attachments = [gripper]
62 spec = mjk.SceneSpec()
65 spec.add_skybox =
True
66 spec.objects = [table, cube]
67 spec.robots = [robot_spec]
68 env = mjk.Env.build(spec)
69 tool = mjk.ToolFrameSpec()
70 tool.tool_body =
"g_base_mount"
71 tool.tcp_site =
"g_pinch"
72 return env, env.create_robot(
"base_link",
"bracelet_link", tool=tool)
164def run_phase(env, ctrl: Achd, fingers, phase: dict, state: dict) -> bool:
165 """One update() per step: it reads the state and applies the previous cycle's command."""
166 print(f
"State: {phase['name']}")
168 phase_start = ctrl.measure()
169 support = state[
"support"]
170 if phase[
"name"] ==
"PLACE_ABOVE" or not support[
"valid"]:
171 support.update(z_ref=ctrl.link_z() + SUPPORT_LIFT, valid=
True)
172 support[
"prev_z"] = support[
"z_ref"]
174 prev_target = phase_start
178 elapsed = env.data.time - t0
179 s =
smoothstep(elapsed / phase[
"duration"])
if phase[
"duration"] > 0.0
else 1.0
180 target = kdl.addDelta(phase_start, kdl.diff(phase_start, phase[
"target"]), s)
181 target_twist = kdl.diff(prev_target, target, ctrl.dt)
184 f_ext = [kdl.Wrench.Zero()
for _
in range(ctrl.n_seg)]
186 vz = (z - support[
"prev_z"]) / ctrl.dt
187 support[
"prev_z"] = z
188 support[
"drop"] = max(support[
"drop"], support[
"z_ref"] - SUPPORT_LIFT - z)
189 fz = min(SUPPORT_F_MAX, max(0.0, SUPPORT_KP * (support[
"z_ref"] - z) - SUPPORT_KD * vz))
190 f_ext[ctrl.support] = kdl.Wrench(kdl.Vector(0.0, 0.0, fz), kdl.Vector.Zero())
191 ctrl.control(target, target_twist, f_ext)
192 fingers.ctrl[0] = phase[
"gripper"]
194 err = kdl.diff(tcp, phase[
"target"])
195 settled = phase[
"pos_tol"] < 0.0
or (
196 err.vel.Norm() <= phase[
"pos_tol"]
and err.rot.Norm() <= phase[
"rot_tol"]
198 if (elapsed >= phase[
"duration"]
and settled)
or elapsed >= phase[
"timeout"]:
203 state[
"reset"] =
False
209 grasp_rot = robot.tip_T_tcp.M
211 z_above = z_grasp + 0.20
212 z_lift = z_grasp + 0.30
214 def at(xy, z: float) -> kdl.Frame:
215 return kdl.Frame(grasp_rot, kdl.Vector(xy[0], xy[1], z))
217 def phase(name, target, duration, timeout, pos_tol, rot_tol, gripper) -> dict:
228 closed = GRIPPER_CLOSED
230 phase(
"HOME", home_tcp, 1.0, 2.5, 0.03, 0.05, 0.0),
231 phase(
"PICK_ABOVE", at(PICK, z_above), 8.0, 14.0, 0.04, 0.03, 0.0),
232 phase(
"PICK", at(PICK, z_grasp), 5.0, 12.0, 0.02, 0.03, 0.0),
233 phase(
"CLOSE", at(PICK, z_grasp), 1.5, 2.5, -1.0, -1.0, closed),
234 phase(
"LIFT", at(PICK, z_lift), 3.0, 8.0, 0.04, 0.03, closed),
235 phase(
"PLACE_ABOVE", at(PLACE, z_above), 5.0, 12.0, 0.04, 0.03, closed),
236 phase(
"PLACE", at(PLACE, z_grasp), 5.0, 14.0, 0.02, 0.03, closed),
237 phase(
"OPEN", at(PLACE, z_grasp), 1.0, 2.0, -1.0, -1.0, 0.0),
238 phase(
"RETREAT", at(PLACE, z_above), 3.0, 6.0, 0.04, 0.08, 0.0),
239 phase(
"HOLD", at(PLACE, z_above), 4.0, 4.0, -1.0, -1.0, 0.0),
244 parser = argparse.ArgumentParser()
245 parser.add_argument(
"--gui", action=
"store_true")
246 args = parser.parse_args()
250 robot.set_control_mode(mjk.CtrlMode.TORQUE)
251 fingers = env.data.actuator(
"g_fingers_actuator")
252 gravity_z = env.spec.gravity_z
253 ctrl =
Achd(robot, gravity_z, env.model.opt.timestep)
254 dyn = kdl.ChainDynParam(ctrl.chain, kdl.Vector(0.0, 0.0, gravity_z))
255 home_tcp = kdl.Frame()
256 ctrl.fk.JntToCart(
jnt(HOME), home_tcp)
258 state = {
"reset":
False}
261 robot.set_joint_pos(HOME)
262 env.set_body_pose(
"cube", CUBE_START)
263 fingers.ctrl[0] = 0.0
264 g = kdl.JntArray(robot.n_joints)
265 dyn.JntToGravity(
jnt(HOME), g)
266 robot.jnt_trq_cmd = [g[i]
for i
in range(robot.n_joints)]
267 state[
"reset"] =
True
269 env.on_reset = on_reset
272 env.open_viewer(
"ex_achd_pick_place.py")
277 reset=
False, support={
"valid":
False,
"z_ref": 0.0,
"prev_z": 0.0,
"drop": 0.0}
280 completed = all(
run_phase(env, ctrl, fingers, p, state)
for p
in phases)
282 except ResetRequested:
286 cube = env.body_frame(
"cube").p
287 place_err = math.hypot(cube.x() - PLACE[0], cube.y() - PLACE[1])
288 on_table = abs(cube.z() - (SURFACE_Z + CUBE_HS)) < 0.002
290 f
"cube final position: [{cube.x():.4f}, {cube.y():.4f}, {cube.z():.4f}] "
291 f
"place error {place_err * 1000:.4f} mm (limit {MAX_PLACE_ERR * 1000} mm)"
292 + (
"" if on_table
else ", not on the table")
294 drop = state[
"support"][
"drop"]
295 print(f
"elbow drop: {drop * 1000:.4f} mm (limit {MAX_ELBOW_DROP * 1000} mm)")
300 ok = completed
and on_table
and place_err <= MAX_PLACE_ERR
and drop <= MAX_ELBOW_DROP
301 print(f
"{'PASS' if ok else 'FAIL'}: the cube was placed with the elbow held up")
302 return 0
if ok
else 1