2"""Scripted floor pick example ported from src/examples/ex_pick.cpp.
4Joint-impedance control (tau = Kp*e - Kd*qdot + g_kdl) drives scripted
5waypoints to grasp and lift a cube. An Env on_reset hook re-homes the arm,
6re-poses the cube, and opens the gripper, so the simulate-UI reset button
10from __future__
import annotations
14from dataclasses
import dataclass
17import mj_kdl_wrapper
as mjk
19HOME = [0.0, 0.2618, 3.1416, -2.2689, 0.0, 0.9599, 1.5708]
23CUBE_Z = CUBE_HALF_SIZE
24KP = [100.0, 200.0, 100.0, 200.0, 100.0, 200.0, 100.0]
25KD = [10.0, 20.0, 10.0, 20.0, 10.0, 20.0, 10.0]
29 """Raised when the simulate UI reset is detected, to restart the sequence."""
32@dataclass(frozen=
True)
43 attach = mjk.AttachmentSpec()
44 attach.mjcf_path = gripper_path
45 attach.attach_to = mjk.AttachTarget(mjk.AttachKind.Site,
"pinch_site")
51 cube = mjk.SceneObject()
53 cube.shape = mjk.Shape.BOX
54 cube.size = [CUBE_HALF_SIZE, CUBE_HALF_SIZE, CUBE_HALF_SIZE]
55 cube.pos = [CUBE_X, CUBE_Y, CUBE_Z]
56 cube.rgba = [1.0, 0.5, 0.0, 1.0]
58 cube.condim = mjk.Condim.Torsional
59 cube.friction = [0.8, 0.02, 0.001]
64 spec = mjk.SceneSpec()
67 spec.add_skybox =
True
70 robot_spec = mjk.RobotSpec()
71 robot_spec.path = mjk.menagerie.model_path(
"kinova_gen3", env_var=
"MJ_KDL_MODEL")
72 robot_spec.attachments = [
74 mjk.menagerie.asset_path(
"robotiq_2f85/2f85.xml", env_var=
"MJ_KDL_GRIPPER")
77 spec.robots = [robot_spec]
79 env = mjk.Env.build(spec)
80 tool = mjk.ToolFrameSpec()
81 tool.tool_body =
"g_base"
82 tool.tcp_site =
"g_pinch"
83 robot = env.create_robot(
"base_link",
"bracelet_link", tool=tool)
87def jnt(values: list[float]) -> kdl.JntArray:
88 q = kdl.JntArray(len(values))
89 for i, value
in enumerate(values):
94def as_list(q: kdl.JntArray) -> list[float]:
95 return [q[i]
for i
in range(q.rows())]
98def clamp(value: float, low: float, high: float) -> float:
99 return max(low, min(high, value))
102def clamp_joint(value: float, limit: tuple[float, float]) -> float:
104 if math.isfinite(low)
and math.isfinite(high)
and high > low:
105 return clamp(value, low, high)
111 seed_values: list[float],
113 joint_limits: list[tuple[float, float]],
115 fk = kdl.ChainFkSolverPos_recursive(chain)
116 ik = kdl.ChainIkSolverVel_wdls(chain)
119 dq = kdl.JntArray(q.rows())
121 current = kdl.Frame()
122 fk.JntToCart(q, current)
123 dx = kdl.diff(current, target)
124 pos_err = dx.vel.Norm()
125 rot_err = dx.rot.Norm()
126 if pos_err <= 0.003
and rot_err <= 0.03:
129 dx.vel = dx.vel * (0.05 / pos_err)
131 dx.rot = dx.rot * (0.20 / rot_err)
132 if ik.CartToJnt(q, dx, dq) < 0:
133 raise RuntimeError(
"PyKDL IK velocity step failed")
134 for i
in range(q.rows()):
136 raise RuntimeError(
"PyKDL IK did not converge")
140 chain = robot.kdl_chain()
141 target_rot = (env.body_frame(
"bracelet_link").Inverse() * env.site_frame(
"g_pinch")).M
144 def solve(x: float, y: float, z: float) -> list[float]:
147 chain, seed, kdl.Frame(target_rot, kdl.Vector(x, y, z)), robot.joint_limits
153 "pregrasp": solve(CUBE_X, CUBE_Y, CUBE_Z + 0.20),
154 "grasp": solve(CUBE_X, CUBE_Y, CUBE_Z),
155 "lift": solve(CUBE_X, CUBE_Y, CUBE_Z + 0.30),
161 gravity = robot.gravity_torques(-9.81)
162 robot.jnt_trq_cmd = [
163 KP[i] * (target[i] - robot.jnt_pos_msr[i]) - KD[i] * robot.jnt_vel_msr[i] + gravity[i]
164 for i
in range(robot.n_joints)
169 return max(abs(target[i] - robot.jnt_pos_msr[i])
for i
in range(robot.n_joints))
172def lerp(start: list[float], target: list[float], alpha: float) -> list[float]:
173 return [a + alpha * (b - a)
for a, b
in zip(start, target)]
177 env: mjk.Env, robot: mjk.Robot, viewer: mjk.SimulateViewer |
None, state: dict
179 ok = viewer.step()
if viewer
is not None else robot.step()
182 if viewer
is not None and env.time() < state[
"prev"] - 1e-6:
184 state[
"prev"] = env.time()
186 state[
"prev"] = env.time()
194 viewer: mjk.SimulateViewer |
None,
197 print(f
"State: {phase.name}")
199 start = robot.jnt_pos_msr[:]
202 elapsed = env.time() - t0
203 alpha =
clamp(elapsed / phase.duration, 0.0, 1.0)
if phase.duration > 0.0
else 1.0
205 if env.has_actuator(
"g_fingers_actuator"):
206 env.set_actuator_ctrl(
"g_fingers_actuator", phase.gripper)
208 done_time = elapsed >= phase.duration
209 done_pose = phase.settle_tol < 0.0
or max_abs_joint_err(robot, phase.target) <= phase.settle_tol
210 done_timeout = phase.timeout > 0.0
and elapsed >= phase.timeout
211 if (done_time
and done_pose)
or done_timeout:
213 if viewer
is not None and not viewer.is_running():
215 if not step_once(env, robot, viewer, state):
220 parser = argparse.ArgumentParser()
221 parser.add_argument(
"--gui", action=
"store_true")
222 parser.add_argument(
"--headless", action=
"store_true", help=argparse.SUPPRESS)
223 args = parser.parse_args()
227 robot.ctrl_mode = mjk.CtrlMode.TORQUE
230 robot.set_joint_pos(HOME, call_forward=
False)
231 env.set_body_pose(
"cube", [CUBE_X, CUBE_Y, CUBE_Z])
232 if env.has_actuator(
"g_fingers_actuator"):
233 env.set_actuator_ctrl(
"g_fingers_actuator", 0.0)
235 env.on_reset = on_reset
240 Phase(
"HOME", waypoints[
"home"], 1.0, 2.5, 0.08, 0.0),
241 Phase(
"PREGRASP", waypoints[
"pregrasp"], 5.0, 7.0, 0.08, 0.0),
242 Phase(
"GRASP", waypoints[
"grasp"], 5.0, 8.0, 0.03, 0.0),
243 Phase(
"CLOSE", waypoints[
"grasp"], 1.5, 2.5, -1.0, 255.0),
244 Phase(
"LIFT", waypoints[
"lift"], 3.0, 5.0, 0.08, 255.0),
245 Phase(
"HOLD", waypoints[
"lift"], 1.0
if not args.gui
else 10.0, 0.0, -1.0, 255.0),
247 state = {
"prev": env.time()}
249 viewer = mjk.SimulateViewer.open(robot,
"ex_pick.py")
251 while viewer.is_running():
254 if not run_phase(env, robot, phase, viewer, state):
257 except ResetRequested:
259 except StopIteration:
265 if not run_phase(env, robot, phase,
None, state):
267 cube = env.body_frame(
"cube")
268 print(f
"final cube height: {cube.p.z():.3f} m")
274if __name__ ==
"__main__":
275 raise SystemExit(
main())
None apply_pd_gravity(mjk.Robot robot, list[float] target)
float clamp(float value, float low, float high)
kdl.JntArray jnt(list[float] values)
list[float] as_list(kdl.JntArray q)
float clamp_joint(float value, tuple[float, float] limit)
list[float] lerp(list[float] start, list[float] target, float alpha)
dict[str, list[float]] build_waypoints(mjk.Env env, mjk.Robot robot)
bool step_once(mjk.Env env, mjk.Robot robot, mjk.SimulateViewer|None viewer, dict state)
list[float] solve_position_ik(kdl.Chain chain, list[float] seed_values, kdl.Frame target, list[tuple[float, float]] joint_limits)
float max_abs_joint_err(mjk.Robot robot, list[float] target)
tuple[mjk.Env, mjk.Robot] build_env()
mjk.SceneObject cube_object()
mjk.AttachmentSpec gripper_attachment(str gripper_path)
bool run_phase(mjk.Env env, mjk.Robot robot, Phase phase, mjk.SimulateViewer|None viewer, dict state)