icub-test
Loading...
Searching...
No Matches
imu.cpp
1#include <iostream>
2#include <cmath>
3#include <numeric>
4
5#include <robottestingframework/TestAssert.h>
6#include <robottestingframework/dll/Plugin.h>
7
8#include <yarp/os/Property.h>
9#include <yarp/os/Stamp.h>
10#include <yarp/os/ResourceFinder.h>
11#include <yarp/os/LogStream.h>
12
13#include <robometry/BufferConfig.h>
14#include <robometry/BufferManager.h>
15
16#include "imu.h"
17
18using namespace robottestingframework;
19ROBOTTESTINGFRAMEWORK_PREPARE_PLUGIN(Imu)
20
21Imu::Imu() : TestCase("Imu") { }
22
23Imu::~Imu() { }
24
25bool Imu::setup(yarp::os::Property& property)
26{
27 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("robot"), "The robot name must be given as the test parameter!");
28 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("port"), "The port name must be given as the test parameter!");
29 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("remoteControlBoards"), "Please, provide the controlboards name.");
30 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("axesNames"), "Please, provide the controlled joints name.");
31 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("model"), "Please, provide the urdf model path.");
32 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("maxError"), "Please, provide the threshold error.");
33 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check("sensorsList"), "Please, provide the list of the sensors you want to check or 'all' if you want to test all the IMUs.");
34
35 robotName = property.find("robot").asString(); // robot name
36 portName = property.find("port").asString(); // name of the port from which the data are streamed
37 errorMax = property.find("maxError").asFloat64(); //error mean
38 modelName = property.find("model").asString(); // urdf model path
39 yarp::os::ResourceFinder &rf = yarp::os::ResourceFinder::getResourceFinderSingleton();
40 std::string modelAbsolutePath = rf.findFileByName(modelName);
41
42 ROBOTTESTINGFRAMEWORK_TEST_REPORT("Running IMU test on "+robotName+"...");
43
44 yarp::os::Property controlBoardOptions;
45 controlBoardOptions.put("device", "remotecontrolboardremapper");
46
47 yarp::os::Bottle remoteControlBoards;
48 yarp::os::Bottle & remoteControlBoardsList = remoteControlBoards.addList();
49 yarp::os::Bottle *inputControlBoards;
50 inputControlBoards = property.find("remoteControlBoards").asList();
51 for(int ctrlBoard = 0; ctrlBoard < inputControlBoards->size(); ctrlBoard++)
52 {
53 partsList.push_back(inputControlBoards->get(ctrlBoard).asString());
54 remoteControlBoardsList.addString("/"+robotName+"/"+inputControlBoards->get(ctrlBoard).asString());
55 }
56
57 yarp::os::Bottle axesNames;
58 yarp::os::Bottle & axesList = axesNames.addList();
59 yarp::os::Bottle *inputJoints;
60 inputJoints = property.find("axesNames").asList();
61 for(int ctrlJoint = 0; ctrlJoint < inputJoints->size(); ctrlJoint++)
62 {
63 axesList.addString(inputJoints->get(ctrlJoint).asString());
64 }
65
66 controlBoardOptions.put("remoteControlBoards", remoteControlBoards.get(0));
67 controlBoardOptions.put("localPortPrefix", "/test");
68 controlBoardOptions.put("axesNames", axesNames.get(0));
69
70 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(controlBoardDriver.open(controlBoardOptions), "Unable to open the controlBoard driver");
71 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(controlBoardDriver.isValid(), "Device driver cannot be opened");
72 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(controlBoardDriver.view(ipos), "Unable to open position control interface");
73 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(controlBoardDriver.view(ienc), "Unable to open encoder interface");
74 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(controlBoardDriver.view(iaxes), "Unable to open axes interface");
75
76 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(ienc->getAxes(&axes), "Cannot get number of controlled axes");
77 std::string axisName;
78
79 for (int axIndex = 0; axIndex < axes; axIndex++)
80 {
81 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(iaxes->getAxisName(axIndex, axisName), "Cannot get the name of controlled axes");
82 axesVec.push_back(axisName);
83 }
84
85 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(model.loadReducedModelFromFile(modelAbsolutePath.c_str(), axesVec), Asserter::format("Cannot load model from %s", modelAbsolutePath.c_str()));
86 kinDynComp.loadRobotModel(model.model());
87
88 yarp::os::Property MASclientOptions;
89 MASclientOptions.put("device", "multipleanalogsensorsclient");
90 MASclientOptions.put("remote", portName);
91 MASclientOptions.put("local", "/imuTest"+portName);
92
93 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(MASclientDriver.open(MASclientOptions), "Unable to open the MAS client driver");
94 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(MASclientDriver.isValid(), "Device driver cannot be opened");
95
96 yarp::os::Bottle & sensorsNamesList = sensorsList.addList();
97 yarp::os::Bottle *inputSensorsList;
98 inputSensorsList = property.find("sensorsList").asList();
99
100 if(inputSensorsList->size() == 0 || inputSensorsList->get(0).asString() == "all")
101 {
102 ROBOTTESTINGFRAMEWORK_TEST_REPORT("Testing all the IMUs available...");
103 std::string sensorName{""};
104 yarp::dev::IOrientationSensors* ior;
105 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(MASclientDriver.view(ior), "Unable to open the orientation interface");
106 for(int sensorIndex = 0; sensorIndex < ior->getNrOfOrientationSensors(); sensorIndex++)
107 {
108 ior->getOrientationSensorName(sensorIndex, sensorName);
109 sensorsNamesList.addString(sensorName);
110 }
111 }
112
113 else
114 {
115 ROBOTTESTINGFRAMEWORK_TEST_REPORT("Testing the list of provided IMUs...");
116 for(int sensorIndex = 0; sensorIndex < inputSensorsList->size(); sensorIndex++)
117 {
118 sensorsNamesList.addString(inputSensorsList->get(sensorIndex).asString());
119 }
120 }
121
122 yarp::os::Property MASremapperOptions;
123 MASremapperOptions.put("device", "multipleanalogsensorsremapper");
124 MASremapperOptions.put("OrientationSensorsNames", sensorsList.get(0));
125 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(MASremapperDriver.open(MASremapperOptions), "Unable to open the MAS remapper driver");
126 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(MASremapperDriver.isValid(), "Device driver cannot be opened");
127
128 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(MASremapperDriver.view(imultiwrap), "Unable to open multiple wrapper interface");
129
130 yarp::dev::PolyDriverList driverList;
131 driverList.push(&MASclientDriver, "alljoints_inertials");
132 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(imultiwrap->attachAll(driverList), "Unable to do the attach");
133 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(MASremapperDriver.view(iorientation), "Unable to open orientation interface");
134
135 iDynTree::Vector3 baseLinkOrientationRad;
136 baseLinkOrientationRad.zero();
137
138 baseLinkOrientation = iDynTree::Rotation::RPY(baseLinkOrientationRad[0], baseLinkOrientationRad[1], baseLinkOrientationRad[2]);
139 I_T_base = iDynTree::Transform(baseLinkOrientation, iDynTree::Position::Zero());
140 iDynTree::Twist baseVelocity = iDynTree::Twist::Zero();
141
142 unsigned int dofs = kinDynComp.model().getNrOfDOFs();
143 s.resize(dofs);
144 ds.resize(dofs);
145
146 positions.resize(axes);
147 velocities.resize(axes);
148 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(ienc->getEncoders(positions.data()), "Cannot get joint positions");
149 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(ienc->getEncoderSpeeds(velocities.data()), "Cannot get joint velocities");
150
151 for(auto axIndex = 0; axIndex < axes; axIndex++)
152 {
153 s.setVal(axIndex, iDynTree::deg2rad(positions[axIndex]));
154 ds.setVal(axIndex, iDynTree::deg2rad(velocities[axIndex]));
155 }
156
157 gravity.zero();
158 gravity(2) = -9.81;
159
160 kinDynComp.setRobotState(
161 I_T_base,
162 s,
163 baseVelocity,
164 ds,
165 gravity
166 );
167
168 setupRobometry();
169 localBroker.resize(remoteControlBoards.get(0).asList()->size());
170
171 return true;
172}
173
174void Imu::tearDown()
175{
176 outputPort.interrupt();
177 outputPort.close();
178
179 controlBoardDriver.close();
180 MASclientDriver.close();
181 MASremapperDriver.close();
182
183 for(int i = 0; i < localBroker.size(); i++)
184 {
185 localBroker[i].stop();
186 }
187
188 scriptBroker.stop();
189}
190
191void Imu::run()
192{
193 ROBOTTESTINGFRAMEWORK_TEST_REPORT("Starting reading IMU orientation values...");
194 rpyValues.resize(sensorsList.get(0).asList()->size());
195 I_R_I_IMU.resize(sensorsList.get(0).asList()->size());
196
197 for (int sensorIndex = 0; sensorIndex < sensorsList.get(0).asList()->size(); sensorIndex++)
198 {
199 std::string sensorName{""};
200 std::string frameName{""};
201 double timestamp;
202 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(iorientation->getOrientationSensorName(sensorIndex, sensorName), "Unable to obtain sensor name.");
203 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(iorientation->getOrientationSensorMeasureAsRollPitchYaw(sensorIndex, rpyValues[sensorIndex], timestamp), "Unable to obtain rpy measurements.");
204 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(iorientation->getOrientationSensorFrameName(sensorIndex, frameName), "Unable to obtain frame name.");
205 iDynTree::Rotation I_R_FK = kinDynComp.getWorldTransform(frameName).getRotation();
206 I_R_I_IMU[sensorIndex] = (I_R_FK * ((iDynTree::Rotation::RPY(iDynTree::deg2rad(rpyValues[sensorIndex][0]), iDynTree::deg2rad(rpyValues[sensorIndex][1]), iDynTree::deg2rad(rpyValues[sensorIndex][2]))).inverse()));
207 }
208
209 setupBrokers();
210 startMove();
211}
212
213bool Imu::startMove()
214{
215 bool done = false;
216 iDynTree::GeomVector3 error;
217 std::vector<std::vector<double>> errorTot(sensorsList.get(0).asList()->size());
218
219 yarp::os::Time::delay(.1);
220
221 while(!done)
222 {
223 for (int sensorIndex = 0; sensorIndex < sensorsList.get(0).asList()->size(); sensorIndex++)
224 {
225 std::string frameName{""};
226 std::string sensorName{""};
227
228 double timestamp;
229 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(iorientation->getOrientationSensorName(sensorIndex, sensorName), "Unable to obtain sensor name.");
230 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(iorientation->getOrientationSensorMeasureAsRollPitchYaw(sensorIndex, rpyValues[sensorIndex], timestamp), "Unable to obtain rpy measurements.");
231 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(iorientation->getOrientationSensorFrameName(sensorIndex, frameName), "Unable to obtain frame name.");
232
233 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(ienc->getEncoders(positions.data()), "Cannot get joint positions");
234 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(ienc->getEncoderSpeeds(velocities.data()), "Cannot get joint velocities");
235
236 for (auto axIndex = 0; axIndex < axes; axIndex++)
237 {
238 s.setVal(axIndex, iDynTree::deg2rad(positions[axIndex]));
239 ds.setVal(axIndex, iDynTree::deg2rad(velocities[axIndex]));
240 }
241
242 kinDynComp.setRobotState(
243 I_T_base,
244 s,
245 baseVelocity,
246 ds,
247 gravity);
248
249 iDynTree::Rotation expectedImuSignal = kinDynComp.getWorldTransform(frameName).getRotation();
250 iDynTree::Rotation imuSignal = (I_R_I_IMU[sensorIndex] * iDynTree::Rotation::RPY(iDynTree::deg2rad(rpyValues[sensorIndex][0]), iDynTree::deg2rad(rpyValues[sensorIndex][1]), iDynTree::deg2rad(rpyValues[sensorIndex][2])));
251 error = (expectedImuSignal * imuSignal.inverse()).log();
252
253 bufferManager.push_back(positions, "joints_state::positions");
254 bufferManager.push_back(velocities, "joints_state::velocities");
255 bufferManager.push_back({expectedImuSignal.asRPY()[0], expectedImuSignal.asRPY()[1], expectedImuSignal.asRPY()[2]}, "orientations::" + sensorName + "::expected");
256 bufferManager.push_back({imuSignal.asRPY()[0], imuSignal.asRPY()[1], imuSignal.asRPY()[2]}, "orientations::" + sensorName + "::measured");
257
258 double sumOfSquares = std::accumulate(error.begin(), error.end(), 0.0,
259 [](double accumulator, double element) {
260 return accumulator + element * element;
261 });
262
263 double mag = std::sqrt(sumOfSquares);
264 bufferManager.push_back(mag, "orientations::" + sensorName + "::error");
265
266 errorTot[sensorIndex].push_back(mag);
267 }
268
269 ipos->checkMotionDone(&done);
270 }
271
272 for(int sensorIndex = 0; sensorIndex < sensorsList.get(0).asList()->size(); sensorIndex++)
273 {
274 auto sensorName = sensorsList.get(0).asList()->get(sensorIndex).asString();
275 auto maxError = std::max_element(errorTot[sensorIndex].begin(), errorTot[sensorIndex].end());
276 ROBOTTESTINGFRAMEWORK_TEST_CHECK(*maxError < errorMax, Asserter::format("Testing sensor %s: the max rotation angle error is %f rad!", sensorName.c_str(), *maxError));
277 }
278
279 return true;
280}
281
282bool Imu::setupRobometry()
283{
284 robometry::BufferConfig bufferConfig;
285 bufferConfig.auto_save = true;
286 bufferConfig.yarp_robot_name = std::getenv("YARP_ROBOT_NAME");
287 bufferConfig.filename = "test_imu";
288 bufferConfig.file_indexing = "%Y_%m_%d_%H_%M_%S";
289 bufferConfig.n_samples = 100000;
290
291 bufferManager.addChannel({"joints_state::positions", {axesVec.size(), 1}, axesVec});
292 bufferManager.addChannel({"joints_state::velocities", {axesVec.size(), 1}, axesVec});
293
294 for(auto sensorIndex = 0; sensorIndex < sensorsList.get(0).asList()->size(); sensorIndex++)
295 {
296 bufferManager.addChannel({"orientations::" + sensorsList.get(0).asList()->get(sensorIndex).asString() + "::expected", {3, 1}, {"r", "p", "y"}});
297 bufferManager.addChannel({"orientations::" + sensorsList.get(0).asList()->get(sensorIndex).asString() + "::measured", {3, 1}, {"r", "p", "y"}});
298 bufferManager.addChannel({"orientations::" + sensorsList.get(0).asList()->get(sensorIndex).asString() + "::error", {1, 1}, {"error"}});
299 }
300
301 return bufferManager.configure(bufferConfig);
302}
303
304void Imu::setupBrokers()
305{
306 strCmd = "ctpService";
307 for(int i = 0; i < localBroker.size(); i++)
308 {
309 strParam = "--robot " + robotName + " --part " + partsList[i];
310 localBroker[i].init(strCmd.c_str(), strParam.c_str(), nullptr, nullptr, nullptr, nullptr);
311 localBroker[i].start();
312 strParam.clear();
313 }
314
315 for (auto part : partsList)
316 {
317 if(part.find("leg") != std::string::npos)
318 strParam = "";
319
320 else
321 strParam = "no_legs";
322 }
323
324 strCmd.clear();
325 strCmd = "move.sh";
326
327 scriptBroker.init(strCmd.c_str(), strParam.c_str(), nullptr, nullptr, nullptr, nullptr);
328 scriptBroker.start();
329}
The purpose of this test is to evaluate the accuracy of the IMU Euler angles measurements.
Definition imu.h:39