added headerfile for clustering and cleaned up code

This commit is contained in:
2025-08-30 14:50:43 +02:00
parent 902de6a12b
commit f537027329
4 changed files with 719 additions and 625 deletions

View File

@@ -38,11 +38,17 @@ ament_target_dependencies(cloud_preprocessing_node
# Node for clustering # Node for clustering
add_executable(cloud_clustering_node add_executable(cloud_clustering_node
src/cloud_clustering.cpp src/cloud_clustering_node.cpp
) )
add_library(cloud_clustering SHARED
src/cloud_clustering.cpp)
target_compile_definitions(cloud_clustering
PRIVATE "COMPOSITION_BUILDING_DLL")
# Dependencies for clustering # Dependencies for clustering
ament_target_dependencies(cloud_clustering_node ament_target_dependencies(cloud_clustering
rclcpp rclcpp
sensor_msgs sensor_msgs
visualization_msgs visualization_msgs
@@ -51,7 +57,10 @@ ament_target_dependencies(cloud_clustering_node
OpenCV OpenCV
) )
#target_link_libraries(target_tracking_node ${PCL_LIBRARIES}) rclcpp_components_register_nodes(cloud_clustering "cloud_clustering::CloudClustering")
set(node_plugins "${node_plugins}cloud_clustering::CloudClustering;$<TARGET_FILE:cloud_clustering>\n")
target_link_libraries(cloud_clustering_node cloud_clustering)
# Install target # Install target
install(TARGETS install(TARGETS
@@ -60,6 +69,13 @@ install(TARGETS
DESTINATION lib/${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME}
) )
install(TARGETS
cloud_clustering
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION lib/${PACKAGE_NAME}
)
# Install launchfile # Install launchfile
install( install(
DIRECTORY launch/ DIRECTORY launch/

View File

@@ -0,0 +1,100 @@
#ifndef CLOUD_CLUSTERING_HPP
#define CLOUD_CLUSTERING_HPP
#include "rclcpp/rclcpp.hpp"
#include <geometry_msgs/msg/point.hpp>
#include "visualization_msgs/msg/marker_array.hpp"
#include "opencv2/video/tracking.hpp"
#include "std_msgs/msg/float32_multi_array.hpp"
#include "std_msgs/msg/int32_multi_array.hpp"
#include "sensor_msgs/msg/point_cloud2.hpp"
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl_conversions/pcl_conversions.h>
#include <pcl/segmentation/extract_clusters.h>
namespace cloud_clustering
{
// KF init
int stateDim = 4; // [x,y,v_x,v_y]//,w,h]
int measDim = 2; // [z_x,z_y,z_w,z_h]
int ctrlDim = 0;
std::string frame_id;
std::string topic_in;
float z_dim_scale;
float cluster_tolerance;
int min_cluster_size;
int max_cluster_size;
cv::KalmanFilter KF0(stateDim, measDim, ctrlDim, CV_32F);
cv::KalmanFilter KF1(stateDim, measDim, ctrlDim, CV_32F);
cv::KalmanFilter KF2(stateDim, measDim, ctrlDim, CV_32F);
cv::KalmanFilter KF3(stateDim, measDim, ctrlDim, CV_32F);
cv::KalmanFilter KF4(stateDim, measDim, ctrlDim, CV_32F);
cv::KalmanFilter KF5(stateDim, measDim, ctrlDim, CV_32F);
std::vector<geometry_msgs::msg::Point> prevClusterCenters;
cv::Mat state(stateDim, 1, CV_32F);
cv::Mat_<float> measurement(2, 1);
std::vector<int> objID; // Output of the data association using KF
// measurement.setTo(Scalar(0));
bool firstFrame = true;
/*
cv::Mat state(stateDim, 1, CV_32F);
cv::Mat_<float> measurement(2, 1);
*/
class CloudClustering : public rclcpp::Node
{
public:
CloudClustering(
const rclcpp::NodeOptions &options = rclcpp::NodeOptions());
CloudClustering(
const std::string &name_space,
const rclcpp::NodeOptions &options = rclcpp::NodeOptions());
private:
rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::SharedPtr sub;
rclcpp::Publisher<visualization_msgs::msg::MarkerArray>::SharedPtr markerPub;
rclcpp::Publisher<std_msgs::msg::Int32MultiArray>::SharedPtr objID_pub;
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pub_cluster0;
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pub_cluster1;
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pub_cluster2;
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pub_cluster3;
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pub_cluster4;
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pub_cluster5;
std::vector<geometry_msgs::msg::Point> prevClusterCenters;
std::vector<int> objID; // Output of the data association using KF
// measurement.setTo(Scalar(0));
bool firstFrame = true;
rclcpp::Clock::SharedPtr clock_;
std::string frame_id;
std::string filtered_cloud;
double euclidean_distance(geometry_msgs::msg::Point &p1, geometry_msgs::msg::Point &p2);
std::pair<int, int> findIndexOfMin(std::vector<std::vector<float>> distMat);
void KFT(const std_msgs::msg::Float32MultiArray ccs);
void publish_cloud(rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pub,
pcl::PointCloud<pcl::PointXYZI>::Ptr cluster);
void parse_cloud(const sensor_msgs::msg::PointCloud2::SharedPtr input,
std::vector<std::pair<pcl::PointCloud<pcl::PointXYZI>::Ptr, float>> &cluster_vec,
std::vector<pcl::PointXYZI> &clusterCentroids);
void cloud_cb(const sensor_msgs::msg::PointCloud2::SharedPtr input);
};
}
#endif // CLOUD_CLUSTERING_HPP

View File

@@ -1,126 +1,152 @@
#include <pcl_conversions/pcl_conversions.h> // Default C++ imports
#include <sensor_msgs/msg/point_cloud2.hpp> #include <limits>
#include <pcl/point_cloud.h> #include <utility>
#include <pcl/point_types.h> #include <string.h>
#include <algorithm>
#include <fstream>
#include <geometry_msgs/msg/point.hpp>
#include <iostream> #include <iostream>
#include <iterator> #include <iterator>
#include <opencv2/video/video.hpp> #include <algorithm>
#include <pcl/io/pcd_io.h> #include <fstream>
#include <pcl/point_types.h>
// Headerfile
#include "cloud_clustering.hpp"
// Ros2 import
#include <rclcpp/rclcpp.hpp> #include <rclcpp/rclcpp.hpp>
#include <opencv2/video/video.hpp>
#include <pcl_conversions/pcl_conversions.h>
// PCL Types
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
// Ros2 messages
#include <std_msgs/msg/float32_multi_array.hpp> #include <std_msgs/msg/float32_multi_array.hpp>
#include <std_msgs/msg/int32_multi_array.hpp> #include <std_msgs/msg/int32_multi_array.hpp>
#include <string.h> #include <geometry_msgs/msg/point.hpp>
#include <sensor_msgs/msg/point_cloud2.hpp>
#include <visualization_msgs/msg/marker.hpp>
#include <visualization_msgs/msg/marker_array.hpp>
#include <pcl/io/pcd_io.h>
#include <pcl/common/centroid.h> #include <pcl/common/centroid.h>
#include <pcl/common/geometry.h> #include <pcl/common/geometry.h>
#include <pcl/features/normal_3d.h> #include <pcl/features/normal_3d.h>
#include <pcl/filters/extract_indices.h> #include <pcl/filters/extract_indices.h>
#include <pcl/filters/voxel_grid.h> #include <pcl/filters/voxel_grid.h>
#include <pcl/kdtree/kdtree.h> #include <pcl/kdtree/kdtree.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/sample_consensus/method_types.h> #include <pcl/sample_consensus/method_types.h>
#include <pcl/sample_consensus/model_types.h> #include <pcl/sample_consensus/model_types.h>
#include <pcl/segmentation/extract_clusters.h> #include <pcl/segmentation/extract_clusters.h>
#include <pcl/segmentation/sac_segmentation.h> #include <pcl/segmentation/sac_segmentation.h>
#include <pcl_conversions/pcl_conversions.h> #include <pcl_conversions/pcl_conversions.h>
#include <sensor_msgs/msg/point_cloud2.hpp>
#include <limits> namespace cloud_clustering
#include <utility> {
#include <visualization_msgs/msg/marker.hpp> static const rclcpp::Logger LOGGER = rclcpp::get_logger("cloud_clustering");
#include <visualization_msgs/msg/marker_array.hpp>
using namespace std; CloudClustering::CloudClustering(
using namespace cv; const rclcpp::NodeOptions &options) : CloudClustering("", options)
{
}
rclcpp::Publisher<std_msgs::msg::Int32MultiArray>::SharedPtr objID_pub; CloudClustering::CloudClustering(
const std::string &name_space,
const rclcpp::NodeOptions &options) : Node("CloudClustering", name_space, options)
{
// Topic parameters
this->declare_parameter<std::string>("topic_in", "filtered_points");
this->declare_parameter<std::string>("frame_id", "velodyne");
// KF init // Cluster parameters
int stateDim = 4; // [x,y,v_x,v_y]//,w,h] this->declare_parameter<float>("z_dim_scale", 0);
int measDim = 2; // [z_x,z_y,z_w,z_h] this->declare_parameter<float>("cluster_tolerance", 0.0f);
int ctrlDim = 0; this->declare_parameter<int>("min_cluster_size", 0);
this->declare_parameter<int>("max_cluster_size", 0);
std::string frame_id; this->get_parameter("topic_in", topic_in);
std::string topic_in; this->get_parameter("frame_id", frame_id);
this->get_parameter("z_dim_scale", z_dim_scale);
this->get_parameter("cluster_tolerance", cluster_tolerance);
this->get_parameter("min_cluster_size", min_cluster_size);
this->get_parameter("max_cluster_size", max_cluster_size);
float z_dim_scale; // Initialize publishers
float cluster_tolerance; objID_pub = this->create_publisher<std_msgs::msg::Int32MultiArray>("object_ids", 10);
int min_cluster_size; pub_cluster0 = this->create_publisher<sensor_msgs::msg::PointCloud2>("cluster0", 10);
int max_cluster_size; pub_cluster1 = this->create_publisher<sensor_msgs::msg::PointCloud2>("cluster1", 10);
pub_cluster2 = this->create_publisher<sensor_msgs::msg::PointCloud2>("cluster2", 10);
pub_cluster3 = this->create_publisher<sensor_msgs::msg::PointCloud2>("cluster3", 10);
pub_cluster4 = this->create_publisher<sensor_msgs::msg::PointCloud2>("cluster4", 10);
pub_cluster5 = this->create_publisher<sensor_msgs::msg::PointCloud2>("cluster5", 10);
markerPub = this->create_publisher<visualization_msgs::msg::MarkerArray>("markers", 10);
cv::KalmanFilter KF0(stateDim, measDim, ctrlDim, CV_32F); RCLCPP_INFO(this->get_logger(), "About to setup callback");
cv::KalmanFilter KF1(stateDim, measDim, ctrlDim, CV_32F); clock_ = this->get_clock();
cv::KalmanFilter KF2(stateDim, measDim, ctrlDim, CV_32F);
cv::KalmanFilter KF3(stateDim, measDim, ctrlDim, CV_32F);
cv::KalmanFilter KF4(stateDim, measDim, ctrlDim, CV_32F);
cv::KalmanFilter KF5(stateDim, measDim, ctrlDim, CV_32F);
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pub_cluster0; // Create a ROS subscriber for the input point cloud
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pub_cluster1; sub = this->create_subscription<sensor_msgs::msg::PointCloud2>(
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pub_cluster2; topic_in,
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pub_cluster3; 10, // queue size
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pub_cluster4; std::bind(&CloudClustering::cloud_cb, this, std::placeholders::_1));
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pub_cluster5;
rclcpp::Publisher<visualization_msgs::msg::MarkerArray>::SharedPtr markerPub; std::cout << "Started clustering node with parameters:\n"
<< "topic_in: " << topic_in << "\n"
<< "frame_id: " << frame_id << "\n"
<< "z_dim_scale: " << z_dim_scale << "\n"
<< "cluster_tolerance: " << cluster_tolerance << "\n"
<< "min_cluster_size: " << min_cluster_size << "\n"
<< "max_cluster_size: " << max_cluster_size << "\n";
}
std::vector<geometry_msgs::msg::Point> prevClusterCenters; // calculate euclidean distance of two points
double CloudClustering::euclidean_distance(geometry_msgs::msg::Point &p1, geometry_msgs::msg::Point &p2)
cv::Mat state(stateDim, 1, CV_32F); {
cv::Mat_<float> measurement(2, 1);
std::vector<int> objID; // Output of the data association using KF
// measurement.setTo(Scalar(0));
bool firstFrame = true;
// calculate euclidean distance of two points
double euclidean_distance(geometry_msgs::msg::Point &p1, geometry_msgs::msg::Point &p2) {
return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) + return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) +
(p1.z - p2.z) * (p1.z - p2.z) * z_dim_scale); (p1.z - p2.z) * (p1.z - p2.z) * z_dim_scale);
} }
/* /*
//Count unique object IDs. just to make sure same ID has not been assigned to //Count unique object IDs. just to make sure same ID has not been assigned to
two KF_Trackers. int countIDs(vector<int> v) two KF_Trackers. int countIDs(vector<int> v)
{ {
transform(v.begin(), v.end(), v.begin(), abs); // O(n) where n = transform(v.begin(), v.end(), v.begin(), abs); // O(n) where n =
distance(v.end(), v.begin()) sort(v.begin(), v.end()); // Average case O(n log distance(v.end(), v.begin()) sort(v.begin(), v.end()); // Average case O(n log
n), worst case O(n^2) (usually implemented as quicksort. n), worst case O(n^2) (usually implemented as quicksort.
// To guarantee worst case O(n log n) replace with make_heap, then // To guarantee worst case O(n log n) replace with make_heap, then
sort_heap. sort_heap.
// Unique will take a sorted range, and move things around to get duplicated // Unique will take a sorted range, and move things around to get duplicated
// items to the back and returns an iterator to the end of the unique // items to the back and returns an iterator to the end of the unique
section of the range auto unique_end = unique(v.begin(), v.end()); // Again n section of the range auto unique_end = unique(v.begin(), v.end()); // Again n
comparisons return distance(unique_end, v.begin()); // Constant time for random comparisons return distance(unique_end, v.begin()); // Constant time for random
access iterators (like vector's) access iterators (like vector's)
} }
*/ */
/* /*
objID: vector containing the IDs of the clusters that should be associated with objID: vector containing the IDs of the clusters that should be associated with
each KF_Tracker objID[0] corresponds to KFT0, objID[1] corresponds to KFT1 etc. each KF_Tracker objID[0] corresponds to KFT0, objID[1] corresponds to KFT1 etc.
*/ */
std::pair<int, int> findIndexOfMin(std::vector<std::vector<float>> distMat) { std::pair<int, int> CloudClustering::findIndexOfMin(std::vector<std::vector<float>> distMat)
{
std::pair<int, int> minIndex; std::pair<int, int> minIndex;
float minEl = std::numeric_limits<float>::max(); float minEl = std::numeric_limits<float>::max();
for (int i = 0; i < distMat.size(); i++) for (int i = 0; i < distMat.size(); i++)
for (int j = 0; j < distMat.at(0).size(); j++) { for (int j = 0; j < distMat.at(0).size(); j++)
if (distMat[i][j] < minEl) { {
if (distMat[i][j] < minEl)
{
minEl = distMat[i][j]; minEl = distMat[i][j];
minIndex = std::make_pair(i, j); minIndex = std::make_pair(i, j);
} }
} }
return minIndex; return minIndex;
} }
void KFT(const std_msgs::msg::Float32MultiArray ccs) {
void CloudClustering::KFT(const std_msgs::msg::Float32MultiArray ccs)
{
// First predict, to update the internal statePre variable // First predict, to update the internal statePre variable
@@ -138,7 +164,8 @@ void KFT(const std_msgs::msg::Float32MultiArray ccs) {
int i = 0; int i = 0;
for (std::vector<float>::const_iterator it = ccs.data.begin(); for (std::vector<float>::const_iterator it = ccs.data.begin();
it != ccs.data.end(); it += 3) { it != ccs.data.end(); it += 3)
{
geometry_msgs::msg::Point pt; geometry_msgs::msg::Point pt;
pt.x = *it; pt.x = *it;
pt.y = *(it + 1); pt.y = *(it + 1);
@@ -149,7 +176,8 @@ void KFT(const std_msgs::msg::Float32MultiArray ccs) {
std::vector<geometry_msgs::msg::Point> KFpredictions; std::vector<geometry_msgs::msg::Point> KFpredictions;
i = 0; i = 0;
for (auto it = pred.begin(); it != pred.end(); it++) { for (auto it = pred.begin(); it != pred.end(); it++)
{
geometry_msgs::msg::Point pt; geometry_msgs::msg::Point pt;
pt.x = (*it).at<float>(0); pt.x = (*it).at<float>(0);
pt.y = (*it).at<float>(1); pt.y = (*it).at<float>(1);
@@ -167,9 +195,11 @@ void KFT(const std_msgs::msg::Float32MultiArray ccs) {
std::vector<geometry_msgs::msg::Point> copyOfClusterCenters(clusterCenters); std::vector<geometry_msgs::msg::Point> copyOfClusterCenters(clusterCenters);
std::vector<std::vector<float>> distMat; std::vector<std::vector<float>> distMat;
for (int filterN = 0; filterN < 6; filterN++) { for (int filterN = 0; filterN < 6; filterN++)
{
std::vector<float> distVec; std::vector<float> distVec;
for (int n = 0; n < 6; n++) { for (int n = 0; n < 6; n++)
{
distVec.push_back( distVec.push_back(
euclidean_distance(KFpredictions[filterN], copyOfClusterCenters[n])); euclidean_distance(KFpredictions[filterN], copyOfClusterCenters[n]));
} }
@@ -185,10 +215,11 @@ void KFT(const std_msgs::msg::Float32MultiArray ccs) {
not assigned to another cluster copyOfClusterCenters[ID].y=10000; not assigned to another cluster copyOfClusterCenters[ID].y=10000;
copyOfClusterCenters[ID].z=10000; copyOfClusterCenters[ID].z=10000;
*/ */
//cout << "filterN=" << filterN << "\n"; // cout << "filterN=" << filterN << "\n";
} }
for (int clusterCount = 0; clusterCount < 6; clusterCount++) { for (int clusterCount = 0; clusterCount < 6; clusterCount++)
{
// 1. Find min(distMax)==> (i,j); // 1. Find min(distMax)==> (i,j);
std::pair<int, int> minIndex(findIndexOfMin(distMat)); std::pair<int, int> minIndex(findIndexOfMin(distMat));
// 2. objID[i]=clusterCenters[j]; counter++ // 2. objID[i]=clusterCenters[j]; counter++
@@ -203,7 +234,7 @@ void KFT(const std_msgs::msg::Float32MultiArray ccs) {
distMat[row][minIndex.second] = 10000.0; distMat[row][minIndex.second] = 10000.0;
} }
// 4. if(counter<6) got to 1. // 4. if(counter<6) got to 1.
//cout << "clusterCount=" << clusterCount << "\n"; // cout << "clusterCount=" << clusterCount << "\n";
} }
// cout<<"Got object IDs"<<"\n"; // cout<<"Got object IDs"<<"\n";
@@ -219,7 +250,8 @@ void KFT(const std_msgs::msg::Float32MultiArray ccs) {
visualization_msgs::msg::MarkerArray clusterMarkers; visualization_msgs::msg::MarkerArray clusterMarkers;
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++)
{
visualization_msgs::msg::Marker m; visualization_msgs::msg::Marker m;
m.id = i; m.id = i;
@@ -254,8 +286,9 @@ void KFT(const std_msgs::msg::Float32MultiArray ccs) {
objID_pub->publish(obj_id); objID_pub->publish(obj_id);
// convert clusterCenters from geometry_msgs::msg::Point to floats // convert clusterCenters from geometry_msgs::msg::Point to floats
std::vector<std::vector<float>> cc; std::vector<std::vector<float>> cc;
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++)
vector<float> pt; {
std::vector<float> pt;
pt.push_back(clusterCenters[objID[i]].x); pt.push_back(clusterCenters[objID[i]].x);
pt.push_back(clusterCenters[objID[i]].y); pt.push_back(clusterCenters[objID[i]].y);
pt.push_back(clusterCenters[objID[i]].z); pt.push_back(clusterCenters[objID[i]].z);
@@ -280,29 +313,28 @@ void KFT(const std_msgs::msg::Float32MultiArray ccs) {
// cout<<"meas0Mat"<<meas0Mat<<"\n"; // cout<<"meas0Mat"<<meas0Mat<<"\n";
if (!(meas0Mat.at<float>(0, 0) == 0.0f || meas0Mat.at<float>(1, 0) == 0.0f)) if (!(meas0Mat.at<float>(0, 0) == 0.0f || meas0Mat.at<float>(1, 0) == 0.0f))
Mat estimated0 = KF0.correct(meas0Mat); cv::Mat estimated0 = KF0.correct(meas0Mat);
if (!(meas1[0] == 0.0f || meas1[1] == 0.0f)) if (!(meas1[0] == 0.0f || meas1[1] == 0.0f))
Mat estimated1 = KF1.correct(meas1Mat); cv::Mat estimated1 = KF1.correct(meas1Mat);
if (!(meas2[0] == 0.0f || meas2[1] == 0.0f)) if (!(meas2[0] == 0.0f || meas2[1] == 0.0f))
Mat estimated2 = KF2.correct(meas2Mat); cv::Mat estimated2 = KF2.correct(meas2Mat);
if (!(meas3[0] == 0.0f || meas3[1] == 0.0f)) if (!(meas3[0] == 0.0f || meas3[1] == 0.0f))
Mat estimated3 = KF3.correct(meas3Mat); cv::Mat estimated3 = KF3.correct(meas3Mat);
if (!(meas4[0] == 0.0f || meas4[1] == 0.0f)) if (!(meas4[0] == 0.0f || meas4[1] == 0.0f))
Mat estimated4 = KF4.correct(meas4Mat); cv::Mat estimated4 = KF4.correct(meas4Mat);
if (!(meas5[0] == 0.0f || meas5[1] == 0.0f)) if (!(meas5[0] == 0.0f || meas5[1] == 0.0f))
Mat estimated5 = KF5.correct(meas5Mat); cv::Mat estimated5 = KF5.correct(meas5Mat);
// Publish the point clouds belonging to each clusters // Publish the point clouds belonging to each clusters
// cout<<"estimate="<<estimated.at<float>(0)<<","<<estimated.at<float>(1)<<"\n"; // cout<<"estimate="<<estimated.at<float>(0)<<","<<estimated.at<float>(1)<<"\n";
// Point statePt(estimated.at<float>(0),estimated.at<float>(1)); // Point statePt(estimated.at<float>(0),estimated.at<float>(1));
// cout<<"DONE KF_TRACKER\n"; // cout<<"DONE KF_TRACKER\n";
} }
void publish_cloud( void CloudClustering::publish_cloud(
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pub, rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pub,
pcl::PointCloud<pcl::PointXYZI>::Ptr cluster, pcl::PointCloud<pcl::PointXYZI>::Ptr cluster)
rclcpp::Node::SharedPtr node) {
{
// Create a PointCloud2 message // Create a PointCloud2 message
auto clustermsg = std::make_shared<sensor_msgs::msg::PointCloud2>(); auto clustermsg = std::make_shared<sensor_msgs::msg::PointCloud2>();
@@ -311,37 +343,19 @@ void publish_cloud(
// Set header info // Set header info
clustermsg->header.frame_id = frame_id; clustermsg->header.frame_id = frame_id;
clustermsg->header.stamp = node->now(); clustermsg->header.stamp = clock_->now();
// Publish the message // Publish the message
pub->publish(*clustermsg); pub->publish(*clustermsg);
}
void parse_cloud(const sensor_msgs::msg::PointCloud2::SharedPtr input,
std::vector<std::pair<
pcl::PointCloud<
pcl::PointXYZI
>::Ptr
,float>>& cluster_vec,
std::vector<pcl::PointXYZI>& clusterCentroids
) {
pcl::PointCloud<pcl::PointXYZI>::Ptr input_a(new pcl::PointCloud<pcl::PointXYZI>);
pcl::fromROSMsg(*input, *input_a);
// Apply filters
pcl::PointCloud<pcl::PointXYZI>::Ptr fov_filtered(new pcl::PointCloud<pcl::PointXYZI>);
fov_filtered->reserve(input_a->size());
for (const auto& point : input_a->points) {
if (point.x < 0 || point.x > 40) continue;
if (isnan(point.x) || isnan(point.y) || isnan(point.z)) continue;
if (point.z < -0.5 || point.z > 1.5) continue;
fov_filtered->points.push_back(point); // emplace_back also works
} }
if (fov_filtered->empty()) return; // nothing to cluster void CloudClustering::parse_cloud(const sensor_msgs::msg::PointCloud2::SharedPtr input,
std::vector<std::pair<pcl::PointCloud<pcl::PointXYZI>::Ptr, float>> &cluster_vec,
std::vector<pcl::PointXYZI> &clusterCentroids)
{
pcl::PointCloud<pcl::PointXYZI>::Ptr input_cloud = fov_filtered; pcl::PointCloud<pcl::PointXYZI>::Ptr input_cloud(new pcl::PointCloud<pcl::PointXYZI>);
pcl::fromROSMsg(*input, *input_cloud);
// Setup KdTree // Setup KdTree
pcl::search::KdTree<pcl::PointXYZI>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZI>); pcl::search::KdTree<pcl::PointXYZI>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZI>);
@@ -360,7 +374,8 @@ void parse_cloud(const sensor_msgs::msg::PointCloud2::SharedPtr input,
std::vector<pcl::PointIndices>::const_iterator it; std::vector<pcl::PointIndices>::const_iterator it;
std::vector<int>::const_iterator pit; std::vector<int>::const_iterator pit;
for (it = cluster_indices.begin(); it != cluster_indices.end(); ++it) { for (it = cluster_indices.begin(); it != cluster_indices.end(); ++it)
{
pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_cluster( pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_cluster(
new pcl::PointCloud<pcl::PointXYZI>); new pcl::PointCloud<pcl::PointXYZI>);
@@ -369,7 +384,8 @@ void parse_cloud(const sensor_msgs::msg::PointCloud2::SharedPtr input,
int numPts = 0; int numPts = 0;
float intensity_sum = 0.0; float intensity_sum = 0.0;
for (pit = it->indices.begin(); pit != it->indices.end(); pit++) { for (pit = it->indices.begin(); pit != it->indices.end(); pit++)
{
cloud_cluster->points.push_back(input_cloud->points[*pit]); cloud_cluster->points.push_back(input_cloud->points[*pit]);
x += input_cloud->points[*pit].x; x += input_cloud->points[*pit].x;
@@ -383,7 +399,8 @@ void parse_cloud(const sensor_msgs::msg::PointCloud2::SharedPtr input,
// mindist_this_cluster); // mindist_this_cluster);
} }
if (numPts == 0) continue; if (numPts == 0)
continue;
pcl::PointXYZI centroid; pcl::PointXYZI centroid;
centroid.x = x / numPts; centroid.x = x / numPts;
@@ -397,7 +414,8 @@ void parse_cloud(const sensor_msgs::msg::PointCloud2::SharedPtr input,
} }
// Ensure at least 6 clusters exist to publish (later clusters may be empty) // Ensure at least 6 clusters exist to publish (later clusters may be empty)
while (cluster_vec.size() < 6) { while (cluster_vec.size() < 6)
{
pcl::PointCloud<pcl::PointXYZI>::Ptr empty_cluster( pcl::PointCloud<pcl::PointXYZI>::Ptr empty_cluster(
new pcl::PointCloud<pcl::PointXYZI>); new pcl::PointCloud<pcl::PointXYZI>);
@@ -413,10 +431,10 @@ void parse_cloud(const sensor_msgs::msg::PointCloud2::SharedPtr input,
centroid.intensity = 0.0; centroid.intensity = 0.0;
cluster_vec.push_back(std::make_pair(empty_cluster, 0.0f)); cluster_vec.push_back(std::make_pair(empty_cluster, 0.0f));
} }
while (clusterCentroids.size() < 6) { while (clusterCentroids.size() < 6)
{
pcl::PointXYZI centroid; pcl::PointXYZI centroid;
centroid.x = 0.0; centroid.x = 0.0;
centroid.y = 0.0; centroid.y = 0.0;
@@ -425,17 +443,18 @@ void parse_cloud(const sensor_msgs::msg::PointCloud2::SharedPtr input,
clusterCentroids.push_back(centroid); clusterCentroids.push_back(centroid);
} }
} }
void cloud_cb(const sensor_msgs::msg::PointCloud2::SharedPtr input, rclcpp::Node::SharedPtr node) void CloudClustering::cloud_cb(const sensor_msgs::msg::PointCloud2::SharedPtr input)
{ {
// Vector of cluster pointclouds // Vector of cluster pointclouds
std::vector<std::pair<pcl::PointCloud<pcl::PointXYZI>::Ptr,float>> cluster_vec; std::vector<std::pair<pcl::PointCloud<pcl::PointXYZI>::Ptr, float>> cluster_vec;
// Cluster centroids // Cluster centroids
std::vector<pcl::PointXYZI> clusterCentroids; std::vector<pcl::PointXYZI> clusterCentroids;
if (firstFrame) { if (firstFrame)
{
// If this is the first frame, initialize kalman filters for the clustered objects // If this is the first frame, initialize kalman filters for the clustered objects
// Initialize 6 Kalman Filters; Assuming 6 max objects in the dataset. // Initialize 6 Kalman Filters; Assuming 6 max objects in the dataset.
// Could be made generic by creating a Kalman Filter only when a new object // Could be made generic by creating a Kalman Filter only when a new object
@@ -445,17 +464,17 @@ void cloud_cb(const sensor_msgs::msg::PointCloud2::SharedPtr input, rclcpp::Node
float dvy = 0.01f; // 1.0 float dvy = 0.01f; // 1.0
float dx = 1.0f; float dx = 1.0f;
float dy = 1.0f; float dy = 1.0f;
KF0.transitionMatrix = (Mat_<float>(4, 4) << dx, 0, 1, 0, 0, dy, 0, 1, 0, 0, KF0.transitionMatrix = (cv::Mat_<float>(4, 4) << dx, 0, 1, 0, 0, dy, 0, 1, 0, 0,
dvx, 0, 0, 0, 0, dvy); dvx, 0, 0, 0, 0, dvy);
KF1.transitionMatrix = (Mat_<float>(4, 4) << dx, 0, 1, 0, 0, dy, 0, 1, 0, 0, KF1.transitionMatrix = (cv::Mat_<float>(4, 4) << dx, 0, 1, 0, 0, dy, 0, 1, 0, 0,
dvx, 0, 0, 0, 0, dvy); dvx, 0, 0, 0, 0, dvy);
KF2.transitionMatrix = (Mat_<float>(4, 4) << dx, 0, 1, 0, 0, dy, 0, 1, 0, 0, KF2.transitionMatrix = (cv::Mat_<float>(4, 4) << dx, 0, 1, 0, 0, dy, 0, 1, 0, 0,
dvx, 0, 0, 0, 0, dvy); dvx, 0, 0, 0, 0, dvy);
KF3.transitionMatrix = (Mat_<float>(4, 4) << dx, 0, 1, 0, 0, dy, 0, 1, 0, 0, KF3.transitionMatrix = (cv::Mat_<float>(4, 4) << dx, 0, 1, 0, 0, dy, 0, 1, 0, 0,
dvx, 0, 0, 0, 0, dvy); dvx, 0, 0, 0, 0, dvy);
KF4.transitionMatrix = (Mat_<float>(4, 4) << dx, 0, 1, 0, 0, dy, 0, 1, 0, 0, KF4.transitionMatrix = (cv::Mat_<float>(4, 4) << dx, 0, 1, 0, 0, dy, 0, 1, 0, 0,
dvx, 0, 0, 0, 0, dvy); dvx, 0, 0, 0, 0, dvy);
KF5.transitionMatrix = (Mat_<float>(4, 4) << dx, 0, 1, 0, 0, dy, 0, 1, 0, 0, KF5.transitionMatrix = (cv::Mat_<float>(4, 4) << dx, 0, 1, 0, 0, dy, 0, 1, 0, 0,
dvx, 0, 0, 0, 0, dvy); dvx, 0, 0, 0, 0, dvy);
cv::setIdentity(KF0.measurementMatrix); cv::setIdentity(KF0.measurementMatrix);
@@ -473,12 +492,12 @@ void cloud_cb(const sensor_msgs::msg::PointCloud2::SharedPtr input, rclcpp::Node
//// [ 0 0 0 0 0 Eh ] //// [ 0 0 0 0 0 Eh ]
float sigmaP = 0.01; float sigmaP = 0.01;
float sigmaQ = 0.1; float sigmaQ = 0.1;
setIdentity(KF0.processNoiseCov, Scalar::all(sigmaP)); setIdentity(KF0.processNoiseCov, cv::Scalar::all(sigmaP));
setIdentity(KF1.processNoiseCov, Scalar::all(sigmaP)); setIdentity(KF1.processNoiseCov, cv::Scalar::all(sigmaP));
setIdentity(KF2.processNoiseCov, Scalar::all(sigmaP)); setIdentity(KF2.processNoiseCov, cv::Scalar::all(sigmaP));
setIdentity(KF3.processNoiseCov, Scalar::all(sigmaP)); setIdentity(KF3.processNoiseCov, cv::Scalar::all(sigmaP));
setIdentity(KF4.processNoiseCov, Scalar::all(sigmaP)); setIdentity(KF4.processNoiseCov, cv::Scalar::all(sigmaP));
setIdentity(KF5.processNoiseCov, Scalar::all(sigmaP)); setIdentity(KF5.processNoiseCov, cv::Scalar::all(sigmaP));
// Meas noise cov matrix R // Meas noise cov matrix R
cv::setIdentity(KF0.measurementNoiseCov, cv::Scalar(sigmaQ)); // 1e-1 cv::setIdentity(KF0.measurementNoiseCov, cv::Scalar(sigmaQ)); // 1e-1
cv::setIdentity(KF1.measurementNoiseCov, cv::Scalar(sigmaQ)); cv::setIdentity(KF1.measurementNoiseCov, cv::Scalar(sigmaQ));
@@ -528,7 +547,8 @@ void cloud_cb(const sensor_msgs::msg::PointCloud2::SharedPtr input, rclcpp::Node
firstFrame = false; firstFrame = false;
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++)
{
geometry_msgs::msg::Point pt; geometry_msgs::msg::Point pt;
pt.x = clusterCentroids.at(i).x; pt.x = clusterCentroids.at(i).x;
pt.y = clusterCentroids.at(i).y; pt.y = clusterCentroids.at(i).y;
@@ -536,11 +556,13 @@ void cloud_cb(const sensor_msgs::msg::PointCloud2::SharedPtr input, rclcpp::Node
} }
} }
else { else
{
parse_cloud(input, cluster_vec, clusterCentroids); parse_cloud(input, cluster_vec, clusterCentroids);
std_msgs::msg::Float32MultiArray cc; std_msgs::msg::Float32MultiArray cc;
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++)
{
cc.data.push_back(clusterCentroids.at(i).x); cc.data.push_back(clusterCentroids.at(i).x);
cc.data.push_back(clusterCentroids.at(i).y); cc.data.push_back(clusterCentroids.at(i).y);
cc.data.push_back(clusterCentroids.at(i).z); cc.data.push_back(clusterCentroids.at(i).z);
@@ -550,41 +572,47 @@ void cloud_cb(const sensor_msgs::msg::PointCloud2::SharedPtr input, rclcpp::Node
KFT(cc); KFT(cc);
int i = 0; int i = 0;
std::sort(objID.begin(), objID.end(), [cluster_vec](const int &a, const int &b) { std::sort(objID.begin(), objID.end(), [cluster_vec](const int &a, const int &b)
return cluster_vec[a].second < cluster_vec[b].second; { return cluster_vec[a].second < cluster_vec[b].second; });
});
for (auto it = objID.begin(); it != objID.end(); it++) { for (auto it = objID.begin(); it != objID.end(); it++)
switch (i) { {
case 0: { switch (i)
{
publish_cloud(pub_cluster0, cluster_vec[*it].first, node); case 0:
{
publish_cloud(pub_cluster0, cluster_vec[*it].first);
i++; i++;
break; break;
} }
case 1: { case 1:
publish_cloud(pub_cluster1, cluster_vec[*it].first, node); {
publish_cloud(pub_cluster1, cluster_vec[*it].first);
i++; i++;
break; break;
} }
case 2: { case 2:
publish_cloud(pub_cluster2, cluster_vec[*it].first, node); {
publish_cloud(pub_cluster2, cluster_vec[*it].first);
i++; i++;
break; break;
} }
case 3: { case 3:
publish_cloud(pub_cluster3, cluster_vec[*it].first, node); {
publish_cloud(pub_cluster3, cluster_vec[*it].first);
i++; i++;
break; break;
} }
case 4: { case 4:
publish_cloud(pub_cluster4, cluster_vec[*it].first, node); {
publish_cloud(pub_cluster4, cluster_vec[*it].first);
i++; i++;
break; break;
} }
case 5: { case 5:
publish_cloud(pub_cluster5, cluster_vec[*it].first, node); {
publish_cloud(pub_cluster5, cluster_vec[*it].first);
i++; i++;
break; break;
} }
@@ -593,72 +621,5 @@ void cloud_cb(const sensor_msgs::msg::PointCloud2::SharedPtr input, rclcpp::Node
} }
} }
} }
} }
int main(int argc, char **argv) {
// ROS init
rclcpp::init(argc, argv);
auto node = rclcpp::Node::make_shared("cloud_clustering");
//Topic parameters
node->declare_parameter<std::string>("topic_in", "!defaultTopic!");
node->declare_parameter<std::string>("frame_id", "!defaultFrameId!");
//Cluster parameters
node->declare_parameter<float>("z_dim_scale", 0);
node->declare_parameter<float>("cluster_tolerance", 0.0f);
node->declare_parameter<int>("min_cluster_size", 0);
node->declare_parameter<int>("max_cluster_size", 0);
node->get_parameter("topic_in", topic_in);
node->get_parameter("frame_id", frame_id);
node->get_parameter("z_dim_scale", z_dim_scale);
node->get_parameter("cluster_tolerance", cluster_tolerance);
node->get_parameter("min_cluster_size", min_cluster_size);
node->get_parameter("max_cluster_size", max_cluster_size);
// Initialize publishers
objID_pub = node->create_publisher<std_msgs::msg::Int32MultiArray>("object_ids", 10);
pub_cluster0 = node->create_publisher<sensor_msgs::msg::PointCloud2>("cluster0", 10);
pub_cluster1 = node->create_publisher<sensor_msgs::msg::PointCloud2>("cluster1", 10);
pub_cluster2 = node->create_publisher<sensor_msgs::msg::PointCloud2>("cluster2", 10);
pub_cluster3 = node->create_publisher<sensor_msgs::msg::PointCloud2>("cluster3", 10);
pub_cluster4 = node->create_publisher<sensor_msgs::msg::PointCloud2>("cluster4", 10);
pub_cluster5 = node->create_publisher<sensor_msgs::msg::PointCloud2>("cluster5", 10);
markerPub = node->create_publisher<visualization_msgs::msg::MarkerArray>("markers", 10);
// Publishers to publish the state of the objects (pos and vel)
// objState1=nh.advertise<geometry_msgs::Twist> ("obj_1",1);
cout << "About to setup callback\n";
// Create a ROS subscriber for the input point cloud
auto sub = node->create_subscription<sensor_msgs::msg::PointCloud2>(
topic_in,
10, // queue size
[node](sensor_msgs::msg::PointCloud2::SharedPtr msg) {
cloud_cb(msg, node);
});
// Publishers
objID_pub = node->create_publisher<std_msgs::msg::Int32MultiArray>("obj_id", 10);
markerPub = node->create_publisher<visualization_msgs::msg::MarkerArray>("viz", 10);
cout << "Started clustering node with parameters:\n"
<< "topic_in: " << topic_in << "\n"
<< "frame_id: " << frame_id << "\n"
<< "z_dim_scale: " << z_dim_scale << "\n"
<< "cluster_tolerance: " << cluster_tolerance << "\n"
<< "min_cluster_size: " << min_cluster_size << "\n"
<< "max_cluster_size: " << max_cluster_size << "\n";
// Example if you still need cluster centers publisher:
// cc_pos = node->create_publisher<std_msgs::msg::Float32MultiArray>("ccs", 10);
/* Point cloud clustering
*/
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
} }

View File

@@ -0,0 +1,17 @@
#include "cloud_clustering.hpp"
int main(int argc, char * argv[])
{
// Force flush of the stdout buffer.
// This ensures a correct sync of all prints
// even when executed simultaneously within a launch file.
setvbuf(stdout, NULL, _IONBF, BUFSIZ);
rclcpp::init(argc, argv);
const rclcpp::NodeOptions options;
rclcpp::spin(std::make_shared<cloud_clustering::CloudClustering>(options));
rclcpp::shutdown();
return 0;
}