29 #include <VirtualRobot/math/FitPlane.h>
30 #include <pcl/features/normal_3d.h>
31 #include <pcl/segmentation/region_growing.h>
36 #include <pcl/common/common.h>
37 #include <pcl/common/transforms.h>
40 #include <pcl/filters/filter.h>
41 #include <pcl/filters/extract_indices.h>
42 #include <pcl/filters/voxel_grid.h>
44 #include <pcl/kdtree/kdtree.h>
46 #include <pcl/sample_consensus/method_types.h>
47 #include <pcl/sample_consensus/model_types.h>
66 offeringTopic(getProperty<std::string>(
"DebugObserverName").getValue());
67 offeringTopic(getProperty<std::string>(
"DebugDrawerTopicName").getValue());
73 ARMARX_INFO <<
"Next timeout at " + nextTimeoutAbs.toDateTime();
75 auto prx = getIceManager()->registerObject(serviceProvider, getName() +
"ServiceProvider");
76 getIceManager()->subscribeTopic(prx.first,
"ServiceRequests");
81 debugObserver = getTopic<DebugObserverInterfacePrx>(getProperty<std::string>(
"DebugObserverName").getValue());
82 debugDrawer = getTopic<DebugDrawerInterfacePrx>(getProperty<std::string>(
"DebugDrawerTopicName").getValue());
84 prop_DistanceThreshold = getProperty<double>(
"DistanceThreshold");
85 prop_EpsAngle = getProperty<double>(
"EpsAngle");
87 prop_IncludeTabletopPlane = getProperty<bool>(
"IncludeTabletopPlane");
88 prop_CalculatePlane = getProperty<bool>(
"CalculatePlane");
90 prop_ClusterTolerance = getProperty<double>(
"ClusterTolerance");
91 prop_MinClusterSize = getProperty<int>(
"MinClusterSize");
92 prop_MaxClusterSize = getProperty<int>(
"MaxClusterSize");
94 prop_HeightMin = getProperty<double>(
"HeightMin");
95 prop_HeightMax = getProperty<double>(
"HeightMax");
97 enableResultPointClouds<PointL>(getName() +
"Result");
98 enableResultPointClouds<PointT>(getName() +
"Plane");
100 getProxyFromProperty(remoteGui,
"RemoteGuiName",
false,
"",
false);
104 remoteGuiTask->start();
113 remoteGuiTask->stop();
114 remoteGuiTask =
nullptr;
124 pcl::PointCloud<PointT>::Ptr inputCloud(
new pcl::PointCloud<PointT>());
125 pcl::PointCloud<PointT>::Ptr plane(
new pcl::PointCloud<PointT>());
127 if (!getProperty<bool>(
"AlwaysActive").getValue() &&
TimeUtil::GetTime() > nextTimeoutAbs)
132 if (!waitForPointClouds())
134 ARMARX_INFO <<
"Timeout or error while waiting for point cloud data";
139 getPointClouds(inputCloud);
144 if (inputCloud->points.size() == 0)
150 bool calculatePlane =
false;
151 bool includeTabletopPlane =
false;
154 std::unique_lock<std::mutex> lock(prop_mutex);
156 seg.setOptimizeCoefficients(
true);
157 seg.setModelType(pcl::SACMODEL_PERPENDICULAR_PLANE);
158 seg.setMethodType(pcl::SAC_RANSAC);
159 seg.setMaxIterations(1000);
160 seg.setDistanceThreshold(prop_DistanceThreshold);
161 seg.setAxis(Eigen::Vector3f::UnitZ());
162 seg.setEpsAngle(prop_EpsAngle);
163 seg.setProbability(0.99);
165 hull.setDimension(2);
166 prism.setHeightLimits(prop_HeightMin, prop_HeightMax);
169 ec.setClusterTolerance(prop_ClusterTolerance);
170 ec.setMinClusterSize(prop_MinClusterSize);
171 ec.setMaxClusterSize(prop_MaxClusterSize);
173 includeTabletopPlane = prop_IncludeTabletopPlane;
174 calculatePlane = prop_CalculatePlane;
177 pcl::PointIndices::Ptr inliers(
new pcl::PointIndices);
178 pcl::ModelCoefficients::Ptr coefficients(
new pcl::ModelCoefficients);
179 pcl::PointCloud<PointT>::Ptr tempCloud(
new pcl::PointCloud<PointT>());
183 seg.setInputCloud(inputCloud);
184 seg.segment(*inliers, *coefficients);
186 if (!inliers->indices.size())
193 extract.setInputCloud(inputCloud);
194 extract.setIndices(inliers);
195 extract.setNegative(
false);
196 extract.filter(*plane);
198 extract.setNegative(
true);
199 extract.filter(*tempCloud);
201 pcl::PointCloud<PointT>::Ptr hullPoints(
new pcl::PointCloud<PointT>());
202 hull.setInputCloud(inputCloud);
203 hull.setIndices(inliers);
204 hull.reconstruct(*hullPoints);
206 prism.setInputCloud(tempCloud);
207 prism.setInputPlanarHull(hullPoints);
208 prism.segment(*inliers);
210 extract.setInputCloud(tempCloud);
211 extract.setIndices(inliers);
212 extract.setNegative(
false);
213 extract.filter(*tempCloud);
217 provideResultPointClouds(plane, getName() +
"Plane");
221 std::vector<Eigen::Vector3f> planePoints;
222 for (
const PointT& p : plane->points)
224 planePoints.emplace_back(Eigen::Vector3f(p.x, p.y, p.z));
226 math::Plane mplane = math::FitPlane::Fit(planePoints);
227 std::unique_lock guard(tablePlaneMutex);
228 Eigen::Vector3f planeNormal = mplane.GetNormal(Eigen::Vector3f::UnitZ());
230 tablePlane =
new visionx::TabletopSegmentationPlane(
new Vector3(mplane.Pos()),
new Vector3(planeNormal));
233 pcl::PointCloud<PointL>::Ptr resultCloud(
new pcl::PointCloud<PointL>());
234 extractEuclideanClusters(tempCloud, resultCloud);
236 if (includeTabletopPlane)
238 pcl::PointCloud<PointL>::Ptr planeLabel(
new pcl::PointCloud<PointL>());
239 pcl::copyPointCloud(*plane, *planeLabel);
240 for (
PointL& p : planeLabel->points)
244 pcl::PointCloud<PointL>::Ptr combinedCloud(
new pcl::PointCloud<PointL>());
245 *combinedCloud = *resultCloud + *planeLabel;
246 resultCloud->swap(*combinedCloud);
248 resultCloud->header.stamp = inputCloud->header.stamp;
251 provideResultPointClouds(resultCloud, getName() +
"Result");
256 void armarx::TabletopSegmentation::extractEuclideanClusters(
const pcl::PointCloud<PointT>::ConstPtr& inputCloud,
const pcl::PointCloud<PointL>::Ptr& resultCloud)
258 pcl::search::KdTree<PointT>::Ptr tree(
new pcl::search::KdTree<PointT>);
259 tree->setInputCloud(inputCloud);
261 std::vector<pcl::PointIndices> clusterIndices;
262 ec.setSearchMethod(tree);
263 ec.setInputCloud(inputCloud);
264 ec.extract(clusterIndices);
267 for (std::vector<pcl::PointIndices>::const_iterator it = clusterIndices.begin(); it != clusterIndices.end(); ++it)
269 for (std::vector<int>::const_iterator pit = it->indices.begin(); pit != it->indices.end(); ++pit)
271 const unsigned int i =
static_cast<unsigned int>(*pit);
272 const PointT& src = inputCloud->points[i];
275 p.getVector3fMap() = src.getVector3fMap();
279 resultCloud->points.push_back(p);
284 resultCloud->width =
static_cast<unsigned int>(resultCloud->points.size());
285 resultCloud->height = 1;
286 resultCloud->is_dense =
false;
289 void TabletopSegmentation::guiTask()
295 while (!remoteGuiTask->isStopped())
297 tab.receiveUpdates();
300 c.waitForCycleDuration();
304 void TabletopSegmentation::guiCreate()
310 grid.addTextLabel(
"Distance Threshold", row, 0);
312 .
value(prop_DistanceThreshold)
316 grid.addChild(slider, row, 1);
320 grid.addTextLabel(
"Eps Angle", row, 0);
322 .
value(prop_EpsAngle)
323 .
min(pcl::deg2rad(1.0))
324 .
max(pcl::deg2rad(30.0))
326 grid.addChild(slider, row, 1);
330 grid.addTextLabel(
"Cluster Tolerance", row, 0);
332 .
value(prop_ClusterTolerance)
336 grid.addChild(slider, row, 1);
340 grid.addTextLabel(
"Min Cluster Size ", row, 0);
342 .
value(prop_MinClusterSize)
345 grid.addChild(slider, row, 1);
349 grid.addTextLabel(
"Max Cluster Size ", row, 0);
351 .
value(prop_MaxClusterSize)
354 grid.addChild(slider, row, 1);
358 grid.addTextLabel(
"Height Min", row, 0);
360 .
value(prop_HeightMin)
364 grid.addChild(slider, row, 1);
368 grid.addTextLabel(
"Height Max", row, 0);
370 .
value(prop_HeightMax)
374 grid.addChild(slider, row, 1);
378 grid.addTextLabel(
"Include Tabletop Plane", row, 0);
380 .
value(prop_IncludeTabletopPlane);
381 grid.addChild(checkbox, row, 1);
385 grid.addTextLabel(
"Calculate Plane", row, 0);
387 .
value(prop_CalculatePlane);
388 grid.addChild(checkbox, row, 1);
392 remoteGui->createTab(
getName(), grid);
397 std::unique_lock<std::mutex> lock(prop_mutex);
398 #define ARMARX_REMOTE_GUI_GET_PROP(name) \
399 prop_ ## name = tab.getValue<decltype(prop_ ## name)>(#name).get()
411 #undef ARMARX_REMOTE_GUI_GET_PROP
417 std::unique_lock guard(tablePlaneMutex);
422 serviceName(serviceName),
430 if (serviceName == this->serviceName)
432 callback(relativeTimeoutMs);