-
Notifications
You must be signed in to change notification settings - Fork 0
/
ftsolver.cpp
378 lines (320 loc) · 11.1 KB
/
ftsolver.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
#include "ftsolver.h"
using namespace Eigen;
forcetorquesolver::forcetorquesolver(const periodic* per){
n = per->get_number_of_dynparts();
nf = per->get_nfeet();
parentis = per->get_parentis();
footis = per->get_footis();
masses = per->get_masses();
mask_l0 = 0;
jzaxis = NULL;
per->hinge_joint_part_ids(jpart_ids);
}
forcetorquesolver::~forcetorquesolver(){
if(!jzaxis){delete [] jzaxis;}
}
// Solves ft system for given dynrec_, outputs joint-ft vector x.
void forcetorquesolver::solve_forcetorques(dynrecord* dynrec_){
VectorXd x, y;
solve_forcetorques(dynrec_,x,y);
cout << "x:" <<endl;
cout << x.head(15) << endl;
}
// Checks perurbation theory result y1 for contact forces,
// by directly solving for minimum cost at some large torso
// penalty factor f. cfs y produced in this way should be
// close to y1 for large f. (For very large f, due to loss
// of precition, y diverges from y1).
void check_contact_forces(const VectorXd& x0, const MatrixXd& N, const VectorXd& c, double f, const VectorXd& y1, list<pair<int,int> >& mask){
VectorXd d = c;
d = d.array().square();
list<pair<int,int> >::iterator it = mask.begin();
for(;it!=mask.end();it++){
int i0 = (*it).first, imax = (*it).second;
for(int i=i0;i<imax;i++){d(i) *= f;}
}
//cout<<d.transpose()<<endl;exit(1);
DiagonalMatrix<double, Dynamic> D (d);
MatrixXd m = N.transpose()*D*N;
//cout<<"det = "<<m.determinant()<<endl;
MatrixXd m_inv = m.inverse();
VectorXd y = -m_inv*N.transpose()*D*x0;
//cout << y.transpose() << endl;
//cout << y1.transpose() << endl;
double del_norm = (y-y1).norm();
//cout << " dely norm = " << del_norm << endl;
if(del_norm>1e-3){cout<<"WARNING: high error in check_contact_forces: "<<del_norm<<endl;}
//return;
VectorXd x = x0 + N*y;
VectorXd x1 = x0 + N*y1;
cout<<x.head(3).transpose()<<endl;
cout<<x1.head(3).transpose()<<endl;
//cout << (x-x1).transpose() << endl;
/*cout << "y1: " << y1.transpose() << endl;
cout << "y: " << y.transpose() << endl;*/
cout << "cost1 = " << x1.transpose()*D*x1 << endl;
cout << "cost = " << x.transpose()*D*x << endl; //exit(1);
}
// Solves for joint forces and torques and for contact forces (cfs),
// given dynamics record dynrec_.
void forcetorquesolver::solve_forcetorques(dynrecord* dynrec_, VectorXd& x, VectorXd& z){
set_dynrec(dynrec_);
VectorXd f; // com-ft vector
SpMat B; // ft matrix
MatrixXd N; // null space
solve_forcetorques_particular(x,B,f); // x is solved for zero cfs
solve_forcetorques_null_space(B,N); // null space of B
N.conservativeResize(x.size(),N.cols());
VectorXd y; // null space coordinates
solve_contact_forces(x,y,N); // solves for optimal torques and cfs
MatrixXd N_cont;
extract_N_contact(N,N_cont);
z = -N_cont*y; // contact forces (ground reaction force)
VectorXd delx = N*y; // change of x due to cfs
x += delx;
fts = x;
return;
cout<<fts.head(3).transpose()<<"; ";
cout<<fts.segment(n*3,3).transpose()<<endl;
}
// Constructs ft matrix B (for zero cfs) and com-ft vector f,
// solves for joint-ft vector x: B * x = f. Such a solution always
// exists, because we turned system into a fully actuated one.
void forcetorquesolver::solve_forcetorques_particular(VectorXd& x, SpMat& B, VectorXd& f){
set_B_and_f_for_zero_cfs(B,f);
//cout<<"f:"<<endl<<f.head(10)<<endl;
SpSolver solver;
solver.compute(B);
x = solver.solve(f);
}
// Gets null space N (of rank size) of matrix B.
void get_null_space(SpMat& B, int size, MatrixXd& N){
int m = B.cols();
SpSolver solver;
solver.compute(B.transpose());
if(solver.rank() < m-size){cout << "WARNING: rank shrank" << endl;}
//cout<<"rank = "<<solver.rank()<<endl;
N.resize(m,size);
VectorXd c (m);
c.setZero();
for(int i=0;i<size;i++){
if(i){c(m-i) = 0;}
c(m-1-i) = 1;
N.col(i) = solver.matrixQ()*c;
}
}
// Extends B to include nonzero cfs (from feet in contact),
// solves null cpace of N.
void forcetorquesolver::solve_forcetorques_null_space(SpMat& B, MatrixXd& N){
int delm = 3*dynrec->get_ncontacts();
int m = B.cols() + delm;
B.conservativeResize(m,m); // sparse solver only worked for square matrices
dynrec->set_forcetorque_system_contacts(B,footis);
get_null_space(B,delm,N);
return;
SpMat N_sp = N.sparseView(1e-6,1e-6);
//cout<<N_sp<<endl;
}
// Constructs submatrix subM out of rows of matrix M
// indicated by ones in mask.
void submatrix_by_row_mask(MatrixXd& subM, MatrixXd& M, list<pair<int,int> >& mask, int mask_l){
int w = M.cols();
subM.resize(mask_l,w);
list<pair<int,int> >::iterator it = mask.begin();
int l = 0;
for(;it!=mask.end();it++){
int i = (*it).first;
int del = (*it).second - i;
//cout<<"i="<<i<<" del="<<del<<endl;
subM.block(l,0,del,w) = M.block(i,0,del,w);
l += del;
}
}
// Constructs subvector subv out of components of vector v
// indicated by ones in mask.
void subvector_by_mask(VectorXd& subv, VectorXd& v, list<pair<int,int> >& mask, int mask_l){
subv.resize(mask_l);
list<pair<int,int> >::iterator it = mask.begin();
int l = 0;
for(;it!=mask.end();it++){
int i = (*it).first;
int del = (*it).second - i;
subv.segment(l,del) = v.segment(i,del);
l += del;
}
}
// Soves for null spates coordinates y, given
// particular solution x and null space (of B) N.
// Note, y is not contact forces, but can be
// easily converted to them. We use perturbation
// theory approach. It ensures, that the constraint
// of no-torso-actuations can be satisied exactly,
// if that at all possible.
void forcetorquesolver::solve_contact_forces(VectorXd& x, VectorXd& y, const MatrixXd& N){
VectorXd c;
set_action_penalties(c);
DiagonalMatrix<double, Dynamic> C (c);
MatrixXd cN = C*N;
VectorXd cx = C*x;
// N and x are split into 0th and 1st order contributions
MatrixXd N0, N1;
submatrix_by_row_mask(N0,cN,penal_mask0,mask_l0);
submatrix_by_row_mask(N1,cN,penal_mask1,mask_l1);
VectorXd x0, x1;
subvector_by_mask(x0,cx,penal_mask0,mask_l0);
subvector_by_mask(x1,cx,penal_mask1,mask_l1);
MatrixXd N0t = N0.transpose(), N1t = N1.transpose();
VectorXd ntx0 = N0t*x0, ntx1 = N1t*x1;
MatrixXd ntn0 = N0t*N0, ntn1 = N1t*N1;
double rel_error;
int k = ntn0.cols();
int rank0 = k;
do {
// zero order solution
FullPivLU<MatrixXd> lu_decomp(ntn0);
//lu_decomp.setThreshold(1e-13);
while (lu_decomp.rank() > rank0) {
lu_decomp.setThreshold(2*lu_decomp.threshold());
}
VectorXd y0 = lu_decomp.solve(-ntx0);
MatrixXd Ny = lu_decomp.kernel(); // null space of ntn0
MatrixXd Ry = lu_decomp.image(ntn0); // range of ntn0 (as well of ntn0.transpose())
if(rank0 == lu_decomp.rank()){cout << "WARNING: decomposition threshold increased" << endl;}
rank0 = lu_decomp.rank();
VectorXd b = -(ntx1 + ntn1*y0);
MatrixXd m (k,k);
m << ntn1*Ny, ntn0*Ry;
// first order solution
VectorXd z = m.colPivHouseholderQr().solve(b);
rel_error = (m*z-b).norm()/b.norm();
rank0--;
y = y0 + Ny*z.head(Ny.cols()); // first order lifts degeneracy of zero order solution
} while (rel_error > 1e-6);
return;
check_contact_forces(x,N,c,1e-8,y,penal_mask1);
}
// Cost matrix is diagonal with c squared as its diagonal
void forcetorquesolver::set_action_penalties(VectorXd& c){
c = VectorXd::Constant(6*n,1);
c.segment(3,3*(n-1)).setZero();
//for(int i=3;i<c.size();i++){c(i) *= (1.1+0*sin(i));}
for(int i=3;i<3*n;i++){c(3*n+i) = jzaxis[i];} // motor torques penalties
if(mask_l0 == 0){cout<<"ERROR: mask0 not set"<<endl;exit(1);}
}
// Computes number of 1s in the mask.
// Mask is encoded by the list of pairs (i0,i1),
// where i0 indicates a location of 1 preceeded by 0,
// and i1 smallest (larger than i0) location of 0 preceeded by 1.
// Example: mask 11100010011 is encoded as (0,3)(6,7)(9,11).
int mask_length(list<pair<int,int> >& mask){
list<pair<int,int> >::iterator it = mask.begin();
int l = 0;
for(;it!=mask.end();it++){l += ((*it).second-(*it).first);}
return l;
}
// Sets mask0 encoding 0th order penalty,
// that (optionally) penalizes torso forces and torques.
void forcetorquesolver::switch_torso_penalty(bool force_flag, bool torque_flag){
penal_mask0.clear();
vector<int> inds;
if(force_flag){inds.push_back(0);}
if(torque_flag){inds.push_back(3*n);}
for(unsigned int i=0;i<inds.size();i++){
int i0 = inds[i];
penal_mask0.push_back(make_pair<int,int>(i0,i0+3));
}
mask_l0 = mask_length(penal_mask0);
set_penal_mask1();
}
// Extracts rows of N contributing to contact forces.
void forcetorquesolver::extract_N_contact(const MatrixXd& N, MatrixXd& N_cont){
N_cont.resize(nf*3,N.cols());
int k = 0;
for(int i=0;i<nf;i++){
for(int j=0;j<3;j++){
N_cont.row(k++) = N.row(3*footis[i]+j);
}
}
}
// Sets mask1 that is opposite of mask0.
void forcetorquesolver::set_penal_mask1(){
penal_mask1.clear();
list<pair<int,int> >::iterator it = penal_mask0.begin();
int i0 = 0;
for(;it!=penal_mask0.end();it++){
int i1 = (*it).first;
if(i1 > i0){
penal_mask1.push_back(make_pair<int,int>(i0,i1));
}
i0 = (*it).second;
}
int i1 = 6*n;
if(i1 > i0){
penal_mask1.push_back(make_pair<int,int>(i0,i1));
}
mask_l1 = mask_length(penal_mask1);
}
void forcetorquesolver::set_jzaxis(){
extvec* jzaxis_extvecs = dynrec->get_jzaxis();
if(jzaxis == NULL){jzaxis = new double [3*n];}
double *p = jzaxis;
for(int i=0;i<n;i++){
double *p1 = jzaxis_extvecs[i].get_data();
for(int j=0;j<3;j++){*p++ = *p1++;}
}
}
// Sets ft matrix B and com-ft vector f for zero contact forces.
void forcetorquesolver::set_B_and_f_for_zero_cfs(SpMat& B, VectorXd& f){
int m = 6*n;
B.resize(m,m);
f.resize(m);
dynrec->set_forcetorque_system(B,f,parentis,masses);
}
// Sets dynamics record and joint axes
void forcetorquesolver::set_dynrec(dynrecord* dynrec_){
dynrec = dynrec_;
set_jzaxis();
}
// Solves for forces given motor torques.
// No torso actuations.
void forcetorquesolver::solve_forces(dynrecord* dynrec_, VectorXd& z, VectorXd& y){
set_dynrec(dynrec_);
// construction of B and f
VectorXd f; // com-ft vector
SpMat B; // ft matrix
set_B_and_f_for_zero_cfs(B,f);
// extention of B to include cfs from all feet
int m = B.cols();
int m1 = m + 3*nf;
B.conservativeResize(m,m1);
dynrec->set_forcetorque_system_contacts(B,footis,false);
// extention of B and f to include torque constraints
add_torque_constraints_to_B(B,f,z);
// solution with zero torso force and torque
for(int i=0;i<2;i++){B.block(0,3*n*i,B.rows(),3) *= 0;}
B.makeCompressed();
SpSolver solver;
solver.compute(B);
VectorXd x = solver.solve(f);
// contact forces
y = x.tail(3*nf);
}
// Expands B and f to constraint motor torques
// to given values (arg z).
void forcetorquesolver::add_torque_constraints_to_B(SpMat& B, VectorXd& f, const VectorXd& z){
int nj = z.size();
int m = B.rows();
B.conservativeResize(m+nj,B.cols());
f.conservativeResize(f.size()+nj);
list<int>::iterator it = jpart_ids.begin();
int i = 0;
for(;it!=jpart_ids.end();it++){
int k = 3*(*it);
int k1 = 3*n+k;
for(int j=0;j<3;j++){
B.insert(m+i,k1+j) = jzaxis[k+j];
}
f(m+i) = z(i);
i++;
}
}