iCub-main
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1
14#include <cstdio>
15#include <math.h>
16#include <string>
17#include <sstream>
18#include <algorithm>
19
20#include <yarp/os/all.h>
21#include <yarp/dev/all.h>
22#include <yarp/sig/all.h>
23#include <yarp/math/Math.h>
24
25#include <iCub/ctrl/tuning.h>
26
27using namespace std;
28using namespace yarp::os;
29using namespace yarp::dev;
30using namespace yarp::sig;
31using namespace yarp::math;
32using namespace iCub::ctrl;
33
34
35/*******************************************************/
36int main(int argc, char *argv[])
37{
38 // usual stuff...
39 Network yarp;
40 if (!yarp.checkNetwork())
41 {
42 printf("YARP is not available!\n");
43 return 1;
44 }
45
46 ResourceFinder rf;
47 rf.configure(argc,argv);
48
49 string name=rf.check("name",Value("tuner")).asString();
50 string robot=rf.check("robot",Value("icub")).asString();
51 string part=rf.check("part",Value("right_arm")).asString();
52 int joint=rf.check("joint",Value(11)).asInt32();
53 double encoder=rf.check("encoder",Value(2.43)).asFloat64();
54
55 Property pOptions;
56 pOptions.put("device","remote_controlboard");
57 pOptions.put("remote","/"+robot+"/"+part);
58 pOptions.put("local","/"+name+"/"+part);
59 PolyDriver driver(pOptions);
60 if (!driver.isValid())
61 {
62 printf("Part \"%s\" is not ready!\n",string("/"+robot+"/"+part).c_str());
63 return 1;
64 }
65
66 // ##### Preamble
67 // The objective is to tune online a P controller that will make
68 // the joint move complying with some given bandwidth specification.
69 //
70 // The plant is to be identified using an Extended-Kalman-Filter (EKF)
71 // under the assumption that the adopted model obeys to the following
72 // tansfer function which gives out the position when fed with voltage:
73 //
74 // K / (s*(1 + tau*s))
75 //
76 // You might have realized that we disregard the electrical dynamics in
77 // favour of the slower mechanical one represented by the time constant tau.
78 //
79 // ##### Additional notes on the control design
80 // The low-level layer provides the user with a PID controller. However, the
81 // I part is not specifically required for such control task since by virtue
82 // of the internal model principle the steady-state error can be already
83 // compensated thanks to the presence of the integrator in the plant. On the
84 // other hand, this principle holds only for linear systems and indeed,
85 // nonlinear effects - such as the stiction - make the final behavior deviate
86 // from this baseline, eventually requiring the integral part. Nonetheless,
87 // if stiction is compensated separately, I is again not strictly needed.
88 //
89 // Finally, a few words about the D part. In order to include D within the design,
90 // the derivative part must be implemented according to the standard which states
91 // that D = Kd*s / (1 + tau_d*s).
92 //
93 // In the following a short example will guide you through the steps required
94 // to identify the plant, validate the retrieved model and then design the
95 // P controller that meets the user specifications. Stiction identification is
96 // carried out as well.
97
98 // First thing to do is to prepare configuration options
99 // which are to be given in the form:
100 //
101 // [general]
102 // joint ...
103 // port ...
104 // [plant_estimation]
105 // Ts ...
106 // ...
107 // [stiction_estimation]
108 // ...
109 Property pGeneral;
110 pGeneral.put("joint",joint);
111 // the "port" option allows opening up a yarp port which
112 // will stream out useful information while identifying
113 // and validating the system
114 pGeneral.put("port","/"+name+"/info:o");
115 string sGeneral="(general ";
116 sGeneral+=pGeneral.toString();
117 sGeneral+=')';
118
119 Bottle bGeneral,bPlantEstimation,bStictionEstimation;
120 bGeneral.fromString(sGeneral);
121 // here follow the parameters for the EKF along with the
122 // initial values for tau and K
123 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))");
124 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)))");
125
126 // compose the overall configuration
127 Bottle bConf=bGeneral;
128 bConf.append(bPlantEstimation);
129 bConf.append(bStictionEstimation);
130 pOptions.fromString(bConf.toString());
131
133 if (!designer.configure(driver,pOptions))
134 {
135 printf("Configuration failed!\n");
136 return 1;
137 }
138
139 // let's start the plant estimation by
140 // setting up an experiment that will
141 // last 20 seconds
142 //
143 // the experiment foresees a direct control
144 // in voltage (pwm)
145 Property pPlantEstimation;
146 pPlantEstimation.put("max_time",20.0);
147 // the switch_timeout option enforces a timeout
148 // in the voltage switching logic to produce
149 // rising and falling transitions.
150 pPlantEstimation.put("switch_timeout",2.0);
151 designer.startPlantEstimation(pPlantEstimation);
152
153 printf("Estimation experiment will last %g seconds...\n",
154 pPlantEstimation.find("max_time").asFloat64());
155
156 double t0=Time::now();
157 while (!designer.isDone())
158 {
159 printf("elapsed %d [s]\n",(int)(Time::now()-t0));
160 Time::delay(1.0);
161 }
162
163 // retrieve the identified values (averaged over time)
164 Property pResults;
165 designer.getResults(pResults);
166 double tau=pResults.find("tau_mean").asFloat64();
167 double K=pResults.find("K_mean").asFloat64();
168
169 printf("plant = K/s * 1/(1+s*tau)\n");
170 printf("Estimated parameters...\n");
171 printf("tau = %g\n",tau);
172 printf("K = %g\n",K);
173
174 // put the model to test for 10 seconds by
175 // simulating the plant with a Kalman filter
176 Property pPlantValidation;
177 pPlantValidation.put("max_time",10.0);
178 pPlantValidation.put("switch_timeout",2.0);
179 pPlantValidation.put("tau",tau);
180 pPlantValidation.put("K",K);
181 // the "measure_update_ticks" option tells that
182 // the measurement update will occur 100 times slower
183 // with respect to the sample time. This way we give
184 // the model enough time to evolve to test its properties
185 // before comparing its response with the real output
186 // to counteract drift phenomena.
187 pPlantValidation.put("measure_update_ticks",100);
188 designer.startPlantValidation(pPlantValidation);
189
190 printf("Validation experiment will last %g seconds...\n",
191 pPlantValidation.find("max_time").asFloat64());
192
193 t0=Time::now();
194 while (!designer.isDone())
195 {
196 printf("elapsed %d [s]\n",(int)(Time::now()-t0));
197 Time::delay(1.0);
198 }
199
200 // The design part...
201 printf("Tuning P controller...\n");
202 Property pControllerRequirements,pController;
203 pControllerRequirements.put("tau",tau);
204 pControllerRequirements.put("K",K);
205 // The bandwidth specification is provided in terms of gain crossover
206 // frequency (in Hz) which amounts to the frequency where the
207 // open loop response given by Kp * plant has a unity-gain;
208 // f_c roughly determines the bandwidth of the closed loop response.
209 // Requirements: track min-jerk profiles whose trajectory
210 // time is 2.0 seconds.
211 // From spectral analysis we know that the most of the
212 // frequency content of a signal composed of the given
213 // min-jerk pulses lies whithin the range [0,0.6] Hz.
214 // A crossover frequency of 0.75 is therefore suitable.
215 pControllerRequirements.put("f_c",0.75);
216 pControllerRequirements.put("type","P");
217 designer.tuneController(pControllerRequirements,pController);
218 printf("tuning results: %s\n",pController.toString().c_str());
219 double Kp=pController.find("Kp").asFloat64();
220 printf("found Kp = %g\n",Kp);
221 int scale=4;
222 double Kp_fw=Kp*encoder*(1<<scale);
223 printf("Kp (firmware) = %g; shift factor = %d\n",Kp_fw,scale);
224
225 // let's identify the stictions values as well
226 Property pStictionEstimation;
227 pStictionEstimation.put("max_time",60.0);
228 // the joint will be controlled under the action
229 // of a high-level PID controller
230 pStictionEstimation.put("Kp",Kp);
231 pStictionEstimation.put("Ki",0.0);
232 pStictionEstimation.put("Kd",0.0);
233 designer.startStictionEstimation(pStictionEstimation);
234
235 printf("Stiction estimation experiment will last no more than %g seconds...\n",
236 pStictionEstimation.find("max_time").asFloat64());
237
238 t0=Time::now();
239 while (!designer.isDone())
240 {
241 printf("elapsed %d [s]\n",(int)(Time::now()-t0));
242 Time::delay(1.0);
243 }
244
245 // retrieve the stiction values
246 designer.getResults(pResults);
247 Vector stiction(2);
248 stiction[0]=pResults.find("stiction").asList()->get(0).asFloat64();
249 stiction[1]=pResults.find("stiction").asList()->get(1).asFloat64();
250 printf("Stiction values: up = %g; down = %g\n",stiction[0],stiction[1]);
251
252 // now that we know P and stiction, let's try out our controller
253 // against the current version
254 Property pControllerValidation;
255 pControllerValidation.put("max_time",60.0);
256 pControllerValidation.put("Kp",Kp_fw);
257 pControllerValidation.put("scale",scale);
258 ostringstream str;
259 str<<"( ";
260 str<<stiction[0];
261 str<<" ";
262 str<<stiction[1];
263 str<<" )";
264 Value val; val.fromString(str.str().c_str());
265 pControllerValidation.put("stiction",val);
266 // we let yarp apply the stiction values upon transitions;
267 // by default the "firmware" takes care of it.
268 pControllerValidation.put("stiction_compensation","middleware");
269 // let's go for the classical "min-jerk" reference input with a
270 // period of 2 seconds.
271 // we have also the "square" waveform at our disposal, just in
272 // case we aim at measuring traditional controller step response.
273 pControllerValidation.put("ref_type","min-jerk");
274 pControllerValidation.put("ref_period",2.0);
275 // for the "min-jerk" reference type it turns to be useful to
276 // have a "sustain" time where the reference is kept to the
277 // final set-point before switching to the next value.
278 pControllerValidation.put("ref_sustain_time",1.0);
279 // in this experiment both the current controller and our controller
280 // will act, one after other, each for 4 cycles of 1 rising and 1 falling
281 // transition in a row.
282 pControllerValidation.put("cycles_to_switch",4);
283 designer.startControllerValidation(pControllerValidation);
284
285 printf("Controller validation will last %g seconds...\n",
286 pControllerValidation.find("max_time").asFloat64());
287
288 t0=Time::now();
289 while (!designer.isDone())
290 {
291 printf("elapsed %d [s]\n",(int)(Time::now()-t0));
292 Time::delay(1.0);
293 }
294
295 return 0;
296}
297
298
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 startPlantValidation(const yarp::os::Property &options)
Start off the plant validation procedure.
Definition tuning.cpp:905
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
virtual bool startControllerValidation(const yarp::os::Property &options)
Start off the controller validation procedure.
Definition tuning.cpp:971
int main()
Definition main.cpp:67
Copyright (C) 2008 RobotCub Consortium.