feat(点云处理): 添加基于Z阈值的点云旋转和保存功能

This commit is contained in:
杰仔 2026-04-25 19:00:49 +08:00
parent 598cd176f5
commit c088ad39c4
6 changed files with 3557 additions and 2116 deletions

View File

@ -6,6 +6,17 @@
#include "VZNL_Types.h" #include "VZNL_Types.h"
#include "VrError.h" #include "VrError.h"
// 保存进度回调接口
class ISaveProgressCallback
{
public:
virtual ~ISaveProgressCallback() = default;
// 进度回调progress 范围 0.0~1.0
virtual void onSaveProgress(float progress) = 0;
// 返回 true 取消保存
virtual bool isSaveCancelled() const { return false; }
};
// 激光数据加载器类 // 激光数据加载器类
class LaserDataLoader class LaserDataLoader
{ {
@ -30,6 +41,8 @@ public:
int clockPerSecond); int clockPerSecond);
int DebugSaveLaser(std::string fileName, std::vector<std::vector<SVzNL3DPosition>> xyzData); int DebugSaveLaser(std::string fileName, std::vector<std::vector<SVzNL3DPosition>> xyzData);
int DebugSaveLaser(std::string fileName, std::vector<std::vector<SVzNL3DPosition>> xyzData,
ISaveProgressCallback* callback);
// 释放统一格式数据内存 // 释放统一格式数据内存
void FreeLaserScanData(std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& laserLines); void FreeLaserScanData(std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& laserLines);

View File

