icub-test
Loading...
Searching...
No Matches
opticalEncodersDrift.cpp
1/*
2 * iCub Robot Unit Tests (Robot Testing Framework)
3 *
4 * Copyright (C) 2015-2022 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 <math.h>
23#include <robottestingframework/TestAssert.h>
24#include <robottestingframework/dll/Plugin.h>
25#include <yarp/os/Time.h>
26#include <yarp/math/Math.h>
27#include <yarp/os/Property.h>
28#include <yarp/conf/environment.h>
29#include <fstream>
30#include <algorithm>
31#include <cstdlib>
32#include "opticalEncodersDrift.h"
33#include <iostream>
34#include <ctime>
35#include <filesystem>
36
37//example -v -t OpticalEncodersDrift.dll -p "--robot icub --part head --joints ""(0 1 2)"" --home ""(0 0 0)" --speed "(20 20 20)" --max "(10 10 10)" --min "(-10 -10 -10)" --cycles 100 --tolerance 1.0 "
38//example2 -v -t OpticalEncodersDrift.dll -p "--robot icub --part head --joints ""(2)"" --home ""(0)"" --speed "(20 )" --max "(10 )" --min "(-10)" --cycles 100 --tolerance 1.0 "
39using namespace robottestingframework;
40using namespace yarp::os;
41using namespace yarp::dev;
42using namespace yarp::math;
43using namespace std;
44using namespace std::filesystem;
45
46// prepare the plugin
47ROBOTTESTINGFRAMEWORK_PREPARE_PLUGIN(OpticalEncodersDrift)
48
49OpticalEncodersDrift::OpticalEncodersDrift() : yarp::robottestingframework::TestCase("OpticalEncodersDrift") {
50 jointsList=0;
51 dd=0;
52 ipos=0;
53 icmd=0;
54 iimd=0;
55 ienc=0;
56 imot=0;
57 enc_jnt=0;
58 enc_mot=0;
59 home_enc_mot=0;
60 end_enc_mot=0;
61 err_enc_mot=0;
62 cycles=100;
63}
64
65OpticalEncodersDrift::~OpticalEncodersDrift() { }
66
67bool OpticalEncodersDrift::setup(yarp::os::Property& property) {
68
69 if(property.check("name"))
70 setName(property.find("name").asString());
71
72 // updating parameters
73 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("robot"), "The robot name must be given as the test parameter!");
74 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("part"), "The part name must be given as the test parameter!");
75 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("joints"), "The joints list must be given as the test parameter!");
76 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("home"), "The home position must be given as the test parameter!");
77 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("max"), "The max position must be given as the test parameter!");
78 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("min"), "The min position must be given as the test parameter!");
79 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("speed"), "The positionMove reference speed must be given as the test parameter!");
80 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("cycles"), "The number of cycles of the control signal must be given as the test parameter!");
81 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("tolerance"), "The max error tolerance must be given as the test parameter!");
82
83 robotName = property.find("robot").asString();
84
85 partName = property.find("part").asString();
86
87 Bottle* jointsBottle = property.find("joints").asList();
88 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(jointsBottle!=0,"unable to parse joints parameter");
89
90 Bottle* homeBottle = property.find("home").asList();
91 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(homeBottle!=0,"unable to parse zero parameter");
92
93 Bottle* maxBottle = property.find("max").asList();
94 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(maxBottle!=0,"unable to parse max parameter");
95
96 Bottle* minBottle = property.find("min").asList();
97 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(minBottle!=0,"unable to parse min parameter");
98
99 Bottle* speedBottle = property.find("speed").asList();
100 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(speedBottle!=0,"unable to parse speed parameter");
101
102 tolerance = property.find("tolerance").asFloat64();
103 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(tolerance>=0,"invalid tolerance");
104
105 cycles = property.find("cycles").asInt32();
106 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(cycles>=0,"invalid cycles");
107
108 plot = property.find("plot_enabled").asBool();
109
110 if(plot)
111 ROBOTTESTINGFRAMEWORK_TEST_REPORT("This test will run gnuplot utility at the end.");
112 else
113 ROBOTTESTINGFRAMEWORK_TEST_REPORT("This test will NOT run gnuplot utility at the end.");
114
115
116
117 Property options;
118 options.put("device", "remote_controlboard");
119 options.put("remote", "/"+robotName+"/"+partName);
120 options.put("local", "/opticalEncodersDrift/"+robotName+"/"+partName);
121
122 dd = new PolyDriver(options);
123 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->isValid(),"Unable to open device driver");
124 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(ienc),"Unable to open encoders interface");
125 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(ipos),"Unable to open position interface");
126 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(icmd),"Unable to open control mode interface");
127 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(iimd),"Unable to open interaction mode interface");
128 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(imot),"Unable to open motor encoders interface");
129
130 if (!ienc->getAxes(&n_part_joints))
131 {
132 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR("unable to get the number of joints of the part");
133 }
134
135 int n_cmd_joints = jointsBottle->size();
136 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(n_cmd_joints>0 && n_cmd_joints<=n_part_joints,"invalid number of joints, it must be >0 & <= number of part joints");
137 for (int i=0; i <n_cmd_joints; i++) jointsList.push_back(jointsBottle->get(i).asInt32());
138
139 enc_jnt.resize(n_part_joints);
140 enc_mot.resize(n_part_joints);
141 home_enc_mot.resize(n_part_joints);
142 end_enc_mot.resize(n_part_joints);
143 err_enc_mot.resize(n_part_joints);
144
145 max.resize (n_cmd_joints); for (int i=0; i< n_cmd_joints; i++) max[i]=maxBottle->get(i).asFloat64();
146 min.resize (n_cmd_joints); for (int i=0; i< n_cmd_joints; i++) min[i]=minBottle->get(i).asFloat64();
147 home.resize (n_cmd_joints); for (int i=0; i< n_cmd_joints; i++) home[i]=homeBottle->get(i).asFloat64();
148 speed.resize(n_cmd_joints); for (int i=0; i< n_cmd_joints; i++) speed[i]=speedBottle->get(i).asFloat64();
149
150 return true;
151}
152
153void OpticalEncodersDrift::tearDown()
154{
155 if (dd) {delete dd; dd =0;}
156}
157
158void OpticalEncodersDrift::setMode(int desired_mode)
159{
160 for (unsigned int i=0; i<jointsList.size(); i++)
161 {
162 icmd->setControlMode((int)jointsList[i],desired_mode);
163 iimd->setInteractionMode((int)jointsList[i],VOCAB_IM_STIFF);
164 yarp::os::Time::delay(0.010);
165 }
166
167 int cmode;
168 yarp::dev::InteractionModeEnum imode;
169 int timeout = 0;
170
171 while (1)
172 {
173 int ok=0;
174 for (unsigned int i=0; i<jointsList.size(); i++)
175 {
176 icmd->getControlMode ((int)jointsList[i],&cmode);
177 iimd->getInteractionMode((int)jointsList[i],&imode);
178 if (cmode==desired_mode && imode==VOCAB_IM_STIFF) ok++;
179 }
180 if (ok==jointsList.size()) break;
181 if (timeout>100)
182 {
183 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR("Unable to set control mode/interaction mode");
184 }
185 yarp::os::Time::delay(0.2);
186 timeout++;
187 }
188}
189
190bool OpticalEncodersDrift::goHome()
191{
192 for (unsigned int i=0; i<jointsList.size(); i++)
193 {
194 ipos->setRefSpeed((int)jointsList[i],speed[i]);
195 ipos->positionMove((int)jointsList[i],home[i]);
196 }
197
198 int timeout = 0;
199 while (1)
200 {
201 int in_position=0;
202 for (unsigned int i=0; i<jointsList.size(); i++)
203 {
204 double tmp=0;
205 ienc->getEncoder((int)jointsList[i],&tmp);
206 if (fabs(tmp-home[i])<tolerance) in_position++;
207 }
208 if (in_position==jointsList.size()) break;
209 if (timeout>100)
210 {
211 ROBOTTESTINGFRAMEWORK_TEST_REPORT("Timeout while reaching home position");
212 return false;
213 }
214 yarp::os::Time::delay(0.2);
215 timeout++;
216 }
217 return true;
218}
219
220bool OpticalEncodersDrift::saveToFile(const std::string &filename, const yarp::os::Bottle &b)
221{
222 std::fstream fs;
223 fs.open (filename.c_str(), std::fstream::out);
224
225 if ( (fs.rdstate() & std::ifstream::failbit ) != 0 )
226 {
227 std::cerr << "Error opening " << filename << "\n";
228 return false;
229 }
230
231 for (unsigned int i=0; i<b.size(); i++)
232 {
233 std::string s = b.get(i).toString();
234 std::replace(s.begin(), s.end(), '(', ' ');
235 std::replace(s.begin(), s.end(), ')', ' ');
236 fs << s << std::endl;
237 }
238
239 fs.close();
240 return true;
241}
242
243void OpticalEncodersDrift::run()
244{
245 setMode(VOCAB_CM_POSITION);
246 ROBOTTESTINGFRAMEWORK_ASSERT_FAIL_IF_FALSE(goHome(), "Test can't run");
247
248 bool go_to_max=false;
249 for (unsigned int i=0; i<jointsList.size(); i++)
250 {
251 ipos->positionMove((int)jointsList[i], min[i]);
252 }
253
254 int curr_cycle=0;
255 double start_time = yarp::os::Time::now();
256 Bottle dataToPlot;
257
258 imot->getMotorEncoders (home_enc_mot.data());
259 while(1)
260 {
261 double curr_time = yarp::os::Time::now();
262 double elapsed = curr_time-start_time;
263
264 //get joint e motor encoders data for the whole robot part
265 ienc->getEncoders (enc_jnt.data());
266 imot->getMotorEncoders (enc_mot.data());
267 //extract only the joints of interest
268 yarp::sig::Vector enc_jnt_of_interest (jointsList.size());
269 yarp::sig::Vector enc_mot_of_interest (jointsList.size());
270 for (size_t i =0; i< jointsList.size(); i++)
271 {
272 enc_jnt_of_interest[i] = enc_jnt[jointsList[i]];
273 enc_mot_of_interest[i] = enc_mot[jointsList[i]];
274 }
275 //put the data into a Bottle
276 //this is the output format: n values for the motor encoders, then n values for the jnt encoders
277 Bottle& row = dataToPlot.addList();
278 Bottle& b_mot = row.addList();
279 Bottle& b_jnt = row.addList();
280 b_mot.read(enc_mot_of_interest);
281 b_jnt.read(enc_jnt_of_interest);
282
283 bool reached= false;
284 int in_position=0;
285 for (unsigned int i=0; i<jointsList.size(); i++)
286 {
287 double curr_val=0;
288 if (go_to_max==false) curr_val = min[i];
289 else curr_val = max[i];
290 if (fabs(enc_jnt[i]-curr_val)<tolerance) in_position++;
291 }
292 if (in_position==jointsList.size()) reached=true;
293
294 if (elapsed >= 20.0)
295 {
296 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR("Timeout while moving joint");
297 }
298
299 if (reached)
300 {
301 if (go_to_max==false)
302 {
303 for (unsigned int i=0; i<jointsList.size(); i++)
304 ipos->positionMove(i,max[i]);
305 go_to_max=true;
306 curr_cycle++;
307 start_time = yarp::os::Time::now();
308 if (curr_cycle % 10 == 0) ROBOTTESTINGFRAMEWORK_TEST_REPORT(Asserter::format("Cycle %d/%d completed", curr_cycle, cycles));
309 }
310 else
311 {
312 for (unsigned int i=0; i<jointsList.size(); i++)
313 ipos->positionMove(i,min[i]);
314 go_to_max=false;
315 curr_cycle++;
316 start_time = yarp::os::Time::now();
317 if (curr_cycle % 10 == 0) ROBOTTESTINGFRAMEWORK_TEST_REPORT(Asserter::format("Cycle %d/%d completed", curr_cycle, cycles));
318 }
319 }
320
321 if (curr_cycle>=cycles) break;
322
323 yarp::os::Time::delay(0.010);
324 }
325
326 bool isInHome = goHome();
327 yarp::os::Time::delay(2.0);
328
329 //automatic check, not complete yet
330 {
331 imot->getMotorEncoders (end_enc_mot.data());
332 for (int i=0; i<n_part_joints; i++)
333 {
334 err_enc_mot[i]=home_enc_mot[i]-end_enc_mot[i];
335
336 if (fabs(err_enc_mot[i]) > tolerance)
337 {
338 //...assert something
339 }
340 }
341 }
342
343 time_t now = time(0);
344 tm *ltm = localtime(&now);
345
346 char folder_time_buffer[80];
347 char file_time_buffer[80];
348
349 strftime(folder_time_buffer, sizeof(folder_time_buffer), "%d%m%Y", ltm);
350 strftime(file_time_buffer, sizeof(file_time_buffer), "%d%m%Y_%H%M", ltm);
351
352 string folder_time_str(folder_time_buffer);
353 string file_time_str(file_time_buffer); //This string contain also minutes
354
355 // Create the filename with date and time
356 string filename = "encDrift_plot_";
357 filename += partName;
358 filename += "_";
359 filename += file_time_str;
360 filename += ".txt";
361
362 constexpr char default_robot_name[] = "RobotName";
363
364 string robot_str = yarp::conf::environment::get_string("YARP_ROBOT_NAME", default_robot_name);
365 string directory_tree = "results/" + robot_str + "/encoders-icub_" + folder_time_str + "/encDrift";
366 create_directories(directory_tree); // This function return false if there is an error or if the directories already exist
367
368 string filename_with_path = directory_tree + "/" + filename;
369 bool saved_files = saveToFile(filename_with_path,dataToPlot);
370
371 if(!saved_files)
372 {
373 ROBOTTESTINGFRAMEWORK_TEST_REPORT("Error saving files to plot!");
374 }
375
376 char plotstring[1000];
377 sprintf (plotstring, "gnuplot -e \" unset key; plot for [col=1:%d] '%s' using col with lines \" -persist", (int)jointsList.size(),filename_with_path.c_str());
378
379 if(plot)
380 {
381 system (plotstring);
382 }
383 else
384 {
385 ROBOTTESTINGFRAMEWORK_TEST_REPORT("Test is finished. Please check if collected date are ok, by using following command: ");
386 ROBOTTESTINGFRAMEWORK_TEST_REPORT(robottestingframework::Asserter::format("%s", plotstring));
387 }
388
389 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(isInHome, "This part is not in home. Suite test will be terminated!");
390 ROBOTTESTINGFRAMEWORK_TEST_REPORT("Test is finished. Your files are saved in: \n" + filename_with_path);
391
392}
This tests checks if the relative encoders measurements are consistent over time, by performing cycli...