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 
50 using namespace armarx;
51 
52 using Line = Eigen::ParametrizedLine<float, 2>;
53 
54 namespace
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 
374 void
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 
415 void
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 
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 
526 void
528 {
529  task->stop();
530  ARMARX_INFO << "Task stopped";
531  if (laserScannerFileLoggingData)
532  {
533  writeLogFile();
534  }
535 }
536 
537 void
539 {
540 }
541 
544 {
547 
548  prop->topic(globalRobotPoseTopic);
549 
550  return prop;
551 }
552 
553 std::string
555 {
556  return reportTopicName;
557 }
558 
559 void
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 
572 void
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 
590 static Eigen::Vector3f
591 addPose(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 
599 static Eigen::Vector3f
600 integratePose(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 
617 void
619  Ice::Float velY,
620  Ice::Float velTheta,
621  const Ice::Current&)
622 {
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 
641 void
643  Ice::Float,
644  Ice::Float,
645  const Ice::Current&)
646 {
647  // nop
648 }
649 
650 void
651 LaserScannerSelfLocalisation::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;
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 
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  {
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 
1187 void
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 
1271 void
1272 LaserScannerSelfLocalisation::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 
1286 void
1287 LaserScannerSelfLocalisation::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 
1354 LineSegment2DSeq
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 
1367 void
1369  const std::set<std::string>& changedProperties)
1370 {
1371  updateProperties();
1372 }
armarx::LaserScanData::scan
LaserScan scan
Definition: LaserScannerSelfLocalisation.h:67
armarx::Variant
The Variant class is described here: Variants.
Definition: Variant.h:223
ARMARX_VERBOSE
#define ARMARX_VERBOSE
Definition: Logging.h:187
armarx::LineSegment2Df::end
Eigen::Vector2f end
Definition: LaserScannerSelfLocalisation.h:52
ColorMap::Value
Value
Color maps that associate a color to every float from [0..1].
Definition: color.h:16
armarx::LaserScanData
Definition: LaserScannerSelfLocalisation.h:62
str
std::string str(const T &t)
Definition: UserAssistedSegmenterGuiWidgetController.cpp:43
MathUtils.h
armarx::VariantType::Float
const VariantTypeId Float
Definition: Variant.h:919
JSONObject.h
armarx::LaserScanData::points
std::vector< Eigen::Vector2f > points
Definition: LaserScannerSelfLocalisation.h:68
armarx::FramedPose
The FramedPose class.
Definition: FramedPose.h:280
armarx::VariantType::FramedPose
const VariantTypeId FramedPose
Definition: FramedPose.h:36
GfxTL::Matrix4f
MatrixXX< 4, 4, float > Matrix4f
Definition: MatrixXX.h:650
armarx::JSONObject
The JSONObject class is used to represent and (de)serialize JSON objects.
Definition: JSONObject.h:43
armarx::GlobalFrame
const std::string GlobalFrame
Definition: FramedPose.h:65
boost::target
Vertex target(const detail::edge_base< Directed, Vertex > &e, const PCG &)
Definition: point_cloud_graph.h:668
armarx::LaserScanData::info
LaserScannerInfo info
Definition: LaserScannerSelfLocalisation.h:65
armarx::max
std::vector< T > max(const std::vector< T > &v1, const std::vector< T > &v2)
Definition: VectorHelpers.h:297
armarx::ExtractedEdge::pointsEnd
Eigen::Vector2f * pointsEnd
Definition: LaserScannerSelfLocalisation.h:59
armarx::LaserScannerSelfLocalisation::onDisconnectComponent
void onDisconnectComponent() override
Definition: LaserScannerSelfLocalisation.cpp:527
armarx::LaserScannerSelfLocalisationPropertyDefinitions
Definition: LaserScannerSelfLocalisation.h:85
armarx::LineSegment2Df::start
Eigen::Vector2f start
Definition: LaserScannerSelfLocalisation.h:51
armarx::LaserScannerSelfLocalisation::LaserScannerSelfLocalisation
LaserScannerSelfLocalisation()
Definition: LaserScannerSelfLocalisation.cpp:369
armarx::LaserScannerSelfLocalisation::componentPropertiesUpdated
void componentPropertiesUpdated(const std::set< std::string > &changedProperties) override
Implement this function if you would like to react to changes in the properties.
Definition: LaserScannerSelfLocalisation.cpp:1368
armarx::LaserScannerSelfLocalisation::setAbsolutePose
void setAbsolutePose(Ice::Float x, Ice::Float y, Ice::Float theta, const Ice::Current &) override
Definition: LaserScannerSelfLocalisation.cpp:560
armarx::ReportedVelocity
Definition: LaserScannerSelfLocalisation.h:73
IceInternal::Handle< FramedPose >
armarx::JSONObject::getJsonValue
const Json::Value & getJsonValue() const
Definition: JSONObject.cpp:96
memoryx::PlatformKalmanFilter
This class is a convenience class for a holonomic platform using a Kalman filter.
Definition: PlatformKalmanFilter.h:37
armarx::PropertyUser::updateProperties
void updateProperties()
Definition: PropertyUser.cpp:151
GfxTL::Identity
void Identity(MatrixXX< N, N, T > *a)
Definition: MatrixXX.h:570
armarx::abs
std::vector< T > abs(const std::vector< T > &v)
Definition: VectorHelpers.h:281
armarx::JSONObject::fromString
void fromString(const ::std::string &jsonString, const ::Ice::Current &=Ice::emptyCurrent) override
Definition: JSONObject.cpp:88
memoryx::SimpleEntity
Simple untyped entity class which allows adding arbitrary attributes.
Definition: SimpleEntity.h:34
GfxTL::Matrix2f
MatrixXX< 2, 2, float > Matrix2f
Definition: MatrixXX.h:648
FramedPose.h
data
uint8_t data[1]
Definition: EtherCATFrame.h:68
ARMARX_DEBUG
#define ARMARX_DEBUG
Definition: Logging.h:184
armarx::LineSegment2Df
Definition: LaserScannerSelfLocalisation.h:49
ARMARX_LSSL_UPDATE_PROPERTY
#define ARMARX_LSSL_UPDATE_PROPERTY(name)
armarx::Pose::toEigen
virtual Eigen::Matrix4f toEigen() const
Definition: Pose.cpp:334
MemoryXCoreObjectFactories.h
filename
std::string filename
Definition: VisualizationRobot.cpp:86
max
T max(T t1, T t2)
Definition: gdiam.h:51
armarx::LaserScannerSelfLocalisation::reportPlatformVelocity
void reportPlatformVelocity(Ice::Float velX, Ice::Float velY, Ice::Float velTheta, const Ice::Current &) override
Definition: LaserScannerSelfLocalisation.cpp:618
ARMARX_ERROR
#define ARMARX_ERROR
Definition: Logging.h:196
armarx::math::MathUtils::angleModPI
static float angleModPI(float value)
Definition: MathUtils.h:173
armarx::LaserScannerSelfLocalisation::getReportTopicName
std::string getReportTopicName(const Ice::Current &) override
Definition: LaserScannerSelfLocalisation.cpp:554
armarx::LaserScannerSelfLocalisation::createPropertyDefinitions
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
Definition: LaserScannerSelfLocalisation.cpp:543
Gaussian.h
IceReportSkipper.h
armarx::armem::Time
armarx::core::time::DateTime Time
Definition: forward_declarations.h:13
armarx::LaserScannerSelfLocalisation::reportSensorValues
void reportSensorValues(const std::string &device, const std::string &name, const LaserScan &scan, const TimestampBasePtr &timestamp, const Ice::Current &) override
Definition: LaserScannerSelfLocalisation.cpp:573
armarx::TimeUtil::GetTime
static IceUtil::Time GetTime(TimeMode timeMode=TimeMode::VirtualTime)
Get the current time.
Definition: TimeUtil.cpp:42
armarx::control::common::getValue
T getValue(nlohmann::json &userConfig, nlohmann::json &defaultConfig, const std::string &entryName)
Definition: utils.h:71
armarx::LaserScannerSelfLocalisation::onExitComponent
void onExitComponent() override
Definition: LaserScannerSelfLocalisation.cpp:538
armarx::ManagedIceObject::usingTopic
void usingTopic(const std::string &name, bool orderedPublishing=false)
Registers a proxy for subscription after initialization.
Definition: ManagedIceObject.cpp:254
armarx::Component::getConfigIdentifier
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition: Component.cpp:79
armarx::ExtractedEdge::pointsBegin
Eigen::Vector2f * pointsBegin
Definition: LaserScannerSelfLocalisation.h:58
armarx::LaserScannerSelfLocalisation::onInitComponent
void onInitComponent() override
Definition: LaserScannerSelfLocalisation.cpp:375
armarx::ctrlutil::v
double v(double t, double v0, double a0, double j)
Definition: CtrlUtil.h:39
LaserScannerSelfLocalisation.h
ARMARX_CHECK_EXPRESSION
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
Definition: ExpressionException.h:73
TimeUtil.h
float
#define float
Definition: 16_Level.h:22
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:181
VAROUT
#define VAROUT(x)
Definition: StringHelpers.h:198
armarx::Quaternion< float, 0 >
armarx::ManagedIceObject::offeringTopic
void offeringTopic(const std::string &name)
Registers a topic for retrival after initialization.
Definition: ManagedIceObject.cpp:300
IceUtil::Handle< class PropertyDefinitionContainer >
armarx::min
std::vector< T > min(const std::vector< T > &v1, const std::vector< T > &v2)
Definition: VectorHelpers.h:327
IceInternal::ProxyHandle<::IceProxy::armarx::SharedRobotInterface >
MemoryXTypesObjectFactories.h
angle
double angle(const Point &a, const Point &b, const Point &c)
Definition: point.hpp:109
armarx::Logging::deactivateSpam
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
distance
double distance(const Point &a, const Point &b)
Definition: point.hpp:95
armarx::ManagedIceObject::getName
std::string getName() const
Retrieve name of object.
Definition: ManagedIceObject.cpp:108
armarx::LaserScannerSelfLocalisation::reportPlatformOdometryPose
void reportPlatformOdometryPose(Ice::Float, Ice::Float, Ice::Float, const Ice::Current &) override
Definition: LaserScannerSelfLocalisation.cpp:642
armarx::ArmarXDataPath::getAbsolutePath
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
Definition: ArmarXDataPath.cpp:109
armarx::PeriodicTask
Definition: ArmarXManager.h:70
memoryx::AgentInstance
Definition: AgentInstance.h:45
GfxTL::Matrix3f
MatrixXX< 3, 3, float > Matrix3f
Definition: MatrixXX.h:649
min
T min(T t1, T t2)
Definition: gdiam.h:44
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:193
armarx::LaserScanData::pose
Eigen::Matrix4f pose
Definition: LaserScannerSelfLocalisation.h:64
ArmarXDataPath.h
armarx::LaserScannerSelfLocalisation::onConnectComponent
void onConnectComponent() override
Definition: LaserScannerSelfLocalisation.cpp:416
armarx::ExtractedEdge::segment
LineSegment2Df segment
Definition: LaserScannerSelfLocalisation.h:57
armarx::ExtractedEdge
Definition: LaserScannerSelfLocalisation.h:55
armarx::ManagedIceObject::usingProxy
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
Definition: ManagedIceObject.cpp:154
armarx::ctrlutil::s
double s(double t, double s0, double v0, double a0, double j)
Definition: CtrlUtil.h:33
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:27
armarx::LaserScannerSelfLocalisation::getMap
LineSegment2DSeq getMap(const Ice::Current &) override
Definition: LaserScannerSelfLocalisation.cpp:1355
dt
constexpr T dt
Definition: UnscentedKalmanFilterTest.cpp:45
Line
Eigen::ParametrizedLine< float, 2 > Line
Definition: LaserScannerSelfLocalisation.cpp:52