LaserScannerSelfLocalisation.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::ArmarXObjects::LaserScannerSelfLocalisation
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
29
32
33// These object factories are required, otherwise a runtime error will occur (static global object registration...)
34#include <cfloat>
35#include <chrono>
36#include <fstream>
37#include <iomanip>
38
39#include <Eigen/Geometry>
40
41#include <IceUtil/UUID.h>
42
43#include <SimoxUtility/json/json.hpp>
44
48
49
50using namespace armarx;
51
52using Line = Eigen::ParametrizedLine<float, 2>;
53
54namespace
55{
56 bool
57 anyIsNaN(Eigen::Vector2f const& v)
58 {
59 return std::isnan(v.x()) || std::isnan(v.y());
60 }
61
62 bool
63 anyIsNaN(Eigen::Vector3f const& v)
64 {
65 return std::isnan(v.x()) || std::isnan(v.y()) || std::isnan(v.z());
66 }
67
68 std::string
69 readWholeFile(std::string const& filename)
70 {
71 std::ifstream t(filename.c_str());
72 std::string str;
73
74 t.seekg(0, std::ios::end);
75 str.reserve(t.tellg());
76 t.seekg(0, std::ios::beg);
77
78 str.assign(std::istreambuf_iterator<char>(t), std::istreambuf_iterator<char>());
79 return str;
80 }
81
82 Eigen::Vector2f
83 parsePosition(Json::Value const& pos)
84 {
85 Eigen::Vector2f result = Eigen::Vector2f::Zero();
86 if (pos.isArray() && pos.size() == 2)
87 {
88 Json::Value const& x = pos[0];
89 Json::Value const& y = pos[1];
90 if (x.isNumeric() && y.isNumeric())
91 {
92 result[0] = (float)x.asDouble();
93 result[1] = (float)y.asDouble();
94 }
95 else
96 {
97 throw std::runtime_error("Map format error: Unexpected types in 2D vector");
98 }
99 }
100 else
101 {
102 throw std::runtime_error(
103 "Map format error: Unexpected number of elements in 2D vector");
104 }
105 return result;
106 }
107
108 std::vector<LineSegment2Df>
109 loadMapFromFile(std::string const& filename)
110 {
111 std::string fullFilename;
112 if (!ArmarXDataPath::getAbsolutePath(filename, fullFilename))
113 {
114 throw std::runtime_error("Could not find map file: " + filename);
115 }
116
117 std::string fileContent = readWholeFile(fullFilename);
118 JSONObject o;
119 o.fromString(fileContent);
120 Json::Value const& j = o.getJsonValue();
121 if (!j.isObject())
122 {
123 throw std::runtime_error("Map format error: expected a JSON object at root level");
124 }
125
126 float lengthUnit = 1.0f; // Default: mm
127 Json::Value const& unitValue = j["LengthUnit"];
128 if (unitValue.isString())
129 {
130 std::string unit = unitValue.asString();
131 if (unit == "m")
132 {
133 lengthUnit = 1000.0f;
134 }
135 else if (unit == "mm")
136 {
137 lengthUnit = 1.0f;
138 }
139 else
140 {
141 throw std::runtime_error("Unknown length unit: " + unit);
142 }
143 }
144
145 Json::Value const& map = j["Map"];
146 if (!map.isArray())
147 {
148 throw std::runtime_error("Map format error: Expected an array at property 'Map'");
149 }
150
151 std::vector<LineSegment2Df> result;
152
153 for (Json::ArrayIndex i = 0; i < map.size(); ++i)
154 {
155 Json::Value const& p = map[i];
156 Json::Value const& position = p["Position"];
157 Eigen::Vector2f pos = parsePosition(position);
158 Json::Value const& strip = p["RelativeLineStrip"];
159 if (!strip.isArray())
160 {
161 throw std::runtime_error(
162 "Map format error: Expected an error at property 'RelativeLineStrip'");
163 }
164
165 Eigen::Vector2f startPos = pos;
166 for (Json::ArrayIndex j = 0; j < strip.size(); ++j)
167 {
168 Eigen::Vector2f nextPos = startPos + parsePosition(strip[j]);
169
170 result.push_back(LineSegment2Df{lengthUnit * startPos, lengthUnit * nextPos});
171 startPos = nextPos;
172 }
173 }
174
175 return result;
176 }
177
178 float
179 lineSegmentToPointDistanceSquared(LineSegment2Df const& segment, Eigen::Vector2f point)
180 {
181 // Return minimum distance between line segment and a point
182 Eigen::Vector2f dir = segment.end - segment.start;
183 float l2 = dir.squaredNorm();
184 if (l2 <= 0.0)
185 {
186 return (point - segment.start).squaredNorm();
187 }
188
189 // Consider the line extending the segment, parameterized as v + t (w - v).
190 // We find projection of point p onto the line.
191 // It falls where t = [(p-v) . (w-v)] / |w-v|^2
192 // We clamp t from [0,1] to handle points outside the segment vw.
193 float t = std::max(0.0f, std::min(1.0f, (point - segment.start).dot(dir) / l2));
194 Eigen::Vector2f projection = segment.start + t * dir; // Projection falls on the segment
195 return (point - projection).squaredNorm();
196 }
197
198 float
199 distance(Line const& line, Eigen::Vector2f point)
200 {
201 Eigen::Vector2f normalizedDir = line.direction();
202 Eigen::Vector2f diff = point - line.origin();
203 float signedDistance = normalizedDir.x() * diff.y() - normalizedDir.y() * diff.x();
204 return std::fabs(signedDistance);
205 }
206
207 Line
208 leastSquareLine(Eigen::Vector2f* begin,
209 Eigen::Vector2f* end,
210 float* maxError = nullptr,
211 float* averageError = nullptr,
212 float* as = nullptr,
213 float* bs = nullptr,
214 float* det = nullptr)
215 {
216 float sumx = 0.0f;
217 float sumy = 0.0f;
218 float sumxx = 0.0f;
219 float sumyy = 0.0f;
220 float sumxy = 0.0f;
221 for (Eigen::Vector2f* point = begin; point != end; ++point)
222 {
223 float x = point->x();
224 float y = point->y();
225 sumx += x;
226 sumy += y;
227 sumxx += x * x;
228 sumyy += y * y;
229 sumxy += x * y;
230 }
231
232 float numx = (float)(end - begin);
233 float calc_det = (numx * sumxx) - (sumx * sumx);
234 float calc_dety = (numx * sumyy) - (sumy * sumy);
235
236 float calc_as = (sumxx * sumy) - (sumx * sumxy);
237 float calc_bs = (numx * sumxy) - (sumx * sumy);
238
239 Line zeroLine;
240 zeroLine.origin() = Eigen::Vector2f::Zero();
241 zeroLine.direction() = Eigen::Vector2f::UnitX();
242 if (std::abs(calc_as) < 0.001f && std::abs(calc_bs) < 0.001f)
243 {
244 ARMARX_VERBOSE << "Vector is close to zero " << VAROUT(calc_as) << " "
245 << VAROUT(calc_bs);
246 return zeroLine;
247 }
248 if (numx == 0.0f)
249 {
250 ARMARX_VERBOSE << "numx is zero " << VAROUT(begin) << " " << VAROUT(end);
251 return zeroLine;
252 }
253
254
255 Line line;
256 if (calc_det > calc_dety)
257 {
258 line.direction() = Eigen::Vector2f(calc_det, calc_bs).normalized();
259 }
260 else
261 {
262 line.direction() = Eigen::Vector2f(calc_bs, calc_dety).normalized();
263 }
264
265 line.origin() = Eigen::Vector2f(sumx / numx, sumy / numx);
266
267 // the error is calculated only if demanded
268 if (numx > 0 && maxError)
269 {
270 float this_average_error = 0.0f;
271 float this_max_error = 0.0f;
272 for (Eigen::Vector2f* point = begin; point != end; ++point)
273 {
274 float point_error = distance(line, *point);
275 this_average_error += point_error;
276 this_max_error = std::max(point_error, this_max_error);
277 }
278 this_average_error = this_average_error / numx;
279
280 *maxError = this_max_error;
281
282 if (averageError)
283 {
284 *averageError = this_average_error;
285 }
286 }
287
288 if (as)
289 {
290 *as = calc_as;
291 }
292 if (bs)
293 {
294 *bs = calc_bs;
295 }
296 if (det)
297 {
298 *det = calc_det;
299 }
300
301 return line;
302 }
303
305 leastSquareLineSegmentInOrder(Eigen::Vector2f* begin,
306 Eigen::Vector2f* end,
307 float* maxError,
308 float* averageError)
309 {
310 LineSegment2Df segment;
311 if ((end - begin) < 2)
312 {
313 segment.start = Eigen::Vector2f::Zero();
314 segment.end = Eigen::Vector2f(5000.0f, 5000.0f);
315 if (maxError)
316 {
317 *maxError = FLT_MAX;
318 }
319 if (averageError)
320 {
321 *averageError = FLT_MAX;
322 }
323 return segment;
324 }
325
326 Line line = leastSquareLine(begin, end, maxError, averageError, nullptr, nullptr, nullptr);
327 if (anyIsNaN(line.direction()))
328 {
329 segment.start = Eigen::Vector2f::Zero();
330 segment.end = Eigen::Vector2f(5000.0f, 5000.0f);
331 if (maxError)
332 {
333 *maxError = FLT_MAX;
334 }
335 if (averageError)
336 {
337 *averageError = FLT_MAX;
338 }
339 return segment;
340 }
341 segment.start = line.projection(*begin);
342 segment.end = line.projection(*(end - 1));
343
344 if (anyIsNaN(segment.start))
345 {
346 ARMARX_WARNING << "Segment start is NaN: " << VAROUT(segment.start)
347 << VAROUT(line.origin()) << VAROUT(line.direction());
348 }
349 if (anyIsNaN(segment.end))
350 {
351 ARMARX_WARNING << "Segment end is NaN: " << VAROUT(segment.end) << VAROUT(line.origin())
352 << VAROUT(line.direction());
353 }
354
355 return segment;
356 }
357
358 float
359 leastSquareEdge(Eigen::Vector2f* begin, Eigen::Vector2f* end, LineSegment2Df& segment)
360 {
361 float maxError{0};
362 float averageError{0};
363 segment = leastSquareLineSegmentInOrder(begin, end, &maxError, &averageError);
364
365 return averageError;
366 }
367} // namespace
368
370 s(0.1) // This frequency will not be used
371{
372}
373
374void
376{
377 reportTopicName = getProperty<std::string>("ReportTopicName");
378 robotStateComponentName = getProperty<std::string>("RobotStateComponentName");
379 platformName = getProperty<std::string>("PlatformName");
380 laserScannerUnitName = getProperty<std::string>("LaserScannerUnitName");
381 workingMemoryName = getProperty<std::string>("WorkingMemoryName");
382 longtermMemoryName = getProperty<std::string>("LongtermMemoryName");
383 mapFilename = getProperty<std::string>("MapFilename");
384 agentName = getProperty<std::string>("AgentName");
385 emergencyStopMasterName = getProperty<std::string>("EmergencyStopMasterName");
386 debugObserverName = getProperty<std::string>("DebugObserverName");
387 updateWorkingMemory = getProperty<bool>("UpdateWorkingMemory");
388 int updatePeriodWorkingMemoryInMs = getProperty<int>("UpdatePeriodWorkingMemoryInMs");
389 workingMemoryUpdateFrequency = 1000.0f / updatePeriodWorkingMemoryInMs;
390 int updatePeriodLongtermMemory = getProperty<int>("UpdatePeriodLongtermMemoryInMs");
391 longtermMemoryUpdateFrequency = 1000.0f / updatePeriodLongtermMemory;
392 robotPositionZ = getProperty<float>("RobotPositionZ");
393 maximalLaserScannerDelay = getProperty<float>("MaximalLaserScannerDelay").getValue();
394
395 updateProperties(true);
396
397 map = loadMapFromFile(mapFilename);
398
399 {
400 const auto min = getPropertyAsCSV<float>("PlatformRectangleMin");
401 const auto max = getPropertyAsCSV<float>("PlatformRectangleMax");
402 std::tie(platformRectMin(0), platformRectMax(0)) = std::minmax(min.at(0), max.at(0));
403 std::tie(platformRectMin(1), platformRectMax(1)) = std::minmax(min.at(1), max.at(1));
404 ARMARX_INFO << "using platform rectangle min / max " << platformRectMin.transpose() << " / "
405 << platformRectMax.transpose();
406 }
407
408
409 usingProxy(robotStateComponentName);
410 usingProxy(laserScannerUnitName);
411 usingProxy(workingMemoryName);
412 usingProxy(longtermMemoryName);
413}
414
415void
417{
418 robotState = getProxy<RobotStateComponentInterfacePrx>(robotStateComponentName);
419 laserScannerUnit = getProxy<LaserScannerUnitInterfacePrx>(laserScannerUnitName);
420 SharedRobotInterfacePrx sharedRobot = robotState->getSynchronizedRobot();
421 for (LaserScannerInfo const& info : laserScannerUnit->getConnectedDevices())
422 {
423 FramedPoseBasePtr basePose = sharedRobot->getRobotNode(info.frame)->getPoseInRootFrame();
424 FramedPose& pose = dynamic_cast<FramedPose&>(*basePose);
425
427 data.pose = pose.toEigen();
428 data.info = info;
429 data.mutex = std::make_shared<std::mutex>();
430 scanData.push_back(data);
431 }
432
433 debugObserver = getTopic<DebugObserverInterfacePrx>(debugObserverName);
434
435 robotRootFrame = sharedRobot->getRootNode()->getName();
436
437 workingMemory = getProxy<memoryx::WorkingMemoryInterfacePrx>(workingMemoryName);
438 agentsMemory = workingMemory->getAgentInstancesSegment();
439 if (agentName.empty())
440 {
441 agentName = robotState->getRobotName();
442 }
443 agentInstance = new memoryx::AgentInstance();
444 agentInstance->setSharedRobot(sharedRobot);
445 agentInstance->setAgentFilePath(robotState->getRobotFilename());
446 agentInstance->setName(agentName);
447
448 longtermMemory = getProxy<memoryx::LongtermMemoryInterfacePrx>(longtermMemoryName);
449 selfLocalisationSegment = longtermMemory->getSelfLocalisationSegment();
450
451 offeringTopic(reportTopicName);
452 reportTopic = getTopic<LaserScannerSelfLocalisationListenerPrx>(reportTopicName);
453 offeringTopic("PlatformState");
454 platformUnitTopic = getTopic<PlatformUnitListenerPrx>("PlatformState");
455 // Load estimatedPose from longterm memory
456 estimatedPose = Eigen::Vector3f::Zero();
457 memoryx::EntityBasePtr poseEntity = selfLocalisationSegment->getEntityByName(agentName);
458 if (poseEntity)
459 {
460 poseEntityId = poseEntity->getId();
461 ARMARX_INFO << "Using pose entity from LTM with ID: " << poseEntityId;
462 memoryx::EntityAttributeBasePtr poseAttribute = poseEntity->getAttribute("pose");
463 if (poseAttribute)
464 {
465 // So this is how you access a typed attribute, fancy
466 FramedPosePtr framedPose = armarx::VariantPtr::dynamicCast(poseAttribute->getValueAt(0))
467 ->getClass<armarx::FramedPose>();
468 if (framedPose)
469 {
470 Eigen::Matrix4f globalPose = framedPose->toGlobalEigen(sharedRobot);
471 Eigen::Matrix3f robotRot = globalPose.block<3, 3>(0, 0);
472 Eigen::Vector2f robotPos = globalPose.block<2, 1>(0, 3);
473 Eigen::Vector2f yWorld(0.0f, 1.0f);
474 Eigen::Vector2f yRobot = robotRot.col(1).head<2>();
475 float robotTheta = acos(yWorld.dot(yRobot));
476 if (yRobot.x() >= 0.0f)
477 {
478 robotTheta = -robotTheta;
479 }
480
481 estimatedPose = Eigen::Vector3f(robotPos.x(), robotPos.y(), robotTheta);
482 }
483 else
484 {
485 ARMARX_WARNING << "'pose' attribute does not contain a framed pose";
486 }
487 }
488 else
489 {
490 ARMARX_WARNING << "No attribute 'pose' in entity for agent '" << agentName << "' found";
491 }
492 }
493 else
494 {
495 ARMARX_WARNING << "No pose in longterm memory found for agent: " << agentName;
496 ARMARX_WARNING << "Self localisation will start at origin";
497
498 FramedPosePtr pose = new FramedPose(Eigen::Matrix4f::Identity(), GlobalFrame, "");
500 poseEntity->setName(agentName);
501 poseEntity->putAttribute("pose", pose);
502 //poseEntity->setId(poseEntityId);
503
504 poseEntityId = selfLocalisationSegment->addEntity(poseEntity);
505 ARMARX_INFO << "New entity ID: " << poseEntityId;
506 }
507 ARMARX_INFO << "Initial pose: (" << estimatedPose[0] << ", " << estimatedPose[1] << ", "
508 << estimatedPose[2] << ")";
509 resetKalmanFilter(estimatedPose);
510 lastVelocityUpdate = TimeUtil::GetTime();
511 lastVelocity = Eigen::Vector3f::Zero();
512
513 usingTopic(laserScannerUnit->getReportTopicName());
514 usingTopic(platformName + "State");
515
516
517 int updatePeriod = getProperty<int>("UpdatePeriodInMs");
519 this,
520 &LaserScannerSelfLocalisation::updateLocalisation,
521 updatePeriod,
522 "UpdateLocalisation");
523 task->start();
524}
525
526void
528{
529 task->stop();
530 ARMARX_INFO << "Task stopped";
531 if (laserScannerFileLoggingData)
532 {
533 writeLogFile();
534 }
535}
536
537void
541
552
553std::string
555{
556 return reportTopicName;
557}
558
559void
561 Ice::Float y,
562 Ice::Float theta,
563 const Ice::Current&)
564{
565 {
566 std::unique_lock lock(setPoseMutex);
567 setPose = Eigen::Vector3f(x, y, theta);
568 resetKalmanFilter(*setPose);
569 }
570}
571
572void
574 const std::string& name,
575 const LaserScan& scan,
576 const TimestampBasePtr& timestamp,
577 const Ice::Current&)
578{
579 for (LaserScanData& data : scanData)
580 {
581 if (data.info.device == device)
582 {
583 std::unique_lock lock(*data.mutex);
584 data.scan = scan;
585 data.measurementTime = IceUtil::Time::microSeconds(timestamp->timestamp);
586 }
587 }
588}
589
590static Eigen::Vector3f
591addPose(Eigen::Vector3f pose, Eigen::Vector3f add)
592{
593 Eigen::Vector3f result = pose + add;
594 // Make sure that theta is in the range [-PI,PI]
595 result[2] = armarx::math::MathUtils::angleModPI(result[2]);
596 return result;
597}
598
599static Eigen::Vector3f
600integratePose(Eigen::Vector3f pose, Eigen::Vector3f v0, Eigen::Vector3f v1, float dt)
601{
602 // Assumption: Linear acceleration between v0 and v1 in dt seconds
603 // Travelled distance d = v0*dt + 0.5*(v1-v0)*dt = 0.5*dt*(v0+v1)
604
605 float dTheta = 0.5f * dt * (v0[2] + v1[2]);
606 // This is an approximation (the right solution would have to integrate over changing theta)
607 float thetaMid = pose[2] + 0.5f * dTheta;
608 Eigen::Matrix2f rotation = Eigen::Rotation2Df(thetaMid).matrix();
609 Eigen::Vector2f globalV0 = rotation * v0.head<2>();
610 Eigen::Vector2f globalV1 = rotation * v1.head<2>();
611 Eigen::Vector2f globalDeltaPos = 0.5f * dt * (globalV0 + globalV1);
612
613 Eigen::Vector3f d(globalDeltaPos.x(), globalDeltaPos.y(), dTheta);
614 return addPose(pose, d);
615}
616
617void
619 Ice::Float velY,
620 Ice::Float velTheta,
621 const Ice::Current&)
622{
623 IceUtil::Time now = TimeUtil::GetTime();
624 auto elapsedSeconds = now - lastVelocityUpdate;
625 lastVelocityUpdate = now;
626 {
627 std::unique_lock lock(kalmanMutex);
628 filter->predict(velX, velY, velTheta, elapsedSeconds);
629 }
630 // Store odometry reports here and calculate the estimated pose in the central update task
631 if (useOdometry)
632 {
633
634 std::unique_lock lock(odometryMutex);
635
636 reportedVelocities.push_back(ReportedVelocity{
637 static_cast<float>(elapsedSeconds.toSecondsDouble()), velX, velY, velTheta});
638 }
639}
640
641void
643 Ice::Float,
644 Ice::Float,
645 const Ice::Current&)
646{
647 // nop
648}
649
650void
651LaserScannerSelfLocalisation::updateLocalisation()
652{
653 // Periodic task to update the self localisation
654
655 // Get the current parameter values;
656 propertyMutex.lock();
657 int halfFrameSize = propSmoothFrameSize / 2;
658 int mergeDistance = propSmoothMergeDistance;
659 float maxDistanceSquared = propMatchingMaxDistance * propMatchingMaxDistance;
660 float minPointsForMatch = propMatchingMinPoints;
661 float matchingCorrectionFactor = propMatchingCorrectionFactor;
662 bool reportPoints = propReportPoints;
663 bool reportEdges = propReportEdges;
664 bool useMapCorrection = propUseMapCorrection;
665 int epsilon = propEdgeEpsilon;
666 int minPointsPerEdge = propEdgeMinPoints;
667 float maxDistanceForEdgeSquared = propEdgeMaxDistance * propEdgeMaxDistance;
668 float pointAddingThreshold = propEdgePointAddingThreshold;
669 float edgeMaxDeltaAngle = propEdgeMaxDeltaAngle;
670 propertyMutex.unlock();
671
672 Eigen::Vector3f currentPose = estimatedPose;
673
674 // Apply the collected odometry pose correction
675 if (useOdometry)
676 {
677 std::unique_lock lock(odometryMutex);
678 for (ReportedVelocity const& report : reportedVelocities)
679 {
680 Eigen::Vector3f currentVelocity(report.x, report.y, report.theta);
681 Eigen::Vector3f motionPrediction =
682 integratePose(currentPose, lastVelocity, currentVelocity, report.dt);
683 if (anyIsNaN(motionPrediction))
684 {
685 ARMARX_WARNING << "Motion prediction is NaN: " << motionPrediction;
686 ARMARX_WARNING << VAROUT(currentPose) << " " << VAROUT(lastVelocity) << " "
687 << VAROUT(currentVelocity) << " " << VAROUT(report.dt);
688 }
689 else
690 {
691 currentPose = motionPrediction;
692 }
693
694 lastVelocity = currentVelocity;
695 }
696 reportedVelocities.clear();
697 }
698
699 // Use the laser scan data to make a global pose correction
700 std::size_t pointsSize = 0;
701 IceUtil::Time now = TimeUtil::GetTime();
702 IceUtil::Time measurementTime = IceUtil::Time::seconds(0);
703 // No data available
704 if (scanData.empty())
705 {
706 measurementTime = now;
707 }
708 for (LaserScanData const& data : scanData)
709 {
710 measurementTime = std::max(measurementTime, data.measurementTime);
711
712 std::unique_lock lock(*data.mutex);
713 if (data.scan.empty())
714 {
715 continue;
716 }
717
718 pointsSize += data.scan.size();
719
720 // Check whether the scan data is out of date
721 double deltaMeasurement = (now - data.measurementTime).toSecondsDouble();
722 if (deltaMeasurement < -0.005)
723 {
725 << "Delta between now and measurement time is negative: "
726 << deltaMeasurement << " now: " << now.toDateTime()
727 << " measurement: " << data.measurementTime.toDateTime();
728 }
729 else if (deltaMeasurement > maximalLaserScannerDelay)
730 {
732 << "Delta between now and measurement time has exceeded the limit ("
733 << deltaMeasurement << "s > " << maximalLaserScannerDelay
734 << "s). Activating emergency stop!";
735
736 EmergencyStopMasterInterfacePrx emergencyStopMaster =
737 getProxy<EmergencyStopMasterInterfacePrx>(emergencyStopMasterName);
738 if (emergencyStopMaster)
739 {
740 emergencyStopMaster->setEmergencyStopState(eEmergencyStopActive);
741 }
742 else
743 {
744 ARMARX_ERROR << "Could not activate emergency stop because proxy for "
745 "EmergencyStopMaster was not found '"
746 << emergencyStopMasterName << "'";
747 }
748 }
749 }
750 if (useMapCorrection && pointsSize > 0)
751 {
752 for (LaserScanData& data : scanData)
753 {
754 Eigen::Rotation2Df globalRotation = Eigen::Rotation2Df(currentPose[2]);
755 Eigen::Vector2f globalTranslation = currentPose.head<2>();
756
757 std::unique_lock lock(*data.mutex);
758 data.points.clear();
759 data.points.reserve(data.scan.size());
760 for (int stepIndex = 0; stepIndex < (int)data.scan.size(); ++stepIndex)
761 {
762 LaserScanStep const& step = data.scan[stepIndex];
763
764 // Smoothing in a frame neighborhood
765 float distanceSum = 0;
766 int distanceElements = 0;
767 int minFrameIndex = std::max(0, stepIndex - halfFrameSize);
768 int maxFrameIndex =
769 std::min((int)(data.scan.size()) - 1, stepIndex + halfFrameSize);
770 for (int frameIndex = minFrameIndex; frameIndex <= maxFrameIndex; ++frameIndex)
771 {
772 float frameDistance = data.scan[frameIndex].distance;
773 int deltaDistance = std::abs(step.distance - frameDistance);
774 if (deltaDistance <= mergeDistance)
775 {
776 distanceSum += frameDistance;
777 ++distanceElements;
778 }
779 }
780 if (distanceElements == 0)
781 {
783 << "No elements with distance smaller than merge distance found.";
784 ARMARX_WARNING << VAROUT(stepIndex) << " " << VAROUT(step.distance) << " "
785 << VAROUT(mergeDistance);
786 }
787 else
788 {
789 float distance = distanceSum / distanceElements;
790
791 // Transform to global 2D point
792 float sinAngle, cosAngle;
793 sincosf(step.angle, &sinAngle, &cosAngle);
794 float x = -distance * sinAngle;
795 float y = distance * cosAngle;
796
797 Eigen::Vector2f localPoint =
798 (data.pose * Eigen::Vector4f(x, y, 0, 1)).head<2>();
799
800 if (anyIsNaN(localPoint))
801 {
802 ARMARX_WARNING << "Local point is NaN";
803 ARMARX_WARNING << VAROUT(localPoint) << " " << VAROUT(x) << " " << VAROUT(y)
804 << " " << VAROUT(step.angle);
805 continue;
806 }
807
808 if (platformRectMin(0) < localPoint(0) && localPoint(0) < platformRectMax(0) &&
809 platformRectMin(1) < localPoint(1) && localPoint(1) < platformRectMax(1))
810 {
812 << "discarding point inside of the platform rectangle: "
813 << localPoint.transpose();
814 continue;
815 }
816
817 Eigen::Vector2f globalPoint = globalRotation * localPoint + globalTranslation;
818
819 if (anyIsNaN(globalPoint))
820 {
821 ARMARX_WARNING << "Global point is NaN";
822 ARMARX_WARNING << VAROUT(globalPoint) << " " << VAROUT(x) << " "
823 << VAROUT(y) << " " << VAROUT(step.angle);
824 }
825 else
826 {
827 data.points.push_back(globalPoint);
828 }
829 }
830 }
831 ARMARX_DEBUG << deactivateSpam() << "using " << data.points.size() << "/"
832 << data.scan.size();
833 }
834 if (reportPoints)
835 {
836 std::vector<armarx::Vector2f> pointsA;
837 pointsA.reserve(pointsSize);
838 for (LaserScanData const& data : scanData)
839 {
840 for (auto& p : data.points)
841 {
842 pointsA.push_back(armarx::Vector2f{p.x(), p.y()});
843 }
844 }
845 if (reportTopic)
846 {
847 reportTopic->begin_reportLaserScanPoints(pointsA);
848 }
849 else
850 {
851 ARMARX_WARNING << "Report topic is not set";
852 }
853 }
854
855 std::size_t edgeCount = 0;
856
857 for (LaserScanData& data : scanData)
858 {
859 auto& points = data.points;
860
861 std::vector<float> angles;
862 angles.reserve(points.size());
863 int pointsSize = (int)points.size();
864 for (int pointIndex = 0; pointIndex < pointsSize; ++pointIndex)
865 {
866 int beginIndex = std::max(pointIndex - epsilon, 0);
867 int endIndex = std::min(pointIndex + epsilon + 1, pointsSize - 1);
868 Eigen::Vector2f* beginPoint = &data.points[beginIndex];
869 Eigen::Vector2f* endPoint = &data.points[endIndex];
870 Line line(leastSquareLine(beginPoint, endPoint));
871 float angle = std::atan2(line.direction().x(), line.direction().y());
872 if (std::isnan(angle))
873 {
874 ARMARX_VERBOSE << "Edge angle is NaN" << VAROUT(beginIndex) << " "
875 << VAROUT(endIndex);
876 }
877 else
878 {
879 angles.push_back(angle);
880 }
881 }
882
883 int beginIndex = 0;
884 int endIndex = beginIndex;
885 int maxEndIndex = pointsSize - 1;
886 int minBeginIndex = 0;
887
888 int maximumPointsInEdge = 0;
889 LineSegment2Df edgeLineSegment[2];
890 int resultIndex = 0;
891 data.edges.clear();
892 while (endIndex < maxEndIndex)
893 {
894 float yaw = angles[endIndex];
895
896 // We compare the angles of following points
897 // If they stay within parametrized limits (angle and distance),
898 // we add them to the edge
899 while (endIndex < maxEndIndex &&
900 std::fabs(yaw - angles[endIndex + 1]) < edgeMaxDeltaAngle &&
901 (points[endIndex + 1] - points[endIndex]).squaredNorm() <
902 maxDistanceForEdgeSquared)
903 {
904 endIndex++;
905 }
906
907 // Corners have different angles but still are part of an edge
908 // Therefore we must extend both ends of the edge
909 // The extension continues until the least squares error exceeds a parameterized limit.
910
911 bool frontExtended = false;
912 bool backExtended = false;
913
914 // Extend the front
915 while (beginIndex > minBeginIndex &&
916 (points[beginIndex - 1] - points[beginIndex]).squaredNorm() <
917 maxDistanceForEdgeSquared &&
918 leastSquareEdge(points.data() + beginIndex - 1,
919 points.data() + endIndex + 1,
920 edgeLineSegment[resultIndex]) < pointAddingThreshold)
921 {
922 beginIndex--;
923 resultIndex = !resultIndex;
924 frontExtended = true;
925 }
926
927 // Extend the back
928 while (endIndex < maxEndIndex &&
929 (points[endIndex] - points[endIndex + 1]).squaredNorm() <
930 maxDistanceForEdgeSquared &&
931 leastSquareEdge(points.data() + beginIndex,
932 points.data() + endIndex + 1,
933 edgeLineSegment[resultIndex]) < pointAddingThreshold)
934 {
935 endIndex++;
936 resultIndex = !resultIndex;
937 backExtended = true;
938 }
939
940 if (!frontExtended && !backExtended)
941 {
942 leastSquareEdge(points.data() + beginIndex,
943 points.data() + endIndex + 1,
944 edgeLineSegment[!resultIndex]);
945 }
946
947 // Check for minimum number of points in the edge
948 int numberOfPoints = endIndex - beginIndex + 1;
949 if (numberOfPoints > minPointsPerEdge)
950 {
951 // Get the extended line segment which did not exceed the error limit
952 // Index = resultIndex: contains the line segment which exceeded the error limit
953 LineSegment2Df& segment = edgeLineSegment[!resultIndex];
954 Eigen::Vector2f* pointsBegin = points.data() + beginIndex;
955 ExtractedEdge edge{segment, pointsBegin, pointsBegin + numberOfPoints};
956 data.edges.push_back(edge);
957 ++edgeCount;
958 maximumPointsInEdge = std::max(numberOfPoints, maximumPointsInEdge);
959 minBeginIndex = endIndex + 1;
960 }
961
962 // Search for the next edge
963 beginIndex = endIndex + 1;
964 endIndex = beginIndex;
965 }
966 }
967
968 if (reportEdges)
969 {
970 std::vector<armarx::LineSegment2D> segments;
971 segments.reserve(edgeCount);
972 for (LaserScanData const& data : scanData)
973 {
974 for (ExtractedEdge const& e : data.edges)
975 {
976 armarx::LineSegment2D s;
977 s.start = armarx::Vector2f{e.segment.start.x(), e.segment.start.y()};
978 s.end = armarx::Vector2f{e.segment.end.x(), e.segment.end.y()};
979 segments.push_back(s);
980 }
981 }
982 reportTopic->begin_reportExtractedEdges(segments);
983 }
984
985 std::size_t rowCount = 0;
986 Eigen::Vector2f robotPos = currentPose.head<2>();
987
988 Eigen::MatrixX3f X(pointsSize, 3);
989 Eigen::VectorXf Y(pointsSize);
990 for (LaserScanData const& data : scanData)
991 {
992 for (ExtractedEdge const& edge : data.edges)
993 {
994 for (auto* p = edge.pointsBegin; p != edge.pointsEnd; ++p)
995 {
996 auto& globalPoint = *p;
997 // Find the line segment closest to this point
998 LineSegment2Df* target = nullptr;
999 float minDistanceSquared = FLT_MAX;
1000 for (LineSegment2Df& segment : map)
1001 {
1002 float distanceSquared =
1003 lineSegmentToPointDistanceSquared(segment, globalPoint);
1004 if (distanceSquared < minDistanceSquared)
1005 {
1006 minDistanceSquared = distanceSquared;
1007 target = &segment;
1008 }
1009 }
1010 if (minDistanceSquared > maxDistanceSquared)
1011 {
1012 continue;
1013 }
1014 if (!target)
1015 {
1016 ARMARX_WARNING << "No line segment closest to point found: " << globalPoint;
1017 continue;
1018 }
1019
1020 Eigen::Vector2f const& v = globalPoint;
1021 Eigen::Vector2f targetDir = target->end - target->start;
1022 Eigen::Vector2f u = Eigen::Vector2f(-targetDir.y(), targetDir.x()).normalized();
1023 float r = target->start.dot(u);
1024 Eigen::Vector2f cToV(v - robotPos);
1025 Eigen::Vector2f cToVOrth(-cToV.y(), cToV.x());
1026 float x3 = u.dot(cToVOrth);
1027 float y = r - u.dot(v);
1028
1029 X(rowCount, 0) = u(0);
1030 X(rowCount, 1) = u(1);
1031 X(rowCount, 2) = x3;
1032 Y(rowCount) = y;
1033 ++rowCount;
1034 }
1035 }
1036 }
1037
1038 Eigen::Vector3f b = Eigen::Vector3f::Zero();
1039 float matched = static_cast<float>(rowCount) / pointsSize;
1040 debugObserver->setDebugDatafield(getName(), "MatchedDataPointRatio", new Variant(matched));
1041 if (matched < minPointsForMatch)
1042 {
1044 << deactivateSpam(5)
1045 << "Too few data points could be associated with edges of the map (associated: "
1046 << rowCount << "/" << pointsSize << " = " << matched << ")";
1047 }
1048 else if (rowCount > 10)
1049 {
1050 b = X.block(0, 0, rowCount, 3)
1051 .jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV)
1052 .solve(Y.head(rowCount));
1053 b *= matchingCorrectionFactor;
1054 if (anyIsNaN(b))
1055 {
1056 ARMARX_WARNING << deactivateSpam(1) << "Equation solution is NaN " << VAROUT(b)
1057 << " " << VAROUT(rowCount);
1058 }
1059 }
1060
1061 Eigen::Vector3f newEstimatedPose = addPose(currentPose, b);
1062 if (anyIsNaN(newEstimatedPose))
1063 {
1064 ARMARX_WARNING << "New estimated pose is NaN " << VAROUT(currentPose) << " "
1065 << VAROUT(b);
1066 }
1067 else
1068 {
1069 estimatedPose = newEstimatedPose;
1070 }
1071 {
1072 std::unique_lock lock(kalmanMutex);
1073 if (propSensorStdDev > 0)
1074 {
1075 filter->update(estimatedPose(0), estimatedPose(1), estimatedPose(2));
1076 }
1077 }
1078 }
1079 else
1080 {
1081 estimatedPose = currentPose;
1082 }
1083 // ARMARX_INFO << deactivateSpam(1) << VAROUT(estimatedPose) << VAROUT(propSensorStdDev);
1084 Eigen::Vector3f filteredPose;
1085 if (propSensorStdDev > 0)
1086 {
1087 Eigen::Matrix3d cov = filter->getCovariance();
1088 reportTopic->begin_reportPoseUncertainty(cov(0, 0), cov(1, 1), cov(2, 2));
1089 // ARMARX_INFO << deactivateSpam(1) << VAROUT(cov);
1090 filteredPose = filter->getPose().cast<float>();
1091 }
1092 else
1093 {
1094 filteredPose = estimatedPose;
1095 }
1096 {
1097 std::unique_lock lock(setPoseMutex);
1098 if (setPose)
1099 {
1100 estimatedPose = *setPose;
1101 resetKalmanFilter(estimatedPose);
1102 setPose.reset();
1103 }
1104 }
1105 reportTopic->begin_reportCorrectedPose(filteredPose[0], filteredPose[1], filteredPose[2]);
1106 PlatformPose newPose;
1107 newPose.timestampInMicroSeconds = measurementTime.toMicroSeconds();
1108 newPose.x = filteredPose[0];
1109 newPose.y = filteredPose[1];
1110 newPose.rotationAroundZ = filteredPose[2];
1111 // platformUnitTopic->reportPlatformPose(newPose);
1112
1113 Eigen::Matrix3f orientation = Eigen::Matrix3f::Identity();
1114 orientation.block<2, 2>(0, 0) = Eigen::Rotation2Df(filteredPose[2]).matrix();
1115 Eigen::Vector3f position(filteredPose[0], filteredPose[1], robotPositionZ);
1116 FramedPosePtr reportPose = new FramedPose(orientation, position, GlobalFrame, "");
1117
1118
1119 FrameHeader header;
1120 header.parentFrame = GlobalFrame;
1121 header.frame = robotRootFrame;
1122 header.agent = agentName;
1123 header.timestampInMicroSeconds = measurementTime.toMicroSeconds();
1124
1125 TransformStamped globalRobotPose;
1126 globalRobotPose.header = header;
1127 globalRobotPose.transform = reportPose->toEigen();
1128
1129 globalRobotPoseTopic->reportGlobalRobotPose(globalRobotPose);
1130
1131 try
1132 {
1133 if (updateWorkingMemory &&
1134 s.checkFrequency("workingMemoryUpdate", workingMemoryUpdateFrequency))
1135 {
1136 // This seems to be the way to update the current pose
1137 // Since in simulation another component (SelfLocalizationDynamicSimulation) updates this memory location
1138 // it is a bad idea to enable the working memory update in simulation
1139 if (agentInstance)
1140 {
1141 agentInstance->setPose(reportPose);
1142 if (agentsMemory)
1143 {
1144 std::string robotAgentId =
1145 agentsMemory->upsertEntityByName(agentInstance->getName(), agentInstance);
1146 agentInstance->setId(robotAgentId);
1147 }
1148 else
1149 {
1150 ARMARX_WARNING << "Agents memory not set";
1151 }
1152 }
1153 else
1154 {
1155 ARMARX_WARNING << "Agent instance not set";
1156 }
1157 }
1158
1159 if (s.checkFrequency("longtermMemoryUpdate", longtermMemoryUpdateFrequency))
1160 {
1161 memoryx::SimpleEntityPtr poseEntity = new memoryx::SimpleEntity();
1162 poseEntity->setName(agentName);
1163 poseEntity->putAttribute("pose", reportPose);
1164 poseEntity->setId(poseEntityId);
1165 // We just update the 'pose' attribute since ID and name have been set by the inital insert
1166 if (selfLocalisationSegment)
1167 {
1168 selfLocalisationSegment->upsertEntity(poseEntityId, poseEntity);
1169 }
1170 else
1171 {
1172 ARMARX_WARNING << "Self localisation memory segment is not set";
1173 }
1174 }
1175 }
1176 catch (IceUtil::Exception const& ex)
1177 {
1178 ARMARX_WARNING << "Caught exception while updating the memory:\n" << ex;
1179 }
1180
1181 if (laserScannerFileLoggingData)
1182 {
1183 laserScannerFileLoggingData->scanDataHistory[now] = (scanData);
1184 }
1185}
1186
1187void
1189{
1190#define ARMARX_LSSL_UPDATE_PROPERTY(name) \
1191 { \
1192 auto prop = getProperty<decltype(prop##name)>(#name); \
1193 if (initial || prop.isSet()) \
1194 { \
1195 prop##name = prop.getValue(); \
1196 ARMARX_VERBOSE << VAROUT(prop##name); \
1197 } \
1198 }
1199 try
1200 {
1201 ARMARX_VERBOSE << "Updating properties:";
1202 std::unique_lock lock(propertyMutex);
1203
1204
1205 if (std::abs(propSensorStdDev - getProperty<float>("SensorStdDev").getValue()) > 0.001)
1206 {
1207 propSensorStdDev = getProperty<float>("SensorStdDev");
1208 ARMARX_VERBOSE << "new " << VAROUT(propSensorStdDev);
1209 if (!initial)
1210 {
1211 resetKalmanFilter(estimatedPose);
1212 }
1213 }
1214 if (std::abs(propVelSensorStdDev - getProperty<float>("VelSensorStdDev").getValue()) >
1215 0.00001)
1216 {
1217 propVelSensorStdDev = getProperty<float>("VelSensorStdDev");
1218 ARMARX_VERBOSE << "new " << VAROUT(propVelSensorStdDev);
1219 if (!initial)
1220 {
1221 resetKalmanFilter(estimatedPose);
1222 }
1223 }
1224
1225
1226 ARMARX_LSSL_UPDATE_PROPERTY(SmoothFrameSize);
1227 ARMARX_LSSL_UPDATE_PROPERTY(SmoothMergeDistance);
1228 ARMARX_LSSL_UPDATE_PROPERTY(MatchingMaxDistance);
1229 ARMARX_LSSL_UPDATE_PROPERTY(MatchingMinPoints);
1230 ARMARX_LSSL_UPDATE_PROPERTY(MatchingCorrectionFactor);
1231 ARMARX_LSSL_UPDATE_PROPERTY(EdgeMaxDistance);
1232 ARMARX_LSSL_UPDATE_PROPERTY(EdgeMaxDeltaAngle);
1233 ARMARX_LSSL_UPDATE_PROPERTY(EdgePointAddingThreshold);
1234 ARMARX_LSSL_UPDATE_PROPERTY(EdgeEpsilon);
1235 ARMARX_LSSL_UPDATE_PROPERTY(EdgeMinPoints);
1236 ARMARX_LSSL_UPDATE_PROPERTY(UseOdometry);
1237 ARMARX_LSSL_UPDATE_PROPERTY(UseMapCorrection);
1238 ARMARX_LSSL_UPDATE_PROPERTY(ReportPoints);
1239 ARMARX_LSSL_UPDATE_PROPERTY(ReportEdges);
1240 ARMARX_LSSL_UPDATE_PROPERTY(LoggingFilePath);
1241 // ARMARX_LSSL_UPDATE_PROPERTY(SensorStdDev);
1242 // ARMARX_LSSL_UPDATE_PROPERTY(VelSensorStdDev);
1243
1244
1245 if (!propLoggingFilePath.empty())
1246 {
1247 if (laserScannerFileLoggingData && propRecordData &&
1248 !getProperty<bool>("RecordData").getValue())
1249 {
1250 writeLogFile();
1251 }
1252 if (!propRecordData && getProperty<bool>("RecordData").getValue())
1253 {
1254 // setup data
1255 laserScannerFileLoggingData.reset(new LaserScannerFileLoggingData());
1256 laserScannerFileLoggingData->filePath = propLoggingFilePath;
1257 }
1258 propRecordData = getProperty<bool>("RecordData").getValue();
1259 }
1260
1261 useOdometry = propUseOdometry;
1262 }
1263 catch (std::exception const& ex)
1264 {
1265 ARMARX_WARNING << "Could not update properties. " << ex.what();
1266 }
1267
1268#undef ARMARX_LSSL_UPDATE_PROPERTY
1269}
1270
1271void
1272LaserScannerSelfLocalisation::resetKalmanFilter(const Eigen::Vector3f& pose)
1273{
1274 ARMARX_INFO << "Position Std Dev: " << propSensorStdDev
1275 << " Vel Std Dev: " << propVelSensorStdDev;
1276 std::unique_lock lock(kalmanMutex);
1277 filter.reset(new memoryx::PlatformKalmanFilter(
1278 pose.head<2>().cast<double>(),
1279 pose(2),
1280 propSensorStdDev,
1281 propSensorStdDev / 100,
1282 propVelSensorStdDev,
1283 propVelSensorStdDev /* no scaling needed since motion noise scaling is done with rad/s*/));
1284}
1285
1286void
1287LaserScannerSelfLocalisation::writeLogFile()
1288{
1289 if (!laserScannerFileLoggingData)
1290 {
1292 << "laserScannerFileLoggingData is NULL - cannot write log file";
1293 return;
1294 }
1295 std::map<IceUtil::Time, std::vector<LaserScanData>> dataMap;
1296 std::string filePath;
1297 {
1298 std::unique_lock lock(laserScannerFileLoggingData->loggingMutex);
1299 dataMap.swap(laserScannerFileLoggingData->scanDataHistory);
1300 filePath = laserScannerFileLoggingData->filePath;
1301 }
1302 ARMARX_INFO << "Writing " << dataMap.size() << " laser scans";
1303 laserScannerFileLoggingData.reset();
1304 nlohmann::json json;
1305 std::ofstream file(filePath, std::ofstream::out | std::ofstream::trunc);
1306 ARMARX_CHECK_EXPRESSION(file.is_open()) << filePath;
1307 // if(!dataMap.empty() && !dataMap.begin()->second.empty())
1308 // {
1309 // auto & LaserScanData = dataMap.begin()->second.empty();
1310 // }
1311 int skipLineAmount = 10;
1312 int i = 0;
1313 for (auto& pair : dataMap)
1314 {
1315 if (i % skipLineAmount != 0)
1316 {
1317 continue;
1318 }
1319 const std::vector<LaserScanData>& scanData = pair.second;
1320 for (auto& elem : scanData)
1321 {
1322 const LaserScanData& scan = elem;
1323 Eigen::Quaternionf quat(scan.pose.block<3, 3>(0, 0));
1324 json["DeviceInfo"][scan.info.device] = {
1325 {"frame", scan.info.frame},
1326 {"minAngle", scan.info.minAngle},
1327 {"maxAngle", scan.info.maxAngle},
1328 {"stepSize", scan.info.stepSize},
1329 {"sensorPosition",
1330 {{"x", scan.pose(0, 3)}, {"y", scan.pose(1, 3)}, {"z", scan.pose(2, 3)}}},
1331 {"sensorOrientation",
1332 {{"w", quat.w()}, {"x", quat.x()}, {"y", quat.y()}, {"z", quat.z()}}}
1333
1334 };
1335 Ice::IntSeq dataPts;
1336 for (const auto& pt : scan.points)
1337 {
1338 dataPts.push_back(pt(0));
1339 dataPts.push_back(pt(1));
1340 }
1341 json["Data"][scan.info.device]["GlobalPoints"] += dataPts;
1342 Ice::IntSeq scanPts;
1343 for (const LaserScanStep& pt : scan.scan)
1344 {
1345 scanPts.push_back(pt.distance);
1346 }
1347 json["Data"][scan.info.device]["ScanDistances"] += scanPts;
1348 }
1349 i++;
1350 }
1351 file << std::setw(1) << json << std::endl;
1352}
1353
1354LineSegment2DSeq
1356{
1357 LineSegment2DSeq result;
1358 for (LineSegment2Df const& segment : map)
1359 {
1360 armarx::Vector2f start{segment.start.x(), segment.start.y()};
1361 armarx::Vector2f end{segment.end.x(), segment.end.y()};
1362 result.push_back(LineSegment2D{start, end});
1363 }
1364 return result;
1365}
1366
1367void
1369 const std::set<std::string>& changedProperties)
1370{
1372}
#define float
Definition 16_Level.h:22
std::string timestamp()
uint8_t data[1]
Eigen::ParametrizedLine< float, 2 > Line
#define ARMARX_LSSL_UPDATE_PROPERTY(name)
#define VAROUT(x)
constexpr T dt
std::string str(const T &t)
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
Property< PropertyType > getProperty(const std::string &name)
The FramedPose class.
Definition FramedPose.h:281
The JSONObject class is used to represent and (de)serialize JSON objects.
Definition JSONObject.h:44
const Json::Value & getJsonValue() const
void fromString(const ::std::string &jsonString, const ::Ice::Current &=Ice::emptyCurrent) override
void componentPropertiesUpdated(const std::set< std::string > &changedProperties) override
Implement this function if you would like to react to changes in the properties.
std::string getReportTopicName(const Ice::Current &) override
void reportPlatformVelocity(Ice::Float velX, Ice::Float velY, Ice::Float velTheta, const Ice::Current &) override
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
LineSegment2DSeq getMap(const Ice::Current &) override
void reportPlatformOdometryPose(Ice::Float, Ice::Float, Ice::Float, const Ice::Current &) override
void setAbsolutePose(Ice::Float x, Ice::Float y, Ice::Float theta, const Ice::Current &) override
void reportSensorValues(const std::string &device, const std::string &name, const LaserScan &scan, const TimestampBasePtr &timestamp, const Ice::Current &) override
SpamFilterDataPtr deactivateSpam(float deactivationDurationSec=10.0f, const std::string &identifier="", bool deactivate=true) const
disables the logging for the current line for the given amount of seconds.
Definition Logging.cpp:99
void offeringTopic(const std::string &name)
Registers a topic for retrival after initialization.
TopicProxyType getTopic(const std::string &name)
Returns a proxy of the specified topic.
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.
std::string getName() const
Retrieve name of object.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
The periodic task executes one thread method repeatedly using the time period specified in the constr...
virtual Eigen::Matrix4f toEigen() const
Definition Pose.cpp:334
void getPropertyAsCSV(ContainerT &val, const std::string &name, const std::string &splitBy=",;", bool trimElements=true, bool removeEmptyElements=true)
Property< PropertyType > getProperty(const std::string &name)
Property creation and retrieval.
static IceUtil::Time GetTime(TimeMode timeMode=TimeMode::VirtualTime)
Get the current time.
Definition TimeUtil.cpp:42
static float angleModPI(float value)
Definition MathUtils.h:173
Simple untyped entity class which allows adding arbitrary attributes.
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
std::string const GlobalFrame
Variable of the global coordinate system.
Definition FramedPose.h:65
Quaternion< float, 0 > Quaternionf
const VariantTypeId FramedPose
Definition FramedPose.h:36
T getValue(nlohmann::json &userConfig, nlohmann::json &defaultConfig, const std::string &entryName)
Definition utils.h:80
double v(double t, double v0, double a0, double j)
Definition CtrlUtil.h:39
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
std::vector< T > max(const std::vector< T > &v1, const std::vector< T > &v2)
::IceInternal::ProxyHandle<::IceProxy::armarx::SharedRobotInterface > SharedRobotInterfacePrx
Definition FramedPose.h:59
std::vector< T > min(const std::vector< T > &v1, const std::vector< T > &v2)
IceInternal::Handle< FramedPose > FramedPosePtr
Definition FramedPose.h:272
Vertex target(const detail::edge_base< Directed, Vertex > &e, const PCG &)
IceInternal::Handle< SimpleEntity > SimpleEntityPtr
double distance(const Point &a, const Point &b)
Definition point.hpp:95
double angle(const Point &a, const Point &b, const Point &c)
Definition point.hpp:109
std::vector< Eigen::Vector2f > points