27#include <Ice/ObjectAdapter.h>
29#include <VirtualRobot/Nodes/RobotNode.h>
30#include <VirtualRobot/RobotNodeSet.h>
31#include <VirtualRobot/XML/RobotIO.h>
32#include <VirtualRobot/math/Helpers.h>
57 _synchronizedPrx->unref();
69 "Set of nodes that is controlled by the KinematicUnit");
71 "RobotFileName",
"Filename of VirtualRobot robot model (e.g. robot_model.xml)");
73 "AgentName",
"Name of the agent for which the sensor values are provided");
75 "RobotStateReportingTopic",
77 "Name of the topic on which updates of the robot state are reported.");
79 "HistoryLength", 10000,
"Number of entries in the robot state history")
83 "TopicPrefix",
"",
"Prefix for the sensor value topic name.");
85 "GlobalRobotPoseLocalization",
86 "Topic where the global robot pose can be reported.");
92 return "RobotStateComponent";
100 const std::size_t historyLength =
103 jointHistory.clear();
104 jointHistoryLength = historyLength;
107 poseHistoryLength = historyLength;
113 throw UserException(
"Could not find robot file " + robotFile);
116 this->_synchronized =
117 VirtualRobot::RobotIO::loadRobot(robotFile, VirtualRobot::RobotIO::eStructure);
121 ARMARX_INFO <<
"scale factor: " << robotModelScaling;
122 if (robotModelScaling != 1.0f)
124 ARMARX_INFO <<
"Scaling robot model with scale factor " << robotModelScaling;
125 _synchronized = _synchronized->clone(
126 _synchronized->getName(), _synchronized->getCollisionChecker(), robotModelScaling);
129 if (this->_synchronized)
132 <<
". Robot name: " << this->_synchronized->getName();
133 this->_synchronized->setPropagatingJointValuesEnabled(
false);
143 if (robotNodeSetName.empty())
145 throw UserException(
"RobotNodeSet not defined");
148 VirtualRobot::RobotNodeSetPtr rns = this->_synchronized->getRobotNodeSet(robotNodeSetName);
152 throw UserException(
"RobotNodeSet not defined");
155 std::vector<RobotNodePtr> nodes = rns->getAllRobotNodes();
157 ARMARX_VERBOSE <<
"Using RobotNodeSet " << robotNodeSetName <<
" with " << nodes.size()
159 for (
const RobotNodePtr& node : nodes)
164 usingTopic(topicPrefix + robotNodeSetName +
"State");
171 readRobotInfo(robotFile);
173 catch (
const std::exception& e)
175 ARMARX_WARNING <<
"Failed to read robot info from file: " << robotFile
176 <<
".\nReason: " << e.what();
180 ARMARX_WARNING <<
"Failed to read robot info from file: " << robotFile
181 <<
" for unknown reason.";
187 RobotStateComponent::readRobotInfo(
const std::string& robotFile)
191 robotInfo = readRobotInfo(robotNode.
first_node(
"RobotInfo"));
195 RobotStateComponent::readRobotInfo(
const RapidXmlReaderNode& infoNode)
197 std::string name = infoNode.name();
198 std::string profile = infoNode.attribute_value_or_default(
"profile",
"");
199 std::string value = infoNode.attribute_value_or_default(
"value",
"");
201 std::vector<RobotInfoNodePtr> children;
202 for (
const RapidXmlReaderNode& childNode : infoNode.nodes())
204 children.push_back(readRobotInfo(childNode));
206 return new RobotInfoNode(name, profile, value, children);
214 _sharedRobotServant =
216 RobotStateComponentInterfacePrx::uncheckedCast(
getProxy()),
217 robotStateListenerPrx);
218 _sharedRobotServant->ref();
220 _sharedRobotServant->setRobotStateComponent(
221 RobotStateComponentInterfacePrx::uncheckedCast(
getProxy()));
222 this->_synchronizedPrx = SharedRobotInterfacePrx::uncheckedCast(
227 robotStateObs->setRobot(_synchronized);
239 if (!this->_synchronizedPrx)
241 throw NotInitializedException(
"Shared Robot is NULL",
"getSynchronizedRobot");
243 return this->_synchronizedPrx;
253 throw NotInitializedException(
"Shared Robot is NULL",
"getRobotSnapshot");
256 auto clone = this->_synchronized->clone(_synchronized->getName());
259 clone, RobotStateComponentInterfacePrx::uncheckedCast(
getProxy()),
nullptr);
261 IceUtil::Time::microSecondsDouble(_sharedRobotServant->getTimestamp()->timestamp));
264 p->setGlobalPose(
new Pose(_synchronized->getGlobalPose()));
265 return SharedRobotInterfacePrx::uncheckedCast(result);
271 const IceUtil::Time time = IceUtil::Time::microSecondsDouble(_time);
275 throw NotInitializedException(
"Shared Robot is NULL",
"getRobotSnapshot");
279 auto config = interpolate(time);
282 ARMARX_WARNING <<
"Could not find or interpolate a robot state for time "
283 << time.toDateTime();
287 clone->setJointValues(config->jointMap);
289 clone, RobotStateComponentInterfacePrx::uncheckedCast(
getProxy()),
nullptr);
290 p->setTimestamp(time);
293 p->setGlobalPose(config->globalPose);
295 return SharedRobotInterfacePrx::uncheckedCast(result);
301 auto jointAngles = interpolateJoints(IceUtil::Time::microSecondsDouble(
timestamp));
302 return jointAngles ? jointAngles->value : NameValueMap();
308 auto config = interpolate(IceUtil::Time::microSecondsDouble(
timestamp));
309 return config ? *config : RobotStateConfig();
315 return relativeRobotFile;
321 return robotModelScaling;
344 <<
" from timestamp " << time.toDateTime()
345 <<
" a value changed: " << aValueChanged;
350 WriteLockPtr lock = _synchronized->getWriteLock();
352 _synchronized->setJointValues(jointAngles);
357 robotStateObs->updatePoses();
360 if (_sharedRobotServant)
362 _sharedRobotServant->setTimestamp(time);
366 std::unique_lock lock(jointHistoryMutex);
367 jointHistory.emplace_hint(jointHistory.end(), time, jointAngles);
369 if (jointHistory.size() > jointHistoryLength)
371 jointHistory.erase(jointHistory.begin());
375 robotStateListenerPrx->reportJointValues(jointAngles,
timestamp, aValueChanged);
384 std::string localRobotName = _synchronized->getName();
385 ARMARX_DEBUG <<
"Comparing " << localRobotName <<
" and "
386 << globalRobotPose.header.agent <<
".";
387 if (localRobotName == globalRobotPose.header.agent)
389 const IceUtil::Time time =
390 IceUtil::Time::microSeconds(globalRobotPose.header.timestampInMicroSeconds);
392 insertPose(time, globalRobotPose.transform);
393 _synchronized->setGlobalPose(globalRobotPose.transform);
395 if (_sharedRobotServant)
397 _sharedRobotServant->setGlobalPose(
new Pose(globalRobotPose.transform));
398 _sharedRobotServant->setTimestamp(time);
404 throw NotInitializedException(
"Robot Ptr is NULL",
"reportGlobalRobotPose");
408 std::vector<std::string>
411 std::vector<std::string> result;
415 for (
const std::string& projectName : packages)
417 if (projectName.empty())
422 result.push_back(projectName);
435 (void)jointModes, (
void)
timestamp, (void)aValueChanged;
448 robotStateObs->updateNodeVelocities(jointVelocities,
timestamp);
459 (void)jointTorques, (
void)
timestamp, (void)aValueChanged;
469 (void)jointAccelerations, (
void)
timestamp, (void)aValueChanged;
479 (void)jointCurrents, (
void)
timestamp, (void)aValueChanged;
489 (void)jointMotorTemperatures, (
void)
timestamp, (void)aValueChanged;
499 (void)jointStatuses, (
void)
timestamp, (void)aValueChanged;
506 std::lock_guard lock(poseHistoryMutex);
510 std::lock_guard lock(jointHistoryMutex);
511 jointHistory.clear();
524 robotStateObs = observer;
532 return _synchronized->getName();
536 throw NotInitializedException(
"Robot Ptr is NULL",
"getName");
543 if (robotNodeSetName.empty())
545 throw NotInitializedException(
"RobotNodeSetName is empty",
"getRobotNodeSetName");
547 return robotNodeSetName;
553 return robotStateTopicName;
557 RobotStateComponent::insertPose(IceUtil::Time
timestamp,
const Matrix4f& globalPose)
559 IceUtil::Time duration;
561 IceUtil::Time start = IceUtil::Time::now();
562 std::unique_lock lock(poseHistoryMutex);
563 duration = IceUtil::Time::now() - start;
565 poseHistory.emplace_hint(
568 if (poseHistory.size() > poseHistoryLength)
570 poseHistory.erase(poseHistory.begin());
576 robotStateObs->updatePoses();
580 std::optional<RobotStateConfig>
581 RobotStateComponent::interpolate(IceUtil::Time time)
const
583 auto joints = interpolateJoints(time);
584 auto globalPose = interpolatePose(time);
586 if (joints && globalPose)
588 RobotStateConfig config;
591 config.timestamp = std::min(joints->timestamp, globalPose->timestamp).toMicroSeconds();
592 config.jointMap = joints->value;
593 config.globalPose = globalPose->value;
603 RobotStateComponent::interpolateJoints(IceUtil::Time time)
const
604 -> std::optional<Timestamped<NameValueMap>>
606 std::shared_lock lock(jointHistoryMutex);
607 if (jointHistory.empty())
610 <<
"Joint history of robot state component is empty!";
614 const IceUtil::Time newestTimeInHistory = jointHistory.rbegin()->first;
615 if (time > newestTimeInHistory)
617 const IceUtil::Time minOffset = IceUtil::Time::milliSeconds(25);
618 const IceUtil::Time maxOffset = IceUtil::Time::seconds(2);
619 if (time > newestTimeInHistory + maxOffset)
622 <<
"Requested joint timestamp is substantially newer (>"
623 << maxOffset.toSecondsDouble()
624 <<
" sec) than newest available timestamp!"
625 <<
"\n- requested timestamp: \t" << time.toDateTime()
626 <<
"\n- newest timestamp: \t" << newestTimeInHistory.toDateTime();
629 else if (time > newestTimeInHistory + minOffset)
632 <<
"Requested joint timestamp is newer than newest available timestamp!"
633 <<
"\n- requested timestamp: \t" << time.toDateTime()
634 <<
"\n- newest timestamp: \t" << newestTimeInHistory.toDateTime()
635 <<
"\n- difference: \t"
636 << (time - newestTimeInHistory).toMicroSeconds() <<
" us";
639 return Timestamped<NameValueMap>{jointHistory.rbegin()->first,
640 jointHistory.rbegin()->second};
644 auto nextIt = jointHistory.lower_bound(time);
645 if (nextIt == jointHistory.end())
648 <<
"Could not find or interpolate a robot joint angles for time "
649 << time.toDateTime() <<
"\n- oldest available value: "
650 << jointHistory.begin()->first.toDateTime()
651 <<
"\n- newest available value: " << newestTimeInHistory.toDateTime();
655 if (nextIt == jointHistory.begin())
658 return Timestamped<NameValueMap>{nextIt->first, nextIt->second};
662 auto prevIt = std::prev(nextIt);
667 IceUtil::Time prevTime = prevIt->first;
668 IceUtil::Time nextTime = nextIt->first;
670 float t =
static_cast<float>((time - prevTime).toSecondsDouble() /
671 (nextTime - prevTime).toSecondsDouble());
674 auto prevJointIt = prevIt->second.begin();
675 for (
const auto& [name, nextAngle] : next)
677 float value = (1 - t) * prevJointIt->second + t * nextAngle;
683 return Timestamped<NameValueMap>{time, std::move(jointAngles)};
687 RobotStateComponent::interpolatePose(IceUtil::Time time)
const
688 -> std::optional<Timestamped<FramedPosePtr>>
690 std::shared_lock lock(poseHistoryMutex);
692 if (poseHistory.empty())
695 <<
"Pose history is empty. This can happen if there is no platform unit.";
697 ReadLockPtr readLock = _synchronized->getReadLock();
701 return Timestamped<FramedPosePtr>{IceUtil::Time::seconds(0), pose};
705 const IceUtil::Time newestTimeInHistory = poseHistory.rbegin()->first;
706 if (time > newestTimeInHistory)
708 IceUtil::Time maxOffset = IceUtil::Time::seconds(2);
709 if (time <= newestTimeInHistory + maxOffset)
712 <<
"Requested pose timestamp is newer than newest available timestamp!"
713 <<
"\n- requested timestamp: \t" << time.toDateTime()
714 <<
"\n- newest timestamp: \t" << newestTimeInHistory.toDateTime()
715 <<
"\n- difference: \t"
716 << (time - newestTimeInHistory).toMicroSeconds() <<
" us";
721 <<
"Requested pose timestamp is substantially newer (>"
722 << maxOffset.toSecondsDouble()
723 <<
" sec) than newest available timestamp!"
724 <<
"\n- requested timestamp: \t" << time.toDateTime()
725 <<
"\n- newest timestamp: \t" << newestTimeInHistory.toDateTime();
728 return Timestamped<FramedPosePtr>{poseHistory.rbegin()->first,
729 poseHistory.rbegin()->second};
733 auto nextIt = poseHistory.lower_bound(time);
734 if (nextIt == poseHistory.end())
737 <<
"Could not find or interpolate platform pose for time "
738 << time.toDateTime() <<
"\n- oldest available value: "
739 << jointHistory.begin()->first.toDateTime()
740 <<
"\n- newest available value: " << newestTimeInHistory.toDateTime();
745 if (nextIt == poseHistory.begin())
748 return Timestamped<FramedPosePtr>{nextIt->first, nextIt->second};
751 auto prevIt = std::prev(nextIt);
760 IceUtil::Time prevTime = prevIt->first;
761 IceUtil::Time nextTime = nextIt->first;
763 float t =
static_cast<float>((time - prevTime).toSecondsDouble() /
764 (nextTime - prevTime).toSecondsDouble());
766 Eigen::Matrix4f globalPoseNext = next->toEigen();
767 Eigen::Matrix4f globalPosePrev = prev->toEigen();
770 Eigen::Matrix4f globalPose;
772 math::Helpers::Position(globalPose) = (1 - t) * math::Helpers::Position(globalPosePrev) +
773 t * math::Helpers::Position(globalPoseNext);
779 math::Helpers::Orientation(globalPose) = rotNew.toRotationMatrix();
781 return Timestamped<FramedPosePtr>{time,
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
static const std::string & GetProjectName()
static const Ice::StringSeq & GetProjectDependencies()
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
ComponentPropertyDefinitions(std::string prefix, bool hasObjectNameParameter=true)
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Property< PropertyType > getProperty(const std::string &name)
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 offeringTopic(const std::string &name)
Registers a topic for retrival after initialization.
TopicProxyType getTopic(const std::string &name)
Returns a proxy of the specified topic.
Ice::ObjectAdapterPtr getObjectAdapter() const
Returns object's Ice adapter.
void usingTopic(const std::string &name, bool orderedPublishing=false)
Registers a proxy for subscription after initialization.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
std::string prefix
Prefix of the properties such as namespace, domain, component name, etc.
PropertyDefinition< PropertyType > & defineOptionalProperty(const std::string &name, PropertyType defaultValue, const std::string &description="", PropertyDefinitionBase::PropertyConstness constness=PropertyDefinitionBase::eConstant)
PropertyDefinition< PropertyType > & defineRequiredProperty(const std::string &name, const std::string &description="", PropertyDefinitionBase::PropertyConstness constness=PropertyDefinitionBase::eConstant)
RapidXmlReaderNode first_node(const char *name=nullptr) const
static RapidXmlReaderPtr FromFile(const std::string &path)
void onInitComponent() override
Load and create a VirtualRobot::Robot instance from the RobotFileName property.
void reportJointMotorTemperatures(const NameValueMap &jointMotorTemperatures, Ice::Long timestamp, bool aValueChanged, const Ice::Current &c=Ice::emptyCurrent) override
Does nothing.
void reportJointVelocities(const NameValueMap &jointVelocities, Ice::Long timestamp, bool aValueChanged, const Ice::Current &c=Ice::emptyCurrent) override
Sends the joint velocities to the robot state observer.
void reportJointAngles(const NameValueMap &jointAngles, Ice::Long timestamp, bool aValueChanged, const Ice::Current &c=Ice::emptyCurrent) override
Stores the reported joint angles in the joint history and publishes the new joint angles.
float getScaling(const Ice::Current &) const override
void onDisconnectComponent() override
Hook for subclass.
void reportJointTorques(const NameValueMap &jointTorques, Ice::Long timestamp, bool aValueChanged, const Ice::Current &c=Ice::emptyCurrent) override
Does nothing.
std::vector< std::string > getArmarXPackages(const Ice::Current &) const override
void reportGlobalRobotPose(const TransformStamped &globalRobotPose, const Ice::Current &=Ice::Current()) override
std::string getRobotName(const Ice::Current &) const override
RobotStateConfig getRobotStateAtTimestamp(double timestamp, const Ice::Current &) const override
SharedRobotInterfacePrx getSynchronizedRobot(const Ice::Current &) const override
void reportControlModeChanged(const NameControlModeMap &jointModes, Ice::Long timestamp, bool aValueChanged, const Ice::Current &c=Ice::emptyCurrent) override
Does nothing.
void reportJointCurrents(const NameValueMap &jointCurrents, Ice::Long timestamp, bool aValueChanged, const Ice::Current &c=Ice::emptyCurrent) override
Does nothing.
std::string getRobotStateTopicName(const Ice::Current &) const override
void onConnectComponent() override
Setup RobotStateObjectFactories needed for creating RemoteRobot instances.
SharedRobotInterfacePrx getRobotSnapshot(const std::string &deprecated, const Ice::Current &) override
Creates a snapshot of the robot state at this moment in time.
void reportJointAccelerations(const NameValueMap &jointAccelerations, Ice::Long timestamp, bool aValueChanged, const Ice::Current &c) override
Does nothing.
void reportJointStatuses(const NameStatusMap &jointStatuses, Ice::Long timestamp, bool aValueChanged, const Ice::Current &c=Ice::emptyCurrent) override
Does nothing.
PropertyDefinitionsPtr createPropertyDefinitions() override
Create an instance of RobotStatePropertyDefinitions.
RobotInfoNodePtr getRobotInfo(const Ice::Current &) const override
std::string getRobotNodeSetName(const Ice::Current &) const override
void setRobotStateObserver(RobotStateObserverPtr observer)
std::string getRobotFilename(const Ice::Current &) const override
NameValueMap getJointConfigAtTimestamp(double timestamp, const Ice::Current &) const override
void simulatorWasReset(const Ice::Current &=Ice::emptyCurrent) override
~RobotStateComponent() override
Calls unref() on RobotStateComponent::_synchronizedPrx.
std::string getDefaultName() const override
Retrieve default name of component.
SharedRobotInterfacePrx getRobotSnapshotAtTimestamp(double time, const Ice::Current ¤t) override
RobotStatePropertyDefinitions(std::string prefix)
The SharedRobotNodeServant class is a remote represenation of a Simox VirtualRobot::Robot.
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
#define ARMARX_INFO
The normal logging level.
#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.
Quaternion< float, 0 > Quaternionf
std::shared_ptr< class Robot > RobotPtr
const KinematicUnitDatafieldCreator jointAngles("jointAngles")
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceUtil::Time mapRtTimestampToNonRtTimestamp(const IceUtil::Time &time_monotic_raw)
std::shared_ptr< RapidXmlReader > RapidXmlReaderPtr
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
::IceInternal::ProxyHandle<::IceProxy::armarx::SharedRobotInterface > SharedRobotInterfacePrx
const LogSender::manipulator flush
IceInternal::Handle< SharedRobotServant > SharedRobotServantPtr
IceInternal::Handle< RobotStateObserver > RobotStateObserverPtr
IceInternal::Handle< FramedPose > FramedPosePtr
std::shared_ptr< Value > value()