Qt上实时点云可视化

在qt上模拟ros去接收点云,并将点云数据3d可视化。不过本人并没有实现使用ros+qt的方法去订阅话题来获得点云,如有大佬实现可以留下链接。可以采用boost::asio去实现,这里采用简单易懂的tcp去接收点云。

服务端

服务器端是随机生成5000-10000个点云,并通过tcp发送给服务器。

tcp.cpp 

#include <QCoreApplication>
#include <QTcpServer>
#include <QTcpSocket>
#include <QThread>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/compression/octree_pointcloud_compression.h>
#include <pcl/io/io.h>

using PointCloud = pcl::PointCloud<pcl::PointXYZ>;

class Server : public QTcpServer {
    Q_OBJECT

public:
    Server(QObject *parent = nullptr) : QTcpServer(parent) {
        if (!this->listen(QHostAddress::Any, 1234)) {
            qCritical() << "Unable to start the server: " << this->errorString();
        } else {
            qDebug() << "Server started...";
        }
    }

protected:
    void incomingConnection(qintptr socketDescriptor) override {
        QTcpSocket *socket = new QTcpSocket(this);
        socket->setSocketDescriptor(socketDescriptor);
        connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater);
        sendPointCloud(socket);
    }

private slots:
    void sendPointCloud(QTcpSocket *socket) {
        while (socket->state() == QAbstractSocket::ConnectedState) {
            PointCloud::Ptr cloud(new PointCloud);
            generateRandomPointCloud(cloud, 5000 + qrand() % 5001);  // 生成5000到10000个点
            //压缩点云数据
            std::stringstream compressedData;
            pcl::io::OctreePointCloudCompression<pcl::PointXYZ> octreeCompression;
            octreeCompression.encodePointCloud(cloud, compressedData);

            QByteArray data(compressedData.str().c_str(), compressedData.str().length());
            socket->write(data);
            socket->flush();
            QThread::sleep(1);  // 每秒发送一次
        }
        qDebug() << "Client disconnected...";
    }

    void generateRandomPointCloud(PointCloud::Ptr cloud, int numPoints) {
        for (int i = 0; i < numPoints; ++i) {
            pcl::PointXYZ point;
            point.x = 1024 * rand() / (RAND_MAX + 1.0f);
            point.y = 1024 * rand() / (RAND_MAX + 1.0f);
            point.z = 1024 * rand() / (RAND_MAX + 1.0f);
            cloud->push_back(point);
        }
        cloud->width = numPoints;
        cloud->height = 1;
        cloud->is_dense = true;
    }
};

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    Server server;
    return a.exec();
}
#include "tcp.moc"

其他地方都很简单,唯一需要注意的地方是代码中这个地方采用了八叉树压缩点云的方法,提高了传输效率,感兴趣可以自行查补。

            //压缩点云数据
            std::stringstream compressedData;
            pcl::io::OctreePointCloudCompression<pcl::PointXYZ> octreeCompression;
            octreeCompression.encodePointCloud(cloud, compressedData);

            QByteArray data(compressedData.str().c_str(), compressedData.str().length());

服务端的CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(TCP)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
 
set(CMAKE_BUILD_TYPE "Release")
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
 
aux_source_directory(./src SRC_LIST)  

find_package(Qt5 COMPONENTS Core Widgets Gui Network   REQUIRED)
find_package(PCL  REQUIRED)

add_executable(${PROJECT_NAME}  ${SRC_LIST})
 
target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network)
target_link_libraries(${PROJECT_NAME} ${PCL_LIBRARIES})

客户端

客户端是一个PLCvsualizer去呈现点云数据然后嵌套在vtk的QVTKOpenGLNativeWidget里,然后更新接收数据的同时去更新界面,这里接收数据需要先去解压数据。

myWidget.h:

//头文件
#ifndef MY_WIDGET_H
#define MY_WIDGET_H 

#include <QWidget>
#include <QTcpSocket>
#include <QVBoxLayout>
#include <iostream>

#include <vtkSmartPointer.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <QVTKOpenGLNativeWidget.h>
#include <vtkGenericOpenGLRenderWindow.h>

#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/compression/octree_pointcloud_compression.h>

using PointCloud = pcl::PointCloud<pcl::PointXYZ>;

class myWidget : public QWidget
{
    Q_OBJECT

public:
    myWidget(QWidget *parent = nullptr);
    ~myWidget();

    void initializeViewer();
    void createCloudPoint();
private slots:
    void handlePointCloudReceived();
   
private:

    QTcpSocket *socket;
    QVTKOpenGLNativeWidget *vtkWidget;
    vtkSmartPointer<vtkRenderer> renderer;
    vtkSmartPointer<vtkGenericOpenGLRenderWindow> renderWindow;
    pcl::visualization::PCLVisualizer::Ptr viewer;
};

#endif

 myWidget.cpp:

#include "myWidget.h"

myWidget::myWidget(QWidget *parent)
    : QWidget(parent)
{
    createCloudPoint();
}

void myWidget::createCloudPoint()
{
    QVBoxLayout *v = new QVBoxLayout();
    setLayout(v);
    this->setMinimumSize(640, 640);
    vtkWidget = new QVTKOpenGLNativeWidget(this);
    v->addWidget(vtkWidget);

    initializeViewer();
    socket = new QTcpSocket(this);

    //一旦收到数据便触发槽函数 渲染并更新界面
    connect(socket, &QTcpSocket::readyRead, this, &myWidget::handlePointCloudReceived);
    socket->connectToHost("127.0.0.1", 1234);
}

