icub-test
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 
33 using namespace robottestingframework;
34 using namespace yarp::os;
35 using namespace yarp::dev;
36 
37 // prepare the plugin
38 ROBOTTESTINGFRAMEWORK_PREPARE_PLUGIN(PositionControlAccuracyExernalPid)
39 
40 PositionControlAccuracyExernalPid::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 
57 PositionControlAccuracyExernalPid::~PositionControlAccuracyExernalPid() { }
58 
59 bool 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 
176 void 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 
184 void 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 
216 bool 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 
247 void 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 
376 void 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...