@ -246,6 +246,12 @@ int LaserDataLoader::SaveLaserScanData(const std::string& fileName,
} }
int LaserDataLoader::DebugSaveLaser(std::string fileName, std::vector<std::vector<SVzNL3DPosition> > xyzData) int LaserDataLoader::DebugSaveLaser(std::string fileName, std::vector<std::vector<SVzNL3DPosition> > xyzData)
{
return DebugSaveLaser(std::move(fileName), std::move(xyzData), nullptr);
}
int LaserDataLoader::DebugSaveLaser(std::string fileName, std::vector<std::vector<SVzNL3DPosition>> xyzData,
ISaveProgressCallback* callback)
{ {
LOG_INFO("Saving unified laser scan data to file: %s\n", fileName.c_str()); LOG_INFO("Saving unified laser scan data to file: %s\n", fileName.c_str());
@ -270,10 +276,18 @@ int LaserDataLoader::DebugSaveLaser(std::string fileName, std::vector<std::vecto
sw << "PointAdjust: 1" << std::endl; sw << "PointAdjust: 1" << std::endl;
sw << "MaxTimeStamp:" << 0 << "_" << 0 << std::endl; sw << "MaxTimeStamp:" << 0 << "_" << 0 << std::endl;
const size_t totalLines = xyzData.size();
int index = 0; int index = 0;
// 写入每条扫描线数据 // 写入每条扫描线数据
for (const auto& linePair : xyzData) { for (const auto& linePair : xyzData) {
sw << "Line_" << index++ << "_" << 0 << "_" << linePair.size() << std::endl; if (callback && callback->isSaveCancelled()) {
sw.close();
m_lastError = "Save cancelled by user";
LOG_INFO("Save cancelled by user at line %d/%zu\n", index, totalLines);
return ERR_CODE(DEV_ARG_INVAILD);
}
sw << "Line_" << index << "_" << 0 << "_" << linePair.size() << std::endl;
// 根据数据类型写入点云数据 // 根据数据类型写入点云数据
for(const auto& point : linePair){ for(const auto& point : linePair){
@ -281,12 +295,17 @@ int LaserDataLoader::DebugSaveLaser(std::string fileName, std::vector<std::vecto
float x = static_cast<float>(point.pt3D.x); float x = static_cast<float>(point.pt3D.x);
float y = static_cast<float>(point.pt3D.y); float y = static_cast<float>(point.pt3D.y);
float z = static_cast<float>(point.pt3D.z); float z = static_cast<float>(point.pt3D.z);
sw << "{ " sw << "{ "
<< std::fixed << std::setprecision(6) << x << ", " << std::fixed << std::setprecision(6) << x << ", "
<< std::fixed << std::setprecision(6) << y << ", " << std::fixed << std::setprecision(6) << y << ", "
<< std::fixed << std::setprecision(6) << z << " } - "; << std::fixed << std::setprecision(6) << z << " } - ";
sw << "{ 0, 0 } - { 0, 0 }" << std::endl; sw << "{ 0, 0 } - { 0, 0 }" << std::endl;
} }
++index;
if (callback) {
callback->onSaveProgress(static_cast<float>(index) / static_cast<float>(totalLines));
}
} }
sw.close(); sw.close();

View File

@ -162,11 +162,16 @@ private slots:
* @brief * @brief
*/ */
void onApplyMatrix(); void onApplyMatrix();
void onApplyEyeInHandTransform();
/** /**
* @brief * @brief
*/ */
void onResetMatrix(); void onResetMatrix();
void onGeneratePointCloud();
void onRotateCloudByZ();
void onSavePointCloud();
void onConvertEulerMatrix();
private: private:
/** /**
@ -256,6 +261,8 @@ private:
* @return * @return
*/ */
bool loadBoundingBoxFile(const QString& fileName); bool loadBoundingBoxFile(const QString& fileName);
void applyTransformToAllClouds(const QMatrix4x4& matrix);
void addGeneratedCloud(const PointCloudXYZ& cloud, const QString& name);
// 点云显示控件 // 点云显示控件
PointCloudGLWidget* m_glWidget; PointCloudGLWidget* m_glWidget;
@ -351,6 +358,13 @@ private:
QTextEdit* m_matrixEdit; QTextEdit* m_matrixEdit;
QPushButton* m_btnLoadMatrix; QPushButton* m_btnLoadMatrix;
QPushButton* m_btnApplyMatrix; QPushButton* m_btnApplyMatrix;
QLineEdit* m_editRobotX;
QLineEdit* m_editRobotY;
QLineEdit* m_editRobotZ;
QLineEdit* m_editRobotRx;
QLineEdit* m_editRobotRy;
QLineEdit* m_editRobotRz;
QPushButton* m_btnApplyEyeInHand;
QPushButton* m_btnResetMatrix; QPushButton* m_btnResetMatrix;
// 悬浮缩放按钮 // 悬浮缩放按钮

View File

@ -12,6 +12,7 @@
#include <vector> #include <vector>
#include "PointCloudConverter.h" #include "PointCloudConverter.h"
#include "VZNL_Types.h"
/** /**
* @brief * @brief
@ -243,6 +244,21 @@ public:
*/ */
void transformAllClouds(const QMatrix4x4& matrix); void transformAllClouds(const QMatrix4x4& matrix);
/**
* @brief Z
* @param rotMatrix
* @param zThreshold Z
* @return
*/
size_t rotateCloudsByZThreshold(const QMatrix4x4& rotMatrix, float zThreshold);
/**
* @brief 线
* @param scanLines 线
* @return
*/
size_t getAllCloudsByLines(std::vector<std::vector<SVzNL3DPosition>>& scanLines) const;
/** /**
* @brief 线 * @brief 线
*/ */

File diff suppressed because it is too large Load Diff

View File

@ -750,6 +750,98 @@ void PointCloudGLWidget::transformAllClouds(const QMatrix4x4& matrix)
update(); update();
} }
size_t PointCloudGLWidget::rotateCloudsByZThreshold(const QMatrix4x4& rotMatrix, float zThreshold)
{
size_t rotatedCount = 0;
for (auto& cloudData : m_pointClouds) {
for (size_t i = 0; i + 2 < cloudData.vertices.size(); i += 3) {
if (cloudData.vertices[i + 2] < zThreshold) {
QVector3D pt(cloudData.vertices[i], cloudData.vertices[i + 1], cloudData.vertices[i + 2]);
QVector3D transformed = rotMatrix.map(pt);
cloudData.vertices[i] = transformed.x();
cloudData.vertices[i + 1] = transformed.y();
cloudData.vertices[i + 2] = transformed.z();
++rotatedCount;
}
}
}
if (rotatedCount > 0) {
makeCurrent();
for (auto& cloudData : m_pointClouds) {
uploadToVBO(cloudData);
}
doneCurrent();
computeBoundingBox();
resetView();
update();
}
return rotatedCount;
}
size_t PointCloudGLWidget::getAllCloudsByLines(std::vector<std::vector<SVzNL3DPosition>>& scanLines) const
{
scanLines.clear();
size_t totalPoints = 0;
for (const auto& cloudData : m_pointClouds) {
const size_t displayCount = cloudData.vertices.size() / 3;
if (displayCount == 0) continue;
if (cloudData.hasLineInfo && cloudData.totalLines > 0 && cloudData.pointsPerLine > 0) {
// 有线信息:用 pointsPerLine 还原每条线的完整点数,过滤掉的 (0,0,0) 点补零
const int lines = cloudData.totalLines;
const int ptsPerLine = cloudData.pointsPerLine;
// 初始化所有线,每条线 pointsPerLine 个零点
const size_t baseIdx = scanLines.size();
for (int i = 0; i < lines; ++i) {
std::vector<SVzNL3DPosition> line(ptsPerLine);
memset(line.data(), 0, sizeof(SVzNL3DPosition) * ptsPerLine);
for (int j = 0; j < ptsPerLine; ++j) {
line[j].nPointIdx = j;
}
scanLines.push_back(std::move(line));
}
// 用实际显示的点填充对应位置
for (size_t i = 0; i < displayCount; ++i) {
const int lineIdx = (i < cloudData.lineIndices.size()) ? cloudData.lineIndices[i] : 0;
const int ptInLine = (i < cloudData.pointInLineIndices.size()) ? cloudData.pointInLineIndices[i] : static_cast<int>(i);
if (lineIdx >= 0 && lineIdx < lines && ptInLine >= 0 && ptInLine < ptsPerLine) {
auto& pos = scanLines[baseIdx + lineIdx][ptInLine];
pos.pt3D.x = cloudData.vertices[i * 3];
pos.pt3D.y = cloudData.vertices[i * 3 + 1];
pos.pt3D.z = cloudData.vertices[i * 3 + 2];
pos.nPointIdx = ptInLine;
}
}
totalPoints += static_cast<size_t>(lines) * ptsPerLine;
} else {
// 无线信息,每个点云作为一条线
std::vector<SVzNL3DPosition> line;
line.reserve(displayCount);
for (size_t i = 0; i < displayCount; ++i) {
SVzNL3DPosition pos;
memset(&pos, 0, sizeof(pos));
pos.nPointIdx = static_cast<int>(i);
pos.pt3D.x = cloudData.vertices[i * 3];
pos.pt3D.y = cloudData.vertices[i * 3 + 1];
pos.pt3D.z = cloudData.vertices[i * 3 + 2];
line.push_back(pos);
}
totalPoints += displayCount;
scanLines.push_back(std::move(line));
}
}
return totalPoints;
}
void PointCloudGLWidget::computeBoundingBox() void PointCloudGLWidget::computeBoundingBox()
{ {
if (m_pointClouds.empty()) { if (m_pointClouds.empty()) {