void myWidget::initializeViewer() {
    renderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
    vtkWidget->setRenderWindow(renderWindow);

    renderer = vtkSmartPointer<vtkRenderer>::New();
    renderWindow->AddRenderer(renderer);

    viewer.reset(new pcl::visualization::PCLVisualizer(renderer, renderWindow, "viewer", false));
    vtkWidget->setRenderWindow(viewer->getRenderWindow());
    viewer->setupInteractor(vtkWidget->interactor(), vtkWidget->renderWindow());

    viewer->setBackgroundColor(0.1, 0.1, 0.1);
    viewer->addCoordinateSystem(1.0);
    viewer->initCameraParameters();
}

void myWidget::handlePointCloudReceived()
{
    QByteArray data = socket->readAll();
    std::stringstream compressedData(data.toStdString());

    PointCloud::Ptr cloud(new PointCloud);
    pcl::io::OctreePointCloudCompression<pcl::PointXYZ> octreeDecompression;
    octreeDecompression.decodePointCloud(compressedData, cloud);

    viewer->removeAllPointClouds();
    viewer->addPointCloud(cloud);
    vtkWidget->update();
    viewer->resetCamera();  // 重置相机以查看新点云
    renderWindow->Render();
    // update();
}

 main.cpp:

#include "myWidget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv); //管理整个应用程序

    myWidget w;       //界面对象
    w.show();           //显示界面

    int result = a.exec();
    return result;
}

客户端的CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(qt_widget)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
 
set(CMAKE_BUILD_TYPE "Release")
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
 
aux_source_directory(./src SRC_LIST)  

# find dependencies

find_package(Qt5 COMPONENTS Core Widgets Gui Network REQUIRED)
find_package(PCL REQUIRED)
find_package(VTK REQUIRED)

link_directories( ${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})

include_directories(include ${PCL_INCLUDE_DIRS} )

add_executable(${PROJECT_NAME}  ${SRC_LIST})
 
target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network)
target_link_libraries(${PROJECT_NAME} ${PCL_LIBRARIES} ${VTK_LIBRARIES})


安装好环境后去跑一下,看不懂哪行去掉哪行看看效果,也就懂了。

效果图:

路径图:

点云可视化窗口的刷新通常需要在界面上触发一个事件或者定时器来进行刷新,具体实现可以参考以下步骤: 1. 在Qt中创建一个QVTKWidget控件用于显示点云,或者使用其他的点云可视化库。 2. 将点云数据加载到PointCloud对象中,例如: ``` pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); // 加载点云数据 pcl::io::loadPCDFile("cloud.pcd", *cloud); ``` 3. 将PointCloud对象转换成VTK的PolyData类型,并将其设置到QVTKWidget控件中,例如: ``` vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New(); pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> color_handler(cloud, 255, 255, 255); // 设置点云颜色 pcl::visualization::PointCloudGeometryHandlerXYZ<pcl::PointXYZ> geometry_handler(cloud); // 获取点云几何信息 pcl::visualization::PCLVisualizer vis; vis.addPointCloud<pcl::PointXYZ>(cloud, color_handler, geometry_handler); vis.getPickedPoint(); // 鼠标选点 pcl::visualization::PCLVisualizerInteractorStyle style(&vis); vis.registerKeyboardCallback(&keyboardEventOccurred, (void*)(&vis)); pcl::visualization::VTKPCLVisualizer::Ptr pclVis(new pcl::visualization::VTKPCLVisualizer("viewer", false)); pclVis->setBackgroundColor(0, 0, 0); pclVis->addPointCloud<pcl::PointXYZ>(cloud, color_handler, geometry_handler); pclVis->addCoordinateSystem(1.0); pclVis->initCameraParameters(); pclVis->setCameraPosition(0, 0, 0, 0, 0, -1, 0, 1, 0); pclVis->setCameraClipDistances(-5.0, 5.0); pclVis->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3); pclVis->registerKeyboardCallback(&keyboardEventOccurred, (void*)(&vis)); ui->qvtkWidget->SetRenderWindow(pclVis->getRenderWindow()); ``` 4. 在界面上添加一个按钮或者定时器,当触发事件时,重新加载点云数据并更新控件,例如: ``` void MainWindow::on_refreshButton_clicked() { pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); // 加载点云数据 pcl::io::loadPCDFile("cloud.pcd", *cloud); // 将PointCloud对象转换成VTK的PolyData类型,并将其设置到QVTKWidget控件中 vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New(); pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> color_handler(cloud, 255, 255, 255); pcl::visualization::PointCloudGeometryHandlerXYZ<pcl::PointXYZ> geometry_handler(cloud); pcl::visualization::PCLVisualizer vis; vis.addPointCloud<pcl::PointXYZ>(cloud, color_handler, geometry_handler); vis.getPickedPoint(); pcl::visualization::PCLVisualizerInteractorStyle style(&vis); vis.registerKeyboardCallback(&keyboardEventOccurred, (void*)(&vis)); pcl::visualization::VTKPCLVisualizer::Ptr pclVis(new pcl::visualization::VTKPCLVisualizer("viewer", false)); pclVis->setBackgroundColor(0, 0, 0); pclVis->addPointCloud<pcl::PointXYZ>(cloud, color_handler, geometry_handler); pclVis->addCoordinateSystem(1.0); pclVis->initCameraParameters(); pclVis->setCameraPosition(0, 0, 0, 0, 0, -1, 0, 1, 0); pclVis->setCameraClipDistances(-5.0, 5.0); pclVis->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3); pclVis->registerKeyboardCallback(&keyboardEventOccurred, (void*)(&vis)); ui->qvtkWidget->SetRenderWindow(pclVis->getRenderWindow()); } void MainWindow::on_refreshTimer_timeout() { // 每隔一段时间重新加载点云数据并更新控件 on_refreshButton_clicked(); } ``` 以上就是基于Qt点云可视化窗口刷新的具体实现方法。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北海__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值