icub-test
PositionControlAccuracy.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 
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 #include <cmath>
31 
32 #include "PositionControlAccuracy.h"
33 
34 using namespace robottestingframework;
35 using namespace yarp::os;
36 using namespace yarp::dev;
37 
38 // prepare the plugin
39 ROBOTTESTINGFRAMEWORK_PREPARE_PLUGIN(PositionControlAccuracy)
40 
41 PositionControlAccuracy::PositionControlAccuracy() : yarp::robottestingframework::TestCase("PositionControlAccuracy") {
42  m_jointsList = 0;
43  m_encoders = 0;
44  m_zeros = 0;
45  dd=0;
46  ipos=0;
47  icmd=0;
48  iimd=0;
49  ienc=0;
50  idir=0;
51  m_home_tolerance=0.5;
52  m_step_duration=4;
53 }
54 
55 PositionControlAccuracy::~PositionControlAccuracy() { }
56 
57 bool PositionControlAccuracy::setup(yarp::os::Property& property) {
58 
59  //updating the test name
60  if(property.check("name"))
61  setName(property.find("name").asString());
62 
63  // updating parameters
64  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("robot"), "The robot name must be given as the test parameter!");
65  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("part"), "The part name must be given as the test parameter!");
66  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("joints"), "The joints list must be given as the test parameter!");
67  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("zeros"), "The zero position list must be given as the test parameter!");
68  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("cycles"), "The number of cycles of the control signal must be given as the test parameter!");
69  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("step"), "The amplitude of the step reference signal expressed in degrees!");
70  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("sampleTime"), "The sampleTime of the control signal must be given as the test parameter!");
71  if(property.check("filename"))
72  {m_requested_filename = property.find("filename").asString();}
73  if(property.check("home_tolerance"))
74  {m_home_tolerance = property.find("home_tolerance").asFloat64();}
75  if(property.check("step_duration"))
76  {m_step_duration = property.find("step_duration").asFloat64();}
77 
78  m_robotName = property.find("robot").asString();
79  m_partName = property.find("part").asString();
80 
81  Bottle* jointsBottle = property.find("joints").asList();
82  Bottle* zerosBottle = property.find("zeros").asList();
83 
84  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(jointsBottle!=0,"unable to parse joints parameter");
85  m_n_cmd_joints = jointsBottle->size();
86  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(m_n_cmd_joints>0, "invalid number of joints, it must be >0");
87 
88  m_step = property.find("step").asFloat64();
89 
90  m_cycles = property.find("cycles").asInt32();
91  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(m_cycles>0, "invalid cycles");
92 
93  m_sampleTime = property.find("sampleTime").asFloat64();
94  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(m_sampleTime>0, "invalid sampleTime");
95 
96  Property options;
97  options.put("device", "remote_controlboard");
98  options.put("remote", "/" + m_robotName + "/" + m_partName);
99  options.put("local", "/positionControlAccuracyTest/" + m_robotName + "/" + m_partName);
100 
101  dd = new PolyDriver(options);
102  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->isValid(),"Unable to open device driver");
103  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(idir),"Unable to open position direct interface");
104  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(ienc),"Unable to open encoders interface");
105  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(ipos),"Unable to open position interface");
106  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(icmd),"Unable to open control mode interface");
107  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(iimd),"Unable to open interaction mode interface");
108  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(ipid),"Unable to open pid interface");
109 
110  if (!ienc->getAxes(&m_n_part_joints))
111  {
112  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR("unable to get the number of joints of the part");
113  }
114 
115  m_zeros = new double[m_n_part_joints];
116  m_encoders = new double[m_n_part_joints];
117  m_jointsList = new int[m_n_cmd_joints];
118  for (int i = 0; i <m_n_cmd_joints; i++) m_jointsList[i] = jointsBottle->get(i).asInt32();
119  for (int i = 0; i <m_n_cmd_joints; i++) m_zeros[i] = zerosBottle->get(i).asFloat64();
120 
121  double p_Kp=std::nanf("");
122  double p_Ki=std::nanf("");
123  double p_Kd=std::nanf("");
124  double p_Max=std::nanf("");
125 
126  int cj=m_jointsList[0];
127  ipid->getPid(VOCAB_PIDTYPE_POSITION,cj,&m_orig_pid);
128  m_new_pid=m_orig_pid;
129 
130  if(property.check("Kp"))
131  {p_Kp = property.find("Kp").asFloat64();}
132  if(property.check("Ki"))
133  {p_Ki = property.find("Ki").asFloat64();}
134  if(property.check("Kd"))
135  {p_Kd = property.find("Kd").asFloat64();}
136  //if(property.check("MaxValue"))
137  // {p_Max = property.find("MaxValue").asFloat64();}
138  if (std::isnan(p_Kp)==false) {m_new_pid.kp=p_Kp;}
139  if (std::isnan(p_Kd)==false) {m_new_pid.kd=p_Kd;}
140  if (std::isnan(p_Ki)==false) {m_new_pid.ki=p_Ki;}
141 
142  /*if (std::isnan(p_Kp)==false ||
143  std::isnan(p_Ki)==false ||
144  std::isnan(p_Kd)==false)
145  {
146  ipid->setPid(cj,m_new_pid);
147  }*/
148 
149  return true;
150 }
151 
152 void PositionControlAccuracy::tearDown()
153 {
154  if (m_jointsList) { delete [] m_jointsList; m_jointsList = 0; }
155  if (m_zeros) { delete [] m_zeros; m_zeros = 0; }
156  if (m_encoders) { delete [] m_encoders; m_encoders = 0; }
157  if (dd) {delete dd; dd =0;}
158 }
159 
160 void PositionControlAccuracy::setMode(int desired_mode)
161 {
162  for (int i = 0; i<m_n_cmd_joints; i++)
163  {
164  icmd->setControlMode(m_jointsList[i], desired_mode);
165  iimd->setInteractionMode(m_jointsList[i], VOCAB_IM_STIFF);
166  yarp::os::Time::delay(0.010);
167  }
168 
169  int cmode;
170  yarp::dev::InteractionModeEnum imode;
171  int timeout = 0;
172 
173  while (1)
174  {
175  int ok=0;
176  for (int i = 0; i<m_n_cmd_joints; i++)
177  {
178  icmd->getControlMode(m_jointsList[i], &cmode);
179  iimd->getInteractionMode(m_jointsList[i], &imode);
180  if (cmode==desired_mode && imode==VOCAB_IM_STIFF) ok++;
181  }
182  if (ok == m_n_cmd_joints) break;
183  if (timeout>100)
184  {
185  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR("Unable to set control mode/interaction mode");
186  }
187  yarp::os::Time::delay(0.2);
188  timeout++;
189  }
190 }
191 
192 bool PositionControlAccuracy::goHome()
193 {
194  for (int i = 0; i<m_n_cmd_joints; i++)
195  {
196  ipos->setRefSpeed(m_jointsList[i], 20.0);
197  ipos->positionMove(m_jointsList[i], m_zeros[i]);
198  }
199 
200  int timeout = 0;
201  while (1)
202  {
203  int in_position=0;
204  for (int i = 0; i<m_n_cmd_joints; i++)
205  {
206  ienc->getEncoder(m_jointsList[i], &m_encoders[m_jointsList[i]]);
207  if (fabs(m_encoders[m_jointsList[i]] - m_zeros[i])<m_home_tolerance) in_position++;
208  }
209  if (in_position == m_n_cmd_joints) break;
210  if (timeout>100)
211  {
212  ROBOTTESTINGFRAMEWORK_ASSERT_ERROR("Timeout while reaching zero position");
213  return false;
214  }
215  yarp::os::Time::delay(0.2);
216  timeout++;
217  }
218  //sleep some additional time to complete movement from m_home_tolerance to 0
219  yarp::os::Time::delay(0.5);
220  return true;
221 }
222 
223 void PositionControlAccuracy::run()
224 {
225  for (int i = 0; i < m_n_cmd_joints; i++)
226  {
227  for (int cycle = 0; cycle < m_cycles; cycle++)
228  {
229  ipid->setPid(VOCAB_PIDTYPE_POSITION,m_jointsList[i],m_orig_pid);
230  setMode(VOCAB_CM_POSITION);
231  if (goHome() == false)
232  {
233  ROBOTTESTINGFRAMEWORK_ASSERT_FAIL("Test stopped");
234  };
235 
236  ipid->setPid(VOCAB_PIDTYPE_POSITION,m_jointsList[i],m_new_pid);
237  setMode(VOCAB_CM_POSITION_DIRECT);
238  double start_time = yarp::os::Time::now();
239 
240  char cbuff[64];
241  sprintf(cbuff, "Testing Joint: %d cycle: %d", i, cycle);
242 
243  std::string buff(cbuff);
244  ROBOTTESTINGFRAMEWORK_TEST_REPORT(buff);
245 
246  double time_zero = 0;
247  yarp::os::Bottle dataToPlotRaw;
248  yarp::os::Bottle dataToPlotSync;
249 
250  while (1)
251  {
252  double curr_time = yarp::os::Time::now();
253  double elapsed = curr_time - start_time;
254 
255  if (elapsed <= 1.0)
256  {
257  m_cmd_single = m_zeros[i]; //0.0;
258  }
259  else if (elapsed > 1.0 && elapsed <= m_step_duration)
260  {
261  m_cmd_single = m_zeros[i] + m_step;
262  if (time_zero == 0) time_zero = elapsed;
263  }
264  else
265  {
266  break;
267  }
268 
269  ienc->getEncoders(m_encoders);
270  idir->setPosition(m_jointsList[i], m_cmd_single);
271 
272  Bottle& b1 = dataToPlotRaw.addList();
273  b1.addInt32(cycle);
274  b1.addFloat64(elapsed);
275  b1.addFloat64(m_encoders[m_jointsList[i]]);
276  b1.addFloat64(m_cmd_single);
277  yarp::os::Time::delay(m_sampleTime);
278  }
279 
280  //reorder data
281  for (int t = 0; t < dataToPlotRaw.size(); t++)
282  {
283  int cycle = dataToPlotRaw.get(t).asList()->get(0).asInt32();
284  double time = dataToPlotRaw.get(t).asList()->get(1).asFloat64();
285  double val = dataToPlotRaw.get(t).asList()->get(2).asFloat64();
286  double cmd = dataToPlotRaw.get(t).asList()->get(3).asFloat64();
287  Bottle& b1 = dataToPlotSync.addList();
288  b1.addInt32(cycle);
289  b1.addFloat64(time - time_zero);
290  b1.addFloat64(val);
291  b1.addFloat64(cmd);
292  }
293 
294  m_dataToSave.append(dataToPlotSync);
295  } //cycle loop
296 
297  //save data
298  std::string filename;
299  if (m_requested_filename=="")
300  {
301  char cfilename[128];
302  sprintf(cfilename, "positionControlAccuracy_plot_%s%d.txt", m_partName.c_str(), i);
303  filename = cfilename;
304  //filename += m_partName;
305  //filename += std::to_string(i);
306  //filename += ".txt";
307  }
308  else
309  {
310  filename=m_requested_filename;
311  }
312  yInfo() << "Saving file to: "<< filename;
313  saveToFile(filename, m_dataToSave);
314  ipid->setPid(VOCAB_PIDTYPE_POSITION,m_jointsList[i],m_orig_pid);
315  } //joint loop
316 
317  //data acquisition ends here
318  setMode(VOCAB_CM_POSITION);
319  goHome();
320  ROBOTTESTINGFRAMEWORK_TEST_REPORT("Data acquisition complete");
321 
322  //plot data
323  /*for (int i = 0; i < m_n_cmd_joints; i++)
324  {
325  std::string filename = "positionControlAccuracy_plot_";
326  filename += m_partName;
327  filename += std::to_string(i);
328  filename += ".txt";
329  char plotstring[1000];
330  sprintf(plotstring, "gnuplot -e \" unset key; plot for [col=1:%d] '%s' using col with lines \" -persist", m_n_cmd_joints, filename.c_str());
331  system(plotstring);
332  }*/
333 }
334 
335 void PositionControlAccuracy::saveToFile(std::string filename, yarp::os::Bottle &b)
336 {
337  std::fstream fs;
338  fs.open(filename.c_str(), std::fstream::out);
339 
340  for (int i = 0; i<b.size(); i++)
341  {
342  std::string s = b.get(i).toString();
343  std::replace(s.begin(), s.end(), '(', ' ');
344  std::replace(s.begin(), s.end(), ')', ' ');
345  fs << s << std::endl;
346  }
347 
348  fs.close();
349 }
This tests checks the a position PID response, sending a step reference signal with a positionDirect ...