PointCloudObjectLocalizer.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2011-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 VisionX::ArmarXObjects::PointCloudObjectLocalizer
19 * @author Markus Grotz ( markus dot grotz at kit dot edu )
20 * @date 2015
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
24
26
27#include <pcl/PointIndices.h>
28
30
31
32using namespace armarx;
33
34namespace visionx
35{
36
37
38 void
40 {
41 std::scoped_lock lock(pointCloudMutex);
42
43 pcl::console::setVerbosityLevel(pcl::console::L_ALWAYS);
44
45 providerName = getProperty<std::string>("providerName").getValue();
46
47 ARMARX_LOG << "using point cloud provider " << providerName << flush;
48
49 agentName = getProperty<std::string>("agentName").getValue();
50
51 sourceNodeName = getProperty<std::string>("sourceNodeName").getValue();
52
53 icpOnly = getProperty<bool>("icpOnly").getValue();
54
55 icp.setMaximumIterations(100);
56 icp.setMaxCorrespondenceDistance(500.0);
57
58 modelKeypoints.reset(new pcl::PointCloud<PointT>);
59
60 std::string modelFileName = getProperty<std::string>("modelFileName").getValue();
61 armarx::CMakePackageFinder finder("VisionX");
63 pcl::PointCloud<PointT>::Ptr cloudPtr(new pcl::PointCloud<PointT>());
64 if (!ArmarXDataPath::getAbsolutePath(modelFileName, modelFileName))
65 {
66 ARMARX_FATAL << "Could not find point cloud file in ArmarXDataPath: " << modelFileName;
67 return;
68 }
69 else if (pcl::io::loadPCDFile<PointT>(modelFileName.c_str(), *cloudPtr) == -1)
70 {
71 ARMARX_FATAL << "unable to load point cloud from file " << modelFileName;
72 return;
73 }
74
75 float leafSize = getProperty<float>("sceneLeafSize").getValue();
76 grid.setLeafSize(Eigen::Vector3f(leafSize, leafSize, leafSize));
77
78 grid.setInputCloud(cloudPtr);
79 grid.filter(*modelKeypoints);
80
81 if (!icpOnly)
82
83 {
84 modelDescriptors.reset(new pcl::PointCloud<PointD>());
85
86 pcl::PointCloud<pcl::Normal>::Ptr pointCloudModelNormals(
87 new pcl::PointCloud<pcl::Normal>);
88 ARMARX_DEBUG << "computing normals";
89 normalEstimation.setInputCloud(cloudPtr);
90 normalEstimation.setRadiusSearch(10.0);
91 normalEstimation.compute(*pointCloudModelNormals);
92
93 ARMARX_DEBUG << "computing features";
94 featureEstimation.setInputCloud(modelKeypoints);
95 featureEstimation.setInputNormals(pointCloudModelNormals);
96 featureEstimation.setSearchSurface(cloudPtr);
97 featureEstimation.setRadiusSearch(20.0);
98 featureEstimation.compute(*modelDescriptors);
99
100 matchSearchTree.setInputCloud(modelDescriptors);
101
102 clusterer.setGCSize(getProperty<double>("recGCSize").getValue());
103 clusterer.setGCThreshold(getProperty<int>("recGCThreshold").getValue());
104 clusterer.setInputCloud(modelKeypoints);
105 }
106
107
108 offeringTopic(getProperty<std::string>("DebugDrawerTopic").getValue());
109
110 usingPointCloudProvider(providerName);
111 }
112
113 void
115 {
116 std::scoped_lock lock(pointCloudMutex);
117
118 debugDrawerPrx = getTopic<DebugDrawerInterfacePrx>(
119 getProperty<std::string>("DebugDrawerTopic").getValue());
120
122 enableResultPointClouds<PointT>("BeforeICPResult");
123 enableResultPointClouds<PointT>("AfterICPResult");
125 }
126
127 void
131
132 void
136
137 void
139 {
140
141 std::scoped_lock lock(pointCloudMutex);
142
143 pcl::PointCloud<PointL>::Ptr cloudPtr(new pcl::PointCloud<PointL>());
144
145 if (!waitForPointClouds(providerName, 10000))
146 {
147 ARMARX_WARNING << "Timeout or error while waiting for point cloud data"
148 << armarx::flush;
149 return;
150 }
151 else
152 {
153 getPointClouds(providerName, cloudPtr);
154 }
155
156 float bestFitnessScore = std::numeric_limits<float>::max();
157 Eigen::Matrix4f bestTransform = Eigen::Matrix4f::Identity();
158
159 pcl::PointCloud<PointT>::Ptr afterICPResultPointCloud(new pcl::PointCloud<PointT>());
160 pcl::PointCloud<PointT>::Ptr beforeICPResultPointCloud(new pcl::PointCloud<PointT>());
161
162 std::map<uint32_t, pcl::PointIndices> labeledPointMap;
164
165 for (auto& it : labeledPointMap)
166 {
167
168 ARMARX_LOG << " checking label " << it.first;
169
170 pcl::PointCloud<PointT>::Ptr segment(new pcl::PointCloud<PointT>());
171 copyPointCloud(*cloudPtr, it.second, *segment);
172
173 pcl::PointCloud<PointT>::Ptr transformedCloudPtr(new pcl::PointCloud<PointT>());
174 pcl::PointCloud<PointT>::Ptr cloudDownsampledPtr(new pcl::PointCloud<PointT>());
175 pcl::PointCloud<pcl::Normal>::Ptr normalPtr(new pcl::PointCloud<pcl::Normal>());
176 pcl::PointCloud<PointD>::Ptr descriptorPtr(new pcl::PointCloud<PointD>());
177 pcl::CorrespondencesPtr correspondencesPtr(new pcl::Correspondences());
178
179 pcl::copyPointCloud(*segment, *transformedCloudPtr);
180
181 grid.setInputCloud(transformedCloudPtr);
182 grid.filter(*cloudDownsampledPtr);
183
184 provideResultPointClouds(cloudDownsampledPtr, "Input");
185
186 std::vector<Eigen::Matrix4f, Eigen::aligned_allocator<Eigen::Matrix4f>> transformations;
187
188 if (icpOnly)
189 {
190 Eigen::Vector4f centroid;
191 pcl::compute3DCentroid(*cloudDownsampledPtr, centroid);
192
193 for (float i = 0.0; i < 360.0; i += 45.0)
194 {
195 Eigen::Affine3f transform(
196 Eigen::AngleAxisf(i * 180.0 / M_PI, Eigen::Vector3f::UnitZ()));
197
198 Eigen::Matrix4f transform2 = transform.matrix();
199 transform2.col(3) = centroid;
200 transformations.push_back(transform2);
201 }
202 }
203 else
204 {
205 ARMARX_LOG << "computing normals";
206 normalEstimation.setInputCloud(transformedCloudPtr);
207 normalEstimation.compute(*normalPtr);
208
209 ARMARX_LOG << "computing features";
210 featureEstimation.setInputCloud(cloudDownsampledPtr);
211 featureEstimation.setInputNormals(normalPtr);
212 featureEstimation.setSearchSurface(transformedCloudPtr);
213 featureEstimation.compute(*descriptorPtr);
214 ARMARX_LOG << "found " << descriptorPtr->size() << " features";
215
216 int i = 0;
217 const int k = 1;
218 for (PointD& d : descriptorPtr->points)
219 {
220 // if (!std::isfinite(d.descriptor[0])) //skipping NaNs
221 // {
222 // continue;
223 // }
224
225 std::vector<int> k_indices(k);
226 std::vector<float> k_sqr_distances(k);
227 int numNeighborsFound =
228 matchSearchTree.nearestKSearch(d, k, k_indices, k_sqr_distances);
229
230 if (numNeighborsFound == 1) // && k_sqr_distances[0] < 0.25f)
231 {
232 pcl::Correspondence corr(k_indices[0], i, k_sqr_distances[0]);
233 correspondencesPtr->push_back(corr);
234 }
235 i++;
236 }
237
238 std::vector<pcl::Correspondences> clusteredCorrespondences;
239
240 clusterer.setSceneCloud(cloudDownsampledPtr);
241 clusterer.setModelSceneCorrespondences(correspondencesPtr);
242 clusterer.recognize(transformations, clusteredCorrespondences);
243
244 ARMARX_LOG << " found " << transformations.size() << " transformations";
245 }
246
247
248 for (Eigen::Matrix4f& t : transformations)
249 {
250 pcl::PointCloud<PointT>::Ptr transformedModelPtr3(new pcl::PointCloud<PointT>());
251 pcl::transformPointCloud(*modelKeypoints, *transformedModelPtr3, t);
252 visionx::tools::colorizeSegment(transformedModelPtr3);
253 *beforeICPResultPointCloud += *transformedModelPtr3;
254
255 icp.setInputTarget(cloudDownsampledPtr);
256 icp.setInputSource(modelKeypoints);
257 pcl::PointCloud<PointT>::Ptr registered(new pcl::PointCloud<PointT>);
258 icp.align(*registered, t);
259
260 if (icp.hasConverged())
261 {
262 float fitnessScore = icp.getFitnessScore();
263 Eigen::Matrix4f transform = icp.getFinalTransformation();
264
265 ARMARX_LOG << "transformation after ICP " << transform;
266 ARMARX_LOG << "fitness score " << fitnessScore;
267
268 pcl::PointCloud<PointT>::Ptr transformedModelPtr2(
269 new pcl::PointCloud<PointT>());
270 pcl::transformPointCloud(*modelKeypoints, *transformedModelPtr2, transform);
271 visionx::tools::colorizeSegment(transformedModelPtr2);
272 *afterICPResultPointCloud += *transformedModelPtr2;
273
274 if (fitnessScore <= bestFitnessScore)
275 {
276 bestFitnessScore = fitnessScore;
277 bestTransform = transform;
278
279 std::scoped_lock lock(localizeMutex);
280
281 //pose = new armarx::FramedPose(bestTransform, poseInRootFrame->frame, agentName);
282 //FramedPosePtr::dynamicCast(pose)->changeFrame(robotStateComponent->getSynchronizedRobot(), sourceNodeName);
283
284 pose = new armarx::FramedPose(bestTransform, sourceNodeName, agentName);
285
286 debugDrawerPrx->setPoseVisu(
287 "PointCloudLocalizer", "label " + it.first, pose);
288 }
289 }
290 }
291 }
292
293 provideResultPointClouds(beforeICPResultPointCloud, "BeforeICPResult");
294 provideResultPointClouds(afterICPResultPointCloud, "AfterICPResult");
295
296 if (bestFitnessScore < 2000.0)
297 {
298 ARMARX_LOG << "best transformation " << bestTransform;
299 pcl::PointCloud<PointT>::Ptr BestResultPointCloud(new pcl::PointCloud<PointT>());
300 pcl::transformPointCloud(*modelKeypoints, *BestResultPointCloud, bestTransform);
301 provideResultPointClouds(BestResultPointCloud, "BestMatch");
302 }
303 }
304
305 memoryx::ObjectLocalizationResultList
307 const memoryx::ObjectClassNameList& objectClassNames,
308 const Ice::Current& c)
309 {
310 std::scoped_lock lock(localizeMutex);
311 ARMARX_LOG << "localizing object...";
312
313 memoryx::ObjectLocalizationResultList resultList;
314
315 std::string objectClassName = getProperty<std::string>("objectClassName").getValue();
316
317 for (std::string name : objectClassNames)
318 {
319 if (name == objectClassName && pose)
320 {
321 memoryx::ObjectLocalizationResult result;
322 result.position = pose->getPosition();
323 result.orientation = pose->getOrientation();
324 result.objectClassName = objectClassName;
325 result.recognitionCertainty = 1.0f;
326 Eigen::Vector3f mean;
327 mean << 0.0, 0.0, 0.0;
328 Eigen::Matrix3f covar = Eigen::Matrix3f::Identity() * 100;
329 memoryx::MultivariateNormalDistributionBasePtr dummy(
331 result.positionNoise = dummy;
332 resultList.push_back(result);
333 }
334 }
335
336 return resultList;
337 }
338
339 void
340 PointCloudObjectLocalizer::getLocation(FramedOrientationBasePtr& orientation,
341 FramedPositionBasePtr& position,
342 const Ice::Current& c)
343 {
344 std::scoped_lock lock(localizeMutex);
345
346 orientation = this->pose->getOrientation();
347 position = this->pose->getPosition();
348 }
349
356} // namespace visionx
#define M_PI
Definition MathTools.h:17
constexpr T c
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)
The FramedPose class.
Definition FramedPose.h:281
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.
The MultivariateNormalDistribution class.
memoryx::ObjectLocalizationResultList localizeObjectClasses(const memoryx::ObjectClassNameList &objectClassNames, const Ice::Current &c=Ice::emptyCurrent) override
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void getLocation(armarx::FramedOrientationBasePtr &orientation, armarx::FramedPositionBasePtr &position, const Ice::Current &c=Ice::emptyCurrent) override
void enableResultPointClouds(std::string resultProviderName="")
Enables visualization.
int getPointClouds(const PointCloudPtrT &pointCloudPtr)
Poll PointClouds from provider.
void usingPointCloudProvider(std::string providerName)
Registers a delayed topic subscription and a delayed provider proxy retrieval which will be available...
bool waitForPointClouds(int milliseconds=1000)
Wait for new PointClouds.
void provideResultPointClouds(const PointCloudPtrT &pointClouds, std::string providerName="")
sends result PointClouds for visualization
#define ARMARX_FATAL
The logging level for unexpected behaviour, that will lead to a seriously malfunctioning program and ...
Definition Logging.h:199
#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_LOG
Definition Logging.h:165
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::optional< float > mean(const boost::circular_buffer< NameValueMap > &buffer, const std::string &key)
auto transform(const Container< InputT, Alloc > &in, OutputT(*func)(InputT const &)) -> Container< OutputT, typename std::allocator_traits< Alloc >::template rebind_alloc< OutputT > >
Convenience function (with less typing) to transform a container of type InputT into the same contain...
Definition algorithm.h:351
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
const LogSender::manipulator flush
Definition LogSender.h:251
std::tuple< uint8_t, uint8_t, uint8_t > colorizeSegment(PointCloudPtrT &segment)
std::enable_if< is_shared_ptr< LabeledPointCloudPtrT >::value, void >::type fillLabelMap(LabeledPointCloudPtrT labeledCloudPtr, std::map< uint32_t, pcl::PointIndices > &labelIndicesMap, bool excludeZero=true)
ArmarX headers.
pcl::FPFHSignature33 PointD