RobotUnitSimulation.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 ArmarXSimulation::ArmarXObjects::RobotUnitSimulation
17 * @author Raphael Grimm ( raphael dot grimm at kit dot edu )
18 * @date 2017
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
23#include "RobotUnitSimulation.h"
24
25#include <chrono>
26#include <set>
27
28#include <VirtualRobot/MathTools.h>
29#include <VirtualRobot/Nodes/ForceTorqueSensor.h>
30#include <VirtualRobot/RobotNodeSet.h>
31
34
35
36using namespace armarx;
37
38void
40{
41 robot = cloneRobot();
42 simulatorPrxName = getProperty<std::string>("SimulatorName").getValue();
44
45 synchronizedSimulation = getProperty<bool>("SynchronizedSimulation").getValue();
46 simulatorRobotListenerInterfaceTopic =
47 getProperty<std::string>("SimulatorRobotListenerInterfaceTopic").getValue();
48 if (simulatorRobotListenerInterfaceTopic.empty())
49 {
50 simulatorRobotListenerInterfaceTopic = "Simulator_Robot_" + getRobotName();
51 }
52 simulatorForceTorqueListenerInterfaceTopic =
53 getProperty<std::string>("SimulatorForceTorqueListenerInterfaceTopic").getValue();
54 std::size_t controlIterationMsProp = getProperty<std::size_t>("ControlIterationMs").getValue();
55 if (!controlIterationMsProp)
56 {
57 ARMARX_WARNING << "the controll iteration was set to 0ms. detting it to 1ms";
58 controlIterationMsProp = 1;
59 }
60 controlIterationMs = IceUtil::Time::milliSeconds(controlIterationMsProp);
61 usingTopic(simulatorRobotListenerInterfaceTopic);
62 usingTopic(simulatorForceTorqueListenerInterfaceTopic);
63
64 //ft mapping
65 {
66 const std::string mappingStr = getProperty<std::string>("ForceTorqueSensorMapping");
67 std::vector<std::string> entries;
68 if (mappingStr != "")
69 {
70 bool trimEntries = true;
71 entries = Split(mappingStr, ",", trimEntries);
72 }
73 for (std::string entry : entries)
74 {
75 ARMARX_CHECK_EXPRESSION(!entry.empty())
76 << "empty entry in ForceTorqueSensorMapping! entries:\n"
77 << entries;
78 bool trimFields = true;
79 std::vector<std::string> fields = Split(entry, ":", trimFields);
80 ARMARX_CHECK_EXPRESSION(fields.size() == 2 || fields.size() == 3)
81 << "invalid entry in ForceTorqueSensorMapping! invalid entry:\n"
82 << fields << "\nall entries:\n"
83 << entries;
84 for (auto& field : fields)
85 {
86 ARMARX_CHECK_EXPRESSION(!field.empty())
87 << "empty field in ForceTorqueSensorMapping entry! invalid entry:\n"
88 << fields << "\nall entries:\n"
89 << entries;
90 }
91 const VirtualRobot::ForceTorqueSensorPtr ftsensor =
92 robot->getSensor<typename VirtualRobot::ForceTorqueSensor>(fields.at(0));
93 if (!ftsensor)
94 {
95 ARMARX_WARNING << "the robot has not ft sensor of name '" << fields.at(0)
96 << "' this ForceTorqueSensorMapping entry has no effect: " << entry;
97 continue;
98 }
99 ARMARX_CHECK_EXPRESSION(fields.size() == 2 || robot->hasRobotNode(fields.at(2)))
100 << VAROUT(fields.size()) << " "
101 << (fields.size() != 2 ? VAROUT(fields.at(2)) : std::string{});
102 ARMARX_CHECK_EXPRESSION(!ftMappings.count(fields.at(0)));
103 FTMappingData& mapping = ftMappings[fields.at(0)];
104 mapping.originalReportFrame = ftsensor->getRobotNode()->getName();
105 mapping.reportFrame = mapping.originalReportFrame;
106 mapping.reportTransformation = Eigen::Matrix3f::Identity();
107 mapping.sensorName = fields.at(1);
108 if (fields.size() == 3)
109 {
110 mapping.reportFrame = fields.at(2);
111 //get rotation from original to target
112 const auto orig = robot->getRobotNode(mapping.originalReportFrame)->getGlobalPose();
113 const auto targ = robot->getRobotNode(mapping.reportFrame)->getGlobalPose();
114 mapping.reportTransformation =
115 (targ.block<3, 3>(0, 0).inverse() * orig.block<3, 3>(0, 0));
116 }
117 }
118 }
119
120 maxLinearPlatformVelocity = getProperty<float>("maxLinearPlatformVelocity").getValue();
122
123 maxAngularPlatformVelocity = getProperty<float>("maxAngularPlatformVelocity").getValue();
125
126 ARMARX_INFO << "using " << VAROUT(simulatorRobotListenerInterfaceTopic);
127 ARMARX_INFO << "using " << VAROUT(simulatorForceTorqueListenerInterfaceTopic);
128 ARMARX_INFO << "using " << VAROUT(controlIterationMs);
129}
130
131void
133{
135 if (!rtThread.joinable())
136 {
137 rtThread = std::thread{[&] { rtTask(); }};
138 }
139}
140
141void
147
148void
150{
152 {
153 ARMARX_IMPORTANT << "exiting rt thread";
154 };
155 try
156 {
157 {
158 const auto timing = -TimeUtil::GetTime(true);
160 //device init
161 {
162 const auto timing = -TimeUtil::GetTime(true);
163 //joints
164 {
165 const auto timing = -TimeUtil::GetTime(true);
166 ARMARX_INFO << "adding devices for joints:";
167 for (const VirtualRobot::RobotNodePtr& node : *(robot->getRobotNodeSet(
168 getProperty<std::string>("RobotNodeSetName").getValue())))
169 {
170 if (node->isJoint())
171 {
172 JointSimulationDevicePtr jdev = std::make_shared<JointSimulationDevice>(
173 node->getName(), anglesCtrl, velocitiesCtrl, torquesCtrl);
174 jointDevs.add(jdev->getDeviceName(), jdev);
175 addSensorDevice(jdev);
176 addControlDevice(jdev);
177 }
178 }
179 ARMARX_INFO << "adding devices for joints done ("
180 << (timing + TimeUtil::GetTime(true)).toMicroSeconds() << " us)";
181 }
182 //platform
183 {
184 const auto timing = -TimeUtil::GetTime(true);
185 ARMARX_INFO << "adding devices for the platform:";
186
187 if (getRobotPlatformName().empty())
188 {
190 << "No platform device will be created since platform name was given";
191 }
192 else
193 {
194 if (robot->hasRobotNode(getRobotPlatformName()))
195 {
196 platformDev =
197 std::make_shared<PlatformSimulationDevice>(getRobotPlatformName());
198 addSensorDevice(platformDev);
199 addControlDevice(platformDev);
200 }
201 else
202 {
203 ARMARX_WARNING << "No robot node with the name '" +
205 "' so no platform device will be created";
206 }
207 }
208 ARMARX_INFO << "adding devices for the platform done ("
209 << (timing + TimeUtil::GetTime(true)).toMicroSeconds() << " us)";
210 }
211 //force torque
212 {
213 const auto timing = -TimeUtil::GetTime(true);
214 ARMARX_INFO << "adding devices for force torque sensors:";
215 for (const auto& ft : robot->getSensors<VirtualRobot::ForceTorqueSensor>())
216 {
217 ForceTorqueSimulationSensorDevicePtr ftdev =
218 std::make_shared<ForceTorqueSimulationSensorDevice>(
219 getMappedFTName(ft->getName()),
220 getMappedFTReportingFrame(ft->getName(),
221 ft->getRobotNode()->getName()),
222 getMappedFTReportingTransformation(ft->getName()));
223 addSensorDevice(ftdev);
224 forceTorqueDevs.add(ftdev->getDeviceName(), ftdev);
225 }
226 ARMARX_INFO << "adding devices for force torque done ("
227 << (timing + TimeUtil::GetTime(true)).toMicroSeconds() << " us)";
228 }
229 //global pose device
230 {
231 // globalPoseDevice = std::make_shared<GlobalRobotPoseSimulationSensorDevice>();
232 // addSensorDevice(globalPoseDevice);
233 }
234 ARMARX_INFO << "transitioning to " << RobotUnitState::InitializingUnits
235 << std::flush;
237 ARMARX_INFO << "device init done ("
238 << (timing + TimeUtil::GetTime(true)).toMicroSeconds() << " us)";
239 ARMARX_INFO << "transitioned to " << getRobotUnitState() << std::flush;
240 }
241 //unit init
242 {
243 const auto timing = -TimeUtil::GetTime(true);
244 //resize my tripple buffers
245 {
246 const auto timing = -TimeUtil::GetTime(true);
247 torquesTB.reinit(std::vector<float>(jointDevs.size(), 0));
248 anglesTB.reinit(std::vector<float>(jointDevs.size(), 0));
249 velocitiesTB.reinit(std::vector<float>(jointDevs.size(), 0));
250 forceTorqueTB.reinit(std::vector<FT>(forceTorqueDevs.size(), FT{}));
251 forceTorqueTimes.clear();
252 forceTorqueTimes.resize(forceTorqueDevs.size(), 0);
254 ARMARX_INFO << "resize buffers done ("
255 << (timing + TimeUtil::GetTime(true)).toMicroSeconds() << " us)";
256 }
258 << std::flush;
260 ARMARX_INFO << "transitioned to " << getRobotUnitState() << std::flush;
261 ARMARX_INFO << "unit init done ("
262 << (timing + TimeUtil::GetTime(true)).toMicroSeconds() << " us)";
263 }
264 //get initial sensor values
265 {
266 const auto timing = -TimeUtil::GetTime(true);
267 gotSensorData = false;
268 ARMARX_INFO << "fetching initial robot state" << std::flush;
269 rtUpdateSensors(true);
270 ARMARX_INFO << "fetching initial robot done" << std::flush;
271 if (platformDev)
272 {
273 const float absolutePositionX = robPoseTB.getReadBuffer()(0, 3);
274 const float absolutePositionY = robPoseTB.getReadBuffer()(1, 3);
275 const float absolutePositionRotation =
276 VirtualRobot::MathTools::eigen4f2rpy(robPoseTB.getReadBuffer())(2);
277
278 platformDev->initAbsolutePositionX = absolutePositionX;
279 platformDev->initAbsolutePositionY = absolutePositionY;
280 platformDev->initAbsolutePositionRotation = absolutePositionRotation;
281 }
282 ARMARX_INFO << "get init sensor values done ("
283 << (timing + TimeUtil::GetTime(true)).toMicroSeconds() << " us)";
284 }
285 //enter RT
286 {
287 if (synchronizedSimulation)
288 {
289 simulatorPrx->stop();
290 ARMARX_IMPORTANT << "using synchronized simulator execution" << std::flush;
291 }
292 ARMARX_INFO << "now transitioning to rt" << std::flush;
293 {
294 const auto timing = -TimeUtil::GetTime(true);
296 ARMARX_INFO << "init rt thread done ("
297 << (timing + TimeUtil::GetTime(true)).toMicroSeconds() << " us)";
298 }
299 ARMARX_INFO << "transitioned to " << getRobotUnitState() << std::flush;
300 }
301 ARMARX_INFO << "pre loop done (" << (timing + TimeUtil::GetTime(true)).toMicroSeconds()
302 << " us)";
303 }
304 //meassure time
305 IceUtil::Time timeSinceLastIteration;
306 IceUtil::Time currentIterationStart;
307 IceUtil::Time lastIterationStart = TimeUtil::GetTime();
308 //loop
309 while (!shutdownRtThread)
310 {
312 //time
313 currentIterationStart = TimeUtil::GetTime();
314 timeSinceLastIteration = currentIterationStart - lastIterationStart;
315 lastIterationStart = currentIterationStart;
316 //call functions
317 rtSwitchControllerSetup(); // << switch controllers
319 rtRunNJointControllers(sensorValuesTimestamp,
320 timeSinceLastIteration); // << run NJointControllers
321 rtHandleInvalidTargets(); // << deactivate broken NJointControllers
322 rtRunJointControllers(sensorValuesTimestamp,
323 timeSinceLastIteration); // << run JointControllers
324 //communicate with the simulator
325 {
328 {
330 };
331 rtSendCommands();
332 //run sim
333 if (synchronizedSimulation)
334 {
335 gotSensorData = false;
336 simulatorPrx->step();
337 }
338 rtUpdateSensors(synchronizedSimulation);
339 }
341 sensorValuesTimestamp,
342 timeSinceLastIteration); // << swap out ControllerTargets / SensorValues
343 ++iterationCount;
345 //sleep remainder
346 //since the timeserver does not end the sleep on shutdown (and this would block shutdown),
347 //we do busy waiting and poll the time
348 const IceUtil::Time sleepUntil = currentIterationStart + controlIterationMs;
349 IceUtil::Time currentTime = TimeUtil::GetTime();
350 while (currentTime < sleepUntil)
351 {
353 {
354 return;
355 }
356 if (currentTime < currentIterationStart)
357 {
358 //this fixes sleeping for a long time
359 //in case the time server is reset
360 break;
361 }
362 std::this_thread::sleep_for(
363 std::chrono::microseconds{100}); //polling is done with 10kHz
364 currentTime = TimeUtil::GetTime();
365 }
366 }
367 }
368 catch (Ice::Exception& e)
369 {
370 ARMARX_ERROR << "exception in rtTask!\nwhat:\n"
371 << e.what() << "\n\tname: " << e.ice_id() << "\n\tfile: " << e.ice_file()
372 << "\n\tline: " << e.ice_line() << "\n\tstack: " << e.ice_stackTrace()
373 << std::flush;
374 throw;
375 }
376 catch (std::exception& e)
377 {
378 ARMARX_ERROR << "exception in rtTask!\nwhat:\n" << e.what() << std::flush;
379 throw;
380 }
381 catch (...)
382 {
383 ARMARX_ERROR << "exception in rtTask!" << std::flush;
384 throw;
385 }
386}
387
388void
389RobotUnitSimulation::reportState(const SimulatedRobotState& state, const Ice::Current&)
390{
391 IceUtil::Time timestamp = IceUtil::Time::microSeconds(state.timestampInMicroSeconds);
392
394 {
395 simulationDataTimestampInMicroSeconds = state.timestampInMicroSeconds;
396 }
397 else
398 {
400 }
401
402 fillTB(anglesTB, state.jointAngles, "JointAngles");
403 fillTB(velocitiesTB, state.jointVelocities, "JointVelocities");
404 fillTB(torquesTB, state.jointTorques, "JointTorques");
405
406 for (ForceTorqueData const& ftData : state.forceTorqueValues)
407 {
408 updateForceTorque(ftData, timestamp);
409 }
410
411 // Pose
412 if (skipReport())
413 {
414 ARMARX_DEBUG << deactivateSpam() << "Skipped all sensor values for robot pose";
415 }
416 else
417 {
418 auto g = robPoseTB.guard();
419 ARMARX_CHECK_NOT_NULL(state.pose)
420 << "Robot Pose is not allowed to be NULL. Maybe no state was reported from Simulator!";
421 robPoseTB.getWriteBuffer() = PosePtr::dynamicCast(state.pose)->toEigen();
422 robPoseTB.write();
423 ARMARX_DEBUG << deactivateSpam() << "Got sensor values for robot pose";
424 }
425
426 if (skipReport())
427 {
428 ARMARX_DEBUG << deactivateSpam() << "Skipped all sensor values for robot vel";
429 return;
430 }
431 else
432 {
433 auto& trans = state.linearVelocity;
434 auto& rotat = state.angularVelocity;
435
436 auto g = robVelTB.guard();
437 robVelTB.getWriteBuffer().lin(0) = trans->x;
438 robVelTB.getWriteBuffer().lin(1) = trans->y;
439 robVelTB.getWriteBuffer().lin(2) = trans->z;
440 robVelTB.getWriteBuffer().lin =
441 robPoseTB.getReadBuffer().block<3, 3>(0, 0).transpose() * robVelTB.getWriteBuffer().lin;
442 robVelTB.getWriteBuffer().ang(0) = rotat->x;
443 robVelTB.getWriteBuffer().ang(1) = rotat->y;
444 robVelTB.getWriteBuffer().ang(2) = rotat->z;
445 robVelTB.getWriteBuffer().ang =
446 robPoseTB.getReadBuffer().block<3, 3>(0, 0).transpose() * robVelTB.getWriteBuffer().ang;
447 robVelTB.write();
448 ARMARX_DEBUG << deactivateSpam() << "Got sensor values for robot vel";
449 }
450
451 gotSensorData = true;
452}
453
454void
455RobotUnitSimulation::updateForceTorque(const ForceTorqueData& ftData, IceUtil::Time timestamp)
456{
457 auto& sensorName = ftData.sensorName;
458 auto& nodeName = ftData.nodeName;
459
460 const std::string sensname =
461 ftMappings.count(sensorName) ? ftMappings.at(sensorName).sensorName : sensorName;
462 if (ftMappings.count(sensorName) && ftMappings.at(sensorName).originalReportFrame != nodeName)
463 {
464 ARMARX_ERROR << deactivateSpam(0.25) << "Sensor values for sensor '" << sensorName << "' ('"
465 << sensname << "') are reported in frame '" << nodeName << "' instead of '"
466 << ftMappings.at(sensorName).originalReportFrame
467 << "' as defined during setup! (this value is skipped!)";
468 return;
469 }
470 if (skipReport())
471 {
472 ARMARX_DEBUG << deactivateSpam(10, sensname)
473 << "Skipped all sensor values for force torque of sensor " << sensname
474 << " (node = " << nodeName << ")";
475 return;
476 }
477 auto g = forceTorqueTB.guard();
478 if (!forceTorqueDevs.has(sensname))
479 {
480 ARMARX_WARNING << deactivateSpam(10, sensname) << "no ftsensor with name " << sensname
481 << "\nftsensors:\n"
482 << forceTorqueDevs.keys();
483 return;
484 }
485 auto i = forceTorqueDevs.index(sensname);
486 ARMARX_CHECK_EXPRESSION(forceTorqueTB.getWriteBuffer().size() > i)
487 << forceTorqueTB.getWriteBuffer().size() << " > " << i;
488 auto& force = ftData.force;
489 auto& torque = ftData.torque;
490 ForceTorqueSimulationSensorDevice& ftdev = *forceTorqueDevs.at(i);
491 ftdev.fx.update(force->x);
492 ftdev.fy.update(force->y);
493 ftdev.fz.update(force->z);
494 ftdev.tx.update(torque->x);
495 ftdev.ty.update(torque->y);
496 ftdev.tz.update(torque->z);
497
498 Eigen::Vector3f filteredTorque;
499 Eigen::Vector3f filteredForce;
500 filteredForce(0) = ftdev.fx.getRawValue();
501 filteredForce(1) = ftdev.fy.getRawValue();
502 filteredForce(2) = ftdev.fz.getRawValue();
503 filteredTorque(0) = ftdev.tx.getRawValue();
504 filteredTorque(1) = ftdev.ty.getRawValue();
505 filteredTorque(2) = ftdev.tz.getRawValue();
506
507 forceTorqueTB.getWriteBuffer().at(i).force = ftdev.reportingTransformation * filteredForce;
508 forceTorqueTB.getWriteBuffer().at(i).torque = ftdev.reportingTransformation * filteredTorque;
509 forceTorqueTB.write();
510 forceTorqueTimes.at(i) = timestamp.toMicroSeconds() * 1000; // Nanoseconds
511 ARMARX_DEBUG << deactivateSpam(10, sensname)
512 << "Got new sensor values for force torque of sensor " << sensname
513 << " (node = " << nodeName << ")";
514}
515
516void
517RobotUnitSimulation::fillTB(TripleBufferWithGuardAndTime<std::vector<float>>& b,
518 const NameValueMap& nv,
519 const std::string name) const
520{
521 if (skipReport())
522 {
523 ARMARX_DEBUG << deactivateSpam(10, name) << "Skipped all sensor values for " << name;
524 return;
525 }
526 auto g = b.guard();
527 std::stringstream ignored;
528 bool someWereIgnored = false;
529 for (const auto& a : nv)
530 {
531 if (jointDevs.has(a.first))
532 {
533 b.getWriteBuffer().at(jointDevs.index(a.first)) = a.second;
534 }
535 else
536 {
537 ignored << a.first << " -> " << a.second << "\n";
538 someWereIgnored = true;
539 }
540 }
541 b.write();
542 ARMARX_DEBUG << deactivateSpam(10, name) << "Got new sensor values for " << name;
543 if (someWereIgnored)
544 {
545 ARMARX_VERBOSE << deactivateSpam(10, name) << "Ignored sensor values for " << name << ":\n"
546 << ignored.str();
547 }
548}
549
550void
551RobotUnitSimulation::rtSendCommands()
552{
553 try
554 {
555 auto prx = simulatorPrx->ice_batchOneway();
556 //send commands
557 if (!velocitiesCtrl.empty())
558 {
559 prx->actuateRobotJointsVel(robotName, velocitiesCtrl);
560 }
561 if (!anglesCtrl.empty())
562 {
563 prx->actuateRobotJointsPos(robotName, anglesCtrl);
564 }
565 if (!torquesCtrl.empty())
566 {
567 prx->actuateRobotJointsTorque(robotName, torquesCtrl);
568 }
569
570 if (platformDev)
571 {
572 Eigen::Vector3f translationVel;
573 translationVel(0) = std::clamp(platformDev->target.velocityX,
576 translationVel(1) = std::clamp(platformDev->target.velocityY,
579 translationVel(2) = 0;
580 translationVel /= 1000;
581
582 Eigen::Vector3f rotationVel(0.f,
583 0.f,
584 std::clamp(platformDev->target.velocityRotation,
587
588 prx->setRobotLinearVelocityRobotRootFrame(
589 robotName, platformDev->getDeviceName(), new Vector3(translationVel));
590 prx->setRobotAngularVelocityRobotRootFrame(
591 robotName, platformDev->getDeviceName(), new Vector3(rotationVel));
593 << "setting platform vel ang: " << rotationVel.transpose();
595 << "setting platform vel lin: " << translationVel.transpose();
596 }
597 prx->ice_flushBatchRequests();
598 }
599 catch (Ice::ConnectionRefusedException&)
600 {
601 if (!shutdownRtThread)
602 {
603 //ignore this when shutting down. this probably happened when the simulator
604 //was shut down while the RobotUnitSimulation was still running
606 << "Ice::ConnectionRefusedException when sending commands to the simulator";
607 }
608 }
609 catch (Ice::NotRegisteredException&)
610 {
611 if (!shutdownRtThread)
612 {
613 //ignore this when shutting down. this probably happened when the simulator
614 //was shut down while the RobotUnitSimulation was still running
615 ARMARX_WARNING << "Ice::NotRegisteredException when sending commands to the simulator";
616 }
617 }
618}
619
620bool
621RobotUnitSimulation::skipReport() const
622{
623 static const std::set<RobotUnitState> reportAcceptingStates{
625 return !reportAcceptingStates.count(getRobotUnitState());
626}
627
628void
629RobotUnitSimulation::rtPollRobotState()
630{
631 if (!isPolling.exchange(true))
632 {
633 std::thread{[&]
634 {
635 try
636 {
637 const auto name = getRobotName();
638 auto rootname = robot->getRootNode()->getName();
639 SimulatedRobotState state = simulatorPrx->getRobotState(robotName);
640
641 reportState(state);
642 }
643 catch (Ice::Exception& e)
644 {
645 ARMARX_ERROR << "exception in polling!\nwhat:\n"
646 << e.what() << "\n\tname: " << e.ice_id()
647 << "\n\tfile: " << e.ice_file()
648 << "\n\tline: " << e.ice_line()
649 << "\n\tstack: " << e.ice_stackTrace() << std::flush;
650 throw;
651 }
652 catch (std::exception& e)
653 {
654 ARMARX_ERROR << "exception in polling!\nwhat:\n"
655 << e.what() << std::flush;
656 throw;
657 }
658 catch (...)
659 {
660 ARMARX_ERROR << "exception in polling!" << std::flush;
661 throw;
662 }
663 isPolling = false;
664 }}
665 .detach();
666 }
667}
668
669void
670RobotUnitSimulation::componentPropertiesUpdated(const std::set<std::string>& changedProperties)
671{
672 RobotUnit::componentPropertiesUpdated(changedProperties);
673 if (changedProperties.count("SynchronizedSimulation"))
674 {
675 synchronizedSimulation = getProperty<bool>("SynchronizedSimulation").getValue();
676 }
677 if (areDevicesReady())
678 {
679 if (synchronizedSimulation)
680 {
681 simulatorPrx->stop();
682 ARMARX_IMPORTANT << "using synchronized simulator execution" << std::flush;
683 }
684 else
685 {
686 simulatorPrx->start();
687 ARMARX_IMPORTANT << "using synchronized simulator execution" << std::flush;
688 }
689 }
690}
691
692void
693RobotUnitSimulation::rtUpdateSensors(bool wait)
694{
695 //wait for all new data
696 if (wait)
697 {
698 std::mutex dummy;
699 std::unique_lock<std::mutex> dummlock{dummy};
700 std::size_t fails = 0;
701 while (!gotSensorData)
702 {
704 << "waiting for up to date sensor values (iteration " << iterationCount
705 << ") ";
706 cvGotSensorData.wait_for(dummlock, std::chrono::milliseconds{10});
707 ++fails;
708 if (!(fails % 10))
709 {
710 rtPollRobotState();
711 }
712 }
713 }
714
715 // This should be the timestamp from the time where the data was queried
716 // We have to use armarx::rtNow() now because of changes in the framework to assess the timestamp in
717 // a realtime-conform way (using MONOTONIC_RAW clock, which is the time difference since last boot).
718 // This probably breaks simulation speeds faster than realtime, but this feature probably was broken
719 // before anyways. See https://git.h2t.iar.kit.edu/sw/armarx/armarx-simulation/-/issues/26.
720 const IceUtil::Time now = armarx::rtNow();
721 //const IceUtil::Time now = IceUtil::Time::microSeconds(simulationDataTimestampInMicroSeconds);
722 const IceUtil::Time timeSinceLastIteration = now - sensorValuesTimestamp;
723 const float dt = static_cast<float>(timeSinceLastIteration.toSecondsDouble());
724 sensorValuesTimestamp = now;
725 torquesTB.read();
726 anglesTB.read();
727 velocitiesTB.read();
728 robPoseTB.read();
729 robVelTB.read();
730 forceTorqueTB.read();
731 //update joint sensors
732 for (std::size_t i = 0; i < jointDevs.size(); ++i)
733 {
734 JointSimulationDevice& jdev = *jointDevs.at(i);
735 const float deltav = velocitiesTB.getReadBuffer().at(i) - jdev.sensorValue.velocity;
736 jdev.sensorValue.acceleration = deltav / dt;
737 jdev.sensorValue.position = anglesTB.getReadBuffer().at(i);
738 jdev.sensorValue.torque = torquesTB.getReadBuffer().at(i);
739 jdev.sensorValue.velocity = velocitiesTB.getReadBuffer().at(i);
740 }
741
742 //ft
743 for (std::size_t i = 0; i < forceTorqueDevs.size(); ++i)
744 {
745 ForceTorqueSimulationSensorDevice& ft = *forceTorqueDevs.at(i);
746 ft.sensorValue.force = forceTorqueTB.getReadBuffer().at(i).force;
747 ft.sensorValue.torque = forceTorqueTB.getReadBuffer().at(i).torque;
748 }
749 //platform
750 if (platformDev)
751 {
752 auto& s = platformDev->sensorValue;
753
754 const float absolutePositionX = robPoseTB.getReadBuffer()(0, 3);
755 const float absolutePositionY = robPoseTB.getReadBuffer()(1, 3);
756 const float absolutePositionRotation =
757 VirtualRobot::MathTools::eigen4f2rpy(robPoseTB.getReadBuffer())(2);
758
759 Eigen::Vector2f relativePositionGlobalFrame;
760 relativePositionGlobalFrame << absolutePositionX - platformDev->initAbsolutePositionX,
761 absolutePositionY - platformDev->initAbsolutePositionY;
762 s.relativePositionRotation =
763 absolutePositionRotation - platformDev->initAbsolutePositionRotation;
764 // Revert the rotation by rotating by the negative angle
765 Eigen::Vector2f relativePosition =
766 Eigen::Rotation2Df(-platformDev->initAbsolutePositionRotation) *
767 relativePositionGlobalFrame;
768 s.relativePositionX = relativePosition(0);
769 s.relativePositionY = relativePosition(1);
770
771 const RobVel& v = robVelTB.getReadBuffer();
772 s.setVelocitiesAndDeriveAcceleration(v.lin(0) / 3.f, v.lin(1) / 3.f, v.ang(2) / 5.0f, dt);
773 }
774 //globalpose
775 // globalPoseDevice->sensor.pose = robPoseTB.getReadBuffer();
776
777 rtSetRobotGlobalPose(robPoseTB.getReadBuffer(), false);
778 //this call should not do anything in this case, since sensors are updated above
779 rtReadSensorDeviceValues(sensorValuesTimestamp, timeSinceLastIteration);
780}
std::string timestamp()
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition Logging.cpp:75
#define VAROUT(x)
constexpr T dt
virtual void componentPropertiesUpdated(const std::set< std::string > &changedProperties)
Implement this function if you would like to react to changes in the properties.
Property< PropertyType > getProperty(const std::string &name)
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.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
virtual void rtMarkRtBusSendReceiveStart()=0
virtual void rtMarkRtBusSendReceiveEnd()=0
void rtResetAllTargets()
Calls rtResetTarget for all active Joint controllers.
void rtUpdateSensorAndControlBuffer(const IceUtil::Time &sensorValuesTimestamp, const IceUtil::Time &timeSinceLastIteration)
Updates the current SensorValues and ControlTargets for code running outside of the ControlThread.
void rtHandleInvalidTargets()
Deactivates all NJointControllers generating invalid targets.
bool rtSwitchControllerSetup(SwitchControllerMode mode=SwitchControllerMode::IceRequests)
Changes the set of active controllers.
void rtRunNJointControllers(const IceUtil::Time &sensorValuesTimestamp, const IceUtil::Time &timeSinceLastIteration)
Runs NJoint controllers.
void rtSetRobotGlobalPose(const Eigen::Matrix4f &gp, bool updateRobot=true)
void rtRunJointControllers(const IceUtil::Time &sensorValuesTimestamp, const IceUtil::Time &timeSinceLastIteration)
Runs Joint controllers and writes target values to ControlDevices.
void rtReadSensorDeviceValues(const IceUtil::Time &sensorValuesTimestamp, const IceUtil::Time &timeSinceLastIteration)
Calls rtReadSensorValues for all SensorDevices.
void addSensorDevice(const SensorDevicePtr &sd)
Adds a SensorDevice to the robot.
void addControlDevice(const ControlDevicePtr &cd)
Adds a ControlDevice to the robot.
RTThreadTimingsSensorDevice & rtGetRTThreadTimingsSensorDevice()
Returns the SensorDevice used to log timings in the ControlThread.
virtual void finishDeviceInitialization()
Transition InitializingDevices -> InitializingUnits.
RobotUnitState getRobotUnitState() const
Returns the RobotUnit's State.
bool areDevicesReady() const
Returns whether Devices are ready.
virtual void finishUnitInitialization()
Transition InitializingUnits -> RobotUnitState::WaitingForRTThreadInitialization.
virtual void finishControlThreadInitialization()
Transition InitializingControlThread -> Running.
const std::string & getRobotPlatformName() const
Returns the name of the robot's platform.
std::string getRobotName() const
Returns the robot's name.
VirtualRobot::RobotPtr cloneRobot(bool updateCollisionModel=false) const
Returns a clone of the robot's model.
virtual void initializeDefaultUnits()
Calls all init hooks for all managed Units.
void joinControlThread() override
Implementations have to join their ControlThread in this hook. (used by RobotUnit::finishRunning())
void componentPropertiesUpdated(const std::set< std::string > &changedProperties) override
std::atomic< long > simulationDataTimestampInMicroSeconds
SimulatorInterfacePrx simulatorPrx
void onConnectRobotUnit() override
called in onConnectComponent
void reportState(SimulatedRobotState const &state, const Ice::Current &=Ice::emptyCurrent) override
void onInitRobotUnit() override
called in onInitComponent
DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION Eigen::Vector3f torque
static IceUtil::Time GetTime(TimeMode timeMode=TimeMode::VirtualTime)
Get the current time.
Definition TimeUtil.cpp:42
static bool HasTimeServer()
check if we have been initialized with a Timeserver
Definition TimeUtil.cpp:124
void update(Ice::Long, const VariantBasePtr &value, const Ice::Current &=Ice::emptyCurrent) override
#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_CHECK_POSITIVE(number)
This macro evaluates whether number is positive (> 0) and if it turns out to be false it will throw a...
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#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
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
#define ARMARX_ON_SCOPE_EXIT
Executes given code when the enclosing scope is left.
double s(double t, double s0, double v0, double a0, double j)
Definition CtrlUtil.h:33
double a(double t, double a0, double j)
Definition CtrlUtil.h:45
double v(double t, double v0, double a0, double j)
Definition CtrlUtil.h:39
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)
std::vector< std::vector< T > > transpose(const std::vector< std::vector< T > > &src, Thrower thrower)
IceUtil::Time rtNow()
Definition RtTiming.h:40
Eigen::Vector3d Vector3
Definition kbm.h:43