#include "clamp_safety_detect.h" bool clamp_safety_detect::fit_line(pcl::PointCloud::Ptr cloud, Line &line) { if (cloud->size() < 20) return false; Eigen::MatrixXd A, B; A.resize(cloud->size(), 2); B.resize(cloud->size(), 1); for (int i = 0; i < cloud->size(); ++i) { A(i, 0) = cloud->points[i].x; A(i, 1) = 1.0; B(i, 0) = cloud->points[i].y; } Eigen::MatrixXd P = (A.transpose() * A).inverse() * (A.transpose()) * B; float a = P(0, 0); float b = -1.0; float c = P(1, 0); float sum = 0; float sumx = 0, sumy = 0, w = 0, sumw = 0; for (int i = 0; i < cloud->size(); ++i) { pcl::PointXYZ pt = cloud->points[i]; w = float(1 / pow(cos(atan(fabs(pt.x / pt.y))), 2)); sumw += w; sumx += w * pt.x; sumy += pt.y; sum += std::pow(a * pt.x + b * pt.y + c, 2) / (a * a + b * b); } line.cov = sqrt(sum) / float(cloud->size()); line.cx = sumx / sumw; line.cy = sumy / float(cloud->size()); line.a = a; line.b = b; line.c = c; line.theta_by_x = acos(abs(b / sqrt(a * a + b * b))) * 180.0 / M_PI; line.cloud = cloud; //printf(" abc(%f,%f,%f) line.vonv cov:%f,%f,%f,%f,%f\n",a,b,c,line.theta_by_x,line.cov); return true; } bool compare_r(const Line &line1, const Line &line2) { return line1.theta_by_x < line2.theta_by_x; } bool pca(pcl::PointCloud::Ptr neighber, float &theta) { if (neighber->size() < 10) { return false; } float sumx = 0, sumy = 0; for (int i = 0; i < neighber->size(); ++i) { sumx += neighber->points[i].x; sumy += neighber->points[i].y; } float mean_x = sumx / float(neighber->size()); float mean_y = sumy / float(neighber->size()); Eigen::MatrixXd A(2, neighber->size()); for (int i = 0; i < neighber->size(); ++i) { pcl::PointXY pt = neighber->points[i]; A(0, i) = pt.x - mean_x; A(1, i) = pt.y - mean_y; } Eigen::MatrixXd cov = A * A.transpose(); Eigen::EigenSolver es(cov); Eigen::VectorXd values = es.eigenvalues().real().normalized(); Eigen::MatrixXd vectors = es.eigenvectors().real(); //printf("pca values: %.5f %.5f ",values[0],values[1]); float max_value = values[0]; Eigen::Vector2d normal = vectors.col(0); if (values[1] > values[0]) { max_value = values[1]; normal = vectors.col(1); } if (isnanf(max_value)) { return false; } if (max_value < 0.90) { return false; } float r = 90 * 0.005 / (2. * M_PI); theta = acos(abs(normal[0])) * r; return true; } void pca_cloud(pcl::PointCloud::Ptr cloud, pcl::PointCloud::Ptr features, int length) { if (cloud->size() < length) { printf("ERROR pca points size < %d\n", length); return; } features->clear(); int fail_pca = 0; for (int i = length / 2; i < cloud->size() - length / 2; ++i) { pcl::PointCloud::Ptr neighber(new pcl::PointCloud); for (int j = i - length / 2; j < i + length / 2; ++j) { pcl::PointXYZ pt = cloud->points[j]; if (pt.y < 0.05 || pt.y > 0.5 || fabs(pt.x) > 0.5) continue; pcl::PointXY p_xy; p_xy.x = cloud->points[j].x; p_xy.y = cloud->points[j].y; if (isnan(p_xy.x) || isnan(p_xy.y)) continue; neighber->push_back(p_xy); } float theta = 0; if (pca(neighber, theta)) { pcl::PointXYZ pt = cloud->points[i]; pt.z = theta; features->push_back(pt); } else { fail_pca++; } } } void pca_cloud(pcl::PointCloud::Ptr cloud, pcl::PointCloud::Ptr features, float radius) { if (cloud->size() < 200) return; pcl::search::KdTree::Ptr tree(new pcl::search::KdTree); //pcl::KdTreeFLANN tree; tree->setInputCloud(cloud); features->clear(); for (int i = 0; i < cloud->size(); ++i) { std::vector distances; //distance Vector std::vector neighber_ids; tree->radiusSearch(cloud->points[i], radius, neighber_ids, distances); //KD tree pcl::PointCloud::Ptr neighber(new pcl::PointCloud); for (int neighber_id : neighber_ids) { pcl::PointXY p_xy{}; p_xy.x = cloud->points[neighber_id].x; p_xy.y = cloud->points[neighber_id].y; neighber->push_back(p_xy); } float theta = 0; if (pca(neighber, theta)) { pcl::PointXYZ pt = cloud->points[i]; pt.z = theta; features->push_back(pt); } } } clamp_safety_detect::clamp_safety_detect() = default; clamp_safety_detect::~clamp_safety_detect() = default; #if __VIEW__PCL void clamp_safety_detect::set_viewer(pcl::visualization::PCLVisualizer* viewer,int port) { m_viewer=viewer; m_port=port; } void clamp_safety_detect::view_cloud(pcl::PointCloud::Ptr cloud,std::string name,int r,int g,int b) { pcl::visualization::PointCloudColorHandlerCustom single_color2(cloud,r,g,b); m_viewer->addPointCloud(cloud, single_color2, name,m_port); m_viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, name,m_port); } #endif std::vector::Ptr> clamp_safety_detect::classify_cloud( pcl::PointCloud::Ptr cloud) { std::vector::Ptr> cluster_cloud; //printf(" cloud size():%d\n",cloud->size()); pcl::PointCloud::Ptr cloud_r(new pcl::PointCloud); pca_cloud(cloud, cloud_r, 20); if (cloud_r->size() == 0) { return cluster_cloud; } pcl::search::KdTree::Ptr tree(new pcl::search::KdTree); tree->setInputCloud(cloud_r); std::vector cluster_indices; pcl::EuclideanClusterExtraction ec; ec.setClusterTolerance(0.1); ec.setMinClusterSize(100); ec.setMaxClusterSize(1000); ec.setSearchMethod(tree); ec.setInputCloud(cloud_r); ec.extract(cluster_indices); cluster_cloud.resize(cluster_indices.size()); for (int i = 0; i < cluster_indices.size(); ++i) { pcl::PointCloud::Ptr clust(new pcl::PointCloud); for (int j = 0; j < cluster_indices[i].indices.size(); ++j) { pcl::PointXYZ pt; pcl::PointXYZ ptr = cloud_r->points[cluster_indices[i].indices[j]]; pt.x = ptr.x; pt.y = ptr.y; pt.z = ptr.z; //z表示角度 clust->push_back(pt); } cluster_cloud[i] = clust; } return cluster_cloud; } bool clamp_safety_detect::check_wheel_line(const Line &line, Safety_status &safety) { if (line.cloud->size() < 20) { printf("line cloud size <20\n"); return false; } ///判断轮胎 pcl::PointXYZ minp, maxp; pcl::getMinMax3D(*line.cloud, minp, maxp); float length = maxp.x - minp.x; if (line.cov < 0.03 && fabs(line.theta_by_x) < 10. && (length > 0.1 && length < 0.5)) { //前方直线是轮胎,判断中心是否居中、直线长度是否在范围内 safety.cx = line.cx; // printf("safety.cx = %.3f\n", safety.cx); //雷达距离轮胎的距离 间隙 safety.gap = fabs(line.cy); //中心偏差5cm内,可夹,plc决定 safety.wheel_exist = true; } #if __VIEW__PCL char name[64]={0}; //显示直线,正确为绿,偏移为黄,错误为红, char buf[64]={0}; sprintf(buf,"cx:%.4f,gap:%.4f,Θ:%.4f cov:%.4f",safety.cx,fabs(line.cy),line.theta_by_x,line.cov); sprintf(name,"wheel_%d",m_port); m_viewer->addText3D(buf,pcl::PointXYZ(line.cx-0.2,line.cy,0),0.01,0,1,0,"wheel",m_port); float x1=line.cx-0.2; float x2=line.cx+0.2; float y1=-(x1*line.a/line.b+line.c/line.b); float y2=-(x2*line.a/line.b+line.c/line.b); sprintf(name,"line_%d",m_port); if (safety.wheel_exist) { m_viewer->addLine(pcl::PointXYZ(x1,y1,0), pcl::PointXYZ(x2,y2,0), 0, 255, 0, name,m_port); } else if (fabs(safety.cx) < 0.5) { m_viewer->addLine(pcl::PointXYZ(x1,y1,0), pcl::PointXYZ(x2,y2,0), 255, 255, 0, name,m_port); } else { m_viewer->addLine(pcl::PointXYZ(x1,y1,0), pcl::PointXYZ(x2,y2,0), 255, 0, 0, name,m_port); } #endif return safety.wheel_exist; } bool clamp_safety_detect::check_clamp_lines(const Line &line1, const Line &line2, Safety_status &safety) { if (fabs(line1.theta_by_x - line2.theta_by_x) > 10 || line1.cov > 0.03 || line2.cov > 0.03) { } if (fabs(line1.theta_by_x - 90.) < 15. && fabs(line2.theta_by_x - 90.) < 15.) safety.clamp_completed = true; #if __VIEW__PCL char name[64]={0}; float x1=line1.cx-0.2; float x2=line1.cx+0.2; float y1=-(x1*line1.a/line1.b+line1.c/line1.b); float y2=-(x2*line1.a/line1.b+line1.c/line1.b); sprintf(name,"line1_%d",m_port); m_viewer->addLine(pcl::PointXYZ(x1,y1,0), pcl::PointXYZ(x2,y2,0), 255, 0, 0, name,m_port); char buf[32]={0}; sprintf(buf,"cx:%.3f,Θ:%.1f cov:%.3f",line1.cx,line1.theta_by_x,line1.cov); sprintf(name,"line1_txt_%d",m_port); m_viewer->addText3D(buf,pcl::PointXYZ(line1.cx-0.15,line1.cy,0),0.01,0,1,0,name,m_port); x1=line2.cx-0.2; x2=line2.cx+0.2; y1=-(x1*line2.a/line2.b+line2.c/line2.b); y2=-(x2*line2.a/line2.b+line2.c/line2.b); sprintf(name,"line2_%d",m_port); m_viewer->addLine(pcl::PointXYZ(x1,y1,0), pcl::PointXYZ(x2,y2,0), 255, 0, 0, name,m_port); memset(buf,0,32); sprintf(buf,"cx:%.3f,Θ:%.3f cov:%.3f",line2.cx,line2.theta_by_x,line2.cov); sprintf(name,"line2_txt%d",m_port); m_viewer->addText3D(buf,pcl::PointXYZ(line2.cx-0.15,line2.cy,0),0.01,0,1,0,name,m_port); #endif return true; } bool clamp_safety_detect::detect(pcl::PointCloud::Ptr cloud, Safety_status &safety) { #if __VIEW__PCL m_viewer->removeAllPointClouds(m_port); m_viewer->removeAllShapes(m_port); char buf[32]={0}; sprintf(buf,"cloud_%d",m_port); view_cloud(cloud,buf,255,255,255); #endif //找直线,记录内点率,直线与x轴夹角,斜率b/a Line line; if (fit_line(cloud, line) && check_wheel_line(line, safety)) { return true; } return false; }