icub-test
Loading...
Searching...
No Matches
PositionControlAccuracyExternalPid.cpp
1/*
2 * iCub Robot Unit Tests (Robot Testing Framework)
3 *
4 * Copyright (C) 2015-2019 Istituto Italiano di Tecnologia (IIT)
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#include <math.h>
22#include <robottestingframework/TestAssert.h>
23#include <robottestingframework/dll/Plugin.h>
24#include <yarp/os/LogStream.h>
25#include <yarp/os/Time.h>
26#include <yarp/os/Property.h>
27#include <fstream>
28#include <algorithm>
29#include <cstdlib>
30
31#include "PositionControlAccuracyExternalPid.h"
32
33using namespace robottestingframework;
34using namespace yarp::os;
35using namespace yarp::dev;
36
37// prepare the plugin
38ROBOTTESTINGFRAMEWORK_PREPARE_PLUGIN(PositionControlAccuracyExernalPid)
39
40PositionControlAccuracyExernalPid::PositionControlAccuracyExernalPid() : yarp::robottestingframework::TestCase("PositionControlAccuracyExernalPid") {
41 m_jointsList = 0;
42 m_encoders = 0;
43 m_zeros = 0;
44 dd=0;
45 ipos=0;
46 icmd=0;
47 iimd=0;
48 ienc=0;
49 idir=0;
50 ipwm=0;
51 m_home_tolerance=0.5;
52 m_step_duration=4;
53 m_pospid_vup=0;
54 m_pospid_vdown=0;
55}
56
57PositionControlAccuracyExernalPid::~PositionControlAccuracyExernalPid() { }
58
59bool PositionControlAccuracyExernalPid::setup(yarp::os::Property& property) {
60
61 //updating the test name
62 if(property.check("name"))
63 setName(property.find("name").asString());
64
65 // updating parameters
66 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("robot"), "The robot name must be given as the test parameter!");
67 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("part"), "The part name must be given as the test parameter!");
68 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("joints"), "The joints list must be given as the test parameter!");
69 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("zeros"), "The zero position list must be given as the test parameter!");
70 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("cycles"), "The number of cycles of the control signal must be given as the test parameter!");
71 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("step"), "The amplitude of the step reference signal expressed in degrees!");
72 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("sampleTime"), "The sampleTime of the control signal must be given as the test parameter!");
73
74 if(property.check("filename"))
75 {m_requested_filename = property.find("filename").asString();}
76 if(property.check("home_tolerance"))
77 {m_home_tolerance = property.find("home_tolerance").asFloat64();}
78 if(property.check("step_duration"))
79 {m_step_duration = property.find("step_duration").asFloat64();}
80 if(property.check("pid_vup"))
81 {m_pospid_vup = property.find("pid_vup").asFloat64();}
82 if(property.check("pid_vdown"))
83 {m_pospid_vdown = property.find("pid_vdown").asFloat64();}
84
85 m_robotName = property.find("robot").asString();
86 m_partName = property.find("part").asString();
87
88 Bottle* jointsBottle = property.find("joints").asList();
89 Bottle* zerosBottle = property.find("zeros").asList();
90
91 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(jointsBottle!=0,"unable to parse joints parameter");
92 m_n_cmd_joints = jointsBottle->size();
93 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(m_n_cmd_joints>0, "invalid number of joints, it must be >0");
94
95 m_step = property.find("step").asFloat64();
96
97 m_cycles = property.find("cycles").asInt32();
98 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(m_cycles>0, "invalid cycles");
99
100 m_sampleTime = property.find("sampleTime").asFloat64();
101 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(m_sampleTime>0, "invalid sampleTime");
102
103 Property options;
104 options.put("device", "remote_controlboard");
105 options.put("remote", "/" + m_robotName + "/" + m_partName);
106 options.put("local", "/positionControlAccuracyTest/" + m_robotName + "/" + m_partName);
107
108 dd = new PolyDriver(options);
109 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->isValid(),"Unable to open device driver");
110 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(idir),"Unable to open position direct interface");
111 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(ienc),"Unable to open encoders interface");
112 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(ipos),"Unable to open position interface");
113 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(icmd),"Unable to open control mode interface");
114 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(iimd),"Unable to open interaction mode interface");
115 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(ipwm),"Unable to open pwm mode interface");
116
117 if (!ienc->getAxes(&m_n_part_joints))
118 {
119 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR("unable to get the number of joints of the part");
120 }
121
122 m_zeros = new double[m_n_part_joints];
123 m_encoders = new double[m_n_part_joints];
124 m_jointsList = new int[m_n_cmd_joints];
125 for (int i = 0; i <m_n_cmd_joints; i++) m_jointsList[i] = jointsBottle->get(i).asInt32();
126 for (int i = 0; i <m_n_cmd_joints; i++) m_zeros[i] = zerosBottle->get(i).asFloat64();
127
128 double p_Ts = m_sampleTime;
129 yarp::sig::Vector p_Kp(1,0.0);
130 yarp::sig::Vector p_Ki(1,0.0);
131 yarp::sig::Vector p_Kd(1,0.0);
132 if(property.check("Kp"))
133 {p_Kp = property.find("Kp").asFloat64();}
134 if(property.check("Ki"))
135 {p_Ki = property.find("Ki").asFloat64();}
136 if(property.check("Kd"))
137 {p_Kd = property.find("Kd").asFloat64();}
138 double p_Max=100;
139 if(property.check("MaxValue"))
140 {p_Max = property.find("MaxValue").asFloat64();}
141 yarp::sig::Vector p_Wp(1,1);
142 yarp::sig::Vector p_Wi(1,1);
143 yarp::sig::Vector p_Wd(1,1);
144 yarp::sig::Vector p_N (1,10);
145 yarp::sig::Vector p_Tt (1,1);
146 yarp::sig::Matrix p_Lim (1,2);
147 yInfo() << "Using gains Kp:" << p_Kp[0] << " Ki:"<<p_Ki[0] << " Kd:"<<p_Kd[0] << " Max:" << p_Max;
148 p_Lim[0][0]=-p_Max;
149 p_Lim[0][1]=+p_Max;
150 ppid = new iCub::ctrl::parallelPID(p_Ts, p_Kp, p_Ki, p_Kd, p_Wp, p_Wi, p_Wd, p_N, p_Tt, p_Lim);
151
152 if (m_requested_filename=="auto")
153 {
154 char buff[50];
155 m_requested_filename="ext_";
156 m_requested_filename+=(m_robotName+"_");
157 m_requested_filename+=(m_partName+"_");
158 sprintf(buff,"%.3f",p_Kp[0]);
159 m_requested_filename+=("Kp_"+std::string(buff)+"_");
160 sprintf(buff,"%.3f",p_Ki[0]);
161 m_requested_filename+=("Ki_"+std::string(buff)+"_");
162 sprintf(buff,"%.3f",p_Kd[0]);
163 m_requested_filename+=("Kdi_"+std::string(buff)+"_");
164 sprintf(buff,"%.3f",m_pospid_vup);
165 m_requested_filename+=("Vup_"+std::string(buff)+"_");
166 sprintf(buff,"%.3f",m_pospid_vdown);
167 m_requested_filename+=("Vdown_"+std::string(buff)+"_");
168 sprintf(buff,"%.1f",m_step);
169 m_requested_filename+=("step_"+std::string(buff)+".txt");
170 }
171 yDebug() << "File: " << m_requested_filename << " will be used";
172
173 return true;
174}
175
176void PositionControlAccuracyExernalPid::tearDown()
177{
178 if (m_jointsList) { delete [] m_jointsList; m_jointsList = 0; }
179 if (m_zeros) { delete [] m_zeros; m_zeros = 0; }
180 if (m_encoders) { delete [] m_encoders; m_encoders = 0; }
181 if (dd) {delete dd; dd =0;}
182}
183
184void PositionControlAccuracyExernalPid::setMode(int desired_mode)
185{
186 for (int i = 0; i<m_n_cmd_joints; i++)
187 {
188 icmd->setControlMode(m_jointsList[i], desired_mode);
189 iimd->setInteractionMode(m_jointsList[i], VOCAB_IM_STIFF);
190 yarp::os::Time::delay(0.010);
191 }
192
193 int cmode;
194 yarp::dev::InteractionModeEnum imode;
195 int timeout = 0;
196
197 while (1)
198 {
199 int ok=0;
200 for (int i = 0; i<m_n_cmd_joints; i++)
201 {
202 icmd->getControlMode(m_jointsList[i], &cmode);
203 iimd->getInteractionMode(m_jointsList[i], &imode);
204 if (cmode==desired_mode && imode==VOCAB_IM_STIFF) ok++;
205 }
206 if (ok == m_n_cmd_joints) break;
207 if (timeout>100)
208 {
209 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR("Unable to set control mode/interaction mode");
210 }
211 yarp::os::Time::delay(0.2);
212 timeout++;
213 }
214}
215
216bool PositionControlAccuracyExernalPid::goHome()
217{
218 for (int i = 0; i<m_n_cmd_joints; i++)
219 {
220 ipos->setRefSpeed(m_jointsList[i], 20.0);
221 ipos->positionMove(m_jointsList[i], m_zeros[i]);
222 }
223
224 int timeout = 0;
225 while (1)
226 {
227 int in_position=0;
228 for (int i = 0; i<m_n_cmd_joints; i++)
229 {
230 ienc->getEncoder(m_jointsList[i], &m_encoders[m_jointsList[i]]);
231 if (fabs(m_encoders[m_jointsList[i]] - m_zeros[i])<m_home_tolerance) in_position++;
232 }
233 if (in_position == m_n_cmd_joints) break;
234 if (timeout>100)
235 {
236 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR("Timeout while reaching zero position");
237 return false;
238 }
239 yarp::os::Time::delay(0.2);
240 timeout++;
241 }
242 //sleep some additional time to complete movement from m_home_tolerance to 0
243 yarp::os::Time::delay(0.5);
244 return true;
245}
246
247void PositionControlAccuracyExernalPid::run()
248{
249 for (int i = 0; i < m_n_cmd_joints; i++)
250 {
251 for (int cycle = 0; cycle < m_cycles; cycle++)
252 {
253 setMode(VOCAB_CM_POSITION);
254 if (goHome() == false)
255 {
256 ROBOTTESTINGFRAMEWORK_ASSERT_FAIL("Test stopped");
257 };
258
259 ppid->reset(yarp::sig::Vector(1,0.0));
260
261 setMode(VOCAB_CM_PWM);
262 double start_time = yarp::os::Time::now();
263
264 char cbuff[64];
265 sprintf(cbuff, "Testing Joint: %d cycle: %d", i, cycle);
266
267 std::string buff(cbuff);
268 ROBOTTESTINGFRAMEWORK_TEST_REPORT(buff);
269
270 double time_zero = 0;
271 yarp::os::Bottle dataToPlotRaw;
272 yarp::os::Bottle dataToPlotSync;
273 ienc->getEncoders(m_encoders);
274
275 while (1)
276 {
277 double curr_time = yarp::os::Time::now();
278 double elapsed = curr_time - start_time;
279 double ref=0;
280 if (elapsed <= 1.0)
281 {
282 ref=m_zeros[i];
283 }
284 else if (elapsed > 1.0 && elapsed <= m_step_duration)
285 {
286 ref=m_zeros[i]+m_step;
287 if (time_zero == 0) time_zero = elapsed;
288 }
289 else
290 {
291 break;
292 }
293
294 //pid computation
295 ienc->getEncoders(m_encoders);
296 m_cmd_single = ppid->compute(yarp::sig::Vector(1,ref),yarp::sig::Vector(1,m_encoders[m_jointsList[i]]))[0];
297
298 //stiction compensation
299 if (ref>m_encoders[m_jointsList[i]])
300 {
301 m_cmd_single+=m_pospid_vup;
302 }
303 else
304 {
305 m_cmd_single+=m_pospid_vdown;
306 }
307
308 //control
309 ipwm->setRefDutyCycle(m_jointsList[i], m_cmd_single);
310
311 Bottle& b1 = dataToPlotRaw.addList();
312 b1.addInt32(cycle);
313 b1.addFloat64(elapsed);
314 b1.addFloat64(m_encoders[m_jointsList[i]]);
315 b1.addFloat64(ref);
316 b1.addFloat64(m_cmd_single);
317 yarp::os::Time::delay(m_sampleTime);
318 }
319
320 //reorder data
321 for (int t = 0; t < dataToPlotRaw.size(); t++)
322 {
323 int cycle = dataToPlotRaw.get(t).asList()->get(0).asInt32();
324 double time = dataToPlotRaw.get(t).asList()->get(1).asFloat64();
325 double val = dataToPlotRaw.get(t).asList()->get(2).asFloat64();
326 double cmd = dataToPlotRaw.get(t).asList()->get(3).asFloat64();
327 double duty = dataToPlotRaw.get(t).asList()->get(4).asFloat64();
328 Bottle& b1 = dataToPlotSync.addList();
329 b1.addInt32(cycle);
330 b1.addFloat64(time - time_zero);
331 b1.addFloat64(val);
332 b1.addFloat64(cmd);
333 b1.addFloat64(duty);
334 }
335
336 m_dataToSave.append(dataToPlotSync);
337 } //cycle loop
338
339 //save data
340 std::string filename;
341 if (m_requested_filename=="")
342 {
343 char cfilename[128];
344 sprintf(cfilename, "positionControlAccuracyExternalPid_plot_%s%d.txt", m_partName.c_str(), i);
345 filename = cfilename;
346 //filename += m_partName;
347 //filename += std::to_string(i);
348 //filename += ".txt";
349 }
350 else
351 {
352 filename=m_requested_filename;
353 }
354 yInfo() << "Saving file to: "<< filename;
355 saveToFile(filename, m_dataToSave);
356 } //joint loop
357
358 //data acquisition ends here
359 setMode(VOCAB_CM_POSITION);
360 goHome();
361 ROBOTTESTINGFRAMEWORK_TEST_REPORT("Data acquisition complete");
362
363 //plot data
364 /*for (int i = 0; i < m_n_cmd_joints; i++)
365 {
366 std::string filename = "positionControlAccuracy_plot_";
367 filename += m_partName;
368 filename += std::to_string(i);
369 filename += ".txt";
370 char plotstring[1000];
371 sprintf(plotstring, "gnuplot -e \" unset key; plot for [col=1:%d] '%s' using col with lines \" -persist", m_n_cmd_joints, filename.c_str());
372 system(plotstring);
373 }*/
374}
375
376void PositionControlAccuracyExernalPid::saveToFile(std::string filename, yarp::os::Bottle &b)
377{
378 std::fstream fs;
379 fs.open(filename.c_str(), std::fstream::out);
380
381 for (int i = 0; i<b.size(); i++)
382 {
383 std::string s = b.get(i).toString();
384 std::replace(s.begin(), s.end(), '(', ' ');
385 std::replace(s.begin(), s.end(), ')', ' ');
386 fs << s << std::endl;
387 }
388
389 fs.close();
390}
This tests checks the response of the system to a position step, sending directly PWM commands to a j...