Diff of /arm_model/projection.py [000000] .. [17a672]

Switch to unified view

a b/arm_model/projection.py
1
import sympy as sp
2
import numpy as np
3
from logger import Logger
4
from util import to_np_mat
5
from analysis import construct_muscle_space_inequality
6
from scipy.optimize import minimize
7
8
# ------------------------------------------------------------------------
9
# Task Space
10
# ------------------------------------------------------------------------
11
12
13
class TaskSpace:
14
    """Task space EoM of a given task.
15
16
    Assuming the following joint space convention:
17
18
    M qddot + f = tau
19
20
    task projection is given by:
21
22
    ft = Lt (xddot - JDotQDot) + JtBarT f
23
24
    Lt = (R M^{-1} R^T)^{-1}
25
26
    JtBarT = Lt R M^{-1}
27
28
    NtT = (I - JtT JtBarT)
29
30
    and the resultant task space forces are given by:
31
32
    tau = JtT ft + NtT f
33
34
    """
35
36
    def __init__(self, model, xt):
37
        """ Constructor.
38
39
        Parameters
40
        ----------
41
42
        model: model
43
        xt: [d x 1] task positions
44
45
        """
46
        self.logger = Logger('TaskSpace')
47
        self.model = model
48
        self.xt = xt
49
        self.__construct_task_space_variables()
50
51
    def __construct_task_space_variables(self):
52
        """Calculate task Jacobian Jt and JtDot * qDot
53
54
        """
55
        self.Jt = self.xt.jacobian(self.model.Q())
56
        self.JtT = self.Jt.transpose()
57
        JtDot = sp.diff(self.Jt, self.model.t)
58
        self.JtDotQDot = JtDot * sp.Matrix(self.model.U())
59
        # the derivative of a matrix with a vector is a rank 3 tensor (3D
60
        # array), [dM/dq1, dM/dq2, ...]
61
        self.JtTDq = sp.derive_by_array(self.JtT, self.model.Q())
62
63
    def calculate_force(self, xddot, pose):
64
        """Calculate task forces.
65
66
        For a given end effector acceleration compute the joint space
67
        torques:
68
69
        ft = Lt (xddot - JDotQDot) + JtBarT f
70
71
        tau = JtT ft + NtT f
72
73
        Parameters
74
        ----------
75
76
        xddot: [2 x 1] a sympy Matrix containing the desired accelerations in 2D
77
        pose: system constants, coordinates and speeds as dictionary
78
79
        Returns
80
        -------
81
82
        (tau, ft): required torque and task forces to track the desired
83
        acceleration
84
85
        """
86
        M = to_np_mat(self.model.M.subs(pose))
87
        f = to_np_mat(self.model.f.subs(pose))
88
        Jt = to_np_mat(self.Jt.subs(pose))
89
        JtDotQDot = to_np_mat(self.JtDotQDot.subs(pose))
90
        JtT = to_np_mat(self.JtT.subs(pose))
91
92
        MInv = np.linalg.inv(M)
93
        LtInv = Jt * MInv * JtT
94
        Lt = np.linalg.pinv(LtInv)
95
        JtBarT = Lt * Jt * MInv
96
        NtT = np.asmatrix(np.eye(len(JtT))) - JtT * JtBarT
97
98
        ft = Lt * (xddot - JtDotQDot) + JtBarT * f
99
100
        return JtT * ft + 0 * NtT * f, ft
101
102
    def x(self, pose):
103
        """For a given pose (q) evaluate the task position.
104
105
        Parameters
106
        ----------
107
108
        pose: dictionary of model parameters and q's
109
110
        Returns
111
        -------
112
113
        x: sympy Matrix
114
115
        """
116
        return self.xt.subs(pose)
117
118
    def u(self, pose, qdot):
119
        """For a given pose (q, u) evaluate the task velocity.
120
121
        Parameters
122
        ----------
123
124
        pose: dictionary of model parameters, q's and u's
125
        qdot: an array of u(t)
126
127
        Returns
128
        ------
129
130
        u: sympy Matrix
131
132
        """
133
        return self.Jt.subs(pose) * sp.Matrix(qdot)
134
135
136
# ------------------------------------------------------------------------
137
# Muscle Space
138
# ------------------------------------------------------------------------
139
140
141
class MuscleSpace:
142
    """ Muscle space EoM.
143
    """
144
145
    def __init__(self, model, use_optimization=False):
146
        """ Constructor
147
148
        Parameters
149
        ----------
150
151
        model: model
152
153
        """
154
        self.logger = Logger('MuscleSpace')
155
        self.model = model
156
        self.use_optimization = use_optimization
157
        self.Fmax = to_np_mat(self.model.Fmax)
158
        self.x_max = np.max(self.Fmax)
159
160
    def calculate_force(self, lmdd, pose):
161
        """Calculate muscle forces.
162
163
        For a given muscle length acceleration compuyte the muscle space EoM of
164
        motion and required muscle force to track the goal acceleration.
165
166
        fm_par = -Lm (lmdd - RDotQDot) - RBarT f
167
168
        tau = -R^T fm_par
169
170
        Parameters
171
        ----------
172
173
        lmdd: [m x 1] a sympy Matrix containing the desired muscle length
174
        accelerations
175
176
        pose: dictionary
177
178
        Returns
179
        -------
180
181
        (tau, fm_par + fm_perp) required torque to track the desired acceleration
182
183
        """
184
185
        M = to_np_mat(self.model.M.subs(pose))
186
        f = to_np_mat(self.model.f.subs(pose))
187
        R = to_np_mat(self.model.R.subs(pose))
188
        RT = R.transpose()
189
        RDotQDot = to_np_mat(self.model.RDotQDot.subs(pose))
190
191
        MInv = np.linalg.inv(M)
192
        LmInv = R * MInv * RT
193
        Lm = np.linalg.pinv(LmInv)
194
        RBarT = np.linalg.pinv(RT)
195
        NR = np.asmatrix(np.eye(len(RBarT)) - RBarT * RT)
196
197
        fm_par = -Lm * (lmdd - RDotQDot) - RBarT * f
198
199
        # Ensure fm_par > 0 not required for simulation, but for muscle analysis
200
        # otherwise muscle forces will be negative. Since RT * NR = 0 the null
201
        # space term does not affect the resultant torques.
202
        m = fm_par.shape[0]
203
        fm_0 = np.zeros((m, 1))
204
        if self.use_optimization:
205
            Z, B = construct_muscle_space_inequality(NR, fm_par, self.Fmax)
206
207
            def objective(x):
208
                return np.sum(x**2)
209
210
            def inequality_constraint(x):
211
                return np.array(B - Z * (x.reshape(-1, 1))).reshape(-1,)
212
213
            x0 = np.zeros(m)
214
            bounds = tuple([(-self.x_max, self.x_max) for i in range(0, m)])
215
            constraints = ({'type': 'ineq', 'fun': inequality_constraint})
216
            sol = minimize(objective, x0,  method='SLSQP',
217
                           bounds=bounds,
218
                           constraints=constraints)
219
            fm_0 = sol.x.reshape(-1, 1)
220
            if sol.success == False:
221
                raise RuntimeError('Some muscles are too week for this action')
222
223
        fm_perp = NR * fm_0
224
225
        return -RT * fm_par, fm_par + fm_perp