33#include <Ice/BuiltinSequences.h>
34#include <Ice/Current.h>
36#include <SimoxUtility/algorithm/get_map_keys_values.h>
37#include <VirtualRobot/Robot.h>
43#include <ArmarXCore/core/ice_conversions/ice_conversions_templates.tpp>
53#include <ArmarXCore/interface/observers/ObserverInterface.h>
54#include <ArmarXCore/interface/observers/VariantBase.h>
71#include <RobotAPI/interface/armem/prediction.h>
72#include <RobotAPI/interface/objectpose/ObjectPoseProvider.h>
73#include <RobotAPI/interface/objectpose/ObjectPoseStorageInterface.h>
74#include <RobotAPI/interface/objectpose/object_pose_types.h>
75#include <RobotAPI/interface/observers/KinematicUnitObserverInterface.h>
80#include <RobotAPI/libraries/armem_objects/aron/ObjectInstance.aron.generated.h>
100 calibration.defineProperties(defs, prefix +
"calibration.");
101 segment.defineProperties(defs, prefix);
102 robotHead.defineProperties(defs, prefix +
"head.");
103 visu.defineProperties(defs, prefix +
"visu.");
106 enableContinuousAttachmentUpdates,
"p.enableContinuousAttachmentUpdates",
"");
113 segment.decay.setTag(
getName());
123 KinematicUnitObserverInterfacePrx kinematicUnitObserver,
127 this->debugObserver = debugObserver;
130 segment.robots.reader = virtualRobotReader;
132 robotHead.kinematicUnitObserver = kinematicUnitObserver;
133 robotHead.debugObserver = debugObserver;
134 robotHead.fetchDatafields();
138 if (!visu.updateTask)
141 visu.updateTask->start();
144 if (enableContinuousAttachmentUpdates)
148 if (not attachmentUpdateTask)
155 while (not this->attachmentUpdateTask->isStopped())
160 segment.doLocked([&]() { this->segment.runUpdateObjectPoses(); });
169 attachmentUpdateTask->start();
179 ARMARX_INFO <<
"Stopping SegmentAdapter running threads...";
182 visu.updateTask->stop();
183 visu.updateTask =
nullptr;
186 if (attachmentUpdateTask)
188 attachmentUpdateTask->stop();
189 attachmentUpdateTask =
nullptr;
195 const objpose::ProviderInfo& info,
198 ARMARX_IMPORTANT <<
"Provider `" << providerName <<
"` is (now) available.";
199 updateProviderInfo(providerName, info);
204 const objpose::data::ProvidedObjectPoseSeq& providedPoses,
207 ARMARX_VERBOSE <<
"Received " << providedPoses.size() <<
" object poses from provider '"
208 << providerName <<
"'.";
209 updateObjectPoses(providerName, providedPoses);
213 SegmentAdapter::updateProviderInfo(
const std::string& providerName,
214 const objpose::ProviderInfo& info)
218 ARMARX_WARNING <<
"Received availability signal by provider '" << providerName <<
"' "
219 <<
"with invalid provider proxy.\nIgnoring provider '" << providerName
224 [
this, &providerName, &info]()
226 std::stringstream ss;
227 for (
const auto&
id : info.supportedObjects)
229 ss <<
"- " <<
id <<
"\n";
231 ARMARX_VERBOSE <<
"Provider '" << providerName <<
"' available.\n"
232 <<
"Supported objects: \n"
234 segment.providers[providerName] = info;
239 SegmentAdapter::updateObjectPoses(
const std::string& providerName,
240 const objpose::data::ProvidedObjectPoseSeq& providedPoses)
244 RobotHeadMovement::Discard discard;
246 std::scoped_lock lock(robotHeadMutex);
252 map[
"Discarding All Updates"] =
new Variant(discard.all ? 1.f : 0.f);
255 map[
"Proportion Updated Poses"] =
new Variant(0.f);
257 debugObserver->setDebugChannel(
getName(), map);
271 Segment::CommitStats stats = segment.commitObjectPoses(
272 providerName, providedPoses, calibration, discard.updatesUntil);
277 const float proportionUpdatedPoses =
278 providedPoses.empty() ? 0.0f
279 :
static_cast<float>(stats.numUpdated) /
280 static_cast<float>(providedPoses.size());
282 debugObserver->setDebugChannel(
284 {{
"Discarding All Updates",
new Variant(discard.all ? 1 : 0)},
285 {
"Proportion Updated Poses",
new Variant(proportionUpdatedPoses)}});
288 handleProviderUpdate(providerName);
293 debugObserver->setDebugChannel(
296 {
"t ReportObjectPoses [ms]",
297 new Variant(tReportObjectPoses.toMilliSecondsDouble())},
298 {
"t MemorySetObjectPoses [ms]",
299 new Variant(tCommitObjectPoses.toMilliSecondsDouble())},
306 SegmentAdapter::handleProviderUpdate(
const std::string& providerName)
309 if (segment.providers.count(providerName) == 0)
311 segment.providers[providerName] = objpose::ProviderInfo();
315 objpose::data::ObjectPoseSeq
322 [
this, &now]() {
return simox::alg::get_values(segment.getObjectPoses(now)); });
323 const objpose::data::ObjectPoseSeq result =
objpose::toIce(objectPoses);
329 debugObserver->setDebugChannel(
332 {
"t GetObjectPoses() [ms]",
333 new Variant(tGetObjectPoses.toMilliSecondsDouble())},
340 objpose::data::ObjectPoseSeq
347 [
this, &now, &providerName]()
349 return simox::alg::get_values(segment.getObjectPosesByProvider(providerName, now));
351 const objpose::data::ObjectPoseSeq result =
objpose::toIce(objectPoses);
357 debugObserver->setDebugChannel(
getName(),
359 {
"t GetObjectPosesByProvider() [ms]",
360 new Variant(GetObjectPoses.toMilliSecondsDouble())},
367 objpose::observer::RequestObjectsOutput
373 std::map<std::string, objpose::provider::RequestObjectsInput> providerRequests;
374 std::map<std::string, objpose::ObjectPoseProviderPrx> proxies;
376 objpose::observer::RequestObjectsOutput output;
378 auto updateProxy = [&](
const std::string& providerName)
380 if (proxies.count(providerName) == 0)
382 if (
auto it = segment.providers.find(providerName); it != segment.providers.end())
384 proxies[providerName] = it->second.proxy;
388 ARMARX_ERROR <<
"No proxy for provider ' " << providerName <<
"'.";
389 proxies[providerName] =
nullptr;
394 if (not input.provider.empty())
396 providerRequests[input.provider] = input.request;
397 updateProxy(input.provider);
405 << simox::alg::get_keys(segment.providers);
407 for (
const auto& objectID : input.request.objectIDs)
410 for (
const auto& [providerName, info] : segment.providers)
413 if (std::find(info.supportedObjects.begin(),
414 info.supportedObjects.end(),
415 objectID) != info.supportedObjects.end())
417 ARMARX_INFO <<
"Found provider for " << objectID <<
": "
419 providerRequests[providerName].relativeTimeoutMS =
420 input.request.relativeTimeoutMS;
421 providerRequests[providerName].objectIDs.push_back(objectID);
422 updateProxy(providerName);
428 ARMARX_ERROR <<
"Did not find a provider for " << objectID <<
".";
429 output.results[objectID].providerName =
"";
437 for (
const auto& [providerName, request] : providerRequests)
439 if (objpose::ObjectPoseProviderPrx proxy = proxies.at(providerName); proxy)
441 ARMARX_INFO <<
"Requesting " << request.objectIDs.size() <<
" objects by provider '"
442 << providerName <<
"' for " << request.relativeTimeoutMS <<
" ms.";
443 objpose::provider::RequestObjectsOutput providerOutput =
444 proxy->requestObjects(request);
447 for (
const auto& [objectID, result] : providerOutput.results)
449 objpose::observer::ObjectRequestResult& res = output.results[objectID];
450 res.providerName = providerName;
452 successful += int(result.success);
454 ARMARX_INFO << successful <<
" of " << request.objectIDs.size()
455 <<
" object requests successful.";
461 objpose::ProviderInfoMap
464 return segment.doLocked([
this]() {
return segment.providers; });
470 return segment.doLocked([
this]() {
return simox::alg::get_keys(segment.providers); });
473 objpose::ProviderInfo
476 return segment.doLocked([
this, &providerName]()
477 {
return segment.getProviderInfo(providerName); });
483 return segment.doLocked([
this, &providerName]()
484 {
return segment.providers.count(providerName) > 0; });
487 objpose::AttachObjectToRobotNodeOutput
491 return segment.doLocked([
this, &input]()
492 {
return segment.attachObjectToRobotNode(input); });
495 objpose::DetachObjectFromRobotNodeOutput
499 return segment.doLocked([
this, &input]()
500 {
return segment.detachObjectFromRobotNode(input); });
503 objpose::DetachAllObjectsFromRobotNodesOutput
505 const objpose::DetachAllObjectsFromRobotNodesInput& input,
508 return segment.doLocked([
this, &input]()
509 {
return segment.detachAllObjectsFromRobotNodes(input); });
512 objpose::AgentFramesSeq
515 return segment.doLocked(
518 objpose::AgentFramesSeq output;
519 for (
const auto& [name, agent] : segment.robots.loaded)
521 objpose::AgentFrames& frames = output.emplace_back();
522 frames.agent = agent->getName();
523 frames.frames = agent->getRobotNodeNames();
529 objpose::SignalHeadMovementOutput
533 std::scoped_lock lock(robotHeadMutex);
534 return robotHead.signalHeadMovement(input);
537 objpose::ObjectPosePredictionResultSeq
541 objpose::ObjectPosePredictionResultSeq results;
542 std::vector<std::map<DateTime, objpose::ObjectPose>> poses;
543 std::vector<objpose::ObjectPose> latestPoses;
546 [
this, &requests, &results, &poses, &latestPoses]()
548 for (
const auto& request : requests)
550 auto& result = results.emplace_back();
555 const wm::Entity* entity = segment.findObjectEntity(objID);
557 result.success =
false;
558 poses.emplace_back();
559 latestPoses.emplace_back();
562 std::stringstream sstream;
563 sstream <<
"Could not find object with ID " << objID <<
".";
564 result.errorMessage = sstream.
str();
571 std::stringstream sstream;
572 sstream <<
"No snapshots of the object " << objID <<
" were found."
573 <<
" Cannot make a prediction.";
574 result.errorMessage = sstream.
str();
578 result.success =
true;
579 poses.back() = segment.getObjectPosesInRange(
585 for (
size_t i = 0; i < requests.size(); ++i)
587 const objpose::ObjectPosePredictionRequest& request = requests.at(i);
588 objpose::ObjectPosePredictionResult& result = results.at(i);
606 "' not available for object pose prediction.";
607 result.success =
false;
614 armem::prediction::data::PredictionEngineSeq
621 SegmentAdapter::visualizeRun()
626 while (
visu.updateTask && !
visu.updateTask->isStopped())
629 std::scoped_lock lock(visuMutex);
635 std::map<std::string, objpose::ObjectPoseMap> objectPosesByProvider;
636 std::map<std::string,
637 std::map<ObjectID, std::map<DateTime, objpose::ObjectPose>>>
638 poseHistoriesByProvider;
642 [
this, &objectPosesByProvider, &poseHistoriesByProvider, &objectFinder]()
648 const float minConf =
segment.decay.removeObjectsBelowConfidence;
649 segment.decay.removeObjectsBelowConfidence = -1;
651 objectPosesByProvider =
segment.getLatestObjectPosesByProviders();
653 segment.decay.removeObjectsBelowConfidence = minConf;
655 objectFinder =
segment.objectFinder;
658 visu.minConfidence =
segment.decay.removeObjectsBelowConfidence;
661 for (
const auto& [provider, objectPoses] : poseHistoriesByProvider)
664 auto& poseHistories = poseHistoriesByProvider[provider];
667 for (
const auto& [
id, objectPose] : objectPoses)
670 if (entity !=
nullptr)
685 objectPosesByProvider, poseHistoriesByProvider, objectFinder);
694 debugValues[
"t Visualize [ms]"] =
new Variant(tVisu.toMilliSecondsDouble());
696 for (
const auto& [provider, objectPoses] : objectPosesByProvider)
698 for (
const auto& [
id, pose] : objectPoses)
700 debugValues[
"confidence(" + provider +
"::" +
id.str() +
")"] =
701 new Variant(pose.confidence);
705 debugObserver->setDebugChannel(
getName(), debugValues);
709 cycle.waitForCycleDuration();
715 {linearPredictionEngineID}};
722 this->
visu.setup(adapter.visu);
723 this->
segment.setup(adapter.segment);
725 this->
robotHead.setup(adapter.robotHead);
734 group.setLabel(
"Instance");
743 std::scoped_lock lock(adapter.visuMutex);
744 this->
visu.update(adapter.visu);
746 this->
segment.update(adapter.segment);
748 adapter.segment.
doLocked([
this, &adapter]()
749 { this->
decay.update(adapter.segment.
decay); });
752 std::scoped_lock lock(adapter.robotHeadMutex);
753 this->
robotHead.update(adapter.robotHead);
static DateTime Now()
Current time on the virtual clock.
This util class helps with keeping a cycle time during a control cycle.
static Frequency HertzDouble(double hertz)
Used to find objects in the ArmarX objects repository [1] (formerly [2]).
A known object ID of the form "Dataset/ClassName" or "Dataset/ClassName/InstanceName".
std::string str() const
Return "dataset/className" or "dataset/className/instanceName".
The Variant class is described here: Variants.
virtual void connect(armem::client::MemoryNameSystem &memoryNameSystem)
The VirtualRobotReader class.
Helps connecting a Memory server to the Ice interface.
virtual objpose::ProviderInfo getProviderInfo(const std::string &providerName, ICE_CURRENT_ARG) override
virtual objpose::data::ObjectPoseSeq getObjectPoses(ICE_CURRENT_ARG) override
static const std::vector< PredictionEngine > predictionEngines
virtual objpose::SignalHeadMovementOutput signalHeadMovement(const objpose::SignalHeadMovementInput &input, ICE_CURRENT_ARG) override
virtual objpose::DetachObjectFromRobotNodeOutput detachObjectFromRobotNode(const objpose::DetachObjectFromRobotNodeInput &input, ICE_CURRENT_ARG) override
void connect(robot_state::VirtualRobotReader *virtualRobotReader, KinematicUnitObserverInterfacePrx kinematicUnitObserver, viz::Client arviz, DebugObserverInterfacePrx debugObserver)
virtual Ice::StringSeq getAvailableProviderNames(ICE_CURRENT_ARG) override
virtual armem::prediction::data::PredictionEngineSeq getAvailableObjectPoseEngines(ICE_CURRENT_ARG) override
std::string getName() const
virtual bool hasProvider(const std::string &providerName, ICE_CURRENT_ARG) override
virtual objpose::ProviderInfoMap getAvailableProvidersInfo(ICE_CURRENT_ARG) override
virtual void reportObjectPoses(const std::string &providerName, const objpose::data::ProvidedObjectPoseSeq &objectPoses, ICE_CURRENT_ARG) override
virtual objpose::DetachAllObjectsFromRobotNodesOutput detachAllObjectsFromRobotNodes(const objpose::DetachAllObjectsFromRobotNodesInput &input, ICE_CURRENT_ARG) override
SegmentAdapter(MemoryToIceAdapter &iceMemory)
static const std::string linearPredictionEngineID
virtual objpose::observer::RequestObjectsOutput requestObjects(const objpose::observer::RequestObjectsInput &input, ICE_CURRENT_ARG) override
virtual objpose::ObjectPosePredictionResultSeq predictObjectPoses(const objpose::ObjectPosePredictionRequestSeq &requests, ICE_CURRENT_ARG) override
virtual void reportProviderAvailable(const std::string &providerName, const objpose::ProviderInfo &info, ICE_CURRENT_ARG) override
void defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string &prefix="")
virtual objpose::AttachObjectToRobotNodeOutput attachObjectToRobotNode(const objpose::AttachObjectToRobotNodeInput &input, ICE_CURRENT_ARG) override
virtual objpose::AgentFramesSeq getAttachableFrames(ICE_CURRENT_ARG) override
virtual objpose::data::ObjectPoseSeq getObjectPosesByProvider(const std::string &providerName, ICE_CURRENT_ARG) override
static std::map< DateTime, ObjectPose > getObjectPosesInRange(const wm::Entity &entity, const DateTime &start, const DateTime &end)
visu::LinearPredictions linearPredictions
Linear predictions for objects.
std::vector< viz::Layer > visualizeCommit(const std::map< std::string, objpose::ObjectPoseSeq > &objectPoses, const std::map< std::string, std::vector< std::map< DateTime, objpose::ObjectPose > > > &poseHistories, const ObjectFinder &objectFinder) const
auto doLocked(FunctionT &&function) const
Execute function under shared (read) lock.
static DateTime Invalid()
static Duration SecondsDouble(double seconds)
Constructs a duration in seconds.
Simple rate limiter for use in loops to maintain a certain frequency given a clock.
Duration waitForNextTick() const
Wait and block until the target period is met.
CommitResult commit(StagedCommit const &commit)
#define ARMARX_INFO
The normal logging level.
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
#define ARMARX_VERBOSE
The logging level for verbose information.
#define TIMING_START(name)
Helper macro to do timing tests.
#define TIMING_END_STREAM(name, os)
Prints duration.
armarx::core::time::DateTime Time
armarx::core::time::Duration Duration
void fromAron(const T &dto, T &bo)
objpose::ObjectPosePredictionResult predictObjectPoseLinear(const std::map< DateTime, ObjectPose > &poses, const DateTime &time, const ObjectPose &latestPose)
Predict the pose of an object given a history of poses based on a linear regression.
std::vector< ObjectPose > ObjectPoseSeq
objpose::AABB toIce(const simox::AxisAlignedBoundingBox &aabb)
::IceInternal::ProxyHandle<::IceProxy::armarx::DebugObserverInterface > DebugObserverInterfacePrx
std::map< std::string, VariantBasePtr > StringVariantBaseMap
std::string GetHandledExceptionString()
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
void fromIce(const std::map< IceKeyT, IceValueT > &iceMap, boost::container::flat_map< CppKeyT, CppValueT > &cppMap)
void toIce(std::map< IceKeyT, IceValueT > &iceMap, const boost::container::flat_map< CppKeyT, CppValueT > &cppMap)
SimpleRunningTask(Ts...) -> SimpleRunningTask< std::function< void(void)> >
static PredictionSettings fromIce(const armem::prediction::data::PredictionSettings &ice)
std::string predictionEngineID
instance::RobotHeadMovement::RemoteGui robotHead
instance::Segment::RemoteGui segment
armarx::RemoteGui::Client::GroupBox group
instance::Visu::RemoteGui visu
armarx::RemoteGui::Client::VBoxLayout layout
void update(SegmentAdapter &adapter)
void setup(const SegmentAdapter &adapter)
instance::Decay::RemoteGui decay
std::optional< AronDtoT > findLatestInstanceDataAs(int instanceIndex=0) const