iCub-main
Loading...
Searching...
No Matches
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
102using namespace std;
103using namespace yarp::os;
104using namespace yarp::dev;
105using namespace yarp::sig;
106using namespace iCub::ctrl;
107
108
109/************************************************************************/
110class Tuner
111{
112protected:
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;
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;
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());
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
392public:
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 /************************************************************************/
595 {
596 interrupting=true;
597 }
598
599 /************************************************************************/
601 {
602 delete driver;
603 }
604};
605
606
607unsigned int Tuner::instances=0;
608/************************************************************************/
609class TunerModule: public RFModule, public fingersTuner_IDL
610{
611protected:
612 ResourceFinder *rf;
613 map<string,Tuner*> tuners;
616 RpcServer rpcPort;
617
618public:
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");
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/************************************************************************/
763int 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
pidStatus
Definition main.cpp:113
@ upload
Definition main.cpp:113
@ download
Definition main.cpp:113
@ synced
Definition main.cpp:113
string name
Definition main.cpp:150
PolyDriver * waitPart(const Property &partOpt, const double ping_robot_tmo)
Definition main.cpp:158
bool configure(ResourceFinder &rf, const string &part)
Definition main.cpp:400
string robot
Definition main.cpp:150
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
string part
Definition main.cpp:150
PolyDriver * driver
Definition main.cpp:151
~Tuner()
Definition main.cpp:600
void idlingCoupledJoints(const int i, const bool sw)
Definition main.cpp:240
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()
Definition main.cpp:67
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