ViewSelection.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2015-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * @package RobotAPI::ViewSelection
19 * @author David Schiebener (schiebener at kit dot edu)
20 * @author Markus Grotz ( markus dot grotz at kit dot edu )
21 * @date 2015
22 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
23 * GNU General Public License
24 */
25
26#include "ViewSelection.h"
27
28#include <thread>
29
32
35
36namespace armarx
37{
38 void
40 {
41 usingProxy(getProperty<std::string>("RobotStateComponentName").getValue());
42 usingProxy(getProperty<std::string>("HeadIKUnitName").getValue());
43
44 offeringTopic("DebugDrawerUpdates");
45 offeringTopic(getName() + "Observer");
46
47 headIKKinematicChainName = getProperty<std::string>("HeadIKKinematicChainName").getValue();
48 headFrameName = getProperty<std::string>("HeadFrameName").getValue();
49 cameraFrameName = getProperty<std::string>("CameraFrameName").getValue();
50 drawViewDirection = getProperty<bool>("VisualizeViewDirection").getValue();
51 visuSaliencyThreshold = getProperty<float>("VisuSaliencyThreshold").getValue();
52
53 std::string graphFileName = "RobotAPI/ViewSelection/graph40k.gra";
54
55 armarx::CMakePackageFinder finder("RobotAPI");
57
58 if (ArmarXDataPath::getAbsolutePath(graphFileName, graphFileName))
59 {
60 visibilityMaskGraph = new CIntensityGraph(graphFileName);
61 TNodeList* nodes = visibilityMaskGraph->getNodes();
62 const float maxOverallHeadTiltAngle =
63 getProperty<float>("MaxOverallHeadTiltAngle").getValue();
64 const float borderAreaAngle = 10.0f;
65 const float centralAngle = getProperty<float>("CentralHeadTiltAngle").getValue();
66
67 for (size_t i = 0; i < nodes->size(); i++)
68 {
69 CIntensityNode* node = (CIntensityNode*)nodes->at(i);
70
71 if (fabs(node->getPosition().fPhi - centralAngle) <
72 maxOverallHeadTiltAngle - borderAreaAngle)
73 {
74 node->setIntensity(1.0f);
75 }
76 else if (fabs(node->getPosition().fPhi - centralAngle) < maxOverallHeadTiltAngle)
77 {
78 node->setIntensity(1.0f - (fabs(node->getPosition().fPhi - centralAngle) -
79 (maxOverallHeadTiltAngle - borderAreaAngle)) /
80 borderAreaAngle);
81 }
82 else
83 {
84 node->setIntensity(0.0f);
85 }
86 }
87 }
88 else
89 {
90 ARMARX_ERROR << "Could not find required1 graph file";
92 return;
93 }
94
95 sleepingTimeBetweenViewDirectionChanges =
96 getProperty<int>("SleepingTimeBetweenViewDirectionChanges").getValue();
97 doAutomaticViewSelection = getProperty<bool>("ActiveAtStartup").getValue();
98
99 timeOfLastViewChange = armarx::TimeUtil::GetTime();
100
101 // this is robot model specific: offset from the used head coordinate system to the actual
102 // head center where the eyes are assumed to be located. Here it is correct for ARMAR-III
103 offsetToHeadCenter << 0, 0, 150;
104
105 processorTask = new PeriodicTask<ViewSelection>(this, &ViewSelection::process, 30);
106 processorTask->setDelayWarningTolerance(sleepingTimeBetweenViewDirectionChanges + 100);
107 }
108
109 void
111 {
112 robotStateComponent = getProxy<RobotStateComponentInterfacePrx>(
113 getProperty<std::string>("RobotStateComponentName").getValue());
114
115 headIKUnitProxy =
117 headIKUnitProxy->request();
118
119 viewSelectionObserver = getTopic<ViewSelectionObserverPrx>(getName() + "Observer");
120 drawer = getTopic<DebugDrawerInterfacePrx>("DebugDrawerUpdates");
121
122 processorTask->start();
123 }
124
125 void
127 {
128 processorTask->stop();
129
130 if (drawer)
131 {
132 drawer->removeArrowDebugLayerVisu("HeadRealViewDirection");
133 }
134
135 try
136 {
137 headIKUnitProxy->release();
138 }
139 catch (...)
140 {
141 ARMARX_WARNING_S << "An exception occured during onDisconnectComponent()";
143 }
144 }
145
146 void
148 {
149 delete visibilityMaskGraph;
150 }
151
152 void
153 ViewSelection::getActiveSaliencyMaps(std::vector<std::string>& activeSaliencyMaps)
154 {
155 std::unique_lock lock(syncMutex);
156
157 IceUtil::Time currentTime = armarx::TimeUtil::GetTime();
158 for (const auto& p : saliencyMaps)
159 {
160 if (p.second->validUntil)
161 {
162 TimestampVariantPtr time = TimestampVariantPtr::dynamicCast(p.second->validUntil);
163 if (time->toTime() > currentTime)
164 {
165 activeSaliencyMaps.push_back(p.second->name);
166 }
167 }
168 else if ((currentTime -
169 TimestampVariantPtr::dynamicCast(p.second->timeAdded)->toTime()) <
170 IceUtil::Time::seconds(5))
171 {
172 activeSaliencyMaps.push_back(p.second->name);
173 }
174 }
175 }
176
177 ViewTargetBasePtr
178 ViewSelection::nextAutomaticViewTarget()
179 {
180 std::vector<std::string> activeSaliencyMaps;
181 getActiveSaliencyMaps(activeSaliencyMaps);
182
183 if (activeSaliencyMaps.empty())
184 {
185 return nullptr;
186 }
187
188
189 SharedRobotInterfacePrx robotPrx = robotStateComponent->getSynchronizedRobot();
190 TNodeList* visibilityMaskGraphNodes = visibilityMaskGraph->getNodes();
191
192 // find the direction with highest saliency
193 int maxIndex = -1;
194 float maxSaliency = std::numeric_limits<float>::min();
195
196 std::unique_lock lock(syncMutex);
197
198 hasNewSaliencyMap = false;
199
200 for (size_t i = 0; i < visibilityMaskGraphNodes->size(); i++)
201 {
202 float saliency = 0.0f;
203 for (const std::string& n : activeSaliencyMaps)
204 {
205 saliency += saliencyMaps[n]->map[i];
206 }
207
208 saliency /= activeSaliencyMaps.size();
209
210 CIntensityNode* visibilityNode = (CIntensityNode*)visibilityMaskGraphNodes->at(i);
211 saliency *= visibilityNode->getIntensity();
212
213 if (saliency > maxSaliency)
214 {
215 maxSaliency = saliency;
216 maxIndex = i;
217 }
218 }
219
220
221 lock.unlock();
222
223 ARMARX_DEBUG << "Highest saliency: " << maxSaliency;
224
225 // select a new view if saliency is bigger than threshold (which converges towards 0 over time)
226 int timeSinceLastViewChange =
227 (armarx::TimeUtil::GetTime() - timeOfLastViewChange).toMilliSeconds();
228 float saliencyThreshold = 0;
229
230 if (timeSinceLastViewChange > 0)
231 {
232 saliencyThreshold =
233 2.0f * sleepingTimeBetweenViewDirectionChanges / timeSinceLastViewChange;
234 }
235
236 if (maxSaliency > saliencyThreshold)
237 {
238 Eigen::Vector3f target;
239 MathTools::convert(visibilityMaskGraphNodes->at(maxIndex)->getPosition(), target);
240 const float distanceFactor = 1500;
241 target = distanceFactor * target + offsetToHeadCenter;
242 FramedPositionPtr viewTargetPositionPtr =
243 new FramedPosition(target, headFrameName, robotPrx->getName());
244
245 ViewTargetBasePtr viewTarget = new ViewTargetBase();
246 viewTarget->priority = armarx::DEFAULT_VIEWTARGET_PRIORITY; // * maxSaliency;
247 viewTarget->timeAdded = new TimestampVariant(armarx::TimeUtil::GetTime());
248 viewTarget->target = viewTargetPositionPtr;
249 viewTarget->duration = 0;
250
251
252 if (visuSaliencyThreshold > 0.0)
253 {
254 drawSaliencySphere(activeSaliencyMaps);
255 }
256
257 return viewTarget;
258 }
259
260 return nullptr;
261 }
262
263 void
264 ViewSelection::process()
265 {
266 /*
267 while(getState() < eManagedIceObjectExiting)
268 {
269 }
270 */
271
272 ViewTargetBasePtr viewTarget;
273
274 if (doAutomaticViewSelection)
275 {
276 viewTarget = nextAutomaticViewTarget();
277 }
278
279 /*
280 //discard outdated view targets
281 IceUtil::Time currentTime = armarx::TimeUtil::GetTime();
282 while (!manualViewTargets.empty())
283 {
284 ViewTargetBasePtr manualViewTarget = manualViewTargets.top();
285 TimestampVariantPtr time = TimestampVariantPtr::dynamicCast(manualViewTarget->validUntil);
286
287 if (time->toTime() < currentTime)
288 {
289 ARMARX_INFO << "view target is no longer valid";
290 manualViewTargets.pop();
291 }
292 else
293 {
294 break;
295 }
296 }
297 */
298
299 if (!manualViewTargets.empty())
300 {
301 std::unique_lock lock(manualViewTargetsMutex);
302
303 ViewTargetBasePtr manualViewTarget = manualViewTargets.top();
304
305 CompareViewTargets c;
306
307 if (!viewTarget)
308 {
309 viewTarget = manualViewTarget;
310 manualViewTargets.pop();
311 }
312 else if (c(viewTarget, manualViewTarget))
313 {
314 viewTarget = manualViewTarget;
315 manualViewTargets.pop();
316 }
317 }
318
319 if (viewTarget)
320 {
321 SharedRobotInterfacePrx robotPrx = robotStateComponent->getSynchronizedRobot();
322 // FramedPositionPtr viewTargetPositionPtr = FramedPositionPtr::dynamicCast(viewTarget->target);
323 FramedPositionPtr viewTargetPositionPtr =
324 new FramedPosition(FramedPositionPtr::dynamicCast(viewTarget->target)->toEigen(),
325 viewTarget->target->frame,
326 robotPrx->getName());
327
328 if (drawer && robotPrx->hasRobotNode("Cameras") && drawViewDirection)
329 {
330 float arrowLength = 1500.0f;
331 Vector3Ptr startPos = new Vector3(
332 FramedPosePtr::dynamicCast(robotPrx->getRobotNode("Cameras")->getGlobalPose())
333 ->toEigen());
334 FramedDirectionPtr currentDirection =
335 new FramedDirection(Eigen::Vector3f(1, 0, 0), "Cameras", robotPrx->getName());
336 drawer->setArrowDebugLayerVisu("CurrentHeadViewDirection",
337 startPos,
338 currentDirection->toGlobal(robotPrx),
339 DrawColor{0, 0.5, 0.5, 0.1},
340 arrowLength,
341 5);
342 Eigen::Vector3f plannedDirectionEigen =
343 viewTargetPositionPtr->toGlobalEigen(robotPrx) - startPos->toEigen();
344 Vector3Ptr plannedDirection = new Vector3(plannedDirectionEigen);
345 drawer->setArrowDebugLayerVisu("PlannedHeadViewDirection",
346 startPos,
347 plannedDirection,
348 DrawColor{0.5, 0.5, 0, 0.1},
349 arrowLength,
350 5);
351 }
352
353 ARMARX_INFO << "Looking at target " << viewTargetPositionPtr->output();
354 headIKUnitProxy->setHeadTarget(headIKKinematicChainName, viewTargetPositionPtr);
355
356 timeOfLastViewChange = TimeUtil::GetTime();
357
358 long duration = viewTarget->duration;
359 if (!viewTarget->duration)
360 {
361 duration = sleepingTimeBetweenViewDirectionChanges;
362 }
363
364 viewSelectionObserver->nextViewTarget(timeOfLastViewChange.toMilliSeconds() + duration);
365
366 std::this_thread::sleep_for(std::chrono::milliseconds(duration));
367 }
368 else
369 {
370 std::this_thread::sleep_for(std::chrono::milliseconds(5));
371 }
372 }
373
374 void
375 ViewSelection::addManualViewTarget(const FramedPositionBasePtr& target, const Ice::Current& c)
376 {
377 std::unique_lock lock(manualViewTargetsMutex);
378
379 //SharedRobotInterfacePrx sharedRobotInterface = robotStateComponent->getRobotSnapshot();
380
381 ViewTargetBasePtr viewTarget = new ViewTargetBase();
382 viewTarget->priority = armarx::DEFAULT_VIEWTARGET_PRIORITY + 1;
383 viewTarget->timeAdded = new TimestampVariant(IceUtil::Time::now());
384 viewTarget->target = target;
385 viewTarget->duration = 0;
386
387 manualViewTargets.push(viewTarget);
388
389 std::unique_lock lock2(syncMutex);
390
391 condition.notify_all();
392 }
393
394 void
396 {
397 std::unique_lock lock(manualViewTargetsMutex);
398
399 while (!manualViewTargets.empty())
400 {
401 manualViewTargets.pop();
402 }
403
404 // std::priority_queue<ViewTargetBasePtr, vector<ViewTargetBasePtr>, CompareViewTargets> temp;
405 // manualViewTargets.swap(temp);
406 }
407
408 ViewTargetList
410 {
411 std::unique_lock lock(manualViewTargetsMutex);
412
413 ViewTargetList result;
414
415 std::priority_queue<ViewTargetBasePtr, std::vector<ViewTargetBasePtr>, CompareViewTargets>
416 temp(manualViewTargets);
417
418 while (!temp.empty())
419 {
420 result.push_back(temp.top());
421
422 temp.pop();
423 }
424
425 return result;
426 }
427
428 void
429 armarx::ViewSelection::updateSaliencyMap(const SaliencyMapBasePtr& map, const Ice::Current&)
430 {
431 std::unique_lock lock(syncMutex);
432
433 hasNewSaliencyMap = true;
434
435 map->timeAdded = TimestampVariant::nowPtr();
436
437 saliencyMaps[map->name] = map;
438
439 condition.notify_all();
440 }
441
442 void
443 ViewSelection::drawSaliencySphere(const ::Ice::StringSeq& names, const Ice::Current& c)
444 {
445 ARMARX_LOG << "visualizing saliency map";
446
447 drawer->clearLayer("saliencyMap");
448
449 SharedRobotInterfacePrx robotPrx = robotStateComponent->getSynchronizedRobot();
450
451 Eigen::Matrix4f cameraToGlobal =
452 FramedPosePtr::dynamicCast(robotPrx->getRobotNode(headFrameName)->getGlobalPose())
453 ->toEigen();
454
455 std::vector<std::string> activeSaliencyMaps;
456
457 if (names.size())
458 {
459 for (std::string n : names)
460 {
461 activeSaliencyMaps.push_back(n);
462 }
463 }
464 else
465 {
466 getActiveSaliencyMaps(activeSaliencyMaps);
467 }
468
469 if (!activeSaliencyMaps.size())
470 {
471 return;
472 }
473
474
475 std::unique_lock lock(syncMutex);
476
477 TNodeList* visibilityMaskGraphNodes = visibilityMaskGraph->getNodes();
478 DebugDrawer24BitColoredPointCloud cloud;
479 cloud.points.reserve(visibilityMaskGraphNodes->size());
480 for (size_t i = 0; i < visibilityMaskGraphNodes->size(); i++)
481 {
482 float saliency = 0.0f;
483 for (const std::string& n : activeSaliencyMaps)
484 {
485 saliency += saliencyMaps[n]->map[i];
486 }
487
488 saliency /= activeSaliencyMaps.size();
489
490 CIntensityNode* visibilityNode = (CIntensityNode*)visibilityMaskGraphNodes->at(i);
491 saliency *= visibilityNode->getIntensity();
492
493 Eigen::Vector3d out;
494 TSphereCoord pos = visibilityNode->getPosition();
495 MathTools::convert(pos, out);
496
497 Eigen::Matrix4f pose = Eigen::Matrix4f::Identity();
498 pose.col(3).head<3>() = out.cast<float>() * 1000.0;
499 pose = cameraToGlobal * pose;
500
501 // float r = std::min(1.0, (1.0 - saliency) * 0.2 + saliency * 0.8 + 0.2);
502 // float g = std::min(1.0, (1.0 - saliency) * 0.8 + saliency * 0.2 + 0.2);
503 // float b = std::min(1.0, (1.0 - saliency) * 0.2 + saliency * 0.2 + 0.2);
504
505 if (saliency < visuSaliencyThreshold)
506 {
507 continue;
508 }
509 DebugDrawer24BitColoredPointCloudElement point;
510 point.color = colorutils::HeatMapRGBColor(saliency);
511 point.x = pose(0, 3);
512 point.y = pose(1, 3);
513 point.z = pose(2, 3);
514 cloud.points.push_back(point);
515 }
516 cloud.pointSize = 10;
517 drawer->set24BitColoredPointCloudVisu("saliencyMap", "SaliencyCloud", cloud);
518 }
519
520 void
522 {
523 drawer->clearLayer("saliencyMap");
524 }
525
526 void
527 ViewSelection::removeSaliencyMap(const std::string& name, const Ice::Current& c)
528 {
529 std::unique_lock lock(syncMutex);
530
531 if (saliencyMaps.count(name))
532 {
533 saliencyMaps.erase(name);
534 }
535
536 condition.notify_all();
537 }
538
539 Ice::StringSeq
541 {
542 std::vector<std::string> names;
543
544 std::unique_lock lock(syncMutex);
545
546 for (const auto& p : saliencyMaps)
547 {
548 names.push_back(p.second->name);
549 }
550
551 return names;
552 }
553
559} // namespace armarx
std::vector< CSGNode * > TNodeList
constexpr T c
void setIntensity(float fIntensity)
TSphereCoord getPosition()
static void convert(TSphereCoord in, Eigen::Vector3d &out)
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
static void addDataPaths(const std::string &dataPathList)
The CMakePackageFinder class provides an interface to the CMake Package finder capabilities.
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
Property< PropertyType > getProperty(const std::string &name)
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.
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)
The periodic task executes one thread method repeatedly using the time period specified in the constr...
static IceUtil::Time GetTime(TimeMode timeMode=TimeMode::VirtualTime)
Get the current time.
Definition TimeUtil.cpp:42
Implements a Variant type for timestamps.
static TimestampVariantPtr nowPtr()
void addManualViewTarget(const FramedPositionBasePtr &target, const Ice::Current &c=Ice::emptyCurrent) override
void onInitComponent() override
void clearSaliencySphere(const Ice::Current &c=Ice::emptyCurrent) override
void removeSaliencyMap(const std::string &name, const Ice::Current &c=Ice::emptyCurrent) override
void onDisconnectComponent() override
ViewTargetList getManualViewTargets(const Ice::Current &c=Ice::emptyCurrent) override
void drawSaliencySphere(const ::Ice::StringSeq &names, const Ice::Current &c=Ice::emptyCurrent) override
::Ice::StringSeq getSaliencyMapNames(const Ice::Current &c=Ice::emptyCurrent) override
void updateSaliencyMap(const SaliencyMapBasePtr &map, const Ice::Current &c=Ice::emptyCurrent) override
void onConnectComponent() override
PropertyDefinitionsPtr createPropertyDefinitions() override
void clearManualViewTargets(const Ice::Current &c=Ice::emptyCurrent) override
void onExitComponent() override
#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_LOG
Definition Logging.h:165
#define ARMARX_WARNING_S
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:213
const VariantTypeId FramedPosition
Definition FramedPose.h:38
const VariantTypeId FramedDirection
Definition FramedPose.h:37
DrawColor24Bit HeatMapRGBColor(float percentage)
Definition ColorUtils.h:169
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceInternal::Handle< Vector3 > Vector3Ptr
Definition Pose.h:165
void handleExceptions()
IceInternal::Handle< FramedDirection > FramedDirectionPtr
Definition FramedPose.h:84
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
IceInternal::Handle< FramedPosition > FramedPositionPtr
Definition FramedPose.h:149
::IceInternal::ProxyHandle<::IceProxy::armarx::SharedRobotInterface > SharedRobotInterfacePrx
Definition FramedPose.h:59
IceInternal::Handle< TimestampVariant > TimestampVariantPtr
Vertex target(const detail::edge_base< Directed, Vertex > &e, const PCG &)
constexpr auto n() noexcept
Eigen::Vector3d Vector3
Definition kbm.h:43
float fPhi
Definition Structs.h:26