iCub-main
nlp.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2014 iCub Facility - Istituto Italiano di Tecnologia
3  * Author: Ugo Pattacini
4  * email: ugo.pattacini@iit.it
5  * Permission is granted to copy, distribute, and/or modify this program
6  * under the terms of the GNU General Public License, version 2 or any
7  * later version published by the Free Software Foundation.
8  *
9  * A copy of the license can be found at
10  * http://www.robotcub.org/icub/license/gpl.txt
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15  * Public License for more details
16 */
17 
18 #include <cmath>
19 #include <algorithm>
20 #include <string>
21 #include <deque>
22 
23 #include <yarp/sig/all.h>
24 #include <yarp/dev/all.h>
25 #include <yarp/math/Math.h>
26 
27 #include <iCub/ctrl/math.h>
28 
29 #include <IpTNLP.hpp>
30 #include <nlp.h>
31 
32 using namespace std;
33 using namespace yarp::os;
34 using namespace yarp::sig;
35 using namespace yarp::math;
36 using namespace iCub::ctrl;
37 
38 
39 /****************************************************************/
40 Matrix computeH(const Vector &x)
41 {
42  Matrix H=rpy2dcm(x.subVector(3,5));
43  H(0,3)=x[0]; H(1,3)=x[1]; H(2,3)=x[2];
44 
45  return H;
46 }
47 
48 
49 /****************************************************************/
50 Matrix computeH(const Ipopt::Number *x)
51 {
52  Vector _x(6);
53  for (size_t i=0; i<_x.length(); i++)
54  _x[i]=x[i];
55 
56  return computeH(_x);
57 }
58 
59 
60 /****************************************************************/
61 class EyeAlignerNLP : public Ipopt::TNLP
62 {
63 protected:
64  const deque<Vector> &p2d;
65  const deque<Vector> &p3d;
66  const Matrix &Prj;
67 
68  Vector min;
69  Vector max;
70  Vector x0;
71  Vector x;
72 
73 public:
74  /****************************************************************/
75  EyeAlignerNLP(const deque<Vector> &_p2d,
76  const deque<Vector> &_p3d,
77  const Vector &_min, const Vector &_max,
78  const Matrix &_Prj) :
79  p2d(_p2d), p3d(_p3d), Prj(_Prj)
80  {
81  min=_min;
82  max=_max;
83  x0=0.5*(min+max);
84  }
85 
86  /****************************************************************/
87  void set_x0(const Vector &x0)
88  {
89  size_t len=std::min(this->x0.length(),x0.length());
90  for (size_t i=0; i<len; i++)
91  this->x0[i]=x0[i];
92  }
93 
94  /****************************************************************/
95  Vector get_result() const
96  {
97  return x;
98  }
99 
100  /****************************************************************/
101  bool get_nlp_info(Ipopt::Index &n, Ipopt::Index &m, Ipopt::Index &nnz_jac_g,
102  Ipopt::Index &nnz_h_lag, IndexStyleEnum &index_style)
103  {
104  n=6;
105  m=nnz_jac_g=nnz_h_lag=0;
106  index_style=TNLP::C_STYLE;
107 
108  return true;
109  }
110 
111  /****************************************************************/
112  bool get_bounds_info(Ipopt::Index n, Ipopt::Number *x_l, Ipopt::Number *x_u,
113  Ipopt::Index m, Ipopt::Number *g_l, Ipopt::Number *g_u)
114  {
115  for (Ipopt::Index i=0; i<n; i++)
116  {
117  x_l[i]=min[i];
118  x_u[i]=max[i];
119  }
120 
121  return true;
122  }
123 
124  /****************************************************************/
125  bool get_starting_point(Ipopt::Index n, bool init_x, Ipopt::Number *x,
126  bool init_z, Ipopt::Number *z_L, Ipopt::Number *z_U,
127  Ipopt::Index m, bool init_lambda, Ipopt::Number *lambda)
128  {
129  for (Ipopt::Index i=0; i<n; i++)
130  x[i]=x0[i];
131 
132  return true;
133  }
134 
135  /****************************************************************/
136  bool eval_f(Ipopt::Index n, const Ipopt::Number *x, bool new_x,
137  Ipopt::Number &obj_value)
138  {
139  Matrix PrjH=Prj*SE3inv(computeH(x));
140 
141  obj_value=0.0;
142  if (p2d.size()>0)
143  {
144  for (size_t i=0; i<p2d.size(); i++)
145  {
146  Vector p2di=PrjH*p3d[i];
147  p2di=p2di/p2di[2];
148  p2di.pop_back();
149 
150  obj_value+=norm2(p2d[i]-p2di);
151  }
152 
153  obj_value/=p2d.size();
154  }
155 
156  return true;
157  }
158 
159  /****************************************************************/
160  bool eval_grad_f(Ipopt::Index n, const Ipopt::Number* x, bool new_x,
161  Ipopt::Number *grad_f)
162  {
163  double roll=x[3]; double cr=cos(roll); double sr=sin(roll);
164  double pitch=x[4]; double cp=cos(pitch); double sp=sin(pitch);
165  double yaw=x[5]; double cy=cos(yaw); double sy=sin(yaw);
166 
167  Matrix Rz=eye(4,4);
168  Rz(0,0)=cy; Rz(1,1)=cy; Rz(0,1)=-sy; Rz(1,0)=sy;
169  Matrix dRz=zeros(4,4);
170  dRz(0,0)=-sy; dRz(1,1)=-sy; dRz(0,1)=-cy; dRz(1,0)=cy;
171 
172  Matrix Ry=eye(4,4);
173  Ry(0,0)=cp; Ry(2,2)=cp; Ry(0,2)=sp; Ry(2,0)=-sp;
174  Matrix dRy=zeros(4,4);
175  dRy(0,0)=-sp; dRy(2,2)=-sp; dRy(0,2)=cp; dRy(2,0)=-cp;
176 
177  Matrix Rx=eye(4,4);
178  Rx(1,1)=cr; Rx(2,2)=cr; Rx(1,2)=-sr; Rx(2,1)=sr;
179  Matrix dRx=zeros(4,4);
180  dRx(1,1)=-sr; dRx(2,2)=-sr; dRx(1,2)=-cr; dRx(2,1)=cr;
181 
182  Matrix invRz=Rz.transposed();
183  Matrix invRy=Ry.transposed();
184  Matrix invRx=Rx.transposed();
185 
186  Matrix invR=(-1.0)*invRx*invRy*invRz;
187  Vector p(4,-1.0); p[0]=-x[0]; p[1]=-x[1]; p[2]=-x[2];
188 
189  Matrix dHdx0=zeros(4,4); dHdx0.setCol(3,invR.getCol(0));
190  Matrix dHdx1=zeros(4,4); dHdx1.setCol(3,invR.getCol(1));
191  Matrix dHdx2=zeros(4,4); dHdx2.setCol(3,invR.getCol(2));
192  Matrix dHdx3=dRx.transposed()*invRy*invRz; dHdx3.setCol(3,dHdx3*p);
193  Matrix dHdx4=invRx*dRy.transposed()*invRz; dHdx4.setCol(3,dHdx4*p);
194  Matrix dHdx5=invRx*invRy*dRz.transposed(); dHdx5.setCol(3,dHdx5*p);
195 
196  Matrix dPrjHdx0=Prj*dHdx0;
197  Matrix dPrjHdx1=Prj*dHdx1;
198  Matrix dPrjHdx2=Prj*dHdx2;
199  Matrix dPrjHdx3=Prj*dHdx3;
200  Matrix dPrjHdx4=Prj*dHdx4;
201  Matrix dPrjHdx5=Prj*dHdx5;
202 
203  Matrix PrjH=Prj*SE3inv(computeH(x));
204 
205  grad_f[0]=grad_f[1]=grad_f[2]=0.0;
206  grad_f[3]=grad_f[4]=grad_f[5]=0.0;
207  if (p2d.size()>0)
208  {
209  for (size_t i=0; i<p2d.size(); i++)
210  {
211  Vector p2di=PrjH*p3d[i];
212  p2di=p2di/p2di[2];
213  p2di.pop_back();
214 
215  Vector d=p2d[i]-p2di;
216 
217  double u_num=dot(PrjH.getRow(0),p3d[i]);
218  double v_num=dot(PrjH.getRow(1),p3d[i]);
219  double lambda=dot(PrjH.getRow(2),p3d[i]);
220  double lambda2=lambda*lambda;
221  double tmp_dot;
222 
223  Vector dp2d_dx0(2);
224  tmp_dot=dot(dPrjHdx0.getRow(2),p3d[i]);
225  dp2d_dx0[0]=(dot(dPrjHdx0.getRow(0),p3d[i])*lambda-tmp_dot*u_num)/lambda2;
226  dp2d_dx0[1]=(dot(dPrjHdx0.getRow(1),p3d[i])*lambda-tmp_dot*v_num)/lambda2;
227 
228  Vector dp2d_dx1(2);
229  tmp_dot=dot(dPrjHdx1.getRow(2),p3d[i]);
230  dp2d_dx1[0]=(dot(dPrjHdx1.getRow(0),p3d[i])*lambda-tmp_dot*u_num)/lambda2;
231  dp2d_dx1[1]=(dot(dPrjHdx1.getRow(1),p3d[i])*lambda-tmp_dot*v_num)/lambda2;
232 
233  Vector dp2d_dx2(2);
234  tmp_dot=dot(dPrjHdx2.getRow(2),p3d[i]);
235  dp2d_dx2[0]=(dot(dPrjHdx2.getRow(0),p3d[i])*lambda-tmp_dot*u_num)/lambda2;
236  dp2d_dx2[1]=(dot(dPrjHdx2.getRow(1),p3d[i])*lambda-tmp_dot*v_num)/lambda2;
237 
238  Vector dp2d_dx3(2);
239  tmp_dot=dot(dPrjHdx3.getRow(2),p3d[i]);
240  dp2d_dx3[0]=(dot(dPrjHdx3.getRow(0),p3d[i])*lambda-tmp_dot*u_num)/lambda2;
241  dp2d_dx3[1]=(dot(dPrjHdx3.getRow(1),p3d[i])*lambda-tmp_dot*v_num)/lambda2;
242 
243  Vector dp2d_dx4(2);
244  tmp_dot=dot(dPrjHdx4.getRow(2),p3d[i]);
245  dp2d_dx4[0]=(dot(dPrjHdx4.getRow(0),p3d[i])*lambda-tmp_dot*u_num)/lambda2;
246  dp2d_dx4[1]=(dot(dPrjHdx4.getRow(1),p3d[i])*lambda-tmp_dot*v_num)/lambda2;
247 
248  Vector dp2d_dx5(2);
249  tmp_dot=dot(dPrjHdx5.getRow(2),p3d[i]);
250  dp2d_dx5[0]=(dot(dPrjHdx5.getRow(0),p3d[i])*lambda-tmp_dot*u_num)/lambda2;
251  dp2d_dx5[1]=(dot(dPrjHdx5.getRow(1),p3d[i])*lambda-tmp_dot*v_num)/lambda2;
252 
253  grad_f[0]-=2.0*dot(d,dp2d_dx0);
254  grad_f[1]-=2.0*dot(d,dp2d_dx1);
255  grad_f[2]-=2.0*dot(d,dp2d_dx2);
256  grad_f[3]-=2.0*dot(d,dp2d_dx3);
257  grad_f[4]-=2.0*dot(d,dp2d_dx4);
258  grad_f[5]-=2.0*dot(d,dp2d_dx5);
259  }
260 
261  for (Ipopt::Index i=0; i<n; i++)
262  grad_f[i]/=p2d.size();
263  }
264 
265  return true;
266  }
267 
268  /****************************************************************/
269  bool eval_g(Ipopt::Index n, const Ipopt::Number *x, bool new_x,
270  Ipopt::Index m, Ipopt::Number *g)
271  {
272  return true;
273  }
274 
275  /****************************************************************/
276  bool eval_jac_g(Ipopt::Index n, const Ipopt::Number *x, bool new_x,
277  Ipopt::Index m, Ipopt::Index nele_jac, Ipopt::Index *iRow,
278  Ipopt::Index *jCol, Ipopt::Number *values)
279  {
280  return true;
281  }
282 
283 
284  /****************************************************************/
285  bool eval_h(Ipopt::Index n, const Ipopt::Number *x, bool new_x,
286  Ipopt::Number obj_factor, Ipopt::Index m, const Ipopt::Number *lambda,
287  bool new_lambda, Ipopt::Index nele_hess, Ipopt::Index *iRow,
288  Ipopt::Index *jCol, Ipopt::Number *values)
289  {
290  return true;
291  }
292 
293 
294  /****************************************************************/
295  void finalize_solution(Ipopt::SolverReturn status, Ipopt::Index n,
296  const Ipopt::Number *x, const Ipopt::Number *z_L,
297  const Ipopt::Number *z_U, Ipopt::Index m,
298  const Ipopt::Number *g, const Ipopt::Number *lambda,
299  Ipopt::Number obj_value, const Ipopt::IpoptData *ip_data,
300  Ipopt::IpoptCalculatedQuantities *ip_cq)
301  {
302  this->x.resize(n);
303  for (Ipopt::Index i=0; i<n; i++)
304  this->x[i]=x[i];
305  }
306 };
307 
308 
309 
310 /****************************************************************/
311 EyeAligner::EyeAligner() : Prj(eye(3,4))
312 {
313  min.resize(6); max.resize(6);
314  min[0]=-1.0; max[0]=1.0;
315  min[1]=-1.0; max[1]=1.0;
316  min[2]=-1.0; max[2]=1.0;
317  min[3]=-M_PI; max[3]=M_PI;
318  min[4]=-M_PI; max[4]=M_PI;
319  min[5]=-M_PI; max[5]=M_PI;
320 
321  x0=0.5*(min+max);
322  Prj.resize(3,4); Prj.zero();
323 }
324 
325 
326 /****************************************************************/
327 double EyeAligner::evalError(const Matrix &H)
328 {
329  Matrix PrjH=Prj*SE3inv(H);
330 
331  double error=0.0;
332  if (p2d.size()>0)
333  {
334  for (size_t i=0; i<p2d.size(); i++)
335  {
336  Vector p2di=PrjH*p3d[i];
337  p2di=p2di/p2di[2];
338  p2di.pop_back();
339 
340  error+=norm(p2d[i]-p2di);
341  }
342 
343  error/=p2d.size();
344  }
345 
346  return error;
347 }
348 
349 
350 /****************************************************************/
351 bool EyeAligner::setProjection(const Matrix &Prj)
352 {
353  if ((Prj.rows()>=3) && (Prj.cols()>=4))
354  {
355  this->Prj=Prj.submatrix(0,2,0,3);
356  return true;
357  }
358  else
359  return false;
360 }
361 
362 
363 /****************************************************************/
365 {
366  return Prj;
367 }
368 
369 
370 /****************************************************************/
371 void EyeAligner::setBounds(const Vector &min, const Vector &max)
372 {
373  size_t len_min=std::min(this->min.length(),min.length());
374  size_t len_max=std::min(this->max.length(),max.length());
375 
376  for (size_t i=0; i<len_min; i++)
377  this->min[i]=min[i];
378 
379  for (size_t i=0; i<len_max; i++)
380  this->max[i]=max[i];
381 }
382 
383 
384 /****************************************************************/
385 bool EyeAligner::addPoints(const Vector &p2di, const Vector &p3di)
386 {
387  if ((p2di.length()>=2) && (p3di.length()>=4))
388  {
389  p2d.push_back(p2di.subVector(0,1));
390  p3d.push_back(p3di.subVector(0,3));
391 
392  return true;
393  }
394  else
395  return false;
396 }
397 
398 
399 /****************************************************************/
401 {
402  p2d.clear();
403  p3d.clear();
404 }
405 
406 
407 /****************************************************************/
409 {
410  return p2d.size();
411 }
412 
413 
414 /****************************************************************/
415 bool EyeAligner::setInitialGuess(const Matrix &H)
416 {
417  if ((H.rows()>=4) && (H.cols()>=4))
418  {
419  Vector rpy=dcm2rpy(H);
420  x0[0]=H(0,3); x0[1]=H(1,3); x0[2]=H(2,3);
421  x0[3]=rpy[0]; x0[4]=rpy[1]; x0[5]=rpy[2];
422 
423  for (size_t i=0; i<min.length(); i++)
424  x0[i]=std::min(std::max(x0[i],min[i]),max[i]);
425 
426  return true;
427  }
428  else
429  return false;
430 }
431 
432 
433 /****************************************************************/
434 bool EyeAligner::calibrate(Matrix &H, double &error, const int max_iter,
435  const int print_level, const string &derivative_test)
436 {
437  if (p2d.size()>0)
438  {
439  Ipopt::SmartPtr<Ipopt::IpoptApplication> app=new Ipopt::IpoptApplication;
440  app->Options()->SetNumericValue("tol",1e-8);
441  app->Options()->SetIntegerValue("acceptable_iter",0);
442  app->Options()->SetStringValue("mu_strategy","adaptive");
443  app->Options()->SetIntegerValue("max_iter",max_iter);
444  app->Options()->SetStringValue("nlp_scaling_method","gradient-based");
445  app->Options()->SetStringValue("hessian_approximation","limited-memory");
446  app->Options()->SetIntegerValue("print_level",print_level);
447  app->Options()->SetStringValue("derivative_test",derivative_test.c_str());
448  app->Options()->SetStringValue("derivative_test_print_all","yes");
449  app->Initialize();
450 
451  Ipopt::SmartPtr<EyeAlignerNLP> nlp=new EyeAlignerNLP(p2d,p3d,min,max,Prj);
452 
453  nlp->set_x0(x0);
454  Ipopt::ApplicationReturnStatus status=app->OptimizeTNLP(GetRawPtr(nlp));
455 
456  Vector x=nlp->get_result();
457  H=computeH(x);
458  error=evalError(H);
459 
460  return (status==Ipopt::Solve_Succeeded);
461  }
462  else
463  return false;
464 }
465 
466 
#define M_PI
Definition: XSensMTx.cpp:24
bool eval_h(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Number obj_factor, Ipopt::Index m, const Ipopt::Number *lambda, bool new_lambda, Ipopt::Index nele_hess, Ipopt::Index *iRow, Ipopt::Index *jCol, Ipopt::Number *values)
Definition: nlp.cpp:285
const deque< Vector > & p3d
Definition: nlp.cpp:65
const Matrix & Prj
Definition: nlp.cpp:66
bool get_starting_point(Ipopt::Index n, bool init_x, Ipopt::Number *x, bool init_z, Ipopt::Number *z_L, Ipopt::Number *z_U, Ipopt::Index m, bool init_lambda, Ipopt::Number *lambda)
Definition: nlp.cpp:125
Vector x0
Definition: nlp.cpp:70
Vector min
Definition: nlp.cpp:68
bool eval_g(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Index m, Ipopt::Number *g)
Definition: nlp.cpp:269
const deque< Vector > & p2d
Definition: nlp.cpp:64
Vector x
Definition: nlp.cpp:71
bool eval_f(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Number &obj_value)
Definition: nlp.cpp:136
bool eval_grad_f(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Number *grad_f)
Definition: nlp.cpp:160
bool get_bounds_info(Ipopt::Index n, Ipopt::Number *x_l, Ipopt::Number *x_u, Ipopt::Index m, Ipopt::Number *g_l, Ipopt::Number *g_u)
Definition: nlp.cpp:112
void finalize_solution(Ipopt::SolverReturn status, Ipopt::Index n, const Ipopt::Number *x, const Ipopt::Number *z_L, const Ipopt::Number *z_U, Ipopt::Index m, const Ipopt::Number *g, const Ipopt::Number *lambda, Ipopt::Number obj_value, const Ipopt::IpoptData *ip_data, Ipopt::IpoptCalculatedQuantities *ip_cq)
Definition: nlp.cpp:295
void set_x0(const Vector &x0)
Definition: nlp.cpp:87
bool get_nlp_info(Ipopt::Index &n, Ipopt::Index &m, Ipopt::Index &nnz_jac_g, Ipopt::Index &nnz_h_lag, IndexStyleEnum &index_style)
Definition: nlp.cpp:101
bool eval_jac_g(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Index m, Ipopt::Index nele_jac, Ipopt::Index *iRow, Ipopt::Index *jCol, Ipopt::Number *values)
Definition: nlp.cpp:276
Vector get_result() const
Definition: nlp.cpp:95
Vector max
Definition: nlp.cpp:69
EyeAlignerNLP(const deque< Vector > &_p2d, const deque< Vector > &_p3d, const Vector &_min, const Vector &_max, const Matrix &_Prj)
Definition: nlp.cpp:75
bool setInitialGuess(const yarp::sig::Matrix &H)
Definition: nlp.cpp:415
yarp::sig::Matrix Prj
Definition: nlp.h:42
yarp::sig::Vector max
Definition: nlp.h:40
std::deque< yarp::sig::Vector > p3d
Definition: nlp.h:45
size_t getNumPoints() const
Definition: nlp.cpp:408
yarp::sig::Vector x0
Definition: nlp.h:41
void setBounds(const yarp::sig::Vector &min, const yarp::sig::Vector &max)
Definition: nlp.cpp:371
double evalError(const yarp::sig::Matrix &H)
Definition: nlp.cpp:327
bool calibrate(yarp::sig::Matrix &H, double &error, const int max_iter=ALIGN_IPOPT_MAX_ITER, const int print_level=0, const std::string &derivative_test="none")
Definition: nlp.cpp:434
EyeAligner()
Definition: nlp.cpp:311
bool addPoints(const yarp::sig::Vector &p2di, const yarp::sig::Vector &p3di)
Definition: nlp.cpp:385
yarp::sig::Vector min
Definition: nlp.h:39
bool setProjection(const yarp::sig::Matrix &Prj)
Definition: nlp.cpp:351
std::deque< yarp::sig::Vector > p2d
Definition: nlp.h:44
void clearPoints()
Definition: nlp.cpp:400
yarp::sig::Matrix getProjection() const
Definition: nlp.cpp:364
zeros(2, 2) eye(2
int n
double dot(const yarp::sig::Matrix &A, int colA, const yarp::sig::Matrix &B, int colB)
Returns the dot product between two vectors given in the form: matrix(:,col).
double norm2(const yarp::sig::Matrix &M, int col)
Returns the squared norm of the vector given in the form: matrix(:,col).
Definition: math.h:91
double norm(const yarp::sig::Matrix &M, int col)
Returns the norm of the vector given in the form: matrix(:,col).
const FSC max
Definition: strain.h:48
const FSC min
Definition: strain.h:49
Matrix computeH(const Vector &x)
Definition: nlp.cpp:40