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
37
38namespace armarx
39{
40 void
42 {
43 usingProxy(getProperty<std::string>("RobotStateComponentName").getValue());
44 usingProxy(getProperty<std::string>("HeadIKUnitName").getValue());
45
46 offeringTopic("DebugDrawerUpdates");
47 offeringTopic(getName() + "Observer");
48
49 headIKKinematicChainName = getProperty<std::string>("HeadIKKinematicChainName").getValue();
50 headFrameName = getProperty<std::string>("HeadFrameName").getValue();
51 cameraFrameName = getProperty<std::string>("CameraFrameName").getValue();
52 drawViewDirection = getProperty<bool>("VisualizeViewDirection").getValue();
53 visuSaliencyThreshold = getProperty<float>("VisuSaliencyThreshold").getValue();
54
55 std::string graphFileName = "RobotAPI/ViewSelection/graph40k.gra";
56
57 armarx::CMakePackageFinder finder("RobotAPI");
59
60 if (ArmarXDataPath::getAbsolutePath(graphFileName, graphFileName))
61 {
62 visibilityMaskGraph = new CIntensityGraph(graphFileName);
63 TNodeList* nodes = visibilityMaskGraph->getNodes();
64 const float maxOverallHeadTiltAngle =
65 getProperty<float>("MaxOverallHeadTiltAngle").getValue();
66 const float borderAreaAngle = 10.0f;
67 const float centralAngle = getProperty<float>("CentralHeadTiltAngle").getValue();
68
69 for (size_t i = 0; i < nodes->size(); i++)
70 {
71 CIntensityNode* node = (CIntensityNode*)nodes->at(i);
72
73 if (fabs(node->getPosition().fPhi - centralAngle) <
74 maxOverallHeadTiltAngle - borderAreaAngle)
75 {
76 node->setIntensity(1.0f);
77 }
78 else if (fabs(node->getPosition().fPhi - centralAngle) < maxOverallHeadTiltAngle)
79 {
80 node->setIntensity(1.0f - (fabs(node->getPosition().fPhi - centralAngle) -
81 (maxOverallHeadTiltAngle - borderAreaAngle)) /
82 borderAreaAngle);
83 }
84 else
85 {
86 node->setIntensity(0.0f);
87 }
88 }
89 }
90 else
91 {
92 ARMARX_ERROR << "Could not find required1 graph file";
94 return;
95 }
96
97 sleepingTimeBetweenViewDirectionChanges =
98 getProperty<int>("SleepingTimeBetweenViewDirectionChanges").getValue();
99 doAutomaticViewSelection = getProperty<bool>("ActiveAtStartup").getValue();
100
101 timeOfLastViewChange = armarx::TimeUtil::GetTime();
102
103 // this is robot model specific: offset from the used head coordinate system to the actual
104 // head center where the eyes are assumed to be located. Here it is correct for ARMAR-III
105 offsetToHeadCenter << 0, 0, 150;
106
107 processorTask = new PeriodicTask<ViewSelection>(this, &ViewSelection::process, 30);
108 processorTask->setDelayWarningTolerance(sleepingTimeBetweenViewDirectionChanges + 100);
109 }
110
111 void
113 {
114 robotStateComponent = getProxy<RobotStateComponentInterfacePrx>(
115 getProperty<std::string>("RobotStateComponentName").getValue());
116
117 headIKUnitProxy =
119 headIKUnitProxy->request();
120
121 viewSelectionObserver = getTopic<ViewSelectionObserverPrx>(getName() + "Observer");
122 drawer = getTopic<DebugDrawerInterfacePrx>("DebugDrawerUpdates");
123
124 processorTask->start();
125 }
126
127 void
129 {
130 processorTask->stop();
131
132 if (drawer)
133 {
134 drawer->removeArrowDebugLayerVisu("HeadRealViewDirection");
135 }
136
137 try
138 {
139 headIKUnitProxy->release();
140 }
141 catch (...)
142 {
143 ARMARX_WARNING_S << "An exception occured during onDisconnectComponent()";
145 }
146 }
147
148 void
150 {
151 delete visibilityMaskGraph;
152 }
153
154 void
155 ViewSelection::getActiveSaliencyMaps(std::vector<std::string>& activeSaliencyMaps)
156 {
157 std::unique_lock lock(syncMutex);
158
159 IceUtil::Time currentTime = armarx::TimeUtil::GetTime();
160 for (const auto& p : saliencyMaps)
161 {
162 if (p.second->validUntil)
163 {
164 TimestampVariantPtr time = TimestampVariantPtr::dynamicCast(p.second->validUntil);
165 if (time->toTime() > currentTime)
166 {
167 activeSaliencyMaps.push_back(p.second->name);
168 }
169 }
170 else if ((currentTime -
171 TimestampVariantPtr::dynamicCast(p.second->timeAdded)->toTime()) <
172 IceUtil::Time::seconds(5))
173 {
174 activeSaliencyMaps.push_back(p.second->name);
175 }
176 }
177 }
178
179 ViewTargetBasePtr
180 ViewSelection::nextAutomaticViewTarget()
181 {
182 std::vector<std::string> activeSaliencyMaps;
183 getActiveSaliencyMaps(activeSaliencyMaps);
184
185 if (activeSaliencyMaps.empty())
186 {
187 return nullptr;
188 }
189
190
191 SharedRobotInterfacePrx robotPrx = robotStateComponent->getSynchronizedRobot();
192 TNodeList* visibilityMaskGraphNodes = visibilityMaskGraph->getNodes();
193
194 // find the direction with highest saliency
195 int maxIndex = -1;
196 float maxSaliency = std::numeric_limits<float>::min();
197
198 std::unique_lock lock(syncMutex);
199
200 hasNewSaliencyMap = false;
201
202 for (size_t i = 0; i < visibilityMaskGraphNodes->size(); i++)
203 {
204 float saliency = 0.0f;
205 for (const std::string& n : activeSaliencyMaps)
206 {
207 saliency += saliencyMaps[n]->map[i];
208 }
209
210 saliency /= activeSaliencyMaps.size();
211
212 CIntensityNode* visibilityNode = (CIntensityNode*)visibilityMaskGraphNodes->at(i);
213 saliency *= visibilityNode->getIntensity();
214
215 if (saliency > maxSaliency)
216 {
217 maxSaliency = saliency;
218 maxIndex = i;
219 }
220 }
221
222
223 lock.unlock();
224
225 ARMARX_DEBUG << "Highest saliency: " << maxSaliency;
226
227 // select a new view if saliency is bigger than threshold (which converges towards 0 over time)
228 int timeSinceLastViewChange =
229 (armarx::TimeUtil::GetTime() - timeOfLastViewChange).toMilliSeconds();
230 float saliencyThreshold = 0;
231
232 if (timeSinceLastViewChange > 0)
233 {
234 saliencyThreshold =
235 2.0f * sleepingTimeBetweenViewDirectionChanges / timeSinceLastViewChange;
236 }
237
238 if (maxSaliency > saliencyThreshold)
239 {
240 Eigen::Vector3f target;
241 MathTools::convert(visibilityMaskGraphNodes->at(maxIndex)->getPosition(), target);
242 const float distanceFactor = 1500;
243 target = distanceFactor * target + offsetToHeadCenter;
244 FramedPositionPtr viewTargetPositionPtr =
245 new FramedPosition(target, headFrameName, robotPrx->getName());
246
247 ViewTargetBasePtr viewTarget = new ViewTargetBase();
248 viewTarget->priority = armarx::DEFAULT_VIEWTARGET_PRIORITY; // * maxSaliency;
249 viewTarget->timeAdded = new TimestampVariant(armarx::TimeUtil::GetTime());
250 viewTarget->target = viewTargetPositionPtr;
251 viewTarget->duration = 0;
252
253
254 if (visuSaliencyThreshold > 0.0)
255 {
256 drawSaliencySphere(activeSaliencyMaps);
257 }
258
259 return viewTarget;
260 }
261
262 return nullptr;
263 }
264
265 void
266 ViewSelection::process()
267 {
268 /*
269 while(getState() < eManagedIceObjectExiting)
270 {
271 }
272 */
273
274 ViewTargetBasePtr viewTarget;
275
276 if (doAutomaticViewSelection)
277 {
278 viewTarget = nextAutomaticViewTarget();
279 }
280
281 /*
282 //discard outdated view targets
283 IceUtil::Time currentTime = armarx::TimeUtil::GetTime();
284 while (!manualViewTargets.empty())
285 {
286 ViewTargetBasePtr manualViewTarget = manualViewTargets.top();
287 TimestampVariantPtr time = TimestampVariantPtr::dynamicCast(manualViewTarget->validUntil);
288
289 if (time->toTime() < currentTime)
290 {
291 ARMARX_INFO << "view target is no longer valid";
292 manualViewTargets.pop();
293 }
294 else
295 {
296 break;
297 }
298 }
299 */
300
301 if (!manualViewTargets.empty())
302 {
303 std::unique_lock lock(manualViewTargetsMutex);
304
305 ViewTargetBasePtr manualViewTarget = manualViewTargets.top();
306
307 CompareViewTargets c;
308
309 if (!viewTarget)
310 {
311 viewTarget = manualViewTarget;
312 manualViewTargets.pop();
313 }
314 else if (c(viewTarget, manualViewTarget))
315 {
316 viewTarget = manualViewTarget;
317 manualViewTargets.pop();
318 }
319 }
320
321 if (viewTarget)
322 {
323 SharedRobotInterfacePrx robotPrx = robotStateComponent->getSynchronizedRobot();
324 // FramedPositionPtr viewTargetPositionPtr = FramedPositionPtr::dynamicCast(viewTarget->target);
325 FramedPositionPtr viewTargetPositionPtr =
326 new FramedPosition(FramedPositionPtr::dynamicCast(viewTarget->target)->toEigen(),
327 viewTarget->target->frame,
328 robotPrx->getName());
329
330 if (drawer && robotPrx->hasRobotNode("Cameras") && drawViewDirection)
331 {
332 float arrowLength = 1500.0f;
333 Vector3Ptr startPos = new Vector3(
334 FramedPosePtr::dynamicCast(robotPrx->getRobotNode("Cameras")->getGlobalPose())
335 ->toEigen());
336 FramedDirectionPtr currentDirection =
337 new FramedDirection(Eigen::Vector3f(1, 0, 0), "Cameras", robotPrx->getName());
338 drawer->setArrowDebugLayerVisu("CurrentHeadViewDirection",
339 startPos,
340 currentDirection->toGlobal(robotPrx),
341 DrawColor{0, 0.5, 0.5, 0.1},
342 arrowLength,
343 5);
344 Eigen::Vector3f plannedDirectionEigen =
345 viewTargetPositionPtr->toGlobalEigen(robotPrx) - startPos->toEigen();
346 Vector3Ptr plannedDirection = new Vector3(plannedDirectionEigen);
347 drawer->setArrowDebugLayerVisu("PlannedHeadViewDirection",
348 startPos,
349 plannedDirection,
350 DrawColor{0.5, 0.5, 0, 0.1},
351 arrowLength,
352 5);
353 }
354
355 ARMARX_INFO << "Looking at target " << viewTargetPositionPtr->output();
356 headIKUnitProxy->setHeadTarget(headIKKinematicChainName, viewTargetPositionPtr);
357
358 timeOfLastViewChange = TimeUtil::GetTime();
359
360 long duration = viewTarget->duration;
361 if (!viewTarget->duration)
362 {
363 duration = sleepingTimeBetweenViewDirectionChanges;
364 }
365
366 viewSelectionObserver->nextViewTarget(timeOfLastViewChange.toMilliSeconds() + duration);
367
368 std::this_thread::sleep_for(std::chrono::milliseconds(duration));
369 }
370 else
371 {
372 std::this_thread::sleep_for(std::chrono::milliseconds(5));
373 }
374 }
375
376 void
377 ViewSelection::addManualViewTarget(const FramedPositionBasePtr& target, const Ice::Current& c)
378 {
379 std::unique_lock lock(manualViewTargetsMutex);
380
381 //SharedRobotInterfacePrx sharedRobotInterface = robotStateComponent->getRobotSnapshot();
382
383 ViewTargetBasePtr viewTarget = new ViewTargetBase();
384 viewTarget->priority = armarx::DEFAULT_VIEWTARGET_PRIORITY + 1;
385 viewTarget->timeAdded = new TimestampVariant(IceUtil::Time::now());
386 viewTarget->target = target;
387 viewTarget->duration = 0;
388
389 manualViewTargets.push(viewTarget);
390
391 std::unique_lock lock2(syncMutex);
392
393 condition.notify_all();
394 }
395
396 void
398 {
399 std::unique_lock lock(manualViewTargetsMutex);
400
401 while (!manualViewTargets.empty())
402 {
403 manualViewTargets.pop();
404 }
405
406 // std::priority_queue<ViewTargetBasePtr, vector<ViewTargetBasePtr>, CompareViewTargets> temp;
407 // manualViewTargets.swap(temp);
408 }
409
410 ViewTargetList
412 {
413 std::unique_lock lock(manualViewTargetsMutex);
414
415 ViewTargetList result;
416
417 std::priority_queue<ViewTargetBasePtr, std::vector<ViewTargetBasePtr>, CompareViewTargets>
418 temp(manualViewTargets);
419
420 while (!temp.empty())
421 {
422 result.push_back(temp.top());
423
424 temp.pop();
425 }
426
427 return result;
428 }
429
430 void
431 armarx::ViewSelection::updateSaliencyMap(const SaliencyMapBasePtr& map, const Ice::Current&)
432 {
433 std::unique_lock lock(syncMutex);
434
435 hasNewSaliencyMap = true;
436
437 map->timeAdded = TimestampVariant::nowPtr();
438
439 saliencyMaps[map->name] = map;
440
441 condition.notify_all();
442 }
443
444 void
445 ViewSelection::drawSaliencySphere(const ::Ice::StringSeq& names, const Ice::Current& c)
446 {
447 ARMARX_LOG << "visualizing saliency map";
448
449 drawer->clearLayer("saliencyMap");
450
451 SharedRobotInterfacePrx robotPrx = robotStateComponent->getSynchronizedRobot();
452
453 Eigen::Matrix4f cameraToGlobal =
454 FramedPosePtr::dynamicCast(robotPrx->getRobotNode(headFrameName)->getGlobalPose())
455 ->toEigen();
456
457 std::vector<std::string> activeSaliencyMaps;
458
459 if (names.size())
460 {
461 for (std::string n : names)
462 {
463 activeSaliencyMaps.push_back(n);
464 }
465 }
466 else
467 {
468 getActiveSaliencyMaps(activeSaliencyMaps);
469 }
470
471 if (!activeSaliencyMaps.size())
472 {
473 return;
474 }
475
476
477 std::unique_lock lock(syncMutex);
478
479 TNodeList* visibilityMaskGraphNodes = visibilityMaskGraph->getNodes();
480 DebugDrawer24BitColoredPointCloud cloud;
481 cloud.points.reserve(visibilityMaskGraphNodes->size());
482 for (size_t i = 0; i < visibilityMaskGraphNodes->size(); i++)
483 {
484 float saliency = 0.0f;
485 for (const std::string& n : activeSaliencyMaps)
486 {
487 saliency += saliencyMaps[n]->map[i];
488 }
489
490 saliency /= activeSaliencyMaps.size();
491
492 CIntensityNode* visibilityNode = (CIntensityNode*)visibilityMaskGraphNodes->at(i);
493 saliency *= visibilityNode->getIntensity();
494
495 Eigen::Vector3d out;
496 TSphereCoord pos = visibilityNode->getPosition();
497 MathTools::convert(pos, out);
498
499 Eigen::Matrix4f pose = Eigen::Matrix4f::Identity();
500 pose.col(3).head<3>() = out.cast<float>() * 1000.0;
501 pose = cameraToGlobal * pose;
502
503 // float r = std::min(1.0, (1.0 - saliency) * 0.2 + saliency * 0.8 + 0.2);
504 // float g = std::min(1.0, (1.0 - saliency) * 0.8 + saliency * 0.2 + 0.2);
505 // float b = std::min(1.0, (1.0 - saliency) * 0.2 + saliency * 0.2 + 0.2);
506
507 if (saliency < visuSaliencyThreshold)
508 {
509 continue;
510 }
511 DebugDrawer24BitColoredPointCloudElement point;
512 point.color = colorutils::HeatMapRGBColor(saliency);
513 point.x = pose(0, 3);
514 point.y = pose(1, 3);
515 point.z = pose(2, 3);
516 cloud.points.push_back(point);
517 }
518 cloud.pointSize = 10;
519 drawer->set24BitColoredPointCloudVisu("saliencyMap", "SaliencyCloud", cloud);
520 }
521
522 void
524 {
525 drawer->clearLayer("saliencyMap");
526 }
527
528 void
529 ViewSelection::removeSaliencyMap(const std::string& name, const Ice::Current& c)
530 {
531 std::unique_lock lock(syncMutex);
532
533 if (saliencyMaps.count(name))
534 {
535 saliencyMaps.erase(name);
536 }
537
538 condition.notify_all();
539 }
540
541 Ice::StringSeq
543 {
544 std::vector<std::string> names;
545
546 std::unique_lock lock(syncMutex);
547
548 for (const auto& p : saliencyMaps)
549 {
550 names.push_back(p.second->name);
551 }
552
553 return names;
554 }
555
561
563} // namespace armarx
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
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()
The ViewSelection class.
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