22#include <unordered_map>
31#include <yarp/os/LogStream.h>
32#include <yarp/os/Property.h>
33#include <yarp/os/Log.h>
34#include <yarp/os/Searchable.h>
42 YARP_LOG_COMPONENT(FineCalibrationCheckerCOMPONENT,
"yarp.device.FineCalibrationChecker")
46 :
yarp::os::Thread(), _deviceName(
"fineCalibrationChecker"), _portPrefix(
"/fineCalibrationChecker"),
47 _robotName(
"icub"), _remoteRawValuesPort(
"/icub/rawvalues"), _axesNamesList(
yarp::os::Bottle()),
48 _goldPositionsList(
yarp::os::Bottle()), _encoderResolutionsList(
yarp::os::Bottle()), _calibrationDeltasList(
yarp::os::Bottle()), _deviceStatus(
deviceStatus::NONE)
51 _remappedControlBoardDevice = std::make_unique<yarp::dev::PolyDriver>();
52 _remappedRawValuesPublisherDevice = std::make_unique<yarp::dev::PolyDriver>();
58 yarp::os::Property property;
59 property.fromString(config.toString().c_str());
60 if (property.isNull())
62 yCError(FineCalibrationCheckerCOMPONENT) <<
"Failed to read configuration file. Stopping device...";
68 if (property.check(
"devicename")) { _deviceName =
property.find(
"devicename").asString(); }
69 if (property.check(
"robotname")) { _robotName =
property.find(
"robotname").asString(); }
70 if (property.check(
"remoteRawValuesPort")) { _remoteRawValuesPort =
property.find(
"remoteRawValuesPort").asString(); }
71 if (property.check(
"axesNamesList"))
73 yarp::os::Bottle* _jointsList =
property.find(
"axesNamesList").asList();
74 yarp::os::Bottle &axesNames = _axesNamesList.addList();
76 for (
size_t i = 0; i < _jointsList->size(); i++)
78 axesNames.addString(_jointsList->get(i).asString());
81 if(property.check(
"goldPositions"))
83 yarp::os::Bottle* _goldPositions =
property.find(
"goldPositions").asList();
84 yarp::os::Bottle &goldPositions = _goldPositionsList.addList();
86 for (
size_t i = 0; i < _goldPositions->size(); i++)
88 goldPositions.addInt32(_goldPositions->get(i).asInt32());
91 if(property.check(
"calibrationDeltas"))
93 yarp::os::Bottle* _calibrationDeltas =
property.find(
"calibrationDeltas").asList();
94 yarp::os::Bottle &calibrationDeltas = _calibrationDeltasList.addList();
96 for (
size_t i = 0; i < _calibrationDeltas->size(); i++)
98 calibrationDeltas.addFloat64(_calibrationDeltas->get(i).asFloat64());
101 if(property.check(
"encoderResolutions"))
103 yarp::os::Bottle* _encoderResolutions =
property.find(
"encoderResolutions").asList();
104 yarp::os::Bottle &encoderResolutions = _encoderResolutionsList.addList();
106 for (
size_t i = 0; i < _encoderResolutions->size(); i++)
108 encoderResolutions.addInt32(_encoderResolutions->get(i).asInt32());
111 if (property.check(
"axesSigns"))
113 yarp::os::Bottle* _axesSigns =
property.find(
"axesSigns").asList();
114 yarp::os::Bottle &axesSigns = _axesSignsList.addList();
116 for (
size_t i = 0; i < _axesSigns->size(); i++)
118 axesSigns.addInt32(_axesSigns->get(i).asInt32());
124 yarp::os::Bottle* axes = _axesNamesList.get(0).asList();
125 yarp::os::Bottle* goldpos = _goldPositionsList.get(0).asList();
126 yarp::os::Bottle* encres = _encoderResolutionsList.get(0).asList();
127 yarp::os::Bottle* caldeltas = _calibrationDeltasList.get(0).asList();
128 yarp::os::Bottle* signs = _axesSignsList.get(0).asList();
131 if (axes->size() != goldpos->size() ||
132 axes->size() != encres->size() ||
133 axes->size() != caldeltas->size() ||
134 axes->size() != signs->size())
136 yCError(FineCalibrationCheckerCOMPONENT) <<
"Axes names, gold positions and encoder resolutions lists must have the same size. Stopping device...";
141 yCDebug(FineCalibrationCheckerCOMPONENT) <<
"Axes names list:" << _axesNamesList.toString();
142 yCDebug(FineCalibrationCheckerCOMPONENT) <<
"Gold positions list:" << goldpos->toString();
143 yCDebug(FineCalibrationCheckerCOMPONENT) <<
"Encoder resolutions list:" << encres->toString();
144 yCDebug(FineCalibrationCheckerCOMPONENT) <<
"Calibration deltas list:" << caldeltas->toString();
145 yCDebug(FineCalibrationCheckerCOMPONENT) <<
"Axes signs list:" << signs->toString();
149 for (
size_t i = 0; i < axes->size(); i++)
151 yCDebug(FineCalibrationCheckerCOMPONENT) <<
"Adding to MAP key:" << axes->get(i).asString()
152 <<
"GP:" << goldpos->get(i).asInt32() <<
"ER:" << encres->get(i).asInt32() <<
"CD:" << caldeltas->get(i).asFloat64();
153 axesRawGoldenPositionsResMap[axes->get(i).asString()] = {goldpos->get(i).asInt32(), encres->get(i).asInt32()};
154 yCDebug(FineCalibrationCheckerCOMPONENT) <<
"Array added to MAP:" << axesRawGoldenPositionsResMap.at(axes->get(i).asString())[0]
155 <<
"and" << axesRawGoldenPositionsResMap.at(axes->get(i).asString())[1];
159 _withGui =
property.check(
"withGui", yarp::os::Value(
false)).asBool();
161 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Initialized device driver";
166 yarp::os::Property deviceProperties;
168 deviceProperties.put(
"device",
"controlboardremapper");
169 deviceProperties.put(
"axesNames", _axesNamesList.get(0));
171 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Configuring device driver with properties:" << deviceProperties.toString();
173 _remappedControlBoardDevice->open(deviceProperties);
175 if (!_remappedControlBoardDevice->isValid())
177 yCError(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Unable to open device driver. Aborting...";
182 if (_remoteRawValuesPort.empty())
184 yCError(FineCalibrationCheckerCOMPONENT) <<
"Remote raw values port is empty. Cannot open device driver. Stopping device...";
189 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Remote raw values port:" << _remoteRawValuesPort;
193 yarp::os::Property rawValuesDeviceProperties;
194 rawValuesDeviceProperties.put(
"device",
"rawvaluespublisherremapper");
195 rawValuesDeviceProperties.put(
"axesNames", _axesNamesList.get(0));
196 rawValuesDeviceProperties.put(
"remote", _remoteRawValuesPort);
197 rawValuesDeviceProperties.put(
"local",
"/" + _deviceName +
"/rawValuesPublisherRemapper");
199 _remappedRawValuesPublisherDevice->open(rawValuesDeviceProperties);
201 if (!_remappedRawValuesPublisherDevice->isValid())
203 yCError(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Unable to open raw values device driver. Aborting...";
207 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Opened all devices successfully";
216 if(_remappedControlBoardDevice->close())
218 yCDebug(FineCalibrationCheckerCOMPONENT) <<
"Closed device" << _deviceName;
222 yCError(FineCalibrationCheckerCOMPONENT) <<
"Unable to close device" << _deviceName;
225 if (_remappedRawValuesPublisherDevice->close())
227 yCDebug(FineCalibrationCheckerCOMPONENT) <<
"Closed raw values publisher device";
231 yCError(FineCalibrationCheckerCOMPONENT) <<
"Unable to close raw values publisher device";
233 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Closed all devices successfully";
241 if (!_remappedControlBoardDevice->view(remappedControlBoardInterfaces._imot) || remappedControlBoardInterfaces._imot ==
nullptr)
243 yCError(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Unable to open motor interface. Aborting...";
247 if (!_remappedControlBoardDevice->view(remappedControlBoardInterfaces._ienc) || remappedControlBoardInterfaces._ienc ==
nullptr)
249 yCError(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Unable to open encoders interface. Aborting...";
253 if (!_remappedControlBoardDevice->view(remappedControlBoardInterfaces._icontrolcalib) || remappedControlBoardInterfaces._icontrolcalib ==
nullptr)
255 yCError(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Unable to open control calibration interface. Aborting...";
259 if (!_remappedRawValuesPublisherDevice->view(remappedRawValuesPublisherInterfaces._iravap) || remappedRawValuesPublisherInterfaces._iravap ==
nullptr)
261 yCError(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Unable to open raw values publisher interface. Aborting...";
267 rawDataMetadata = {};
268 remappedRawValuesPublisherInterfaces._iravap->getMetadataMap(rawDataMetadata);
269 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Configured raw values with metadata";
272 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"\n"
273 <<
"\t Key: " << k <<
"\n"
274 <<
"\t axesName: " << m.axesNames <<
"\n"
275 <<
"\t rawValueNames: " << m.rawValueNames
279 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Opened remote calibrator and control calibration interfaces successfully";
289 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Thread started to run";
292 static auto lastTimerLog = std::chrono::steady_clock::now();
293 static auto shoutdownTimer = std::chrono::steady_clock::now();
295 while(!this->isStopping())
299 auto now = std::chrono::steady_clock::now();
300 if (std::chrono::duration_cast<std::chrono::milliseconds>(now - lastTimerLog).count() > 1000)
302 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Device configured, waiting for attachAll() to be called.";
305 yarp::os::Time::delay(0.1);
312 if(!_axesNamesList.isNull() && _axesNamesList.size() > 0 && _axesNamesList.get(0).isList())
314 numAxes = _axesNamesList.get(0).asList()->size();
317 bool calibDone =
true;
318 for (
size_t i = 0; i < numAxes; i++)
320 calibDone &= remappedControlBoardInterfaces._icontrolcalib->calibrationDone(i);
325 yCWarning(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Calib not complete for all axis. Waiting for calibration to finish...";
326 yarp::os::Time::delay(0.1);
331 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Calibration done successfully";
342 if(!remappedRawValuesPublisherInterfaces._iravap->getRawDataMap(rawDataValuesMap))
344 yCWarning(FineCalibrationCheckerCOMPONENT) <<
"embObjMotionControl::IRawValuesPublisher warning : raw_data_values map was not read correctly";
348 yCDebug(FineCalibrationCheckerCOMPONENT) <<
"Get raw values from encoders:";
349 for (
auto [key,value] : rawDataValuesMap)
351 yCDebug(FineCalibrationCheckerCOMPONENT) <<
"\t key:" << key <<
"value:" << value;
357 evaluateHardStopPositionDelta(_rawValuesTag,
"zeroPositionsDataDelta.csv");
363 auto now = std::chrono::steady_clock::now();
364 if (std::chrono::duration_cast<std::chrono::milliseconds>(now - shoutdownTimer).count() > 5000)
366 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Operation completed successfully. Waiting yarprobotinterface to stop the thread...";
372 yCError(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Device is in unknown state. Stopping thread...";
378 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Thread stopping";
392 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Attaching all devices";
396 if (!_remappedControlBoardDevice->view(remappedControlBoardInterfaces._imultwrap) || remappedControlBoardInterfaces._imultwrap ==
nullptr)
398 yCError(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Unable to open multiple wrapper interface. Aborting...";
402 if (!_remappedControlBoardDevice->isValid())
404 yCError(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Device is not valid. Cannot attach.";
408 if(!_remappedRawValuesPublisherDevice->view(remappedRawValuesPublisherInterfaces._imultwrap) || remappedRawValuesPublisherInterfaces._imultwrap ==
nullptr)
410 yCError(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Unable to open multiple wrapper interface for raw values publisher. Aborting...";
414 if(!_remappedRawValuesPublisherDevice->isValid())
416 yCError(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Raw values publisher device is not valid. Cannot attach.";
421 if (!this->attachToAllControlBoards(device2attach))
423 yCError(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Failed to attach all devices.";
427 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Successfully attached all devices. Starting the thread...";
431 yCError(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Failed to start the thread.";
434 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Thread started successfully";
443 if (this->isRunning())
445 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Stopping the thread before detaching all devices";
448 if(!remappedControlBoardInterfaces._imultwrap->detachAll() || !remappedRawValuesPublisherInterfaces._imultwrap->detachAll())
450 yCError(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Failed to detach all devices";
455 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Detached all devices successfully";
468bool FineCalibrationChecker::attachToAllControlBoards(
const yarp::dev::PolyDriverList& polyList)
471 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Attaching all control boards";
472 if (!_remappedControlBoardDevice->isValid())
474 yCError(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Device is not valid. Cannot attach.";
479 yarp::dev::PolyDriverList controlBoardsList;
480 for (
size_t i = 0; i < polyList.size(); i++)
482 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Attaching control board" << polyList[i]->key;
483 controlBoardsList.push(
const_cast<yarp::dev::PolyDriverDescriptor&
>(*polyList[i]));
486 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Control boards list size:" << controlBoardsList.size();
489 if(!remappedControlBoardInterfaces._imultwrap->attachAll(controlBoardsList))
491 yCError(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Failed to attach all control boards";
495 if(!remappedRawValuesPublisherInterfaces._imultwrap->attachAll(controlBoardsList))
497 yCError(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Failed to attach all control boards to raw values publisher";
501 yCDebug(FineCalibrationCheckerCOMPONENT) << _deviceName <<
"Successfully attached all control boards";
506void FineCalibrationChecker::evaluateHardStopPositionDelta(
const std::string& key,
const std::string& outputFileName)
510 const int32_t RAW_VALUES_STRIDE = 3;
511 const int64_t ICUB_DEGREES_RANGE = (65535);
513 std::filesystem::path outputPath(outputFileName);
514 int64_t goldPosition = 0;
515 int64_t rawPosition = 0;
516 int64_t resolution = 0;
517 int64_t rescaledPos = 0;
519 std::vector<ItemData> sampleItems = {};
521 yarp::os::Bottle* caldeltas = _calibrationDeltasList.get(0).asList();
522 yarp::os::Bottle* axesSigns = _axesSignsList.get(0).asList();
524 std::ofstream
outFile(outputPath);
527 yCError(FineCalibrationCheckerCOMPONENT) <<
"Unable to open output file:" << outputPath.string();
531 outFile <<
"AxisName,GoldPosition,RescaledPosition,RawPosition,Delta\n";
532 yCDebug(FineCalibrationCheckerCOMPONENT) <<
"Evaluating deltas.....";
533 if(
auto it = rawDataValuesMap.find(key); it != rawDataValuesMap.end())
535 std::vector<std::string> axesNames = {};
536 std::vector<std::int32_t> rawData = {};
539 axesNames = rawDataMetadata.
metadataMap.at(it->first).axesNames;
540 rawData = it->second;
542 std::vector<double> homePositions(axesNames.size(), 0);
543 std::vector<double> calibrationDelta(axesNames.size(), 0.0);
546 for (
size_t i = 0; i < axesNames.size(); ++i)
552 yCDebug(FineCalibrationCheckerCOMPONENT) <<
"Evaluating axis:" << axesNames[i];
553 if (
auto it = axesRawGoldenPositionsResMap.find(axesNames[i]); it != axesRawGoldenPositionsResMap.end())
555 calibrationDelta[i] = caldeltas->get(i).asFloat64();
557 remappedControlBoardInterfaces._ienc->getEncoder(i, &pos);
558 homePositions[i] = (axesSigns->get(i).asInt32() > 0) ? pos : -pos;
559 goldPosition = it->second[0];
560 resolution = it->second[1];
561 rawPosition = rawData[RAW_VALUES_STRIDE*i];
565 rescaledPos = rawPosition * ICUB_DEGREES_RANGE / resolution;
577 delta =
static_cast<double>((goldPosition - rescaledPos) / (ICUB_DEGREES_RANGE/360.0)) + homePositions[i];
578 calibrationDelta[i] = (axesSigns->get(i).asInt32() > 0) ? calibrationDelta[i] : -calibrationDelta[i];
579 delta += calibrationDelta[i];
580 yCDebug(FineCalibrationCheckerCOMPONENT) <<
"GP:" << goldPosition <<
"HP:" << homePositions[i] <<
"RSP:" << rescaledPos <<
"RWP:" << rawPosition <<
"DD:" << delta;
584 yCWarning(FineCalibrationCheckerCOMPONENT) <<
"This device axes has not ben requested to be checked. Continue...";
589 outFile << axesNames[i] <<
"," << goldPosition <<
"," << rescaledPos <<
"," << rawPosition <<
"," << delta <<
"\n";
590 sampleItems.push_back({axesNames[i], goldPosition, rescaledPos, rawPosition, delta});
595 yCError(FineCalibrationCheckerCOMPONENT) <<
"Key" << key <<
"not found in rawDataValuesMap";
596 outFile <<
"Key not found in rawDataValuesMap\n";
600 yCDebug(FineCalibrationCheckerCOMPONENT) <<
"Output CSV written to:" << outputPath.string();
603 generateOutputImage(1800, 400, sampleItems);
607void FineCalibrationChecker::generateOutputImage(
int frameWidth,
int frameHeight,
const std::vector<ItemData>& items)
609 cv::Mat image = cv::Mat::zeros(frameHeight, frameWidth, CV_8UC3);
610 image.setTo(cv::Scalar(255, 255, 255));
615 int colWidthAvg = (frameWidth - 2 * padding) / 5;
618 std::vector<std::string> headers = {
"AxisName",
"GoldPosition[iCubDegrees]",
"RescaledPosition[iCubDegrees]",
"RawPosition[Ticks]",
"Delta[Degrees]"};
619 std::vector<int> colWidths(5, colWidthAvg);
620 std::vector<int> colX(headers.size());
621 colX[0] = padding + 5;
622 for (
size_t i = 1; i < headers.size(); ++i) {
623 colX[i] = colX[i-1] + colWidths[i-1];
627 int headerY = padding;
628 cv::Scalar headerBgColor(220, 220, 220);
631 cv::Point(padding, headerY),
632 cv::Point(frameWidth - padding, headerY + lineHeight - 5),
633 headerBgColor, cv::FILLED);
636 cv::Point(padding, headerY),
637 cv::Point(frameWidth - padding, headerY + lineHeight - 5),
638 cv::Scalar(100, 100, 100), 1);
639 for (
size_t c = 0; c < headers.size(); ++c) {
640 cv::putText(image, headers[c], cv::Point(colX[c], headerY + textOffset),
641 cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(0, 0, 0), 2);
645 for (
size_t i = 0; i < items.size(); ++i)
647 int y = padding + (i + 1) * lineHeight;
649 cv::Scalar bgColor = getColorForDelta(items[i].val4, 1.0, 3.0);
651 cv::Point(padding,
y),
652 cv::Point(frameWidth - padding,
y + lineHeight - 5),
653 bgColor, cv::FILLED);
657 cv::Point(padding,
y),
658 cv::Point(frameWidth - padding,
y + lineHeight - 5),
659 cv::Scalar(200, 200, 200), 1);
662 std::vector<std::string> rowVals = {
664 std::to_string(items[i].val1),
665 std::to_string(items[i].val2),
666 std::to_string(items[i].val3),
667 std::to_string(items[i].val4)
669 for (
size_t c = 0; c < rowVals.size(); ++c) {
670 cv::putText(image, rowVals[c], cv::Point(colX[c],
y + textOffset),
671 cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(0, 0, 0), 2);
675 cv::imwrite(
"output_frame.png", image);
677 cv::imshow(
"Output Frame", image);
679 cv::destroyAllWindows();
682cv::Scalar FineCalibrationChecker::getColorForDelta(
double delta,
double threshold_1,
double threshold_2)
684 if (std::abs(delta) > threshold_2)
return cv::Scalar(0, 0, 255);
685 else if (std::abs(delta) > threshold_1)
return cv::Scalar(0, 165, 255);
686 else return cv::Scalar(0, 255, 0);
bool threadInit() override
bool open(yarp::os::Searchable &config) override
bool detachAll() override
bool isCalibrationSuccessful() const
bool attachAll(const yarp::dev::PolyDriverList &device2attach) override
Copyright (C) 2008 RobotCub Consortium.