RobotUnitModuleSelfCollisionChecker.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 RobotAPI::ArmarXObjects::RobotUnit
17 * @author Raphael Grimm ( raphael dot grimm at kit dot edu )
18 * @date 2018
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
24
25#include <algorithm>
26#include <cstddef>
27#include <string>
28
29#include <SimoxUtility/algorithm/string/string_tools.h>
30#include <VirtualRobot/CollisionDetection/CollisionChecker.h>
31#include <VirtualRobot/CollisionDetection/CollisionModel.h>
32#include <VirtualRobot/Nodes/RobotNode.h>
33#include <VirtualRobot/Obstacle.h>
34#include <VirtualRobot/Robot.h>
35#include <VirtualRobot/RobotNodeSet.h>
36
40
45
47
48#define FLOOR_OBJ_STR "FLOOR"
49
51{
52 void
53 SelfCollisionChecker::_preOnInitRobotUnit()
54 {
55 throwIfInControlThread(BOOST_CURRENT_FUNCTION);
56 //get the robot
57 selfCollisionAvoidanceRobot = _module<RobotData>().cloneRobot(true);
58 //get pairs to check
59 {
60 const std::string colModelsString =
61 getProperty<std::string>("SelfCollisionCheck_ModelGroupsToCheck").getValue();
62 std::vector<std::string> groups;
63 if (!colModelsString.empty())
64 {
65 groups = armarx::Split(colModelsString, ";", true, true);
66 }
67 ARMARX_DEBUG << "Processing groups for self collision checking:";
68 for (std::string& group : groups)
69 {
70 ARMARX_DEBUG << "---- group: " << group;
71 // Removing parentheses
72 simox::alg::trim_if(group, " \t{}");
73 std::set<std::set<std::string>> setsOfNode;
74 {
75 auto splittedRaw = armarx::Split(group, ",", true, true);
76 if (splittedRaw.size() < 2)
77 {
78 continue;
79 }
80 for (auto& subentry : splittedRaw)
81 {
82 simox::alg::trim_if(subentry, " \t{}");
83 if (selfCollisionAvoidanceRobot->hasRobotNodeSet(subentry))
84 {
85 std::set<std::string> nodes;
86 //the entry is a set
87 for (const auto& node :
88 selfCollisionAvoidanceRobot->getRobotNodeSet(subentry)
89 ->getAllRobotNodes())
90 {
91 if (!node->getCollisionModel())
92 {
93
94 ARMARX_WARNING << "Self Collision Avoidance: No collision "
95 "model found for '"
96 << node->getName() << "'";
97 continue;
98 }
99 nodes.emplace(node->getName());
100 ARMARX_DEBUG << "-------- from set: " << subentry
101 << ", node: " << node->getName();
102 }
103 setsOfNode.emplace(std::move(nodes));
104 }
105 else if (selfCollisionAvoidanceRobot->hasRobotNode(subentry))
106 {
107 //the entry is a node
108 if (!selfCollisionAvoidanceRobot->getRobotNode(subentry)
109 ->getCollisionModel())
110 {
111
113 << "Self Collision Avoidance: No collision model found for '"
114 << selfCollisionAvoidanceRobot->getRobotNode(subentry)
115 ->getName()
116 << "'";
117 continue;
118 }
119 setsOfNode.emplace(std::set<std::string>{subentry});
120 ARMARX_DEBUG << "-------- node: " << subentry;
121 }
122 else if (subentry == FLOOR_OBJ_STR)
123 {
124 //the entry is the floor
125 setsOfNode.emplace(std::set<std::string>{subentry});
126 ARMARX_DEBUG << "-------- floor: " << subentry;
127 }
128 else
129 {
130 ARMARX_ERROR << "No RobotNodeSet or RobotNode with name '" << subentry
131 << "' defined in "
132 << _module<RobotData>().getRobotFileName()
133 << ". Skipping.";
134 continue;
135 }
136 }
137 }
138
139 auto addCombinationOfSetsToCollisionCheck =
140 [this](const std::set<std::string>& a, const std::set<std::string>& b)
141 {
142 for (const auto& nodeA : a)
143 {
144 for (const auto& nodeB : b)
145 {
146 if (nodeA == nodeB)
147 {
148 continue;
149 }
150 if (nodeA < nodeB)
151 {
152 ARMARX_DEBUG << "------------ " << nodeA << " " << nodeB;
153 namePairsToCheck.emplace(nodeA, nodeB);
154 }
155 else
156 {
157 ARMARX_DEBUG << "------------ " << nodeB << " " << nodeA;
158 namePairsToCheck.emplace(nodeB, nodeA);
159 }
160 }
161 }
162 };
163
164 ARMARX_DEBUG << "-------- adding pairs to check:";
165 for (auto setAIt = setsOfNode.begin(); setAIt != setsOfNode.end(); ++setAIt)
166 {
167 auto setBIt = setAIt;
168 ++setBIt;
169 for (; setBIt != setsOfNode.end(); ++setBIt)
170 {
171 addCombinationOfSetsToCollisionCheck(*setAIt, *setBIt);
172 }
173 }
174
175
176 ARMARX_DEBUG << "-------- group: " << group << "...DONE!\n";
177 }
178 ARMARX_DEBUG << "Processing groups for self collision checking...DONE!";
179 }
181 getProperty<float>("SelfCollisionCheck_Frequency").getValue());
183 getProperty<float>("SelfCollisionCheck_MinSelfDistance").getValue());
184 }
185
186 void
188 const Ice::Current&)
189 {
190 throwIfInControlThread(BOOST_CURRENT_FUNCTION);
191 std::lock_guard<std::mutex> guard{selfCollisionDataMutex};
192 if (distance < 0)
193 {
194 throw InvalidArgumentException{std::string{__FILE__} + ": " + BOOST_CURRENT_FUNCTION +
195 ": illegal distance:" + std::to_string(distance)};
196 }
197 if (distance == minSelfDistance && !nodePairsToCheck.empty())
198 {
199 return;
200 }
201 //reset data
203 minSelfDistance = distance;
204 selfCollisionAvoidanceRobot = _module<RobotData>().cloneRobot(true);
205 selfCollisionAvoidanceRobotNodes = selfCollisionAvoidanceRobot->getRobotNodes();
206 nodePairsToCheck.clear();
207 //set floor
208 {
209 floor.reset(new VirtualRobot::SceneObjectSet(
210 "FLOORSET", selfCollisionAvoidanceRobot->getCollisionChecker()));
211 static constexpr float floorSize = 1e16f;
212 VirtualRobot::ObstaclePtr boxOb = VirtualRobot::Obstacle::createBox(
213 floorSize,
214 floorSize,
215 std::min(0.001f, minSelfDistance / 2),
216 VirtualRobot::VisualizationFactory::Color::Red(),
217 "",
218 selfCollisionAvoidanceRobot->getCollisionChecker());
219 boxOb->setGlobalPose(Eigen::Matrix4f::Identity());
220 boxOb->setName(FLOOR_OBJ_STR);
221 floor->addSceneObject(boxOb);
222 }
223 //inflate robot
224 for (const auto& node : selfCollisionAvoidanceRobotNodes)
225 {
226 if (node->getCollisionModel())
227 {
228 node->getCollisionModel()->inflateModel(minSelfDistance / 2.f);
229 }
230 }
231
232 // Remove / filter collision pairs according to robot model (XML: Physics/IgnoreCollision)
233 {
234 ARMARX_VERBOSE << "Removing ignored collision pairs";
235 // introduce vector to remove elements "in-place" via remove-erase-if idiom (not possible for sets)
236 std::vector<std::pair<std::string, std::string>> validNamePairsToCheck(
237 namePairsToCheck.begin(), namePairsToCheck.end());
238
239 const auto isCollisionIgnored = [this](const std::string& a,
240 const std::string& b) -> bool
241 {
242 if (a == FLOOR_OBJ_STR or b == FLOOR_OBJ_STR)
243 {
244 return false;
245 }
246
247 const auto nodeA = selfCollisionAvoidanceRobot->getRobotNode(a);
248 const auto nodeB = selfCollisionAvoidanceRobot->getRobotNode(b);
249
250 if (nodeA == nullptr or nodeB == nullptr)
251 {
252 return false;
253 }
254
255 const std::vector<std::string> nodesIgnoredByA = nodeA->getIgnoredCollisionModels();
256 const std::vector<std::string> nodesIgnoredByB = nodeB->getIgnoredCollisionModels();
257
258 if (std::find(nodesIgnoredByA.begin(), nodesIgnoredByA.end(), b) !=
259 nodesIgnoredByA.end())
260 {
261 ARMARX_VERBOSE << "Ignoring collision between nodes: " << a << " -- " << b;
262 return true;
263 }
264
265 if (std::find(nodesIgnoredByB.begin(), nodesIgnoredByB.end(), a) !=
266 nodesIgnoredByB.end())
267 {
268 ARMARX_VERBOSE << "Ignoring collision between nodes: " << b << " -- " << a;
269 return true;
270 }
271
272 return false;
273 };
274
275 validNamePairsToCheck.erase(std::remove_if(validNamePairsToCheck.begin(),
276 validNamePairsToCheck.end(),
277 [&isCollisionIgnored](const auto& p) -> bool
278 {
279 const auto& [a, b] = p;
280 return isCollisionIgnored(a, b);
281 }),
282 validNamePairsToCheck.end());
283
284 ARMARX_VERBOSE << "Removed " << (namePairsToCheck.size() - validNamePairsToCheck.size())
285 << " collision pairs.";
286
287 // copy over name pairs which should not be ignored
288 namePairsToCheck = std::set(validNamePairsToCheck.begin(), validNamePairsToCheck.end());
289 }
290
291 //collect pairs
292 for (const auto& pair : namePairsToCheck)
293 {
294 VirtualRobot::SceneObjectPtr first =
295 (pair.first == FLOOR_OBJ_STR)
296 ? floor->getSceneObject(0)
297 : selfCollisionAvoidanceRobot->getRobotNode(pair.first);
298
299 ARMARX_CHECK_NOT_NULL(first) << pair.first;
300
301 VirtualRobot::SceneObjectPtr second =
302 (pair.second == FLOOR_OBJ_STR)
303 ? floor->getSceneObject(0)
304 : selfCollisionAvoidanceRobot->getRobotNode(pair.second);
305
306 ARMARX_CHECK_NOT_NULL(second) << pair.second;
307
308 nodePairsToCheck.emplace_back(first, second);
309 }
310 ARMARX_CHECK_EQUAL(nodePairsToCheck.size(), namePairsToCheck.size());
311 }
312
313 void
315 {
316 throwIfInControlThread(BOOST_CURRENT_FUNCTION);
317 if (freq < 0)
318 {
319 throw InvalidArgumentException{std::string{__FILE__} + ": " + BOOST_CURRENT_FUNCTION +
320 ": illegal frequency:" + std::to_string(freq)};
321 }
322 checkFrequency = freq;
323 }
324
325 bool
327 {
328 throwIfInControlThread(BOOST_CURRENT_FUNCTION);
329 return checkFrequency != 0;
330 }
331
332 float
334 {
335 throwIfInControlThread(BOOST_CURRENT_FUNCTION);
336 return checkFrequency;
337 }
338
339 float
341 {
342 throwIfInControlThread(BOOST_CURRENT_FUNCTION);
343 return minSelfDistance;
344 }
345
346 void
347 SelfCollisionChecker::_componentPropertiesUpdated(const std::set<std::string>& changed)
348 {
349 throwIfInControlThread(BOOST_CURRENT_FUNCTION);
350 if (changed.count("SelfCollisionCheck_Frequency"))
351 {
353 getProperty<float>("SelfCollisionCheck_Frequency").getValue());
354 }
355 if (changed.count("SelfCollisionCheck_MinSelfDistance"))
356 {
358 getProperty<float>("SelfCollisionCheck_MinSelfDistance").getValue());
359 }
360 }
361
362 void
363 SelfCollisionChecker::_postFinishControlThreadInitialization()
364 {
365 selfCollisionAvoidanceThread = std::thread{[&] { selfCollisionAvoidanceTask(); }};
366 }
367
368 void
369 SelfCollisionChecker::selfCollisionAvoidanceTask()
370 {
371 throwIfInControlThread(BOOST_CURRENT_FUNCTION);
372 ARMARX_INFO << "Self collision checker: entered selfCollisionAvoidanceTask";
374 {
375 ARMARX_INFO << "Self collision checker: leaving selfCollisionAvoidanceTask";
376 };
377 while (true)
378 {
379 const auto freq = checkFrequency.load();
380
381 core::time::Metronome metronome(Frequency::Hertz(freq));
382
383 //done
384 if (isShuttingDown())
385 {
386 return;
387 }
388 const bool inEmergencyStop =
389 _module<ControlThread>().getEmergencyStopState() == eEmergencyStopActive;
390 if (inEmergencyStop || freq == 0)
391 {
392 ARMARX_VERBOSE << deactivateSpam() << "Self collision checker: skipping check "
393 << VAROUT(freq) << " " << VAROUT(inEmergencyStop);
394 //currently wait
395 std::this_thread::sleep_for(std::chrono::microseconds{1'000});
396 continue;
397 }
398 //update robot + check
399 {
400 std::lock_guard<std::mutex> guard{selfCollisionDataMutex};
401 //update robot
402 _module<ControlThreadDataBuffer>().updateVirtualRobot(
403 selfCollisionAvoidanceRobot, selfCollisionAvoidanceRobotNodes);
404
405 //check for all nodes 0
406 {
407 bool allJoints0 = true;
408 for (const auto& node : selfCollisionAvoidanceRobotNodes)
409 {
411 if (0 != node->getJointValue())
412 {
413 allJoints0 = false;
414 break;
415 }
416 }
417 if (allJoints0)
418 {
419 continue;
420 }
421 }
422
423 bool collision = false;
424 for (std::size_t idx = 0; idx < nodePairsToCheck.size(); ++idx)
425 {
426 const auto& pair = nodePairsToCheck.at(idx);
427
428 ARMARX_CHECK_NOT_NULL(pair.first);
429 ARMARX_CHECK_NOT_NULL(pair.second);
430
431 if (selfCollisionAvoidanceRobot->getCollisionChecker()->checkCollision(
432 pair.first, pair.second))
433 {
434 collision = true;
435 lastCollisionPairIndex = idx;
436 ARMARX_WARNING << "Self collision checker: COLLISION: '"
437 << pair.first->getName() << "' and '"
438 << pair.second->getName() << "'";
439 _module<Units>().getEmergencyStopMaster()->setEmergencyStopState(
440 EmergencyStopState::eEmergencyStopActive);
441 // since at least one of the NJoint-Controllers is going to cause a collision, we just kick them all.
442 _module<ControlThreadDataBuffer>().setActivateControllersRequest({});
443 break;
444 }
445 }
446 if (!collision)
447 {
449 << "Self collision checker: no collision found between the "
450 << nodePairsToCheck.size() << " pairs";
451 }
452 }
453
454 //sleep remaining
455 const auto duration = metronome.waitForNextTick();
456
457 if (not duration.isPositive())
458 {
460 << "Self collision checking took too long. "
461 "Exceeding time budget by "
462 << duration.toMilliSecondsDouble() << "ms.";
463 }
464 }
465 }
466
467 void
468 SelfCollisionChecker::_preFinishRunning()
469 {
470 throwIfInControlThread(BOOST_CURRENT_FUNCTION);
471 ARMARX_INFO << "Stopping Self Collision Avoidance.";
472 if (selfCollisionAvoidanceThread.joinable())
473 {
474 selfCollisionAvoidanceThread.join();
475 }
476 }
477} // namespace armarx::RobotUnitModule
#define VAROUT(x)
Property< PropertyType > getProperty(const std::string &name)
static Frequency Hertz(std::int64_t hertz)
Definition Frequency.cpp:20
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.
Definition Logging.cpp:99
T & _module()
Returns this as ref to the given type.
void throwIfInControlThread(const std::string &fnc) const
Throws if the current thread is the ControlThread.
bool isShuttingDown() const
Returns whether the RobotUnit is shutting down.
bool isSelfCollisionCheckEnabled(const Ice::Current &=Ice::emptyCurrent) const override
Returns whether the frequency of self collision checks is above 0.
void setSelfCollisionAvoidanceDistance(Ice::Float distance, const Ice::Current &=Ice::emptyCurrent) override
Sets the minimal distance (mm) between a configured pair of bodies to 'distance'.
float getSelfCollisionAvoidanceFrequency(const Ice::Current &=Ice::emptyCurrent) const override
Returns the frequency of self collision checks.
float getSelfCollisionAvoidanceDistance(const Ice::Current &=Ice::emptyCurrent) const override
Returns the minimal distance (mm) between a pair of bodies.
void setSelfCollisionAvoidanceFrequency(Ice::Float freq, const Ice::Current &=Ice::emptyCurrent) override
Sets the frequency of self collision checks.
#define ARMARX_CHECK_GREATER(lhs, rhs)
This macro evaluates whether lhs is greater (>) than rhs and if it turns out to be false it will thro...
#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.
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
#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.
std::vector< std::string > Split(const std::string &source, const std::string &splitBy, bool trimElements=false, bool removeEmptyElements=false)
double distance(const Point &a, const Point &b)
Definition point.hpp:95