ForceTorqueObserver.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 ArmarX::
17* @author Mirko Waechter ( mirko.waechter at kit dot edu)
18* @date 2013
19* @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20* GNU General Public License
21*/
22
23#include "ForceTorqueObserver.h"
24
25#include <SimoxUtility/algorithm/string/string_tools.h>
26
35
40
41#define RAWFORCE "_rawforcevectors"
42#define OFFSETFORCE "_forceswithoffsetvectors"
43#define FILTEREDFORCE "_filteredforcesvectors"
44#define RAWTORQUE "_rawtorquevectors"
45#define OFFSETTORQUE "_torqueswithoffsetvectors"
46#define FORCETORQUEVECTORS "_forceTorqueVectors"
47#define POD_SUFFIX "_pod"
48
49namespace armarx
50{
54
55 void
56 ForceTorqueObserver::setTopicName(std::string topicName)
57 {
58 this->topicName = topicName;
59 }
60
61 void
63 {
64 if (topicName.empty())
65 {
66 usingTopic(getProperty<std::string>("ForceTorqueTopicName").getValue());
67 }
68 else
69 {
70 usingTopic(topicName);
71 }
72
73 // register all checks
78 offerConditionCheck("magnitudeinrange", new ConditionCheckMagnitudeInRange());
80 offerConditionCheck("magnitudelarger", new ConditionCheckMagnitudeLarger());
81 offerConditionCheck("magnitudesmaller", new ConditionCheckMagnitudeSmaller());
82
83 usingProxy(getProperty<std::string>("RobotStateComponentName").getValue());
84 offeringTopic("DebugDrawerUpdates");
85
86 auto sensorRobotNodeSplit =
87 armarx::Split(getProperty<std::string>("SensorRobotNodeMapping").getValue(), ",");
88 for (auto& elem : sensorRobotNodeSplit)
89 {
90 simox::alg::trim(elem);
91 auto split = armarx::Split(elem, ":");
92 if (split.size() >= 2)
93 {
94 sensorRobotNodeMapping.emplace(
95 simox::alg::trim_copy(split.at(0)),
96 std::make_pair(simox::alg::trim_copy(split.at(1)),
97 split.size() >= 3 ? simox::alg::trim_copy(split.at(2)) : ""));
98 }
99 }
100 }
101
102 void
104 {
106 getProperty<std::string>("RobotStateComponentName").getValue());
107 localRobot =
108 RemoteRobot::createLocalCloneFromFile(robot, VirtualRobot::RobotIO::eStructure);
109 debugDrawer = getTopic<DebugDrawerInterfacePrx>("DebugDrawerUpdates");
110 if (getProperty<bool>("VisualizeForce").getValue())
111 {
112 visualizerTask = new PeriodicTask<ForceTorqueObserver>(
113 this,
115 1000 / getProperty<int>("VisualizeForceUpdateFrequency").getValue(),
116 false,
117 "visualizerFunction");
118 visualizerTask->start();
119 }
120 updateRobotTask = new PeriodicTask<ForceTorqueObserver>(
121 this,
122 &ForceTorqueObserver::updateRobot,
123 1000 / getProperty<int>("RobotUpdateFrequency").getValue(),
124 false,
125 "updateRobot",
126 false);
127 updateRobotTask->start();
128 }
129
130 void
132 {
133 if (!localRobot)
134 {
135 return;
136 }
137 float maxTorque = getProperty<float>("MaxExpectedTorqueValue");
138 float torqueVisuDeadZone = getProperty<float>("TorqueVisuDeadZone");
139 float arrowLength = getProperty<float>("MaxForceArrowLength").getValue();
140 float forceFactor = getProperty<float>("ForceVisualizerFactor").getValue();
141 auto channels = getAvailableChannels(false);
142 auto batchPrx = debugDrawer->ice_batchOneway();
143 {
144 std::unique_lock lock(dataMutex);
145
146 std::set<std::string> frameAlreadyReported;
147 for (auto& channel : channels)
148 {
149 try
150 {
151 // if (localRobot->hasRobotNode(channel.first))
152 {
153 DatafieldRefPtr field = DatafieldRefPtr::dynamicCast(
154 getForceDatafield(channel.first, Ice::emptyCurrent));
155
156 FramedDirectionPtr value = field->getDataField()->get<FramedDirection>();
157 if (frameAlreadyReported.count(value->frame) > 0 ||
158 (value->frame != GlobalFrame && !value->frame.empty() &&
159 !localRobot->hasRobotNode(value->frame)))
160 {
161 continue;
162 }
163 frameAlreadyReported.insert(value->frame);
164 auto force = value->toGlobal(localRobot);
165 ARMARX_DEBUG << deactivateSpam(5, channel.first) << "new force "
166 << channel.first << " : " << force->toEigen()
167 << " original frame: " << value->frame;
168
169 float forceMag = force->toEigen().norm() * forceFactor;
170 Vector3Ptr pos =
171 new Vector3(localRobot->getRobotNode(value->frame)->getGlobalPose());
172 forceMag = std::min(1.0f, forceMag);
173 batchPrx->setArrowVisu("Forces",
174 "Force" + channel.first,
175 pos,
176 force,
177 DrawColor{forceMag, 1.0f - forceMag, 0.0f, 0.5f},
178 std::max(arrowLength * forceMag, 10.f),
179 3);
180
181 field = DatafieldRefPtr::dynamicCast(
182 getTorqueDatafield(channel.first, Ice::emptyCurrent));
183 value = field->getDataField()->get<FramedDirection>();
184 auto torque = value;
185 // ARMARX_INFO << deactivateSpam(1, torque->frame) << "Reporting for " << channel.first << " in frame " << torque->frame;
186
187 Eigen::Vector3f dirXglobal =
188 FramedDirection(Eigen::Vector3f::UnitX(), torque->frame, torque->agent)
189 .toGlobalEigen(localRobot);
190 Eigen::Vector3f dirYglobal =
191 FramedDirection(Eigen::Vector3f::UnitY(), torque->frame, torque->agent)
192 .toGlobalEigen(localRobot);
193 Eigen::Vector3f dirZglobal =
194 FramedDirection(Eigen::Vector3f::UnitZ(), torque->frame, torque->agent)
195 .toGlobalEigen(localRobot);
196 // ARMARX_INFO << channel.first << VAROUT(torque) << VAROUT(dirXglobal) << VAROUT(dirYglobal) << VAROUT(dirZglobal);
197 if (fabs(torque->x) > torqueVisuDeadZone)
198 {
199 batchPrx->setCircleArrowVisu("Torques",
200 "TorqueX" + channel.first,
201 pos,
202 new Vector3(dirXglobal),
203 50,
204 torque->x / maxTorque,
205 3 * std::max(1.0f, torque->x / maxTorque),
206 DrawColor{1.0f, 0.0f, 0.0f, 0.5f});
207 }
208 else
209 {
210 batchPrx->removeCircleVisu("Torques", "TorqueX" + channel.first);
211 }
212 if (fabs(torque->y) > torqueVisuDeadZone)
213 {
214 batchPrx->setCircleArrowVisu("Torques",
215 "TorqueY" + channel.first,
216 pos,
217 new Vector3(dirYglobal),
218 50,
219 torque->y / maxTorque,
220 3 * std::max(1.0f, torque->y / maxTorque),
221 DrawColor{0.0f, 1.0f, 0.0f, 0.5f});
222 }
223 else
224 {
225 batchPrx->removeCircleVisu("Torques", "TorqueY" + channel.first);
226 }
227 if (fabs(torque->z) > torqueVisuDeadZone)
228 {
229 batchPrx->setCircleArrowVisu("Torques",
230 "TorqueZ" + channel.first,
231 pos,
232 new Vector3(dirZglobal),
233 50,
234 torque->z / maxTorque,
235 3 * std::max(1.0f, torque->z / maxTorque),
236 DrawColor{0.0f, 0.0f, 1.0f, 0.5f});
237 }
238 else
239 {
240 batchPrx->removeCircleVisu("Torques", "TorqueZ" + channel.first);
241 }
242 }
243
244 // else
245 // {
246 // // ARMARX_INFO << deactivateSpam(5,channel.first) << "No node name " << channel.first;
247 // }
248 }
249 catch (...)
250 {
251 }
252 }
253 }
254 batchPrx->ice_flushBatchRequests();
255 }
256
263
264 void
265 ForceTorqueObserver::updateRobot()
266 {
267 std::unique_lock lock(dataMutex);
268 RemoteRobot::synchronizeLocalClone(localRobot, robot);
269 }
270
271 void
272 ForceTorqueObserver::offerValue(const std::string& nodeName,
273 const std::string& type,
274 const FramedDirectionBasePtr& value,
275 const DataFieldIdentifierPtr& id)
276 {
277 FramedDirectionPtr vec = FramedDirectionPtr::dynamicCast(value);
278
279
280 try
281 {
282 setDataFieldFlatCopy(id->channelName, id->datafieldName, new Variant(value));
283 }
284 catch (...)
285 {
286 ARMARX_INFO << "Creating force/torque fields for node " << nodeName << " with id "
287 << id->getIdentifierStr();
288 if (!existsDataField(id->channelName, id->datafieldName))
289 {
290 if (!existsChannel(id->channelName))
291 {
292 offerChannel(id->channelName,
293 type + " vectors on specific parts of the robot.");
294 }
295 offerDataFieldWithDefault(id->channelName,
296 id->datafieldName,
297 Variant(value),
298 "3D vector for " + type + " of " + nodeName);
299 }
300 }
301
302
303 // pod = plain old data
304 std::string pod_channelName = id->channelName + POD_SUFFIX;
305
306 try
307 {
309 values[id->datafieldName + "_x"] = new Variant(vec->x);
310 values[id->datafieldName + "_y"] = new Variant(vec->y);
311 values[id->datafieldName + "_z"] = new Variant(vec->z);
312 values[id->datafieldName + "_frame"] = new Variant(vec->frame);
313 setDataFieldsFlatCopy(pod_channelName, values);
314 }
315 catch (...)
316 {
317 ARMARX_INFO << "Creating force/torque pod fields";
318 if (!existsChannel(pod_channelName))
319 {
320 offerChannel(pod_channelName,
321 id->datafieldName + " on " + nodeName + " as plain old data (pod)");
322 }
324 pod_channelName, id->datafieldName + "_x", Variant(vec->x), type + " on X axis");
326 pod_channelName, id->datafieldName + "_y", Variant(vec->y), type + " on Y axis");
328 pod_channelName, id->datafieldName + "_z", Variant(vec->z), type + " on Z axis");
329 offerOrUpdateDataField(pod_channelName,
330 id->datafieldName + "_frame",
331 Variant(vec->frame),
332 "Frame of " + value->frame);
333 }
334 }
335
336 void
337 armarx::ForceTorqueObserver::reportSensorValues(const std::string& sensorNodeName,
338 const FramedDirectionBasePtr& forces,
339 const FramedDirectionBasePtr& torques,
340 const Ice::Current& c)
341 {
342 std::unique_lock lock(dataMutex);
343
344 auto offerForceAndTorqueValue = [=, this](std::string const& sensorNodeName,
345 std::string const& frame,
346 std::string channelName,
347 const FramedDirectionBasePtr& forces,
348 const FramedDirectionBasePtr& torques)
349 {
350 try
351 {
353
354 if (forces)
355 {
356 if (channelName.empty())
357 {
358 id = getForceDatafieldId(sensorNodeName, frame);
359 }
360 else
361 {
362 id = new DataFieldIdentifier(getName(), channelName, "forces");
363 }
364
365 offerValue(sensorNodeName, id->datafieldName, forces, id);
366 }
367
368 if (torques)
369 {
370 if (channelName.empty())
371 {
372 id = getTorqueDatafieldId(sensorNodeName, frame);
373 }
374 else
375 {
376 id = new DataFieldIdentifier(getName(), channelName, "torques");
377 }
378
379 offerValue(sensorNodeName, id->datafieldName, torques, id);
380 }
381
382 updateChannel(id->channelName);
383 updateChannel(id->channelName + POD_SUFFIX);
384 }
385 catch (std::exception&)
386 {
387 ARMARX_ERROR << "Reporting force torque for " << sensorNodeName << " failed! ";
389 }
390 };
391
392 if (!localRobot)
393 {
394 return;
395 }
396
397
398 FramedDirectionPtr realTorques = FramedDirectionPtr::dynamicCast(torques);
399 realTorques->changeFrame(localRobot, forces->frame);
400
401 FramedDirectionPtr realForces = FramedDirectionPtr::dynamicCast(forces);
402 offerForceAndTorqueValue(sensorNodeName, forces->frame, "", realForces, realTorques);
403
404 auto sensorMappingRange = sensorRobotNodeMapping.equal_range(sensorNodeName);
405 for (auto iter = sensorMappingRange.first; iter != sensorMappingRange.second; ++iter)
406 {
407 auto& sensorName = iter->first;
408 auto& robotNodeName = iter->second.first;
409 auto& channelName = iter->second.second;
410 FramedDirectionPtr forcesCopy = FramedDirectionPtr::dynamicCast(realForces->clone());
411 FramedDirectionPtr torquesCopy = FramedDirectionPtr::dynamicCast(realTorques->clone());
412 forcesCopy->changeFrame(localRobot, robotNodeName);
413 torquesCopy->changeFrame(localRobot, robotNodeName);
414
415 offerForceAndTorqueValue(
416 sensorName, robotNodeName, channelName, forcesCopy, torquesCopy);
417 }
418 }
419
420 DatafieldRefBasePtr
422 const DatafieldRefBasePtr& forceTorqueDatafieldRef,
423 const Ice::Current&)
424 {
425 return createFilteredDatafield(new filters::OffsetFilter(), forceTorqueDatafieldRef);
426 }
427
428 DatafieldRefBasePtr
429 ForceTorqueObserver::getForceDatafield(const std::string& nodeName, const Ice::Current&)
430 {
431 auto id = getForceDatafieldId(nodeName, nodeName);
432
433 if (!existsChannel(id->channelName))
434 {
435 throw UserException("No sensor for node '" + nodeName + "'available: channel " +
436 id->channelName);
437 }
438
439 if (!existsDataField(id->channelName, id->datafieldName))
440 {
441 throw UserException("No sensor for node '" + nodeName + "'available: datafield " +
442 id->datafieldName);
443 }
444
445 return new DatafieldRef(this, id->channelName, id->datafieldName);
446 }
447
448 DatafieldRefBasePtr
449 ForceTorqueObserver::getTorqueDatafield(const std::string& nodeName, const Ice::Current&)
450 {
451 auto id = getTorqueDatafieldId(nodeName, nodeName);
452
453 if (!existsChannel(id->channelName))
454 {
455 throw UserException("No sensor for node '" + nodeName + "'available: channel " +
456 id->channelName);
457 }
458
459 if (!existsDataField(id->channelName, id->datafieldName))
460 {
461 throw UserException("No sensor for node '" + nodeName + "'available: datafield " +
462 id->datafieldName);
463 }
464
465 return new DatafieldRef(this, id->channelName, id->datafieldName);
466 }
467
469 ForceTorqueObserver::getForceDatafieldId(const std::string& nodeName, const std::string& frame)
470 {
471 std::string channelName;
472
473 if (frame == nodeName)
474 {
475 channelName = nodeName;
476 }
477 else
478 {
479 channelName = nodeName + "_" + frame;
480 }
481
482 std::string datafieldName = "forces";
483 DataFieldIdentifierPtr id = new DataFieldIdentifier(getName(), channelName, datafieldName);
484 return id;
485 }
486
488 ForceTorqueObserver::getTorqueDatafieldId(const std::string& nodeName, const std::string& frame)
489 {
490 std::string channelName;
491
492 if (frame == nodeName)
493 {
494 channelName = nodeName;
495 }
496 else
497 {
498 channelName = nodeName + "_" + frame;
499 }
500
501 std::string datafieldName = "torques";
502 DataFieldIdentifierPtr id = new DataFieldIdentifier(getName(), channelName, datafieldName);
503 return id;
504 }
505
506 void
508 {
509 if (visualizerTask)
510 {
511 visualizerTask->stop();
512 }
513 if (updateRobotTask)
514 {
515 updateRobotTask->stop();
516 }
517 }
518
519 void
523} // namespace armarx
#define POD_SUFFIX
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition Logging.cpp:75
constexpr T c
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
Property< PropertyType > getProperty(const std::string &name)
Checks if the numbers published in the relevant data fields equal reference value with respect to a t...
Checks if the numbers published in the relevant data fields equal a reference value.
Checks if the numbers published in the relevant data fields are larger than a reference value.
Checks if the numbers published in the relevant data fields are smaller than a reference value.
Checks if the relevant data fields have been updated since the installation of this condition.
DataFieldIdentifier provide the basis to identify data field within a distributed ArmarX scenario.
The DatafieldRef class is similar to the ChannelRef, but points to a specific Datafield instead of to...
void reportSensorValues(const std::string &sensorNodeName, const FramedDirectionBasePtr &forces, const FramedDirectionBasePtr &torques, const Ice::Current &) override
void onConnectObserver() override
Framework hook.
void setTopicName(std::string topicName)
void onExitObserver() override
Framework hook.
void onDisconnectComponent() override
Hook for subclass.
DatafieldRefBasePtr getForceDatafield(const std::string &nodeName, const Ice::Current &) override
DatafieldRefBasePtr getTorqueDatafield(const std::string &nodeName, const Ice::Current &) override
DataFieldIdentifierPtr getTorqueDatafieldId(const std::string &nodeName, const std::string &frame)
PropertyDefinitionsPtr createPropertyDefinitions() override
void onInitObserver() override
Framework hook.
DatafieldRefBasePtr createNulledDatafield(const DatafieldRefBasePtr &forceTorqueDatafieldRef, const Ice::Current &) override
DataFieldIdentifierPtr getForceDatafieldId(const std::string &nodeName, const std::string &frame)
FramedDirection is a 3 dimensional direction vector with a reference frame.
Definition FramedPose.h:87
Eigen::Vector3f toGlobalEigen(const SharedRobotInterfacePrx &referenceRobot) const
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.
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
void usingTopic(const std::string &name, bool orderedPublishing=false)
Registers a proxy for subscription after initialization.
std::string getName() const
Retrieve name of object.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
DatafieldRefBasePtr createFilteredDatafield(const DatafieldFilterBasePtr &filter, const DatafieldRefBasePtr &datafieldRef)
This function creates a new datafield with new filter on the given datafield.
bool existsChannel(const std::string &channelName) const
void offerChannel(std::string channelName, std::string description)
Offer a channel.
Definition Observer.cpp:131
void offerConditionCheck(std::string checkName, ConditionCheck *conditionCheck)
Offer a condition check.
Definition Observer.cpp:301
void updateChannel(const std::string &channelName, const std::set< std::string > &updatedDatafields=std::set< std::string >())
Update all conditions for a channel.
Definition Observer.cpp:788
void setDataFieldsFlatCopy(const std::string &channelName, const StringVariantBaseMap &datafieldValues, bool triggerFilterUpdate=true)
Definition Observer.cpp:728
void offerDataFieldWithDefault(std::string channelName, std::string datafieldName, const Variant &defaultValue, std::string description)
Offer a datafield with default value.
Definition Observer.cpp:160
bool existsDataField(const std::string &channelName, const std::string &datafieldName) const
ChannelRegistry getAvailableChannels(bool includeMetaChannels)
Retrieve information on all sensory data channels available from the observer.
void setDataFieldFlatCopy(const std::string &channelName, const std::string &datafieldName, const VariantPtr &value, bool triggerFilterUpdate=true)
Definition Observer.cpp:548
bool offerOrUpdateDataField(std::string channelName, std::string datafieldName, const Variant &value, const std::string &description)
Definition Observer.cpp:242
The periodic task executes one thread method repeatedly using the time period specified in the constr...
static VirtualRobot::RobotPtr createLocalCloneFromFile(RobotStateComponentInterfacePrx robotStatePrx, VirtualRobot::RobotIO::RobotDescription loadMode=VirtualRobot::RobotIO::eFull)
This is a convenience function for createLocalClone, which automatically gets the filename from the R...
static bool synchronizeLocalClone(VirtualRobot::RobotPtr robot, RobotStateComponentInterfacePrx robotStatePrx)
The Vector3 class.
Definition Pose.h:113
The OffsetFilter class returns values relative to value from the first call of the filter.
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
std::string const GlobalFrame
Variable of the global coordinate system.
Definition FramedPose.h:65
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::vector< std::string > split(const std::string &source, const std::string &splitBy, bool trimElements=false, bool removeEmptyElements=false)
IceInternal::Handle< Vector3 > Vector3Ptr
Definition Pose.h:165
std::map< std::string, VariantBasePtr > StringVariantBaseMap
void handleExceptions()
std::vector< std::string > Split(const std::string &source, const std::string &splitBy, bool trimElements=false, bool removeEmptyElements=false)
IceInternal::Handle< FramedDirection > FramedDirectionPtr
Definition FramedPose.h:84
IceInternal::Handle< DatafieldRef > DatafieldRefPtr
Definition Observer.h:43
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
IceInternal::Handle< DataFieldIdentifier > DataFieldIdentifierPtr
Typedef of DataFieldIdentifierPtr as IceInternal::Handle<DataFieldIdentifier> for convenience.
std::shared_ptr< Value > value()
Definition cxxopts.hpp:855