Mujoco KDL Wrapper  0.2.2
MuJoCo + KDL bridge for robot kinematics and dynamics
Loading...
Searching...
No Matches
ex_achd_table_slide.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""ACHD table-slide example using PyKDL's Vereshchagin solver directly.
3
4Ported from src/examples/ex_achd_table_slide.cpp. Slides the TCP along +X under
5an acceleration-constrained hybrid-dynamics controller. An Env on_reset hook
6restores the start pose (and re-primes the PID) so the simulate-UI reset replays
7the slide.
8"""
9
10from __future__ import annotations
11
12import argparse
13
14import PyKDL as kdl
15import mj_kdl_wrapper as mjk
16
17TABLE_POSE = [-0.00258, 1.43, 3.14, -1.70, -0.018, 1.74, 1.57]
18TABLE_Z = 0.447
19MOVE_X = 0.20
20KPLIN, KDLIN = 200.0, 30.0
21KPROT, KDROT = 175.0, 28.0
22BETA_MAX, TAU_MAX = 120.0, 59.0
23
24
25def build_env() -> tuple[mjk.Env, mjk.Robot]:
26 table = mjk.SceneObject()
27 table.name = "table"
28 table.mjcf_path = mjk.menagerie.asset_path("table.xml", env_var="MJ_KDL_TABLE")
29 table.pos = [0.0, 0.0, TABLE_Z]
30 table.fixed = True
31 attach = mjk.AttachmentSpec()
32 attach.mjcf_path = mjk.menagerie.asset_path("robotiq_2f85/2f85.xml", env_var="MJ_KDL_GRIPPER")
33 attach.attach_to = mjk.AttachTarget(mjk.AttachKind.Site, "pinch_site")
34 attach.prefix = "g_"
35 spec = mjk.SceneSpec()
36 spec.timestep = 0.002
37 spec.add_floor = True
38 spec.add_skybox = True
39 spec.objects = [table]
40 robot_spec = mjk.RobotSpec()
41 robot_spec.path = mjk.menagerie.model_path("kinova_gen3", env_var="MJ_KDL_MODEL")
42 robot_spec.pos = [0.0, 0.0, TABLE_Z]
43 robot_spec.attachments = [attach]
44 spec.robots = [robot_spec]
45 env = mjk.Env.build(spec)
46 tool = mjk.ToolFrameSpec()
47 tool.tool_body = "g_base"
48 tool.tcp_site = "g_pinch"
49 robot = env.create_robot("base_link", "bracelet_link", tool=tool)
50 return env, robot
51
52
53def jnt(values: list[float]) -> kdl.JntArray:
54 out = kdl.JntArray(len(values))
55 for i, v in enumerate(values):
56 out[i] = v
57 return out
58
59
60def clamp_abs(value: float, limit: float) -> float:
61 return max(-limit, min(limit, value))
62
63
64def alpha_no_linear_z() -> kdl.Jacobian:
65 alpha = kdl.Jacobian(5)
66 alpha.setColumn(0, kdl.Twist(kdl.Vector(1, 0, 0), kdl.Vector.Zero()))
67 alpha.setColumn(1, kdl.Twist(kdl.Vector(0, 1, 0), kdl.Vector.Zero()))
68 alpha.setColumn(2, kdl.Twist(kdl.Vector.Zero(), kdl.Vector(1, 0, 0)))
69 alpha.setColumn(3, kdl.Twist(kdl.Vector.Zero(), kdl.Vector(0, 1, 0)))
70 alpha.setColumn(4, kdl.Twist(kdl.Vector.Zero(), kdl.Vector(0, 0, 1)))
71 return alpha
72
73
74def achd_step(robot, chain, fk, achd, rnea, target, err_prev, first_pid):
75 robot.update()
76 q = jnt(robot.jnt_pos_msr)
77 qd = jnt(robot.jnt_vel_msr)
78 current = kdl.Frame()
79 fk.JntToCart(q, current)
80 err = kdl.diff(current, target)
81 dt = 0.002
82 e = [err.vel.x(), err.vel.y(), err.rot.x(), err.rot.y(), err.rot.z()]
83 if first_pid[0]:
84 err_prev[:] = e
85 first_pid[0] = False
86 de = [(e[i] - err_prev[i]) / dt for i in range(5)]
87 err_prev[:] = e
88 beta = kdl.JntArray(5)
89 beta[0] = clamp_abs(KPLIN * e[0] + KDLIN * de[0], BETA_MAX)
90 beta[1] = clamp_abs(KPLIN * e[1] + KDLIN * de[1], BETA_MAX)
91 beta[2] = clamp_abs(KPROT * e[2] + KDROT * de[2], BETA_MAX)
92 beta[3] = clamp_abs(KPROT * e[3] + KDROT * de[3], BETA_MAX)
93 beta[4] = clamp_abs(KPROT * e[4] + KDROT * de[4], BETA_MAX)
94
95 n = robot.n_joints
96 qdd = kdl.JntArray(n)
97 ff = kdl.JntArray(n)
98 constraint_tau = kdl.JntArray(n)
99 f_ext = [kdl.Wrench.Zero() for _ in range(chain.getNrOfSegments())]
100 if achd.CartToJnt(q, qd, qdd, alpha_no_linear_z(), beta, f_ext, ff, constraint_tau) < 0:
101 raise RuntimeError("PyKDL ACHD failed")
102
103 tau = kdl.JntArray(n)
104 rnea_wrenches = [kdl.Wrench.Zero() for _ in range(chain.getNrOfSegments())]
105 if rnea.CartToJnt(q, qd, qdd, rnea_wrenches, tau) < 0:
106 raise RuntimeError("PyKDL RNEA failed")
107 robot.jnt_trq_cmd = [clamp_abs(tau[i], TAU_MAX) for i in range(n)]
108
109
110def main() -> int:
111 parser = argparse.ArgumentParser()
112 parser.add_argument("--gui", action="store_true")
113 args = parser.parse_args()
114
115 env, robot = build_env()
116 try:
117 chain = robot.kdl_chain()
118 fk = kdl.ChainFkSolverPos_recursive(chain)
119 achd = kdl.ChainHdSolver_Vereshchagin_Fixed_Joint(
120 chain, kdl.Twist(kdl.Vector(0.0, 0.0, 9.81), kdl.Vector.Zero()), 5
121 )
122 rnea = kdl.ChainIdSolver_RNE(chain, kdl.Vector(0.0, 0.0, -9.81))
123 robot.ctrl_mode = mjk.CtrlMode.TORQUE
124
125 err_prev = [0.0] * 5
126 first_pid = [True]
127
128 def on_reset(ctx):
129 robot.set_joint_pos(TABLE_POSE, call_forward=False)
130 first_pid[0] = True # re-prime PID after a reset
131
132 env.on_reset = on_reset
133 env.reset()
134
135 robot.update()
136 start = kdl.Frame()
137 fk.JntToCart(jnt(TABLE_POSE), start)
138 target = kdl.Frame(start.M, start.p + kdl.Vector(MOVE_X, 0.0, 0.0))
139
140 def step():
141 achd_step(robot, chain, fk, achd, rnea, target, err_prev, first_pid)
142 if env.has_actuator("g_fingers_actuator"):
143 env.set_actuator_ctrl("g_fingers_actuator", 255.0)
144
145 if args.gui:
146 viewer = mjk.SimulateViewer.open(robot, "ex_achd_table_slide.py")
147 prev = env.time()
148 try:
149 while viewer.is_running():
150 if env.time() < prev - 1e-6:
151 env.reset()
152 prev = env.time()
153 step()
154 if not viewer.step():
155 break
156 finally:
157 viewer.close()
158 else:
159 end = env.time() + 2.0
160 while env.time() < end:
161 step()
162 if not robot.step():
163 break
164 print(f"tcp target x shift: {MOVE_X:.3f} m")
165 final_frame = robot.fk_frame()
166 final_pos = [final_frame.p.x(), final_frame.p.y(), final_frame.p.z()]
167 print(f"final bracelet frame: {[round(x, 3) for x in final_pos]}")
168 finally:
169 env.close()
170 return 0
171
172
173if __name__ == "__main__":
174 raise SystemExit(main())
float clamp_abs(float value, float limit)
achd_step(robot, chain, fk, achd, rnea, target, err_prev, first_pid)
tuple[mjk.Env, mjk.Robot] build_env()
kdl.Jacobian alpha_no_linear_z()
kdl.JntArray jnt(list[float] values)