8#include <Eigen/Geometry>
10#include <SimoxUtility/algorithm/apply.hpp>
11#include <SimoxUtility/algorithm/get_map_keys_values.h>
12#include <SimoxUtility/color/Color.h>
13#include <SimoxUtility/color/ColorMap.h>
14#include <SimoxUtility/color/hsv.h>
15#include <SimoxUtility/math/pose.h>
20#include <ArmarXCore/interface/core/PackagePath.h>
30#include <RobotAPI/interface/components/TrajectoryPlayerInterface.h>
32#include <RobotAPI/libraries/armem_laser_scans/aron/LaserScan.aron.generated.h>
49 p.enabled, prefix +
"enabled",
"Enable or disable visualization of objects.");
50 defs->optional(p.frequencyHz, prefix +
"frequenzyHz",
"Frequency of visualization.");
52 p.uniformColor, prefix +
"uniformColor",
"If enabled, points will be drawn in red.");
53 defs->optional(p.maxRobotAgeMs,
54 prefix +
"maxRobotAgeMs",
55 "Maximum age of robot state before a new one is retrieved in milliseconds.");
56 defs->optional(p.colorByIntensity,
"colorByIntensity",
"");
57 defs->optional(p.pointSizeInPixels, prefix +
"pointSizeInPixels",
"Point size in pixels.");
64 this->coreSegment = coreSegment;
65 this->virtualRobotReader = virtualRobotReader;
74 bool batchMode =
true;
103 updateTask =
nullptr;
109 const std::string& tabName)
112 remoteGuiTabName_ = tabName;
118 if (needsTabRebuild_.exchange(
false))
120 createOrUpdateCalibrationTab();
124 bool anyChanged =
false;
125 for (
const auto& [frame, spinboxes] : calibrationTab_.sensors)
127 for (
const auto& sb : spinboxes)
129 anyChanged = anyChanged || sb.hasValueChanged();
135 std::lock_guard<std::mutex> lock(sensorMutex_);
136 for (
const auto& [frame, spinboxes] : calibrationTab_.sensors)
138 calibrationData_[frame] = {
139 spinboxes[0].getValue(), spinboxes[1].getValue(), spinboxes[2].getValue()};
145 Visu::createOrUpdateCalibrationTab()
150 std::set<std::string> frames;
151 std::map<std::string, SensorCalibrationData> currentData;
153 std::lock_guard<std::mutex> lock(sensorMutex_);
154 frames = knownSensorFrames_;
155 currentData = calibrationData_;
159 calibrationTab_.sensors.clear();
164 grid.
add(
Label(
"Sensor"), {.row = row, .column = 0})
165 .add(
Label(
"x [mm]"), {.row = row, .column = 1})
166 .add(
Label(
"y [mm]"), {.row = row, .column = 2})
167 .add(
Label(
"yaw [deg]"), {.row = row, .column = 3});
170 for (
const auto& frame : frames)
172 const auto& d = currentData[frame];
173 auto& sb = calibrationTab_.sensors[frame];
175 sb[0].setRange(-10.f, 10.f);
176 sb[0].setDecimals(1);
179 sb[1].setRange(-10.f, 10.f);
180 sb[1].setDecimals(1);
183 sb[2].setRange(-5.f, 5.f);
184 sb[2].setDecimals(2);
185 sb[2].setValue(d.yaw);
187 grid.
add(
Label(frame), {.row = row, .column = 0})
188 .add(sb[0], {.row = row, .column = 1})
189 .add(sb[1], {.row = row, .column = 2})
190 .add(sb[2], {.row = row, .column = 3});
194 VBoxLayout root = {grid, VSpacer()};
195 guiUser_->RemoteGui_createTab(remoteGuiTabName_, root, &calibrationTab_);
199 Visu::getCalibrationOffset(
const std::string& sensorFrame)
const
201 std::lock_guard<std::mutex> lock(sensorMutex_);
202 const auto it = calibrationData_.find(sensorFrame);
203 if (it == calibrationData_.end())
205 return Eigen::Isometry3f::Identity();
208 const auto& d = it->second;
209 constexpr float kDegToRad =
static_cast<float>(
M_PI) / 180.f;
211 Eigen::Isometry3f offset = Eigen::Isometry3f::Identity();
212 offset.translation().x() = d.x;
213 offset.translation().y() = d.y;
215 Eigen::AngleAxisf(d.yaw * kDegToRad, Eigen::Vector3f::UnitZ()).toRotationMatrix();
222 CycleUtil cycle(
static_cast<int>(1000 / p.frequencyHz));
223 while (updateTask and not updateTask->isStopped())
234 catch (
const std::exception& e)
236 ARMARX_WARNING <<
"Caught exception while visualizing robots: \n" << e.what();
240 ARMARX_WARNING <<
"Caught unknown exception while visualizing robots.";
243 if (debugObserver.has_value())
245 debugObserver->sendDebugObserverBatch();
248 cycle.waitForCycleDuration();
253 Visu::visualizeScan(
const std::vector<ScanPoint>& points,
254 const std::string& sensorName,
255 const std::string& agentName,
256 const viz::Color& color)
258 viz::PointCloud pointCloud(
"laser_scan");
260 ARMARX_VERBOSE <<
"Point cloud with " << points.size() <<
" points";
262 for (
const auto& point : points)
266 const viz::Color specificColor = [&point, &color,
this]() -> viz::Color
268 if (p.colorByIntensity)
270 Eigen::Vector3f hsv = simox::color::rgb_to_hsv(
271 Eigen::Vector3f(
static_cast<float>(color.r) / 255.f,
272 static_cast<float>(color.g) / 255.f,
273 static_cast<float>(color.b) / 255.f));
277 hsv(2) = std::clamp<float>(point.intensity, 0., 1.);
279 const Eigen::Vector3f rgb = simox::color::hsv_to_rgb(hsv);
281 return viz::Color{rgb(0), rgb(1), rgb(2)};
287 pointCloud.addPoint(point.point.x(), point.point.y(), point.point.z(), specificColor);
290 pointCloud.pointSizeInPixels(p.pointSizeInPixels);
292 viz::Layer l = arviz.layer(agentName +
"/" + sensorName);
298 std::vector<ScanPoint>
300 const Eigen::Isometry3f& global_T_sensor)
302 const auto scanCartesian =
305 std::vector<ScanPoint> points;
306 points.reserve(scan.
data.size());
308 for (std::size_t i = 0; i < scan.
data.size(); i++)
310 const auto& point = scanCartesian.at(i);
311 const auto& raw = scan.
data.at(i);
313 const Eigen::Vector3f pointGlobal = global_T_sensor * point;
314 points.push_back(
ScanPoint{.point = pointGlobal, .intensity = raw.intensity});
362 std::map<std::string, armem::laser_scans::LaserScanStamped>
363 Visu::getCurrentLaserScans()
370 const std::optional<armarx::armem::laser_scans::arondto::LaserScanStamped>
dto =
378 const auto ndArrayNavigator =
383 laserScanStamped.
data =
388 return laserScanStamped;
391 std::map<std::string, armem::laser_scans::LaserScanStamped> scans;
395 const auto scan =
convert(instance);
396 scans[instance.id().providerSegmentName +
"/" + instance.id().entityName] = scan;
399 const auto applyToEntity = [&](
const wm::Entity& entity)
406 const auto& snapshot = entity.getLatestSnapshot();
408 snapshot.forEachInstance(applyToInstance);
411 const auto applyToProviderSegment = [&](
const auto& providerSegment)
412 { providerSegment.forEachEntity(applyToEntity); };
418 coreSegment->doLocked([&] { coreSegment->forEachProviderSegment(applyToProviderSegment); });
427 std::map<std::string, armem::laser_scans::LaserScanStamped> currentLaserScans =
428 getCurrentLaserScans();
433 bool newSensors =
false;
435 std::lock_guard<std::mutex> lock(sensorMutex_);
436 for (
const auto& [provider, scan] : currentLaserScans)
438 if (knownSensorFrames_.insert(scan.header.frame).second)
440 calibrationData_[scan.header.frame] = {};
447 needsTabRebuild_ =
true;
453 for (
const auto& [provider, scan] : currentLaserScans)
457 const auto global_T_sensor = [&]() -> Eigen::Isometry3f
459 const auto robot = getSynchronizedRobot(scan.header.agent, scan.header.timestamp);
464 return Eigen::Isometry3f::Identity();
467 const auto sensorNode = robot->getRobotNode(scan.header.frame);
469 <<
"` for robot `" << scan.header.agent <<
"`";
471 ARMARX_VERBOSE <<
"Sensor position for sensor `" << scan.header.frame <<
"` is "
472 << sensorNode->getGlobalPosition();
473 return Eigen::Isometry3f{sensorNode->getGlobalPose()};
476 const Eigen::Isometry3f global_T_sensorCalibrated =
477 global_T_sensor * getCalibrationOffset(scan.header.frame);
479 const std::vector<ScanPoint> points =
482 const auto color = [&]() -> simox::Color
486 return simox::Color::red();
489 return simox::color::GlasbeyLUT::at(i++);
492 visualizeScan(points, scan.header.frame, scan.header.agent, color);
497 Visu::getSynchronizedRobot(
const std::string& name,
const DateTime&
timestamp)
499 if (robots.count(name) == 0)
502 const auto robot = virtualRobotReader->getRobot(name);
514 auto& entry = robots.at(name);
515 if (entry.second.isInvalid() ||
518 if (virtualRobotReader->synchronizeRobotPose(*entry.first,
timestamp))
int Label(int n[], int size, int *curLabel, MiscLib::Vector< std::pair< int, size_t > > *labels)
static DateTime Invalid()
Brief description of class DebugObserverHelper.
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.
void setTag(const LogTag &tag)
The VirtualRobotReader class.
void defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string &prefix="visu.")
void connect(const viz::Client &arviz, DebugObserverInterfacePrx debugObserver=nullptr)
void disconnect()
Stop and join the visualization task. Safe to call more than once.
void init(const wm::CoreSegment *coreSegment, armem::robot_state::VirtualRobotReader *virtualRobotReader)
void connectRemoteGui(armarx::LightweightRemoteGuiComponentPluginUser *guiUser, const std::string &tabName)
static PointerType DynamicCast(const VariantPtr &n)
static Duration MilliSeconds(std::int64_t milliSeconds)
Constructs a duration in milliseconds.
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#define ARMARX_INFO
The normal logging level.
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
#define ARMARX_VERBOSE
The logging level for verbose information.
std::shared_ptr< class Robot > RobotPtr
EigenVectorT toCartesian(const LaserScanStep &laserScanStep)
SensorHeader fromAron(const arondto::SensorHeader &aronSensorHeader)
This file is part of ArmarX.
std::vector< ScanPoint > convertScanToGlobal(const armem::laser_scans::LaserScanStamped &scan, const Eigen::Isometry3f &global_T_sensor)
armem::wm::EntityInstance EntityInstance
void fromAron(const arondto::MemoryID &dto, MemoryID &bo)
armarx::core::time::DateTime Time
std::string toStringMilliSeconds(const Time &time, int decimals=3)
Returns time as e.g.
std::optional< AronClass > tryCast(const wm::EntityInstance &item)
Tries to cast a armem::EntityInstance to AronClass.
::IceInternal::ProxyHandle<::IceProxy::armarx::DebugObserverInterface > DebugObserverInterfacePrx
armem::articulated_object::ArticulatedObject convert(const VirtualRobot::Robot &obj, const armem::Time ×tamp)
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
SimpleRunningTask(Ts...) -> SimpleRunningTask< std::function< void(void)> >
GridLayout & add(Widget const &child, Pos pos, Span span=Span{1, 1})
void add(ElementT const &element)