LaserScannerSelfLocalisationWidgetController.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * \package RobotComponents::gui-plugins::LaserScannerSelfLocalisationWidgetController
17 * \author Fabian Paus ( fabian dot paus at kit dot edu )
18 * \date 2017
19 * \copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
24
25#include <cfloat>
26#include <string>
27
28#include <QCheckBox>
29#include <QDoubleSpinBox>
30#include <QSpinBox>
31#include <QTimer>
32
33using namespace armarx;
34
36 setPoseState(SetPoseState::None)
37{
38 widget.setupUi(getWidget());
39 painterWidget = new QPainterWidget();
40 painterWidget->setupUi(widget.frame);
41 painterWidget->setPaintCallback([this](QPainter& painter)
42 { onPaintCanvas(painter, painterWidget->size()); });
43
44 paintTimerId = startTimer(16);
45}
46
50
51void
55
56void
60
61void
66
67void
69{
70 localisation = getProxy<LaserScannerSelfLocalisationInterfacePrx>(localisationName);
71 localisationManager = getProxy<armarx::ArmarXManagerInterfacePrx>(localisationName + "Manager");
72 localisationAdmin = localisationManager->getPropertiesAdmin();
73 readProperties();
74
75 map = localisation->getMap();
76
77 connect(
78 widget.smoothingFrameSize, SIGNAL(valueChanged(int)), this, SLOT(onSpinBoxChanged(int)));
79 connect(widget.smoothingMergeDistance,
80 SIGNAL(valueChanged(int)),
81 this,
82 SLOT(onSpinBoxChanged(int)));
83 connect(widget.matchingMaxDistance,
84 SIGNAL(valueChanged(double)),
85 this,
86 SLOT(onSpinBoxChanged(double)));
87 connect(widget.matchingMinPoints,
88 SIGNAL(valueChanged(double)),
89 this,
90 SLOT(onSpinBoxChanged(double)));
91 connect(widget.matchingCorrectionFactor,
92 SIGNAL(valueChanged(double)),
93 this,
94 SLOT(onSpinBoxChanged(double)));
95 connect(
96 widget.edgeMaxDistance, SIGNAL(valueChanged(double)), this, SLOT(onSpinBoxChanged(double)));
97 connect(widget.edgeMaxDeltaAngle,
98 SIGNAL(valueChanged(double)),
99 this,
100 SLOT(onSpinBoxChanged(double)));
101 connect(widget.edgePointAddingThreshold,
102 SIGNAL(valueChanged(double)),
103 this,
104 SLOT(onSpinBoxChanged(double)));
105 connect(widget.edgeEpsilon, SIGNAL(valueChanged(int)), this, SLOT(onSpinBoxChanged(int)));
106 connect(widget.edgeMinPoints, SIGNAL(valueChanged(int)), this, SLOT(onSpinBoxChanged(int)));
107 connect(widget.useMapCorrection, SIGNAL(stateChanged(int)), this, SLOT(onSpinBoxChanged(int)));
108 connect(widget.useOdometry, SIGNAL(stateChanged(int)), this, SLOT(onSpinBoxChanged(int)));
109 connect(widget.reportPoints, SIGNAL(stateChanged(int)), this, SLOT(onSpinBoxChanged(int)));
110 connect(widget.reportEdges, SIGNAL(stateChanged(int)), this, SLOT(onSpinBoxChanged(int)));
111 connect(widget.setPoseButton, SIGNAL(clicked()), this, SLOT(onSetPoseClick()));
112 connect(
113 widget.sensorStdDev, SIGNAL(valueChanged(double)), this, SLOT(onSpinBoxChanged(double)));
114 connect(
115 widget.velSensorStdDev, SIGNAL(valueChanged(double)), this, SLOT(onSpinBoxChanged(double)));
116
117 connect(painterWidget, SIGNAL(mousePressed(QPoint)), this, SLOT(onCanvasClick(QPoint)));
118
119 connect(this, SIGNAL(newDataReported()), this, SLOT(onNewDataReported()), Qt::QueuedConnection);
120
121 usingTopic(localisation->getReportTopicName());
122}
123
124QPointer<QDialog>
126{
127 if (!dialog)
128 {
129 dialog = new SimpleConfigDialog(parent);
130 dialog->addProxyFinder<LaserScannerSelfLocalisationInterfacePrx>(
131 {"LaserScannerSelfLocalisation", "", "*"});
132 }
133 return qobject_cast<SimpleConfigDialog*>(dialog);
134}
135
136void
138{
139 if (dialog)
140 {
141 localisationName = dialog->getProxyName("LaserScannerSelfLocalisation");
142 }
143}
144
145void
147{
148 float maxX = 0.0f;
149 float minX = FLT_MAX;
150 float maxY = 0.0f;
151 float minY = FLT_MAX;
152 for (LineSegment2D& s : map)
153 {
154 maxX = std::max(maxX, s.start.e0);
155 maxY = std::max(maxY, s.start.e1);
156 minX = std::min(minX, s.start.e0);
157 minY = std::min(minY, s.start.e1);
158 maxX = std::max(maxX, s.end.e0);
159 maxY = std::max(maxY, s.end.e1);
160 minX = std::min(minX, s.end.e0);
161 minY = std::min(minY, s.end.e1);
162 }
163
164 float normX = 1.0f / (maxX - minX);
165 float normY = 1.0f / (maxY - minY);
166 float scaleFactorX = size.width() * normX;
167 float scaleFactorY = size.height() * normY;
168 float scaleFactor = 0.95f * std::min(scaleFactorX, scaleFactorY);
169 float invScaleFactor = 1.0f / scaleFactor;
170
171 auto toCanvas = [=](armarx::Vector2f p)
172 {
173 float xCoord = scaleFactor * (p.e0 - minX);
174 float yCoord = scaleFactor * (p.e1 - minY);
175 return QPoint((int)xCoord, (int)(size.height() - 1 - yCoord));
176 };
177
178 auto toCanvasE = [=](Eigen::Vector2f p)
179 {
180 float xCoord = scaleFactor * (p.x() - minX);
181 float yCoord = scaleFactor * (p.y() - minY);
182 return QPoint((int)xCoord, (int)(size.height() - 1 - yCoord));
183 };
184
185 auto fromCanvas = [=](QPoint p)
186 {
187 float globalX = (invScaleFactor * p.x()) + minX;
188 float globalY = invScaleFactor * (size.height() - 1 - p.y()) + minY;
189 return Eigen::Vector2f(globalX, globalY);
190 };
191
192 if (setPoseState == SetPoseState::Wait)
193 {
194 setPoseState = SetPoseState::Orientation;
195 }
196 else if (setPoseState == SetPoseState::Finished)
197 {
198 // Transform click points into global coordinates
199 Eigen::Vector2f globalPos = fromCanvas(setPosition);
200 Eigen::Vector2f globalOr = fromCanvas(setOrientation);
201 Eigen::Vector2f fromPosToOr = globalOr - globalPos;
202 float theta = std::atan2(fromPosToOr.y(), fromPosToOr.x()) - M_PI_2;
203
204 try
205 {
206 ARMARX_INFO << "Set global pose to (" << globalPos.x() << ", " << globalPos.y() << ", "
207 << theta << ")";
208 localisation->setAbsolutePose(globalPos.x(), globalPos.y(), theta);
209 }
210 catch (std::exception const& ex)
211 {
212 ARMARX_WARNING << "Could not set absolute pose: " << ex.what();
213 }
214
215 setPoseState = SetPoseState::None;
216 }
217
218 QBrush whiteBrush(QColor(240, 240, 240));
219 QPen blackPen(QColor(0, 0, 0));
220 blackPen.setWidth(1);
221 painter.setBrush(whiteBrush);
222 painter.setPen(blackPen);
223
224 lines.clear();
225 lines.reserve(map.size());
226 for (LineSegment2D const& s : map)
227 {
228 lines.emplace_back(toCanvas(s.start), toCanvas(s.end));
229 }
230 painter.drawLines(lines.data(), lines.size());
231
232 if (setPoseState == SetPoseState::Orientation)
233 {
234 QPoint setOrientation = painterWidget->mapFromGlobal(QCursor::pos());
235 Eigen::Vector2f globalPos = fromCanvas(setPosition);
236 Eigen::Vector2f globalOr = fromCanvas(setOrientation);
237
238 painter.setBrush(QColor(255, 0, 0));
239 painter.drawEllipse(toCanvasE(globalPos), 10, 10);
240 painter.drawLine(toCanvasE(globalPos), toCanvasE(globalOr));
241 }
242
243 // Draw the robot pose
244 Eigen::Vector2f robotPos(poseX, poseY);
245 float robotTheta = poseTheta;
246
247 armarx::Vector2f estRobotPos{robotPos.x(), robotPos.y()};
248 Eigen::Vector2f estRobotEnd =
249 robotPos +
250 500.0f * Eigen::Vector2f(Eigen::Rotation2Df(robotTheta) * Eigen::Vector2f::UnitY());
251 armarx::Vector2f estRobotEnda{estRobotEnd.x(), estRobotEnd.y()};
252 painter.setBrush(QColor(0, 255, 255));
253 painter.drawEllipse(toCanvas(estRobotPos), 10, 10);
254 painter.drawLine(toCanvas(estRobotPos), toCanvas(estRobotEnda));
255 painter.setBrush(QColor(255, 255, 0, 25));
256 float posUncertaintyAverage = (poseUncertaintyX + poseUncertaintyY) * 0.5;
257 painter.drawEllipse(toCanvas(estRobotPos),
258 (int)(posUncertaintyAverage * scaleFactor),
259 (int)(posUncertaintyAverage * scaleFactor));
260
261 qpoints.clear();
262 qedges.clear();
263 {
264
265 if (widget.reportPoints->checkState() == Qt::Checked)
266 {
267 ScopedLock lock(pointsMutex);
268 qpoints.reserve(points.size());
269 for (armarx::Vector2f const& point : points)
270 {
271 qpoints.push_back(toCanvas(point));
272 }
273 }
274
275 if (widget.reportEdges->checkState() == Qt::Checked)
276 {
277 ScopedLock lock(pointsMutex);
278 qedges.reserve(edges.size());
279 for (armarx::LineSegment2D const& s : edges)
280 {
281 QPoint start = toCanvas(s.start);
282 QPoint end = toCanvas(s.end);
283 qedges.push_back(QLine(start, end));
284 }
285 }
286 }
287
288 // Draw the laser scanner points
289 float lineWidth = 1.0f;
290 QPen pointPen(QColor(0, 0, 255), lineWidth);
291 painter.setPen(pointPen);
292
293 painter.drawPoints(qpoints.data(), qpoints.size());
294
295 // Draw extracted edges
296 QPen edgePen(QColor(0, 255, 0), lineWidth);
297 painter.setPen(edgePen);
298 painter.drawLines(qedges.data(), qedges.size());
299}
300
301void
303 Ice::Float y,
304 Ice::Float theta,
305 const Ice::Current&)
306{
307 poseX = x;
308 poseY = y;
309 poseTheta = theta;
310
311 emit newDataReported();
312}
313
314void
316 Ice::Float y,
317 Ice::Float theta,
318 const Ice::Current&)
319{
320 poseUncertaintyX = x;
321 poseUncertaintyY = y;
322 poseUncertaintyTheta = theta;
323
324 emit newDataReported();
325}
326
327void
329 const Vector2fSeq& globalPoints,
330 const Ice::Current&)
331{
332 {
333 ScopedLock guard(pointsMutex);
334 points = globalPoints;
335 }
336 emit newDataReported();
337}
338
339void
341 const LineSegment2DSeq& globalEdges,
342 const Ice::Current&)
343{
344 {
345 ScopedLock guard(pointsMutex);
346 edges = globalEdges;
347 }
348 emit newDataReported();
349}
350
351void
353{
354 updateProperties();
355}
356
357void
359{
360 updateProperties();
361}
362
363void
365{
366 QString poseString = QString("Pose: ") + QString::number(poseX) + ", " +
367 QString::number(poseY) + ", " + QString::number(poseTheta) +
368 "\nUncertainty: " + QString::number(poseUncertaintyX, 'g', 4) + ", " +
369 QString::number(poseUncertaintyY, 'g', 4) + ", " +
370 QString::number(poseUncertaintyTheta, 'g', 4);
371 widget.poseLabel->setText(poseString);
372}
373
374void
376{
377 setPoseState = SetPoseState::Position;
378 ARMARX_INFO << "Starting to set new position";
379}
380
381void
383{
384 if (setPoseState == SetPoseState::Position)
385 {
386 setPosition = setOrientation = p;
387 setPoseState = SetPoseState::Wait;
388 ARMARX_INFO << "Poistion set, now setting orientation";
389 }
390 else if (setPoseState == SetPoseState::Orientation)
391 {
392 setOrientation = p;
393 setPoseState = SetPoseState::Finished;
394 ARMARX_INFO << "Orientation set, waiting for finish";
395 }
396}
397
398static std::string
399floatToString(float v)
400{
401 std::ostringstream s;
402 s.imbue(std::locale::classic());
403 s << v;
404 return s.str();
405}
406
407static float
408stringToFloat(std::string const& string)
409{
410 std::istringstream s(string);
411 s.imbue(std::locale::classic());
412 float v = 0.0f;
413 s >> v;
414 return v;
415}
416
417const std::string PROPERTIES_PREFIX = "ArmarX.LaserScannerSelfLocalisation.";
418
419void
420LaserScannerSelfLocalisationWidgetController::updateProperties()
421{
422 if (!localisationAdmin)
423 {
424 ARMARX_WARNING << "Could not set properties because no admin proxy is set";
425 return;
426 }
427
428 ARMARX_INFO << "start updating LaserScannerSelfLocalisationWidget properties";
429 int smoothingFrameSize = widget.smoothingFrameSize->value();
430 int smoothingMergeDistance = widget.smoothingMergeDistance->value();
431 float matchingMaxDistance = widget.matchingMaxDistance->value();
432 float matchingMinPoints = widget.matchingMinPoints->value();
433 float matchingCorrectionFactor = widget.matchingCorrectionFactor->value();
434 float edgeMaxDistance = widget.edgeMaxDistance->value();
435 float edgeMaxDeltaAngle = widget.edgeMaxDeltaAngle->value();
436 float edgePointAddingThreshold = widget.edgePointAddingThreshold->value();
437 int edgeEpsilon = widget.edgeEpsilon->value();
438 int edgeMinPoints = widget.edgeMinPoints->value();
439 bool useMapCorrection = widget.useMapCorrection->checkState() == Qt::Checked;
440 bool useOdometry = widget.useOdometry->checkState() == Qt::Checked;
441 bool reportPoints = widget.reportPoints->checkState() == Qt::Checked;
442 bool reportEdges = widget.reportEdges->checkState() == Qt::Checked;
443 float sensorStdDev = widget.sensorStdDev->value();
444 float velSensorStdDev = widget.velSensorStdDev->value();
445
446 Ice::PropertyDict properties;
447 properties[PROPERTIES_PREFIX + "SmoothFrameSize"] = to_string(smoothingFrameSize);
448 properties[PROPERTIES_PREFIX + "SmoothMergeDistance"] = to_string(smoothingMergeDistance);
449 properties[PROPERTIES_PREFIX + "MatchingMaxDistance"] = floatToString(matchingMaxDistance);
450 properties[PROPERTIES_PREFIX + "MatchingMinPoints"] = floatToString(matchingMinPoints);
451 properties[PROPERTIES_PREFIX + "MatchingCorrectionFactor"] =
452 floatToString(matchingCorrectionFactor);
453 properties[PROPERTIES_PREFIX + "EdgeMaxDistance"] = floatToString(edgeMaxDistance);
454 properties[PROPERTIES_PREFIX + "EdgeMaxDeltaAngle"] =
455 floatToString(edgeMaxDeltaAngle * M_PI / 180.0);
456 properties[PROPERTIES_PREFIX + "EdgePointAddingThreshold"] =
457 floatToString(edgePointAddingThreshold);
458 properties[PROPERTIES_PREFIX + "EdgeEpsilon"] = to_string(edgeEpsilon);
459 properties[PROPERTIES_PREFIX + "EdgeMinPoints"] = to_string(edgeMinPoints);
460 properties[PROPERTIES_PREFIX + "UseMapCorrection"] = useMapCorrection ? "1" : "0";
461 properties[PROPERTIES_PREFIX + "UseOdometry"] = useOdometry ? "1" : "0";
462 properties[PROPERTIES_PREFIX + "ReportPoints"] = reportPoints ? "1" : "0";
463 properties[PROPERTIES_PREFIX + "ReportEdges"] = reportEdges ? "1" : "0";
464 properties[PROPERTIES_PREFIX + "SensorStdDev"] = floatToString(sensorStdDev);
465 properties[PROPERTIES_PREFIX + "VelSensorStdDev"] = floatToString(velSensorStdDev);
466
467 ARMARX_INFO << "setting properties on local admin";
468 localisationAdmin->setProperties(properties);
469 ARMARX_INFO << "done updating LaserScannerSelfLocalisationWidget properties";
470}
471
472void
473LaserScannerSelfLocalisationWidgetController::readProperties()
474{
475 ARMARX_IMPORTANT << "Trying to read properties";
476 ObjectPropertyInfos properties = localisationManager->getObjectPropertyInfos(localisationName);
477
478 int smoothingFrameSize = std::stoi(properties[PROPERTIES_PREFIX + "SmoothFrameSize"].value);
479 int smoothingMergeDistance =
480 std::stoi(properties[PROPERTIES_PREFIX + "SmoothMergeDistance"].value);
481 float matchingMaxDistance =
482 stringToFloat(properties[PROPERTIES_PREFIX + "MatchingMaxDistance"].value);
483 float matchingMinPoints =
484 stringToFloat(properties[PROPERTIES_PREFIX + "MatchingMinPoints"].value);
485 float matchingCorrectionFactor =
486 stringToFloat(properties[PROPERTIES_PREFIX + "MatchingCorrectionFactor"].value);
487 float edgeMaxDistance = stringToFloat(properties[PROPERTIES_PREFIX + "EdgeMaxDistance"].value);
488 float edgeMaxDeltaAngle =
489 180.0 / M_PI * stringToFloat(properties[PROPERTIES_PREFIX + "EdgeMaxDeltaAngle"].value);
490 float edgePointAddingThreshold =
491 stringToFloat(properties[PROPERTIES_PREFIX + "EdgePointAddingThreshold"].value);
492 int edgeEpsilon = std::stoi(properties[PROPERTIES_PREFIX + "EdgeEpsilon"].value);
493 int edgeMinPoints = std::stoi(properties[PROPERTIES_PREFIX + "EdgeMinPoints"].value);
494 bool useMapCorrection = properties[PROPERTIES_PREFIX + "UseMapCorrection"].value == "true";
495 bool useOdometry = properties[PROPERTIES_PREFIX + "UseOdometry"].value == "true";
496 bool reportPoints = properties[PROPERTIES_PREFIX + "ReportPoints"].value == "true";
497 bool reportEdges = properties[PROPERTIES_PREFIX + "ReportEdges"].value == "true";
498 float sensorStdDev = stringToFloat(properties[PROPERTIES_PREFIX + "SensorStdDev"].value);
499 float velSensorStdDev = stringToFloat(properties[PROPERTIES_PREFIX + "VelSensorStdDev"].value);
500
501 widget.smoothingFrameSize->setValue(smoothingFrameSize);
502 widget.smoothingMergeDistance->setValue(smoothingMergeDistance);
503 widget.matchingMaxDistance->setValue(matchingMaxDistance);
504 widget.matchingMinPoints->setValue(matchingMinPoints);
505 widget.matchingCorrectionFactor->setValue(matchingCorrectionFactor);
506 widget.edgeMaxDistance->setValue(edgeMaxDistance);
507 widget.edgeMaxDeltaAngle->setValue(edgeMaxDeltaAngle);
508 widget.edgePointAddingThreshold->setValue(edgePointAddingThreshold);
509 widget.edgeEpsilon->setValue(edgeEpsilon);
510 widget.edgeMinPoints->setValue(edgeMinPoints);
511 widget.useMapCorrection->setChecked(useMapCorrection);
512 widget.useOdometry->setChecked(useOdometry);
513 widget.reportPoints->setChecked(reportPoints);
514 widget.reportEdges->setChecked(reportEdges);
515 widget.sensorStdDev->setValue(sensorStdDev);
516 widget.velSensorStdDev->setValue(velSensorStdDev);
517}
518
519void
521{
522 painterWidget->update();
523}
const std::string PROPERTIES_PREFIX
#define M_PI
Definition MathTools.h:17
virtual QPointer< QWidget > getWidget()
getWidget returns a pointer to the a widget of this controller.
void reportExtractedEdges(const LineSegment2DSeq &, const Ice::Current &) override
void reportCorrectedPose(Ice::Float x, Ice::Float y, Ice::Float theta, const Ice::Current &) override
void reportPoseUncertainty(Ice::Float x, Ice::Float y, Ice::Float theta, const Ice::Current &) override
void configured() override
This function must be implemented by the user, if he supplies a config dialog.
QPointer< QDialog > getConfigDialog(QWidget *parent) override
getConfigDialog returns a pointer to the a configuration widget of this controller.
void reportLaserScanPoints(const Vector2fSeq &globalPoints, const Ice::Current &) override
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
void usingTopic(const std::string &name, bool orderedPublishing=false)
Registers a proxy for subscription after initialization.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
A config-dialog containing one (or multiple) proxy finders.
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
Mutex::scoped_lock ScopedLock
This file offers overloads of toIce() and fromIce() functions for STL container types.
const std::string & to_string(const std::string &s)