5 #include <robottestingframework/TestAssert.h>
6 #include <robottestingframework/dll/Plugin.h>
8 #include <yarp/os/Property.h>
9 #include <yarp/os/Stamp.h>
10 #include <yarp/os/ResourceFinder.h>
11 #include <yarp/os/LogStream.h>
13 #include <robometry/BufferConfig.h>
14 #include <robometry/BufferManager.h>
18 using namespace robottestingframework;
19 ROBOTTESTINGFRAMEWORK_PREPARE_PLUGIN(
Imu)
21 Imu::Imu() : TestCase(
"Imu") { }
25 bool Imu::setup(yarp::os::Property& property)
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.");
35 robotName =
property.find(
"robot").asString();
36 portName =
property.find(
"port").asString();
37 errorMax =
property.find(
"maxError").asFloat64();
38 modelName =
property.find(
"model").asString();
39 yarp::os::ResourceFinder &rf = yarp::os::ResourceFinder::getResourceFinderSingleton();
40 std::string modelAbsolutePath = rf.findFileByName(modelName);
42 ROBOTTESTINGFRAMEWORK_TEST_REPORT(
"Running IMU test on "+robotName+
"...");
44 yarp::os::Property controlBoardOptions;
45 controlBoardOptions.put(
"device",
"remotecontrolboardremapper");
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++)
53 partsList.push_back(inputControlBoards->get(ctrlBoard).asString());
54 remoteControlBoardsList.addString(
"/"+robotName+
"/"+inputControlBoards->get(ctrlBoard).asString());
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++)
63 axesList.addString(inputJoints->get(ctrlJoint).asString());
66 controlBoardOptions.put(
"remoteControlBoards", remoteControlBoards.get(0));
67 controlBoardOptions.put(
"localPortPrefix",
"/test");
68 controlBoardOptions.put(
"axesNames", axesNames.get(0));
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");
76 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(ienc->getAxes(&axes),
"Cannot get number of controlled axes");
79 for (
int axIndex = 0; axIndex < axes; axIndex++)
81 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(iaxes->getAxisName(axIndex, axisName),
"Cannot get the name of controlled axes");
82 axesVec.push_back(axisName);
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());
88 yarp::os::Property MASclientOptions;
89 MASclientOptions.put(
"device",
"multipleanalogsensorsclient");
90 MASclientOptions.put(
"remote", portName);
91 MASclientOptions.put(
"local",
"/imuTest"+portName);
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");
96 yarp::os::Bottle & sensorsNamesList = sensorsList.addList();
97 yarp::os::Bottle *inputSensorsList;
98 inputSensorsList =
property.find(
"sensorsList").asList();
100 if(inputSensorsList->size() == 0 || inputSensorsList->get(0).asString() ==
"all")
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++)
108 ior->getOrientationSensorName(sensorIndex, sensorName);
109 sensorsNamesList.addString(sensorName);
115 ROBOTTESTINGFRAMEWORK_TEST_REPORT(
"Testing the list of provided IMUs...");
116 for(
int sensorIndex = 0; sensorIndex < inputSensorsList->size(); sensorIndex++)
118 sensorsNamesList.addString(inputSensorsList->get(sensorIndex).asString());
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");
128 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(MASremapperDriver.view(imultiwrap),
"Unable to open multiple wrapper interface");
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");
135 iDynTree::Vector3 baseLinkOrientationRad;
136 baseLinkOrientationRad.zero();
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();
142 unsigned int dofs = kinDynComp.model().getNrOfDOFs();
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");
151 for(
auto axIndex = 0; axIndex < axes; axIndex++)
153 s.setVal(axIndex, iDynTree::deg2rad(positions[axIndex]));
154 ds.setVal(axIndex, iDynTree::deg2rad(velocities[axIndex]));
160 kinDynComp.setRobotState(
169 localBroker.resize(remoteControlBoards.get(0).asList()->size());
176 outputPort.interrupt();
179 controlBoardDriver.close();
180 MASclientDriver.close();
181 MASremapperDriver.close();
183 for(
int i = 0; i < localBroker.size(); i++)
185 localBroker[i].stop();
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());
197 for (
int sensorIndex = 0; sensorIndex < sensorsList.get(0).asList()->size(); sensorIndex++)
199 std::string sensorName{
""};
200 std::string frameName{
""};
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()));
213 bool Imu::startMove()
216 iDynTree::GeomVector3 error;
217 std::vector<std::vector<double>> errorTot(sensorsList.get(0).asList()->size());
219 yarp::os::Time::delay(.1);
223 for (
int sensorIndex = 0; sensorIndex < sensorsList.get(0).asList()->size(); sensorIndex++)
225 std::string frameName{
""};
226 std::string sensorName{
""};
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.");
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");
236 for (
auto axIndex = 0; axIndex < axes; axIndex++)
238 s.setVal(axIndex, iDynTree::deg2rad(positions[axIndex]));
239 ds.setVal(axIndex, iDynTree::deg2rad(velocities[axIndex]));
242 kinDynComp.setRobotState(
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();
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");
258 double sumOfSquares = std::accumulate(error.begin(), error.end(), 0.0,
259 [](
double accumulator,
double element) {
260 return accumulator + element * element;
263 double mag = std::sqrt(sumOfSquares);
264 bufferManager.push_back(mag,
"orientations::" + sensorName +
"::error");
266 errorTot[sensorIndex].push_back(mag);
269 ipos->checkMotionDone(&done);
272 for(
int sensorIndex = 0; sensorIndex < sensorsList.get(0).asList()->size(); sensorIndex++)
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));
282 bool Imu::setupRobometry()
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;
291 bufferManager.addChannel({
"joints_state::positions", {axesVec.size(), 1}, axesVec});
292 bufferManager.addChannel({
"joints_state::velocities", {axesVec.size(), 1}, axesVec});
294 for(
auto sensorIndex = 0; sensorIndex < sensorsList.get(0).asList()->size(); sensorIndex++)
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"}});
301 return bufferManager.configure(bufferConfig);
304 void Imu::setupBrokers()
306 strCmd =
"ctpService";
307 for(
int i = 0; i < localBroker.size(); i++)
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();
315 for (
auto part : partsList)
317 if(part.find(
"leg") != std::string::npos)
321 strParam =
"no_legs";
327 scriptBroker.init(strCmd.c_str(), strParam.c_str(),
nullptr,
nullptr,
nullptr,
nullptr);
328 scriptBroker.start();
The purpose of this test is to evaluate the accuracy of the IMU Euler angles measurements.