iCub-main
main.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2013 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 
88 #include <cmath>
89 #include <string>
90 #include <sstream>
91 #include <fstream>
92 #include <vector>
93 #include <map>
94 
95 #include <yarp/os/all.h>
96 #include <yarp/dev/all.h>
97 #include <yarp/sig/Vector.h>
98 #include <iCub/ctrl/tuning.h>
99 
100 #include "fingersTuner_IDL.h"
101 
102 using namespace std;
103 using namespace yarp::os;
104 using namespace yarp::dev;
105 using namespace yarp::sig;
106 using namespace iCub::ctrl;
107 
108 
109 /************************************************************************/
110 class Tuner
111 {
112 protected:
113  typedef enum { download, upload, synced } pidStatus;
114  struct PidData
115  {
116  double Kp;
117  double Ki;
118  double Kd;
119  double scale;
120  double st_up;
121  double st_down;
122  double encs_ratio;
124  vector<int> idling_joints;
125  PidData() : Kp(0.0), Ki(0.0), Kd(0.0), scale(0.0),
126  st_up(0.0), st_down(0.0), encs_ratio(1.0),
127  status(download) { }
128  void toRobot(Pid &pid)
129  {
130  pid.kp=Kp;
131  pid.ki=Ki;
132  pid.kd=Kd;
133  pid.scale=scale;
134  pid.stiction_up_val=st_up;
135  pid.stiction_down_val=st_down;
136  }
137  void fromRobot(const Pid &pid)
138  {
139  Kp=pid.kp;
140  Ki=pid.ki;
141  Kd=pid.kd;
142  scale=pid.scale;
143  st_up=pid.stiction_up_val;
144  st_down=pid.stiction_down_val;
145  }
146  };
147 
148  static unsigned int instances;
150  string name,robot,part,device;
151  PolyDriver *driver;
152 
153  Bottle rJoints;
154  map<int,PidData> pids;
155  map<string,Bottle> alias;
156 
157  /************************************************************************/
158  PolyDriver *waitPart(const Property &partOpt, const double ping_robot_tmo)
159  {
160  PolyDriver *pDrv=NULL;
161 
162  double t0=Time::now();
163  while (Time::now()-t0<ping_robot_tmo)
164  {
165  if (pDrv!=NULL)
166  delete pDrv;
167 
168  pDrv=new PolyDriver(const_cast<Property&>(partOpt));
169  bool ok=pDrv->isValid();
170 
171  if (ok)
172  {
173  yInfo("Checking if %s is active ... yes",device.c_str());
174  return pDrv;
175  }
176  else
177  {
178  double dt=ping_robot_tmo-(Time::now()-t0);
179  yInfo("Checking if %s is active ... not yet: still %.1f [s] to timeout expiry",
180  device.c_str(),dt>0.0?dt:0.0);
181 
182  double t1=Time::now();
183  while (Time::now()-t1<1.0)
184  Time::delay(0.1);
185  }
186 
187  if (interrupting)
188  break;
189  }
190 
191  return pDrv;
192  }
193 
194  /************************************************************************/
195  PidData getPidData(Bottle &bGroup, const int i)
196  {
197  PidData pid;
198 
199  ostringstream joint;
200  joint<<"joint_"<<i;
201  Bottle &bJoint=bGroup.findGroup(joint.str());
202 
203  pid.Kp=bJoint.check("Kp",Value(0.0)).asFloat64();
204  pid.Ki=bJoint.check("Ki",Value(0.0)).asFloat64();
205  pid.Kd=bJoint.check("Kd",Value(0.0)).asFloat64();
206  pid.scale=bJoint.check("scale",Value(0.0)).asFloat64();
207  pid.st_up=bJoint.check("st_up",Value(0.0)).asFloat64();
208  pid.st_down=bJoint.check("st_down",Value(0.0)).asFloat64();
209  pid.encs_ratio=bJoint.check("encs_ratio",Value(1.0)).asFloat64();
210  pid.status=(bJoint.check("status",Value("download")).asString()=="download"?download:upload);
211  if (bJoint.check("idling"))
212  {
213  if (Bottle *bIdlingJoints=bJoint.find("idling").asList())
214  {
215  pid.idling_joints.clear();
216  for (int j=0; j<bIdlingJoints->size(); j++)
217  {
218  int k=bIdlingJoints->get(j).asInt32();
219 
220  int l;
221  for (l=0; l<rJoints.size(); l++)
222  {
223  if (rJoints.get(l).asInt32()==k)
224  {
225  pid.idling_joints.push_back(k);
226  break;
227  }
228  }
229 
230  if (l>=rJoints.size())
231  yError("unrecognized joint %d to put in idle",k);
232  }
233  }
234  }
235 
236  return pid;
237  }
238 
239  /************************************************************************/
240  void idlingCoupledJoints(const int i, const bool sw)
241  {
242  IControlMode *imod;
243  driver->view(imod);
244 
245  PidData &pid=pids[i];
246  for (size_t j=0; j<pid.idling_joints.size(); j++)
247  imod->setControlMode(pid.idling_joints[j],
248  sw?VOCAB_CM_IDLE:VOCAB_CM_POSITION);
249  }
250 
251  /************************************************************************/
252  bool tune(const int i)
253  {
254  PidData &pid=pids[i];
255 
256  Property pGeneral;
257  pGeneral.put("joint",i);
258  string sGeneral="(general ";
259  sGeneral+=pGeneral.toString();
260  sGeneral+=')';
261 
262  Bottle bGeneral,bPlantEstimation,bStictionEstimation;
263  bGeneral.fromString(sGeneral);
264  bPlantEstimation.fromString("(plant_estimation (Ts 0.01) (Q 1.0) (R 1.0) (P0 100000.0) (tau 1.0) (K 1.0) (max_pwm 800.0))");
265  bStictionEstimation.fromString("(stiction_estimation (Ts 0.01) (T 2.0) (vel_thres 5.0) (e_thres 1.0) (gamma (10.0 10.0)) (stiction (0.0 0.0)))");
266 
267  Bottle bConf=bGeneral;
268  bConf.append(bPlantEstimation);
269  bConf.append(bStictionEstimation);
270 
271  Property pOptions(bConf.toString().c_str());
272  OnlineCompensatorDesign designer;
273  if (!designer.configure(*driver,pOptions))
274  {
275  yError("designer configuration failed!");
276  return false;
277  }
278 
279  idlingCoupledJoints(i,true);
280 
281  Property pPlantEstimation;
282  pPlantEstimation.put("max_time",20.0);
283  pPlantEstimation.put("switch_timeout",2.0);
284  designer.startPlantEstimation(pPlantEstimation);
285 
286  yInfo("Estimating plant for joint %d: max duration = %g seconds",
287  i,pPlantEstimation.find("max_time").asFloat64());
288 
289  double t0=Time::now();
290  while (!designer.isDone())
291  {
292  yInfo("elapsed %d [s]",(int)(Time::now()-t0));
293  Time::delay(1.0);
294  if (interrupting)
295  {
296  idlingCoupledJoints(i,false);
297  return false;
298  }
299  }
300 
301  Property pResults;
302  designer.getResults(pResults);
303  double tau=pResults.find("tau_mean").asFloat64();
304  double K=pResults.find("K_mean").asFloat64();
305  yInfo("plant = %g/s * 1/(1+s*%g)",K,tau);
306 
307  Property pControllerRequirements,pController;
308  pControllerRequirements.put("tau",tau);
309  pControllerRequirements.put("K",K);
310  pControllerRequirements.put("f_c",0.75);
311 
312  if (i!=15)
313  {
314  pControllerRequirements.put("T_dr",1.0);
315  pControllerRequirements.put("type","PI");
316  }
317  else
318  pControllerRequirements.put("type","P");
319 
320  designer.tuneController(pControllerRequirements,pController);
321  yInfo("tuning results: %s",pController.toString().c_str());
322  double Kp=pController.find("Kp").asFloat64();
323  double Ki=pController.find("Ki").asFloat64();
324  pid.scale=4.0;
325  int scale=(int)pid.scale; int shift=1<<scale;
326  double fwKp=floor(Kp*pid.encs_ratio*shift);
327  double fwKi=floor(Ki*pid.encs_ratio*shift/1000.0);
328  pid.Kp=yarp::math::sign(pid.Kp*fwKp)>0.0?fwKp:-fwKp;
329  pid.Ki=yarp::math::sign(pid.Ki*fwKi)>0.0?fwKi:-fwKi;
330  pid.Kd=0.0;
331  yInfo("Kp (FW) = %g; Ki (FW) = %g; Kd (FW) = %g; shift factor = %d",pid.Kp,pid.Ki,pid.Kd,scale);
332 
333  Property pStictionEstimation;
334  pStictionEstimation.put("max_time",60.0);
335  pStictionEstimation.put("Kp",Kp);
336  pStictionEstimation.put("Ki",0.0);
337  pStictionEstimation.put("Kd",0.0);
338  designer.startStictionEstimation(pStictionEstimation);
339 
340  yInfo("Estimating stiction for joint %d: max duration = %g seconds",
341  i,pStictionEstimation.find("max_time").asFloat64());
342 
343  t0=Time::now();
344  while (!designer.isDone())
345  {
346  yInfo("elapsed %d [s]",(int)(Time::now()-t0));
347  Time::delay(1.0);
348  if (interrupting)
349  {
350  idlingCoupledJoints(i,false);
351  return false;
352  }
353  }
354 
355  designer.getResults(pResults);
356  pid.st_up=floor(pResults.find("stiction").asList()->get(0).asFloat64());
357  pid.st_down=floor(pResults.find("stiction").asList()->get(1).asFloat64());
358  yInfo("Stiction values: up = %g; down = %g",pid.st_up,pid.st_down);
359 
360  IControlMode *imod;
361  IPositionControl *ipos;
362  IEncoders *ienc;
363  driver->view(imod);
364  driver->view(ipos);
365  driver->view(ienc);
366  imod->setControlMode(i,VOCAB_CM_POSITION);
367  ipos->setRefSpeed(i,50.0);
368  ipos->positionMove(i,0.0);
369  yInfo("Driving the joint back to rest... ");
370  t0=Time::now();
371  while (Time::now()-t0<5.0)
372  {
373  double enc;
374  ienc->getEncoder(i,&enc);
375  if (fabs(enc)<1.0)
376  break;
377 
378  if (interrupting)
379  {
380  idlingCoupledJoints(i,false);
381  return false;
382  }
383 
384  Time::delay(0.2);
385  }
386  yInfo("done!");
387 
388  idlingCoupledJoints(i,false);
389  return true;
390  }
391 
392 public:
393  /************************************************************************/
394  Tuner() : interrupting(false), driver(NULL)
395  {
396  instances++;
397  }
398 
399  /************************************************************************/
400  bool configure(ResourceFinder &rf, const string &part)
401  {
402  this->part=part;
403  Bottle &bGeneral=rf.findGroup("general");
404  if (bGeneral.isNull())
405  {
406  yError("group [general] is missing!");
407  return false;
408  }
409 
410  Bottle &bPart=rf.findGroup(part);
411  if (bPart.isNull())
412  {
413  yError("group [%s] is missing!",part.c_str());
414  return false;
415  }
416 
417  if (!bPart.check("device"))
418  {
419  yError("\"device\" option is missing!");
420  return false;
421  }
422 
423  name=bGeneral.check("name",Value("fingersTuner")).asString();
424  robot=bGeneral.check("robot",Value("icub")).asString();
425  double ping_robot_tmo=bGeneral.check("ping_robot_tmo",Value(0.0)).asFloat64();
426  device=bPart.find("device").asString();
427 
428  if (Bottle *rj=bGeneral.find("relevantJoints").asList())
429  rJoints=*rj;
430  else
431  {
432  yError("\"relevantJoints\" option is missing!");
433  return false;
434  }
435 
436  int numAlias=bGeneral.check("numAlias",Value(0)).asInt32();
437  for (int i=0; i<numAlias; i++)
438  {
439  ostringstream item;
440  item<<"alias_"<<i;
441  Bottle &bAlias=bGeneral.findGroup(item.str());
442  if (Bottle *joints=bAlias.find("joints").asList())
443  alias[bAlias.find("tag").asString()]=*joints;
444  }
445 
446  // special wildcard to point to all the joints
447  alias["*"]=rJoints;
448 
449  for (int i=0; i<rJoints.size(); i++)
450  {
451  int j=rJoints.get(i).asInt32();
452  pids[j]=getPidData(bPart,j);
453  }
454 
455  ostringstream portsSuffix;
456  portsSuffix<<instances<<"/"<<device;
457 
458  Property option("(device remote_controlboard)");
459  option.put("remote","/"+robot+"/"+device);
460  option.put("local","/"+name+"/"+portsSuffix.str());
461 
462  if (ping_robot_tmo>0.0)
463  driver=waitPart(option,ping_robot_tmo);
464  else
465  driver=new PolyDriver(option);
466 
467  if (!driver->isValid())
468  {
469  yError("%s device driver not available!",device.c_str());
470  return false;
471  }
472 
473  return true;
474  }
475 
476  /************************************************************************/
477  bool sync(const Value &sel)
478  {
479  Bottle joints;
480  if (sel.isInt32())
481  joints.addInt32(sel.asInt32());
482  else if (sel.isString())
483  {
484  map<string,Bottle>::iterator it=alias.find(sel.asString());
485  if (it!=alias.end())
486  joints=it->second;
487  else
488  return false;
489  }
490  else
491  return false;
492 
493  for (int i=0; i<joints.size(); i++)
494  {
495  int j=rJoints.get(i).asInt32();
496  map<int,PidData>::iterator it=pids.find(j);
497  if (it==pids.end())
498  continue;
499 
500  IPidControl *ipid;
501  driver->view(ipid);
502  Pid _pid;
503  ipid->getPid(VOCAB_PIDTYPE_POSITION,j,&_pid);
504 
505  PidData &pid=it->second;
506  if (pid.status==download)
507  pid.fromRobot(_pid);
508  else
509  {
510  pid.toRobot(_pid);
511  ipid->setPid(VOCAB_PIDTYPE_POSITION,j,_pid);
512  }
513 
514  pid.status=synced;
515  }
516 
517  return true;
518  }
519 
520  /************************************************************************/
521  bool tune(const Value &sel)
522  {
523  Bottle joints;
524  if (sel.isInt32())
525  joints.addInt32(sel.asInt32());
526  else if (sel.isString())
527  {
528  map<string,Bottle>::iterator it=alias.find(sel.asString());
529  if (it!=alias.end())
530  joints=it->second;
531  else
532  return false;
533  }
534  else
535  return false;
536 
537  for (int i=0; i<joints.size(); i++)
538  {
539  int j=joints.get(i).asInt32();
540  map<int,PidData>::iterator it=pids.find(j);
541  if (it==pids.end())
542  continue;
543 
544  if (tune(j))
545  {
546  IPidControl *ipid;
547  driver->view(ipid);
548  Pid _pid;
549  ipid->getPid(VOCAB_PIDTYPE_POSITION,j,&_pid);
550 
551  PidData &pid=it->second;
552  pid.toRobot(_pid);
553  ipid->setPid(VOCAB_PIDTYPE_POSITION,j,_pid);
554  pid.status=synced;
555  }
556 
557  if (interrupting)
558  return false;
559  }
560 
561  return true;
562  }
563 
564  /************************************************************************/
565  string toString()
566  {
567  ostringstream stream;
568  stream<<"["<<part<<"]"<<endl;
569  stream<<"device "<<device<<endl;
570 
571  for (int i=0; i<rJoints.size(); i++)
572  {
573  int j=rJoints.get(i).asInt32();
574  PidData &pid=pids[j];
575 
576  Property prop;
577  prop.put("Kp",pid.Kp);
578  prop.put("Ki",pid.Ki);
579  prop.put("Kd",pid.Kd);
580  prop.put("scale",pid.scale);
581  prop.put("st_up",pid.st_up);
582  prop.put("st_down",pid.st_down);
583  prop.put("encs_ratio",pid.encs_ratio);
584  prop.put("status",pid.status==download?"download":"upload");
585 
586  stream<<"joint_"<<j<<" ";
587  stream<<prop.toString()<<endl;
588  }
589 
590  return stream.str();
591  }
592 
593  /************************************************************************/
594  void interrupt()
595  {
596  interrupting=true;
597  }
598 
599  /************************************************************************/
601  {
602  delete driver;
603  }
604 };
605 
606 
607 unsigned int Tuner::instances=0;
608 /************************************************************************/
609 class TunerModule: public RFModule, public fingersTuner_IDL
610 {
611 protected:
612  ResourceFinder *rf;
613  map<string,Tuner*> tuners;
615  bool closing;
616  RpcServer rpcPort;
617 
618 public:
619  /************************************************************************/
620  bool configure(ResourceFinder &rf)
621  {
622  interrupting=false;
623  closing=false;
624  this->rf=&rf;
625 
626  Bottle &bGeneral=rf.findGroup("general");
627  if (bGeneral.isNull())
628  {
629  yError("group [general] is missing!");
630  return false;
631  }
632 
633  string name=bGeneral.check("name",Value("fingersTuner")).asString();
634  setName(name.c_str());
635 
636  if (Bottle *bParts=bGeneral.find("relevantParts").asList())
637  {
638  for (int i=0; (i<bParts->size()) && !interrupting; i++)
639  {
640  string part=bParts->get(i).asString();
641  tuners[part]=new Tuner;
642  Tuner *tuner=tuners[part];
643 
644  if (!tuner->configure(rf,part))
645  {
646  dispose();
647  return false;
648  }
649 
650  tuner->sync(Value("*"));
651  }
652  }
653  else
654  {
655  yError("\"relevantParts\" option is missing!");
656  return false;
657  }
658 
659  rpcPort.open("/"+name+"/rpc");
660  attach(rpcPort);
661 
662  return true;
663  }
664 
665  /************************************************************************/
666  bool attach(RpcServer &source)
667  {
668  return this->yarp().attachAsServer(source);
669  }
670 
671  /************************************************************************/
672  double getPeriod()
673  {
674  return 1.0;
675  }
676 
677  /************************************************************************/
679  {
680  return !closing;
681  }
682 
683  /************************************************************************/
685  {
686  interrupting=true;
687  for (map<string,Tuner*>::iterator it=tuners.begin(); it!=tuners.end(); ++it)
688  it->second->interrupt();
689 
690  return true;
691  }
692 
693  /************************************************************************/
694  void dispose()
695  {
696  for (map<string,Tuner*>::iterator it=tuners.begin(); it!=tuners.end(); ++it)
697  delete it->second;
698  }
699 
700  /************************************************************************/
701  bool sync(const string &part, const Value &val)
702  {
703  map<string,Tuner*>::iterator it=tuners.find(part);
704  if (it!=tuners.end())
705  if (it->second->sync(val))
706  return true;
707 
708  return false;
709  }
710 
711  /************************************************************************/
712  bool tune(const string &part, const Value &val)
713  {
714  map<string,Tuner*>::iterator it=tuners.find(part);
715  if (it!=tuners.end())
716  if (it->second->tune(val))
717  return true;
718 
719  return false;
720  }
721 
722  /************************************************************************/
723  bool save()
724  {
725  string fileName=rf->getHomeContextPath();
726  fileName+="/";
727  fileName+=rf->find("from").asString();
728 
729  ofstream fout;
730  fout.open(fileName.c_str());
731 
732  Bottle &bGeneral=rf->findGroup("general");
733  fout<<"["<<bGeneral.get(0).asString()<<"]"<<endl;
734  for (int i=1; i<bGeneral.size(); i++)
735  fout<<bGeneral.get(i).toString()<<endl;
736 
737  fout<<endl;
738  for (map<string,Tuner*>::iterator it=tuners.begin(); it!=tuners.end(); ++it)
739  fout<<it->second->toString()<<endl;
740 
741  fout.close();
742  return true;
743  }
744 
745  /************************************************************************/
746  bool quit()
747  {
748  return closing=true;
749  }
750 
751  /************************************************************************/
752  bool close()
753  {
754  save();
755  dispose();
756  rpcPort.close();
757  return true;
758  }
759 };
760 
761 
762 /************************************************************************/
763 int main(int argc, char *argv[])
764 {
765  Network yarp;
766  if (!yarp.checkNetwork())
767  {
768  yError("YARP server not available!");
769  return 1;
770  }
771 
772  ResourceFinder rf;
773  rf.setDefaultContext("fingersTuner");
774  rf.setDefaultConfigFile("config.ini");
775  rf.configure(argc,argv);
776 
777  TunerModule mod;
778  return mod.runModule(rf);
779 }
void dispose()
Definition: main.cpp:694
RpcServer rpcPort
Definition: main.cpp:616
bool quit()
Quit the module.
Definition: main.cpp:746
bool updateModule()
Definition: main.cpp:678
bool configure(ResourceFinder &rf)
Definition: main.cpp:620
bool attach(RpcServer &source)
Definition: main.cpp:666
bool save()
Save the PID parameters on configuration file.
Definition: main.cpp:723
ResourceFinder * rf
Definition: main.cpp:612
bool interruptModule()
Definition: main.cpp:684
map< string, Tuner * > tuners
Definition: main.cpp:613
bool sync(const string &part, const Value &val)
Definition: main.cpp:701
bool interrupting
Definition: main.cpp:614
double getPeriod()
Definition: main.cpp:672
bool closing
Definition: main.cpp:615
bool close()
Definition: main.cpp:752
bool tune(const string &part, const Value &val)
Definition: main.cpp:712
Definition: main.cpp:111
pidStatus
Definition: main.cpp:113
@ download
Definition: main.cpp:113
bool configure(ResourceFinder &rf, const string &part)
Definition: main.cpp:400
bool tune(const Value &sel)
Definition: main.cpp:521
bool sync(const Value &sel)
Definition: main.cpp:477
map< string, Bottle > alias
Definition: main.cpp:155
Tuner()
Definition: main.cpp:394
PidData getPidData(Bottle &bGroup, const int i)
Definition: main.cpp:195
PolyDriver * driver
Definition: main.cpp:151
~Tuner()
Definition: main.cpp:600
void idlingCoupledJoints(const int i, const bool sw)
Definition: main.cpp:240
PolyDriver * waitPart(const Property &partOpt, const double ping_robot_tmo)
Definition: main.cpp:158
void interrupt()
Definition: main.cpp:594
Bottle rJoints
Definition: main.cpp:153
map< int, PidData > pids
Definition: main.cpp:154
string toString()
Definition: main.cpp:565
bool tune(const int i)
Definition: main.cpp:252
bool interrupting
Definition: main.cpp:149
string device
Definition: main.cpp:150
static unsigned int instances
Definition: main.cpp:148
fingersTuner_IDL IDL Interface to Fingers PID Tuner services.
Online Compensator Design.
Definition: tuning.h:348
virtual bool isDone()
Check the status of the current ongoing operation.
Definition: tuning.cpp:1025
virtual bool tuneController(const yarp::os::Property &options, yarp::os::Property &results)
Tune the controller once given the plant characteristics.
Definition: tuning.cpp:838
virtual bool startStictionEstimation(const yarp::os::Property &options)
Start off the stiction estimation procedure.
Definition: tuning.cpp:953
virtual bool startPlantEstimation(const yarp::os::Property &options)
Start off the plant estimation procedure.
Definition: tuning.cpp:891
virtual bool getResults(yarp::os::Property &results)
Retrieve the results of the current ongoing operation.
Definition: tuning.cpp:1048
virtual bool configure(yarp::dev::PolyDriver &driver, const yarp::os::Property &options)
Configure the design.
Definition: tuning.cpp:459
int main(int argc, char *argv[])
Definition: main.cpp:31
Copyright (C) 2008 RobotCub Consortium.
double Ki
Definition: main.cpp:117
void fromRobot(const Pid &pid)
Definition: main.cpp:137
void toRobot(Pid &pid)
Definition: main.cpp:128
pidStatus status
Definition: main.cpp:123
vector< int > idling_joints
Definition: main.cpp:124
double Kd
Definition: main.cpp:118
double scale
Definition: main.cpp:119
double encs_ratio
Definition: main.cpp:122
double Kp
Definition: main.cpp:116
double st_down
Definition: main.cpp:121
double st_up
Definition: main.cpp:120