在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})
安装好环境后去跑一下,看不懂哪行去掉哪行看看效果,也就懂了。
效果图:
路径图: