static-contact-dynamics.py
Go to the documentation of this file.
1 import numpy as np
2 import pinocchio as pin
3 
4 from os.path import dirname, join, abspath
5 np.set_printoptions(linewidth=np.inf)
6 
7 # ----- PROBLEM STATEMENT ------
8 #
9 # We want to find the contact forces and torques required to stand still at a configuration 'q0'.
10 # We assume 3D contacts at each of the feet
11 #
12 # The dynamic equation would look like:
13 #
14 # M*q_ddot + g(q) + C(q, q_dot) = tau + J^T*lambda --> (for the static case) --> g(q) = tau + Jc^T*lambda (1)
15 
16 # ----- SOLVING STRATEGY ------
17 
18 # Split the equation between the base link (_bl) joint and the rest of the joints (_j). That is,
19 #
20 # | g_bl | | 0 | | Jc__feet_bl.T | | l1 |
21 # | g_j | = | tau | + | Jc__feet_j.T | * | l2 | (2)
22 # | l3 |
23 # | l4 |
24 
25 # First, find the contact forces l1, l2, l3, l4 (these are 3 dimensional) by solving for the first 6 rows of (2).
26 # That is,
27 #
28 # g_bl = Jc__feet_bl.T * | l1 |
29 # | l2 | (3)
30 # | l3 |
31 # | l4 |
32 #
33 # Thus we find the contact froces by computing the jacobian pseudoinverse,
34 #
35 # | l1 | = pinv(Jc__feet_bl.T) * g_bl (4)
36 # | l2 |
37 # | l3 |
38 # | l4 |
39 #
40 # Now, we can find the necessary torques using the bottom rows in (2). That is,
41 #
42 # | l1 |
43 # tau = g_j - Jc__feet_j.T * | l2 | (5)
44 # | l3 |
45 # | l4 |
46 
47 # ----- SOLUTION ------
48 
49 # 0. DATA
50 pinocchio_model_dir = join(dirname(dirname(str(abspath(__file__)))), "models")
51 
52 model_path = join(pinocchio_model_dir, "example-robot-data/robots")
53 mesh_dir = pinocchio_model_dir
54 urdf_filename = "solo12.urdf"
55 urdf_model_path = join(join(model_path, "solo_description/robots"),
56  urdf_filename)
57 
58 model, collision_model, visual_model = pin.buildModelsFromUrdf(
59  urdf_model_path, mesh_dir, pin.JointModelFreeFlyer())
60 data = model.createData()
61 
62 q0 = np.array([
63  0., 0., 0.235, 0., 0., 0., 1., 0., 0.8, -1.6, 0., -0.8, 1.6, 0., 0.8, -1.6,
64  0., -0.8, 1.6
65 ])
66 v0 = np.zeros(model.nv)
67 a0 = np.zeros(model.nv)
68 
69 # 1. GRAVITY TERM
70 
71 # We compute the gravity terms by using the ID at desired configuration q0, with velocity and acceleration being 0. I.e., ID with a = v = 0
72 g_grav = pin.rnea(model, data, q0, v0, a0)
73 
74 g_bl = g_grav[:6]
75 g_j = g_grav[6:]
76 
77 # 2. FIND CONTACTS
78 
79 # First, we set the frame for our contacts. We assume the contacts are placed at the following 4 frames and they are 3D
80 feet_names = ['FL_FOOT', 'FR_FOOT', 'HL_FOOT', 'HR_FOOT']
81 feet_ids = [model.getFrameId(n) for n in feet_names]
82 bl_id = model.getFrameId('base_link')
83 ncontact = len(feet_names)
84 
85 # Now, we need to find the contact Jacobians appearing in (1).
86 # These are the Jacobians that relate the joint velocity to the velocity of each feet
87 Js__feet_q = [
88  np.copy(pin.computeFrameJacobian(model, data, q0, id, pin.LOCAL))
89  for id in feet_ids
90 ]
91 
92 Js__feet_bl = [np.copy(J[:3, :6]) for J in Js__feet_q]
93 
94 # Notice that we can write the equation above as an horizontal stack of Jacobians transposed and vertical stack of contact forces
95 Jc__feet_bl_T = np.zeros([6, 3 * ncontact])
96 Jc__feet_bl_T[:, :] = np.vstack(Js__feet_bl).T
97 
98 # Now I only need to do the pinv to compute the contact forces
99 
100 ls = np.linalg.pinv(Jc__feet_bl_T) @ g_bl # This is (3)
101 
102 # Contact forces at local coordinates (at each foot coordinate)
103 ls__f = np.split(ls, ncontact)
104 
105 pin.framesForwardKinematics(model, data, q0)
106 
107 # Contact forces at base link frame
108 ls__bl = []
109 for l__f, foot_id in zip(ls__f, feet_ids):
110  l_sp__f = pin.Force(l__f, np.zeros(3))
111  l_sp__bl = data.oMf[bl_id].actInv(data.oMf[foot_id].act(l_sp__f))
112  ls__bl.append(np.copy(l_sp__bl.vector))
113 
114 print("\n--- CONTACT FORCES ---")
115 for l__f, foot_id, name in zip(ls__bl, feet_ids, feet_names):
116  print("Contact force at foot {} expressed at the BL is: {}".format(
117  name, l__f))
118 
119 # Notice that if we add all the contact forces are equal to the g_grav
120 print("Error between contact forces and gravity at base link: {}".format(
121  np.linalg.norm(g_bl - sum(ls__bl))))
122 
123 # 3. FIND TAU
124 # Find Jc__feet_j
125 Js_feet_j = [np.copy(J[:3, 6:]) for J in Js__feet_q]
126 
127 Jc__feet_j_T = np.zeros([12, 3 * ncontact])
128 Jc__feet_j_T[:, :] = np.vstack(Js_feet_j).T
129 
130 # Apply (5)
131 tau = g_j - Jc__feet_j_T @ ls
132 
133 # 4. CROSS CHECKS
134 
135 # INVERSE DYNAMICS
136 # We can compare this torques with the ones one would obtain when computing the ID considering the external forces in ls
137 pin.framesForwardKinematics(model, data, q0)
138 
139 joint_names = ['FL_KFE', 'FR_KFE', 'HL_KFE', 'HR_KFE']
140 joint_ids = [model.getJointId(n) for n in joint_names]
141 
142 fs_ext = [pin.Force(np.zeros(6)) for _ in range(len(model.joints))]
143 for idx, joint in enumerate(model.joints):
144  if joint.id in joint_ids:
145  fext__bl = pin.Force(ls__bl[joint_ids.index(joint.id)])
146  fs_ext[idx] = data.oMi[joint.id].actInv(data.oMf[bl_id].act(fext__bl))
147 
148 tau_rnea = pin.rnea(model, data, q0, v0, a0, fs_ext)
149 
150 print("\n--- ID: JOINT TORQUES ---")
151 print("Tau from RNEA: {}".format(tau_rnea))
152 print("Tau computed manually: {}".format(np.append(np.zeros(6), tau)))
153 print("Tau error: {}".format(
154  np.linalg.norm(np.append(np.zeros(6), tau) - tau_rnea)))
155 
156 # FORWARD DYNAMICS
157 # We can also check the results using FD. FD with the tau we got, q0 and v0, should give 0 acceleration and the contact forces
158 Js_feet3d_q = [np.copy(J[:3, :]) for J in Js__feet_q]
159 acc = pin.forwardDynamics(model, data, q0, v0, np.append(np.zeros(6), tau),
160  np.vstack(Js_feet3d_q), np.zeros(12))
161 
162 print("\n--- FD: ACC. & CONTACT FORCES ---")
163 print("Norm of the FD acceleration: {}".format(np.linalg.norm(acc)))
164 print("Contact forces manually: {}".format(ls))
165 print("Contact forces FD: {}".format(data.lambda_c))
166 print("Contact forces error: {}".format(np.linalg.norm(data.lambda_c - ls)))
static void act(const Eigen::MatrixBase< Mat > &iV, const ForceDense< ForceDerived > &f, Eigen::MatrixBase< MatRet > const &jF)
Action of a motion set on a force object. The input motion set is represented by a 6xN matrix whose e...


pinocchio
Author(s):
autogenerated on Fri Jun 23 2023 02:38:32