17#include <sys/inotify.h>
22#include <Eigen/Geometry>
24#include <range/v3/algorithm/all_of.hpp>
25#include <range/v3/algorithm/contains.hpp>
26#include <range/v3/range/conversion.hpp>
27#include <range/v3/view/filter.hpp>
28#include <range/v3/view/transform.hpp>
30#include <SimoxUtility/algorithm/get_map_keys_values.h>
31#include <SimoxUtility/algorithm/string.h>
32#include <SimoxUtility/json.h>
33#include <SimoxUtility/math/pose/pose.h>
34#include <SimoxUtility/shapes/OrientedBox.h>
35#include <VirtualRobot/Nodes/RobotNode.h>
36#include <VirtualRobot/RobotNodeSet.h>
37#include <VirtualRobot/VirtualRobot.h>
38#include <VirtualRobot/XML/RobotIO.h>
68#include <RobotAPI/interface/objectpose/ObjectPoseProvider.h>
69#include <RobotAPI/interface/objectpose/ObjectPoseStorageInterface.h>
70#include <RobotAPI/interface/objectpose/object_pose_types.h>
73#include <RobotAPI/libraries/ArmarXObjects/aron/ObjectPose.aron.generated.h>
79#include <RobotAPI/libraries/armem_objects/aron/ObjectClass.aron.generated.h>
80#include <RobotAPI/libraries/armem_objects/aron/ObjectInstance.aron.generated.h>
86#include <linux/limits.h>
97 "Name of robot whose note can be calibrated.\n"
98 "If not given, the 'fallbackName' is used.");
99 defs->optional(
robotNode, prefix +
"robotNode",
"Robot node which can be calibrated.");
100 defs->optional(
offset, prefix +
"offset",
"Offset for the node to be calibrated.");
105 objects::instaceSegmentID.coreSegmentName,
109 oobbCache.setFetchFn(
110 [
this](
const ObjectID&
id) -> std::optional<simox::OrientedBoxf>
113 if (std::optional<ObjectInfo> objectInfo =
objectFinder.findObject(
id))
117 objectInfo->setLogError(
false);
118 return objectInfo->loadOOBB();
120 catch (
const std::ios_base::failure& e)
123 ARMARX_WARNING <<
"Could not get OOBB of object " <<
id <<
".\n- "
134 classNameToDatasetCache.setFetchFn(
135 [
this](
const std::string& className)
137 std::optional<ObjectInfo> objectInfo =
objectFinder.findObject(className);
138 return objectInfo ? objectInfo->dataset() :
"";
149 SpecializedCoreSegment::defineProperties(defs, prefix);
152 p.discardSnapshotsWhileAttached,
153 prefix +
"DiscardSnapshotsWhileAttached",
154 "If true, no new snapshots are stored while an object is attached to a robot node.\n"
155 "If false, new snapshots are stored, but the attachment is kept in the new snapshots.");
157 defs->optional(p.separateProviderForAttachedObjects,
158 prefix +
"separateProviderForAttachedObjects",
159 "If true, attached objects are stored with a separate provider (see "
160 "`attachedProviderName`).");
162 defs->optional(p.attachedProviderName,
163 prefix +
"attachedProviderName",
164 "If `separateProviderForAttachedObjects` is true, this provider will be "
165 "used for attached objects. Otherwise, attached objects will be stored with "
166 "the same provider as non-attached objects.");
168 defs->optional(p.automaticallyDetachObjectsOnHandOpening,
169 prefix +
"AutomaticallyDetachObjectsOnHandOpening",
170 "If true, the hand state will be monitored and objects will be detached "
171 "automatically when the hand opens.");
173 defs->optional(p.automaticallyDetachPositionThreshold,
174 prefix +
"AutomaticallyDetachPositionThreshold",
175 "See `AutomaticallyDetachObjectsOnHandOpening`. Internal threshold to "
176 "determine whether hand is opened.");
178 defs->optional(
robots.fallbackName,
179 prefix +
"robots.FallbackName",
180 "Robot name to use as fallback if the robot name is not specified "
181 "in a provided object pose.");
184 p.sceneSnapshotsPackage,
185 prefix +
"scene.10_Package",
186 "ArmarX package containing the scene snapshots.\n"
187 "Scene snapshots are expected to be located in Package/data/Package/Scenes/*.json.");
188 defs->optional(p.sceneSnapshotsDirectory,
189 prefix +
"scene.11_Directory",
190 "Directory in Package/data/Package/ containing the scene snapshots.");
192 std::vector<std::string> sceneSnapshotToLoadDescription = {
193 "Scene(s) to load on startup.",
194 "Specify multiple scenes in a ; separated list.",
195 "Each entry must be one of the following:",
196 "(1) A scene file in 'Package/scenes/' (with or without '.json' extension), "
197 "e.g. 'MyScene', 'MyScene.json'",
198 "(2) A path to a scene file relative to 'Package/scenes/' (with or without '.json' "
200 "e.g. 'path/to/MyScene', 'path/to/MyScene.json'",
201 "(3) An ArmarX data path to a scene file, e.g. 'Package/scenes/path/to/MyScene.json'",
203 defs->optional(p.sceneSnapshotsToLoad,
204 prefix +
"scene.12_SnapshotToLoad",
205 simox::alg::join(sceneSnapshotToLoadDescription,
" \n"));
207 defs->optional(p.autoReloadSceneSnapshotsOnFileChange,
208 prefix +
"scene.13_autoReloadSceneSnapshotsOnFileChange");
210 decay.defineProperties(defs, prefix +
"decay.");
213 std::vector<std::string>
214 Segment::Properties::getSceneSnapshotsToLoad()
const
216 if (sceneSnapshotsToLoad.empty())
221 return simox::alg::split(sceneSnapshotsToLoad,
";", trim);
227 SpecializedCoreSegment::init();
229 const std::vector<std::string> scenes = p.getSceneSnapshotsToLoad();
230 for (
const std::string& scene : scenes)
232 const bool lockMemory =
false;
233 commitSceneSnapshotFromFilename(scene, lockMemory);
238 if (p.autoReloadSceneSnapshotsOnFileChange)
246 inotifyFd = inotify_init();
253 std::map<int, std::string> wds;
256 for (
const auto& scene : scenes)
258 if (std::optional<std::filesystem::path> path = resolveSceneFilepath(scene))
260 auto wd = inotify_add_watch(inotifyFd, path.value().c_str(), IN_MODIFY);
263 ARMARX_WARNING <<
"inotify_add_watch for scene: " << scene <<
"` failed.";
266 ARMARX_INFO <<
"inotify_add_watch for scene: " << scene <<
"` added.";
267 wds.emplace(wd, scene);
271 ARMARX_WARNING <<
"Faild to add file watcher for scene: `" << scene <<
"`.";
278 [
this, inotifyFd, wds]()
280 constexpr std::size_t BUF_LEN = (10 * (
sizeof(inotify_event) + NAME_MAX + 1));
288 numRead =
read(inotifyFd, buf, BUF_LEN);
301 for (
char* p = buf; p < buf + numRead;)
303 auto*
event =
reinterpret_cast<inotify_event*
>(p);
305 const auto& scene = wds.at(event->wd);
308 p +=
sizeof(
struct inotify_event) + event->len;
310 const bool lockMemory =
true;
311 commitSceneSnapshotFromFilename(scene, lockMemory);
316 fileWatcherTask->start();
331 const objpose::data::ProvidedObjectPoseSeq& providedPoses,
332 const Calibration& calibration,
333 std::optional<armem::Time> discardUpdatesUntil)
339 stats.numUpdated = 0;
344 for (
const objpose::data::ProvidedObjectPose& provided : providedPoses)
349 std::optional<objpose::ObjectPose> previousPose;
360 bool discard =
false;
361 if (discardUpdatesUntil &&
timestamp < discardUpdatesUntil.value())
366 else if (previousPose)
368 if (p.discardSnapshotsWhileAttached && previousPose->attachment)
373 else if (
timestamp == previousPose->timestamp)
392 << provided.robotName <<
"`.";
397 bool synchronized =
false;
400 if (robot and robotSyncTimestamp !=
timestamp)
403 if (not
synchronized)
406 <<
timestamp <<
" of provided object pose (" << provided.objectID
408 <<
" seconds in the past.";
415 if (calibration.offset != 0 and robot->hasRobotNode(calibration.robotNode))
417 VirtualRobot::RobotNodePtr robotNode =
418 robot->getRobotNode(calibration.robotNode);
420 float value = robotNode->getJointValue();
421 float newValue =
value + calibration.offset;
429 VirtualRobot::RobotNode::JointLimits limits = robotNode->getJointLimits();
430 bool limitsChanged =
false;
431 if (newValue < limits.low)
433 limits.low = newValue;
434 limitsChanged =
true;
436 if (newValue > limits.high)
438 limits.high = newValue;
439 limitsChanged =
true;
443 robotNode->setJointLimits(limits.low, limits.high);
446 robotNode->setJointValue(newValue);
450 else if (robot and
timestamp == robotSyncTimestamp)
455 if (not
synchronized)
462 <<
"Robot could not be synchronized; discarding robot";
466 objpose::ObjectPose& newPose = newObjectPoses.emplace_back();
467 if (provided.objectPoseFrame.empty())
469 objpose::data::ProvidedObjectPose copy = provided;
471 newPose.fromProvidedPose(copy, robot);
475 newPose.fromProvidedPose(provided, robot);
478 if (previousPose && previousPose->attachment)
482 newPose.attachment = previousPose->attachment;
485 if (newPose.objectID.dataset().empty())
488 const std::string dataset =
489 classNameToDatasetCache.get(newPose.objectID.className());
490 if (!dataset.empty())
493 dataset, newPose.objectID.className(), newPose.objectID.instanceName()};
496 if (!provided.localOOBB)
522 providerName.empty() ? pose.providerName : providerName);
525 update.arrivedTime = now;
526 update.referencedTime = pose.timestamp;
527 update.confidence = pose.confidence;
529 arondto::ObjectInstance
dto;
532 if (
auto instance = findClassInstance(pose.objectID))
541 update.instancesData.push_back(
dto.toAron());
555 for (
auto& [providerName, objectPoses] : objectPosesForProvider)
559 auto attachedObjects =
560 ranges::views::filter(objectPoses,
566 ranges::to<ObjectPoseMap>();
568 updateObjectPoses(attachedObjects, now);
570 std::map<std::string, std::vector<objpose::ObjectPose>> objectPosesByProviderToUpdate;
572 for (
const auto& [
id, objectPose] : attachedObjects)
574 objectPosesByProviderToUpdate[objectPose.providerName].push_back(objectPose);
586 for (
const auto& [providerName, objectPoses] : objectPosesByProviderToUpdate)
589 std::vector<EntityUpdate> updates;
594 << pose.objectID <<
" from provider " << providerName;
599 providerName.empty() ? pose.providerName : providerName);
603 update.arrivedTime = now;
604 update.referencedTime = now;
605 update.sentTime = now;
607 update.confidence = pose.confidence;
609 arondto::ObjectInstance
dto;
613 dto.pose.timestamp = now;
616 if (
auto instance = findClassInstance(pose.objectID))
625 update.instancesData.push_back(
dto.toAron());
630 ARMARX_VERBOSE <<
"No object poses to commit for provider " << providerName
636 <<
" object poses updated due to attachment for provider "
637 << providerName <<
".";
641 commit.
updates.insert(commit.
updates.end(), updates.begin(), updates.end());
654 updateObjectPoses(objectPoses, now);
655 return filterObjectPoses(objectPoses);
664 updateObjectPoses(objectPoses, now);
665 return filterObjectPoses(objectPoses);
673 if (providerName.empty())
691 if (
segmentPtr->hasProviderSegment(providerName))
706 bool agentSynchronized =
false;
708 for (
auto& [
id, objectPose] : objectPoses)
710 std::string robotName = objectPose.robotName;
713 if (objectPose.attachment)
715 robotName = objectPose.attachment->agentName;
719 updateObjectPose(objectPose, now, robot, agentSynchronized);
727 bool& agentSynchronized)
const
729 for (
auto& [
id, objectPose] : objectPoses)
731 updateObjectPose(objectPose, now, agent, agentSynchronized);
736 Segment::updateObjectPose(
ObjectPose& objectPose,
739 bool& agentSynchronized)
const
750 Segment::filterObjectPoses(
const ObjectPoseMap& objectPoses)
const
753 for (
const auto& [
id, objectPose] : objectPoses)
755 if (!(
decay.enabled && objectPose.confidence <
decay.removeObjectsBelowConfidence))
757 result[
id] = objectPose;
766 const auto children = node->getChildren<VirtualRobot::RobotNode>();
768 for (
const auto& child : children)
770 names.insert(child->getName());
778 bool&
synchronized)
const
794 if (not
synchronized)
802 if (not
synchronized)
810 if (p.automaticallyDetachObjectsOnHandOpening)
813 const std::string SideIdentifierLeft =
"Left";
814 const std::string SideIdentifierRight =
"Right";
817 const auto getHand = [
this, SideIdentifierLeft, SideIdentifierRight](
818 const std::string& nodeName,
819 const VirtualRobot::Robot& robot) -> std::optional<std::string>
821 const auto getNodesForSide =
822 [](
const std::string& nodeSetName,
823 const VirtualRobot::Robot& robot) -> std::set<std::string>
825 const auto nodeSet = robot.getRobotNodeSet(nodeSetName);
827 <<
"Node set " <<
QUOTED(nodeSet) <<
" not found in robot";
829 const auto rootNode = nodeSet->getKinematicRoot();
832 std::set<std::string> names;
838 const std::set<std::string> leftArmNodeNames = getNodesForSide(
"LeftArm", robot);
839 const std::set<std::string> rightArmNodeNames = getNodesForSide(
"RightArm", robot);
841 ARMARX_DEBUG <<
"Left arm nodes: " << leftArmNodeNames.size();
842 ARMARX_DEBUG <<
"Right arm nodes: " << rightArmNodeNames.size();
845 const auto matchToSide =
850 SideIdentifierRight](
const std::string& nodeName) -> std::optional<std::string>
852 if (ranges::contains(leftArmNodeNames, nodeName))
854 return SideIdentifierLeft;
856 if (ranges::contains(rightArmNodeNames, nodeName))
858 return SideIdentifierRight;
862 <<
" to left or right arm";
866 return matchToSide(nodeName);
869 const auto getOpenHandJointValues =
870 [](
const std::string& hand,
871 const VirtualRobot::Robot& robot) -> std::map<std::string, float>
874 std::stringstream eefSS;
875 eefSS <<
"Hand_" << hand.front() <<
"_EEF";
876 const std::string endEffectorName = eefSS.str();
878 const auto endEffector = robot.getEndEffector(endEffectorName);
881 const auto openShape = endEffector->getPreshape(
"Open");
883 <<
"Shape " <<
QUOTED(
"Open") <<
" not found for end-effector "
884 <<
QUOTED(endEffectorName);
886 return openShape->getRobotNodeJointValueMap();
890 const std::optional<std::string> hand =
891 getHand(objectPose.
attachment->frameName, *agent);
894 const auto isHandOpen =
895 [&getOpenHandJointValues, &agent,
this](
const std::string& hand) ->
bool
898 const std::map<std::string, float> currentJointValues = agent->getJointValues();
901 const std::map<std::string, float> openHandJointValues =
902 getOpenHandJointValues(hand, *agent);
903 const std::vector<std::string> involvedHandJointNames =
904 simox::get_keys(openHandJointValues);
908 const auto positionDifference =
909 [&](
const std::string& nodeName)
noexcept(
false) ->
float
911 ARMARX_CHECK(currentJointValues.count(nodeName) > 0) << nodeName;
912 const float currentVal = currentJointValues.at(nodeName);
914 ARMARX_CHECK(openHandJointValues.count(nodeName) > 0) << nodeName;
915 const float openVal = openHandJointValues.at(nodeName);
917 return std::abs(openVal - currentVal);
920 const std::vector<float> positionDifferences =
921 involvedHandJointNames | ranges::views::transform(positionDifference) |
924 const bool isHandOpen =
925 ranges::all_of(positionDifferences,
926 [automaticallyDetachPositionThreshold =
927 p.automaticallyDetachPositionThreshold](
const float diff)
928 { return diff < automaticallyDetachPositionThreshold; });
933 if (hand.has_value())
935 if (isHandOpen(hand.value()))
938 <<
" is now open. Invalidating attachment for "
949 <<
" could not be linked to a hand.";
952 if (isHandOpen(SideIdentifierLeft) and isHandOpen(SideIdentifierRight))
954 ARMARX_INFO <<
"As both hands are open, the object "
973 std::map<std::string, objpose::ObjectPoseMap>
996 std::map<std::string, objpose::ObjectPoseMap>
999 std::map<std::string, ObjectPoseMap> result;
1021 std::map<std::string, ObjectPoseMap>& out)
1037 if (!entity.
empty())
1045 if (it->second.timestamp < pose.
timestamp)
1060 arondto::ObjectInstance
dto;
1067 arondto::ObjectInstance
1076 arondto::ObjectInstance
data;
1087 ARMARX_INFO <<
"Found " << objectPoses.size() <<
" object poses";
1090 for (
const auto& [objectId, objectPose] : objectPoses)
1093 articulatedObject.
config.
jointMap = objectPose.objectJointValues;
1096 articulatedObject.
instance = objectPose.objectID.instanceName();
1097 articulatedObject.
timestamp = objectPose.timestamp;
1100 ARMARX_INFO <<
"Object id for objectPose is " << objectPose.objectID.str();
1103 if (
auto classInstance = findClassInstance(objectPose.objectID))
1105 arondto::ObjectClass
dto;
1109 dto.fromAron(classInstance->data());
1129 objects.push_back(articulatedObject);
1136 std::map<DateTime, objpose::ObjectPose>
1141 std::map<DateTime, objpose::ObjectPose> result;
1151 arondto::ObjectInstance
dto;
1157 result.emplace(snapshot.
time(), pose);
1164 std::optional<simox::OrientedBoxf>
1167 return oobbCache.get(
id);
1170 objpose::ProviderInfo
1177 catch (
const std::out_of_range&)
1179 std::stringstream ss;
1180 ss <<
"No provider with name '" << providerName <<
"' available.\n";
1181 ss <<
"Available are:\n";
1184 ss <<
"- '" << name <<
"'\n";
1186 throw std::out_of_range(ss.str());
1190 objpose::AttachObjectToRobotNodeOutput
1195 objpose::AttachObjectToRobotNodeOutput output;
1196 output.success =
false;
1203 std::stringstream ss;
1204 ss <<
"Tried to attach object " << objectID <<
" to unknown agent '" << input.agentName
1206 <<
"\n(You can leave the agent name empty if there is only one agent.)\n"
1207 <<
"\nKnown agents: ";
1208 for (
const auto& [name, robot] :
robots.loaded)
1210 ss <<
"\n- '" << name <<
"'";
1217 if (!agent->hasRobotNode(input.frameName))
1219 ARMARX_WARNING <<
"Tried to attach object " << objectID <<
" to unknown node '"
1220 << input.frameName <<
"' of agent '" << agent->getName() <<
"'.";
1223 std::string frameName = input.frameName;
1228 if (!objectEntity || objectEntity->
empty())
1230 ARMARX_WARNING <<
"Tried to attach object " << objectID <<
" to node '" << frameName
1231 <<
"' of agent '" << agent->getName()
1232 <<
"', but object is currently not provided.";
1241 if (input.poseInFrame)
1244 info.
poseInFrame = PosePtr::dynamicCast(input.poseInFrame)->toEigen();
1270 update.entityID = objectEntity->
id();
1272 if (p.separateProviderForAttachedObjects)
1274 update.entityID.providerSegmentName = p.attachedProviderName;
1275 data.pose.providerName = p.attachedProviderName;
1276 data.pose.confidence = 1;
1279 update.referencedTime = now;
1281 arondto::ObjectInstance updated =
data;
1282 toAron(updated.pose.attachment, info);
1283 updated.pose.attachmentValid =
true;
1284 update.instancesData = {updated.toAron()};
1289 ARMARX_INFO <<
"Attached object " << objectID <<
" by provider '" <<
data.pose.providerName
1292 <<
"Object pose in frame: \n"
1295 output.success =
true;
1296 output.attachment =
new objpose::data::ObjectAttachmentInfo();
1297 output.attachment->frameName = info.
frameName;
1298 output.attachment->agentName = info.
agentName;
1304 objpose::DetachObjectFromRobotNodeOutput
1310 std::string providerName = input.providerName;
1312 if (p.separateProviderForAttachedObjects)
1314 providerName = p.attachedProviderName;
1317 std::optional<objpose::arondto::ObjectAttachmentInfo>
attachment;
1325 if (
data.pose.attachmentValid)
1330 storeDetachedSnapshot(
1331 *entity,
data, now, input.commitAttachedPose, input.forgetObject);
1333 if (providerName.empty())
1335 providerName =
data.pose.providerName;
1340 objpose::DetachObjectFromRobotNodeOutput output;
1344 ARMARX_INFO <<
"Detached object " << objectID <<
" by provider '" << providerName
1345 <<
"' from robot node '" <<
attachment->frameName <<
"' of agent '"
1350 ARMARX_INFO <<
"Tried to detach object " << objectID <<
" by provider '" << providerName
1352 <<
"from robot node, but it was not attached.";
1358 objpose::DetachAllObjectsFromRobotNodesOutput
1360 const objpose::DetachAllObjectsFromRobotNodesInput& input)
1366 objpose::DetachAllObjectsFromRobotNodesOutput output;
1367 output.numDetached = 0;
1369 [
this, now, &input, &output](
wm::Entity& entity)
1372 if (
data.pose.attachmentValid)
1374 ++output.numDetached;
1376 this->storeDetachedSnapshot(
1377 entity,
data, now, input.commitAttachedPose, input.forgetObject);
1381 ARMARX_INFO <<
"Detached all objects (" << output.numDetached <<
") from robot nodes.";
1387 Segment::storeDetachedSnapshot(
wm::Entity& entity,
1388 const arondto::ObjectInstance&
data,
1390 const bool commitAttachedPose,
1391 const bool forgetObject)
1395 update.entityID = entity.
id();
1396 update.referencedTime = now;
1398 arondto::ObjectInstance updated;
1399 if (commitAttachedPose and
data.pose.attachmentValid)
1405 robots.
get(objectPose.robotName, objectPose.providerName);
1406 bool agentSynchronized =
false;
1409 objectPose.attachment = std::nullopt;
1410 toAron(updated, objectPose);
1412 else if (forgetObject)
1418 updated.pose.attachmentValid =
false;
1421 updated.pose.confidence = 0;
1426 updated.pose.attachmentValid =
false;
1430 update.instancesData = {updated.toAron()};
1435 std::optional<wm::EntityInstance>
1436 Segment::findClassInstance(
const ObjectID& objectID)
const
1438 const ObjectID classID = {objectID.dataset(), objectID.className()};
1441 std::optional<wm::EntityInstance> result;
1449 const wm::CoreSegment& classCoreSeg =
1450 iceMemory.workingMemory->getCoreSegment(
"Class");
1452 [&classCoreSeg, &classID, &result]()
1455 [&classID, &result](
const wm::ProviderSegment& provSeg)
1459 result = provSeg.
getEntity(classID.str())
1460 .getLatestSnapshot()
1468 if (not result.has_value())
1471 << classID.str() <<
" found";
1475 catch (
const armem::error::ArMemError& e)
1479 return std::nullopt;
1484 Segment::storeScene(
const std::string& filename,
const armarx::objects::Scene& scene)
1486 if (
const std::optional<std::filesystem::path> path = resolveSceneFilepath(filename))
1488 ARMARX_INFO <<
"Storing scene snapshot at: \n" << path.value();
1491 simox::json::write(path.value(), scene, 2);
1493 catch (
const simox::json::error::JsonError& e)
1495 ARMARX_WARNING <<
"Storing scene snapshot failed: \n" << e.what();
1500 std::optional<armarx::objects::Scene>
1501 Segment::loadScene(
const std::string& filename)
1503 if (
const std::optional<std::filesystem::path> path = resolveSceneFilepath(filename))
1505 return loadScene(path.value());
1509 return std::nullopt;
1513 std::optional<armarx::objects::Scene>
1514 Segment::loadScene(
const std::filesystem::path& path)
1519 return simox::json::read<armarx::objects::Scene>(path);
1521 catch (
const simox::json::error::JsonError& e)
1523 ARMARX_WARNING <<
"Loading scene snapshot failed: \n" << e.what();
1524 return std::nullopt;
1528 const std::string Segment::timestampPlaceholder =
"%TIMESTAMP";
1530 std::optional<std::filesystem::path>
1531 Segment::resolveSceneFilepath(
const std::string& _filename)
1533 std::stringstream log;
1534 std::filesystem::path filepath = _filename;
1536 filepath = simox::alg::replace_all(
1538 if (not simox::alg::ends_with(filepath,
".json"))
1540 filepath +=
".json";
1543 if (filepath.is_absolute())
1549 if (filepath.has_parent_path())
1554 const std::string packageName = *filepath.begin();
1556 const PackagePath pp(packageName, std::filesystem::relative(filepath, packageName));
1562 const std::filesystem::path systemPath = pp.toSystemPath();
1564 if (std::filesystem::exists(pp.toSystemPath()))
1570 <<
"' as ArmarX package name. Relative path is '"
1571 << pp.serialize().path
1572 <<
"'. But the file does not exist at system path '" << systemPath
1575 catch (
const armarx::PackageNotFoundException& e)
1578 <<
"' as ArmarX package name. Relative path is '"
1579 << pp.serialize().path <<
"'.";
1586 const PackagePath pp(p.sceneSnapshotsPackage, p.sceneSnapshotsDirectory / filepath);
1591 const std::filesystem::path systemPath = pp.toSystemPath();
1593 if (std::filesystem::exists(pp.toSystemPath()))
1599 <<
"' as ArmarX package name. Relative path is '" << pp.serialize().path
1600 <<
"'. But the file does not exist at system path '" << systemPath
1603 catch (
const armarx::PackageNotFoundException& e)
1605 ARMARX_VERBOSE <<
"Could not interpret '" << p.sceneSnapshotsPackage
1606 <<
"' as ArmarX package name. Relative path is '" << pp.serialize().path
1610 return std::nullopt;
1613 armarx::objects::Scene
1614 Segment::getSceneSnapshot()
const
1616 using armarx::objects::SceneObject;
1620 struct StampedSceneObject
1626 std::map<ObjectID, StampedSceneObject> objects;
1628 [&objects](wm::Entity& entity)
1633 std::optional<arondto::ObjectInstance> objectInstance =
1640 auto it = objects.find(objectID);
1641 if (it == objects.end() or
1642 objectInstance->pose.timestamp > it->second.timestamp)
1644 StampedSceneObject& stamped = objects[objectID];
1645 stamped.timestamp = objectInstance->pose.timestamp;
1647 SceneObject&
object = stamped.object;
1648 object.className = objectID.getClassID().str();
1649 object.instanceName = objectID.instanceName();
1650 object.collection =
"";
1653 simox::math::position(objectInstance->pose.objectPoseGlobal);
1654 object.orientation =
1655 simox::math::orientation(objectInstance->pose.objectPoseGlobal);
1657 object.isStatic = objectInstance->pose.isStatic;
1658 object.jointValues = objectInstance->pose.objectJointValues;
1664 armarx::objects::Scene scene;
1665 for (
const auto& [
id, stamped] : objects)
1667 scene.
objects.emplace_back(std::move(stamped.object));
1673 Segment::commitSceneSnapshot(
const armarx::objects::Scene& scene,
const std::string& sceneName)
1676 std::map<ObjectID, int> idCounters;
1680 for (
const auto&
object : scene.
objects)
1682 if (simox::alg::starts_with(
object.className,
"#"))
1690 objpose::ObjectPose& pose = objectPoses.emplace_back();
1692 pose.providerName = sceneName;
1693 pose.objectType = objpose::ObjectType::KnownObject;
1695 pose.isStatic =
object.isStatic.has_value() ?
object.isStatic.value() :
true;
1696 pose.objectID = classID.withInstanceName(
object.instanceName.empty()
1697 ? std::to_string(idCounters[classID]++)
1698 :
object.instanceName);
1700 pose.objectPoseGlobal = simox::math::pose(
object.position,
object.orientation);
1701 pose.objectPoseRobot = pose.objectPoseGlobal;
1702 pose.objectPoseOriginal = pose.objectPoseGlobal;
1705 pose.objectJointValues =
object.jointValues;
1707 pose.robotConfig = {};
1708 pose.robotPose = Eigen::Matrix4f::Identity();
1709 pose.robotName =
"";
1711 pose.confidence = 1.0;
1713 pose.timestamp = now;
1720 Segment::commitSceneSnapshotFromFilename(
const std::string& filename,
bool lockMemory)
1722 std::stringstream ss;
1723 ss <<
"Loading scene '" << filename <<
"' ...";
1724 if (std::optional<std::filesystem::path> path = resolveSceneFilepath(filename))
1726 ARMARX_INFO << ss.str() <<
"\nfrom " << path.value();
1728 if (
const auto snapshot = loadScene(path.value()))
1730 auto makeSceneName = [](
const std::filesystem::path& path)
1732 std::filesystem::path filename = path.filename();
1733 filename.replace_extension();
1734 return filename.string();
1736 std::string sceneName = makeSceneName(path.value());
1741 segmentPtr->doLocked([
this, &snapshot, &sceneName]()
1742 { commitSceneSnapshot(snapshot.value(), sceneName); });
1746 commitSceneSnapshot(snapshot.value(), sceneName);
1752 ARMARX_INFO << ss.str() <<
" failed: Could not resolve scene name or path.";
1766 const std::string storeLoadPath = [&
data]()
1768 const std::vector<std::string> scenes =
data.p.getSceneSnapshotsToLoad();
1771 std::string storeLoadFilename =
"Scene_" + timestampPlaceholder;
1772 return data.p.sceneSnapshotsPackage +
"/" +
data.p.sceneSnapshotsToLoad;
1776 return scenes.front();
1789 HBoxLayout detachAllObjectsCommitAttachedPoseLayout(
1796 grid.
add(storeLoadLineLayout, {row, 0}, {1, 2});
1798 grid.
add(storeLoadButtonsLayout, {row, 0}, {1, 2});
1805 grid.
add(
Label(
"Discard Snapshots while Attached"), {row, 0})
1810 .add(detachAllObjectsCommitAttachedPoseLayout, {row, 1});
1812 group.setLabel(
"Data");
1813 group.addChild(grid);
1821 bool lockMemory =
true;
1840 segment.properties.maxHistorySize =
1844 segment.segmentPtr->setMaxHistorySize(
1845 long(
segment.properties.maxHistorySize));
1849 segment.p.discardSnapshotsWhileAttached =
1856 objpose::DetachAllObjectsFromRobotNodesInput input;
1859 objpose::DetachAllObjectsFromRobotNodesOutput output =
segment.doLocked(
1860 [&
segment, &input]() {
return segment.detachAllObjectsFromRobotNodes(input); });
1868 std::string robotName = _robotName;
1870 if (robotName.empty())
1874 std::stringstream ss;
1875 if (providerName.empty())
1877 ss <<
"The provided object pose";
1881 ss <<
"The object pose provided by '" << providerName <<
"'";
1883 ss <<
" does not specify a robot name";
1887 ss <<
", and no fallback robot name was configured (e.g. via the properties).\n"
1888 <<
"For these object poses, the object instance segment is not able "
1889 <<
"to provide transformed object poses (global and in robot root frame).";
1896 ss <<
". The object instance segment is falling back to '" <<
fallbackName <<
"'.";
1905 if (
auto it =
loaded.find(robotName); it !=
loaded.end())
1915 robotName,
Clock::Now(), VirtualRobot::RobotIO::RobotDescription::eStructure);
1920 if (not
synchronized)
1922 ARMARX_INFO <<
"The robot '" << robotName <<
"' could be loaded, but not"
1923 <<
" synchronized successfully (e.g., the global localization "
1924 "could be missing). "
1925 <<
"Make sure to synchronize it before use if necessary.";
1928 loaded.emplace(robotName, robot);
int Label(int n[], int size, int *curLabel, MiscLib::Vector< std::pair< int, size_t > > *labels)
#define ARMARX_CHECK_NOT_EMPTY(c)
static DateTime Now()
Current time on the virtual clock.
Eigen::Matrix4f toGlobalEigen(const SharedRobotInterfacePrx &referenceRobot) const
void changeFrame(const SharedRobotInterfacePrx &referenceRobot, const std::string &newFrame)
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.
A known object ID of the form "Dataset/ClassName" or "Dataset/ClassName/InstanceName".
static ObjectID FromString(const std::string &idString)
Construct from a string produced by str(), e.g. ("mydataset/myobject", "mydataset/myclass/myinstance"...
std::string str() const
Return "dataset/className" or "dataset/className/instanceName".
virtual Eigen::Matrix4f toEigen() const
MemoryID withProviderSegmentName(const std::string &name) const
MemoryID withEntityName(const std::string &name) const
MemoryID withTimestamp(Time time) const
std::string providerSegmentName
bool forEachProviderSegment(ProviderSegmentFunctionT &&func)
void forEachSnapshotInTimeRange(const Time &min, const Time &max, FunctionT &&func) const
Return all snapshots between, including, min and max.
auto * findLatestInstance(int instanceIndex=0)
bool forEachInstance(InstanceFunctionT &&func)
EntityInstanceT & getInstance(int index)
Get the given instance.
bool hasEntity(const std::string &name) const
EntityT & getEntity(const std::string &name)
bool forEachEntity(EntityFunctionT &&func)
Helps connecting a Memory server to the Ice interface.
data::CommitResult commit(const data::Commit &commitIce, Time timeArrived)
void updateConfidence(objpose::ObjectPose &pose, const DateTime &now) const
void defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string &prefix="") override
static ObjectPose getLatestObjectPose(const wm::Entity &entity)
std::map< ObjectID, ObjectPose > ObjectPoseMap
objpose::AttachObjectToRobotNodeOutput attachObjectToRobotNode(const objpose::AttachObjectToRobotNodeInput &input)
::armarx::armem::articulated_object::ArticulatedObjects getArticulatedObjects()
objpose::ProviderInfo getProviderInfo(const std::string &providerName)
CommitStats commitObjectPoses(const std::string &providerName, const objpose::data::ProvidedObjectPoseSeq &providedPoses, const Calibration &calibration, std::optional< Time > discardUpdatesUntil=std::nullopt)
void runUpdateObjectPoses()
objpose::ProviderInfoMap providers
objpose::DetachObjectFromRobotNodeOutput detachObjectFromRobotNode(const objpose::DetachObjectFromRobotNodeInput &input)
std::optional< simox::OrientedBoxf > getObjectOOBB(const ObjectID &id)
objpose::ObjectPoseMap getObjectPosesByProvider(const std::string &providerName, const DateTime &now)
objpose::ObjectPoseSeq ObjectPoseSeq
static ObjectPoseMap getLatestObjectPoses(const wm::CoreSegment &coreSeg)
objpose::DetachAllObjectsFromRobotNodesOutput detachAllObjectsFromRobotNodes(const objpose::DetachAllObjectsFromRobotNodesInput &input)
void connect(viz::Client arviz)
objpose::ObjectPoseMap getObjectPoses(const DateTime &now)
static arondto::ObjectInstance getLatestInstanceData(const wm::Entity &entity)
ObjectFinder objectFinder
static std::map< DateTime, ObjectPose > getObjectPosesInRange(const wm::Entity &entity, const DateTime &start, const DateTime &end)
objpose::ObjectPose ObjectPose
void updateAttachement(ObjectPose &objectPose, VirtualRobot::RobotPtr agent, bool &synchronized) const
If the object is attached to a robot node, update it according to the current robot state.
std::map< std::string, ObjectPoseMap > getLatestObjectPosesByProviders()
Segment(server::MemoryToIceAdapter &iceMemory)
wm::Entity * findObjectEntity(const ObjectID &objectID, const std::string &providerName="")
virtual ~Segment() override
SpecializedCoreSegment(MemoryToIceAdapter &iceMemory, const std::string &defaultCoreSegmentName="", aron::type::ObjectPtr coreSegmentAronType=nullptr, int defaultMaxHistorySize=10, const std::vector< PredictionEngine > &predictionEngines={})
MemoryToIceAdapter & iceMemory
server::wm::CoreSegment * segmentPtr
auto doLocked(FunctionT &&function) const
Execute function under shared (read) lock.
Represents a point in time.
static DateTime Invalid()
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_CHECK_GREATER_EQUAL(lhs, rhs)
This macro evaluates whether lhs is greater or equal (>=) rhs and if it turns out to be false it will...
#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_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
#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::string const GlobalFrame
Variable of the global coordinate system.
std::shared_ptr< class Robot > RobotPtr
armarx::armem::robot_state::Robots ArticulatedObjects
armarx::armem::robot_state::Robot ArticulatedObject
void collectNamesRecursively(const VirtualRobot::RobotNodePtr &node, std::set< std::string > &names)
armem::wm::EntitySnapshot EntitySnapshot
armem::wm::EntityInstance EntityInstance
void fromAron(const arondto::MemoryID &dto, MemoryID &bo)
armarx::core::time::DateTime Time
std::optional< AronClass > tryCast(const wm::EntityInstance &item)
Tries to cast a armem::EntityInstance to AronClass.
void toAron(arondto::MemoryID &dto, const MemoryID &bo)
std::map< ObjectID, ObjectPose > ObjectPoseMap
std::vector< ObjectPose > ObjectPoseSeq
const char * toString(InteractionFeedbackType type)
void read(auto &eigen, auto *table)
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
void fromIce(const std::map< IceKeyT, IceValueT > &iceMap, boost::container::flat_map< CppKeyT, CppValueT > &cppMap)
SimpleRunningTask(Ts...) -> SimpleRunningTask< std::function< void(void)> >
std::shared_ptr< Value > value()
GridLayout & add(Widget const &child, Pos pos, Span span=Span{1, 1})
A bundle of updates to be sent to the memory.
std::vector< EntityUpdate > updates
The entity updates.
An update of an entity for a specific point in time.
auto & getLatestSnapshot(int snapshotIndex=0)
Retrieve the latest entity snapshot.
description::RobotDescription description
void defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string &prefix="calibration.")
armarx::RemoteGui::Client::CheckBox detachAllObjectsCommitAttachedPoseCheckBox
armarx::RemoteGui::Client::CheckBox infiniteHistory
armarx::RemoteGui::Client::GroupBox group
armarx::RemoteGui::Client::Button detachAllObjectsButton
armarx::RemoteGui::Client::IntSpinBox maxHistorySize
armarx::RemoteGui::Client::LineEdit storeLoadLine
armarx::RemoteGui::Client::CheckBox discardSnapshotsWhileAttached
void update(Segment &data)
armarx::RemoteGui::Client::Button storeButton
armarx::RemoteGui::Client::Button loadButton
void setup(const Segment &data)
robot_state::VirtualRobotReader * reader
std::map< std::string, VirtualRobot::RobotPtr > loaded
VirtualRobot::RobotPtr get(const std::string &robotName, const std::string &providerName="")
std::vector< SceneObject > objects
Eigen::Matrix4f poseInFrame
An object pose as stored by the ObjectPoseStorage.
armarx::ObjectID objectID
The object ID, i.e. dataset, class name and instance name.
std::optional< ObjectAttachmentInfo > attachment
Attachment information.
void updateAttached(VirtualRobot::RobotPtr agent)
Update an the object pose and robot state of an attached object pose according to the given agent sta...
DateTime timestamp
Source timestamp.