update: 扩大ip搜索范围,修改点云可视化逻辑,添加上位机与下位机曝光值同步
This commit is contained in:
@@ -145,10 +145,10 @@ install(DIRECTORY ${CMAKE_SOURCE_DIR}/bin/platforms/
|
||||
set(CPACK_PACKAGE_NAME "Viewer")
|
||||
set(CPACK_PACKAGE_VENDOR "Lorenzo Zhao")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Depth Camera Control System")
|
||||
set(CPACK_PACKAGE_VERSION "0.3.0")
|
||||
set(CPACK_PACKAGE_VERSION "0.3.2")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "0")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "3")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "0")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "2")
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "Viewer")
|
||||
|
||||
# WiX生成器配置(用于MSI)
|
||||
|
||||
@@ -167,16 +167,16 @@ C:\Program Files\D330Viewer\
|
||||
- ✅ 网络配置(IP地址、端口设置)
|
||||
- ✅ 连接状态指示
|
||||
- ✅ 配置持久化(QSettings)
|
||||
- ✅ 点云颜色映射(深度着色)
|
||||
- ✅ 多视角预设(正视、侧视、俯视)
|
||||
|
||||
### 🚧 当前开发计划
|
||||
|
||||
根据需求文档和用户反馈,后续待添加功能如下:
|
||||
|
||||
- 录制功能(连续保存多帧)
|
||||
- 点云颜色映射(深度着色)
|
||||
- 点云滤波选项(降噪、平滑)
|
||||
- 测量工具(距离、角度测量)
|
||||
- 多视角预设(正视、侧视、俯视)
|
||||
- 性能监控(CPU/GPU使用率、内存使用)
|
||||
- 其他相机参数调节(增益、白平衡等)
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ public:
|
||||
void updatePointCloud(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud);
|
||||
void setColorMode(bool enabled) { m_colorMode = enabled ? 1 : 0; update(); }
|
||||
bool colorMode() const { return m_colorMode != 0; }
|
||||
void resetView(); // 重置视角到初始状态
|
||||
|
||||
protected:
|
||||
void initializeGL() override;
|
||||
|
||||
@@ -45,7 +45,7 @@ void ConfigManager::setDataPort(int port)
|
||||
// ========== 相机配置 ==========
|
||||
int ConfigManager::getExposureTime() const
|
||||
{
|
||||
return m_settings->value("Camera/ExposureTime", 1000).toInt();
|
||||
return m_settings->value("Camera/ExposureTime", 5980).toInt();
|
||||
}
|
||||
|
||||
void ConfigManager::setExposureTime(int exposure)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "DeviceScanner.h"
|
||||
#include <QNetworkDatagram>
|
||||
#include <QNetworkInterface>
|
||||
#include <QCoreApplication>
|
||||
#include <QThread>
|
||||
#include <QDebug>
|
||||
|
||||
DeviceScanner::DeviceScanner(QObject *parent)
|
||||
@@ -8,8 +10,9 @@ DeviceScanner::DeviceScanner(QObject *parent)
|
||||
, m_socket(new QUdpSocket(this))
|
||||
, m_timeoutTimer(new QTimer(this))
|
||||
, m_scanTimer(new QTimer(this))
|
||||
, m_currentHost(HOST_START)
|
||||
, m_totalHosts(HOST_END - HOST_START + 1)
|
||||
, m_prefixLength(24)
|
||||
, m_currentHost(0)
|
||||
, m_totalHosts(0)
|
||||
, m_isScanning(false)
|
||||
{
|
||||
connect(m_socket, &QUdpSocket::readyRead, this, &DeviceScanner::onReadyRead);
|
||||
@@ -33,8 +36,9 @@ void DeviceScanner::startScan(const QString &subnet)
|
||||
return;
|
||||
}
|
||||
|
||||
m_subnet = subnet.isEmpty() ? getLocalSubnet() : subnet;
|
||||
m_currentHost = HOST_START;
|
||||
// Get network info (base IP and prefix length)
|
||||
getLocalNetworkInfo(m_baseIp, m_prefixLength);
|
||||
|
||||
m_foundDevices.clear();
|
||||
m_isScanning = true;
|
||||
|
||||
@@ -44,17 +48,62 @@ void DeviceScanner::startScan(const QString &subnet)
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "Starting fast device scan on subnet:" << m_subnet;
|
||||
|
||||
// Send DISCOVER to all hosts at once (batch mode)
|
||||
for (int host = HOST_START; host <= HOST_END; host++) {
|
||||
QString ip = m_subnet + "." + QString::number(host);
|
||||
sendDiscoveryPacket(ip);
|
||||
// Calculate IP range based on prefix length
|
||||
QStringList parts = m_baseIp.split('.');
|
||||
if (parts.size() != 4) {
|
||||
emit scanError("Invalid base IP");
|
||||
m_isScanning = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait 3 seconds for all responses
|
||||
m_timeoutTimer->start(3000);
|
||||
qDebug() << "Sent discovery packets to all hosts, waiting for responses...";
|
||||
quint32 baseAddr = (parts[0].toUInt() << 24) | (parts[1].toUInt() << 16) |
|
||||
(parts[2].toUInt() << 8) | parts[3].toUInt();
|
||||
quint32 mask = (0xFFFFFFFF << (32 - m_prefixLength)) & 0xFFFFFFFF;
|
||||
quint32 networkAddr = baseAddr & mask;
|
||||
quint32 broadcastAddr = networkAddr | (~mask & 0xFFFFFFFF);
|
||||
|
||||
qDebug() << "Starting device scan - Base IP:" << m_baseIp << "Prefix:" << m_prefixLength;
|
||||
|
||||
int packetsSent = 0;
|
||||
|
||||
// For large subnets (/16 or larger), use broadcast + batched unicast
|
||||
if (m_prefixLength <= 16) {
|
||||
// First: send to subnet broadcast address
|
||||
QHostAddress broadcast(broadcastAddr);
|
||||
qDebug() << "Large subnet detected, using broadcast discovery:" << broadcast.toString();
|
||||
sendDiscoveryPacket(broadcast.toString());
|
||||
packetsSent++;
|
||||
|
||||
// Second: scan all /24 subnets with throttling to avoid buffer overflow
|
||||
qDebug() << "Scanning all /24 subnets within /16 range (throttled)...";
|
||||
for (int oct3 = 0; oct3 <= 255; oct3++) {
|
||||
quint32 subNetBase = (networkAddr & 0xFFFF0000) | (oct3 << 8);
|
||||
for (int oct4 = 1; oct4 <= 254; oct4++) {
|
||||
quint32 addr = subNetBase | oct4;
|
||||
sendDiscoveryPacket(QHostAddress(addr).toString());
|
||||
packetsSent++;
|
||||
}
|
||||
// Process events and add small delay every /24 subnet to avoid buffer overflow
|
||||
QCoreApplication::processEvents();
|
||||
QThread::msleep(5);
|
||||
}
|
||||
} else {
|
||||
// For smaller subnets, scan all hosts
|
||||
qDebug() << "Network range:" << QHostAddress(networkAddr + 1).toString()
|
||||
<< "to" << QHostAddress(broadcastAddr - 1).toString();
|
||||
|
||||
for (quint32 addr = networkAddr + 1; addr < broadcastAddr; addr++) {
|
||||
sendDiscoveryPacket(QHostAddress(addr).toString());
|
||||
packetsSent++;
|
||||
}
|
||||
}
|
||||
|
||||
m_totalHosts = packetsSent;
|
||||
qDebug() << "Sent" << packetsSent << "discovery packets, waiting for responses...";
|
||||
|
||||
// Adjust timeout based on network size
|
||||
int timeout = (m_prefixLength >= 24) ? 3000 : 10000;
|
||||
m_timeoutTimer->start(timeout);
|
||||
}
|
||||
|
||||
void DeviceScanner::stopScan()
|
||||
@@ -91,7 +140,14 @@ void DeviceScanner::onReadyRead()
|
||||
|
||||
qDebug() << "Received response from" << senderIp << ":" << response;
|
||||
|
||||
if (response.contains("D330M_CAMERA")) {
|
||||
if (response.startsWith("EXPOSURE:")) {
|
||||
bool ok;
|
||||
int exposure = response.mid(9).trimmed().toInt(&ok);
|
||||
if (ok && exposure > 0) {
|
||||
qDebug() << "Received exposure from camera:" << exposure << "us";
|
||||
emit exposureReceived(exposure);
|
||||
}
|
||||
} else if (response.contains("D330M_CAMERA")) {
|
||||
DeviceInfo device;
|
||||
device.ipAddress = senderIp;
|
||||
device.deviceName = "Camera";
|
||||
@@ -122,7 +178,7 @@ void DeviceScanner::sendDiscoveryPacket(const QString &ip)
|
||||
m_socket->writeDatagram(data, QHostAddress(ip), SCAN_PORT);
|
||||
}
|
||||
|
||||
QString DeviceScanner::getLocalSubnet()
|
||||
void DeviceScanner::getLocalNetworkInfo(QString &baseIp, int &prefixLength)
|
||||
{
|
||||
// Get all network interfaces
|
||||
QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
|
||||
@@ -144,12 +200,14 @@ QString DeviceScanner::getLocalSubnet()
|
||||
for (const QNetworkAddressEntry &entry : entries) {
|
||||
QHostAddress addr = entry.ip();
|
||||
if (addr.protocol() == QAbstractSocket::IPv4Protocol && !addr.isLoopback()) {
|
||||
QString ip = addr.toString();
|
||||
QStringList parts = ip.split('.');
|
||||
if (parts.size() == 4) {
|
||||
qDebug() << "Found Ethernet adapter:" << iface.humanReadableName() << "IP:" << ip;
|
||||
return parts[0] + "." + parts[1] + "." + parts[2];
|
||||
baseIp = addr.toString();
|
||||
prefixLength = entry.prefixLength();
|
||||
if (prefixLength <= 0 || prefixLength > 32) {
|
||||
prefixLength = 24; // Default to /24 if invalid
|
||||
}
|
||||
qDebug() << "Found Ethernet adapter:" << iface.humanReadableName()
|
||||
<< "IP:" << baseIp << "Prefix:" << prefixLength;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,15 +227,19 @@ QString DeviceScanner::getLocalSubnet()
|
||||
for (const QNetworkAddressEntry &entry : entries) {
|
||||
QHostAddress addr = entry.ip();
|
||||
if (addr.protocol() == QAbstractSocket::IPv4Protocol && !addr.isLoopback()) {
|
||||
QString ip = addr.toString();
|
||||
QStringList parts = ip.split('.');
|
||||
if (parts.size() == 4) {
|
||||
qDebug() << "Found adapter:" << iface.humanReadableName() << "IP:" << ip;
|
||||
return parts[0] + "." + parts[1] + "." + parts[2];
|
||||
baseIp = addr.toString();
|
||||
prefixLength = entry.prefixLength();
|
||||
if (prefixLength <= 0 || prefixLength > 32) {
|
||||
prefixLength = 24;
|
||||
}
|
||||
qDebug() << "Found adapter:" << iface.humanReadableName()
|
||||
<< "IP:" << baseIp << "Prefix:" << prefixLength;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "192.168.0";
|
||||
// Default fallback
|
||||
baseIp = "192.168.0.1";
|
||||
prefixLength = 24;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ public:
|
||||
|
||||
signals:
|
||||
void deviceFound(const DeviceInfo &device);
|
||||
void exposureReceived(int exposureUs);
|
||||
void scanProgress(int current, int total);
|
||||
void scanFinished(int devicesFound);
|
||||
void scanError(const QString &error);
|
||||
@@ -40,13 +41,14 @@ private slots:
|
||||
|
||||
private:
|
||||
void sendDiscoveryPacket(const QString &ip);
|
||||
QString getLocalSubnet();
|
||||
void getLocalNetworkInfo(QString &baseIp, int &prefixLength);
|
||||
|
||||
QUdpSocket *m_socket;
|
||||
QTimer *m_timeoutTimer;
|
||||
QTimer *m_scanTimer;
|
||||
|
||||
QString m_subnet;
|
||||
QString m_baseIp;
|
||||
int m_prefixLength;
|
||||
int m_currentHost;
|
||||
int m_totalHosts;
|
||||
bool m_isScanning;
|
||||
@@ -55,8 +57,6 @@ private:
|
||||
|
||||
static constexpr int SCAN_PORT = 6790; // Control port for device discovery
|
||||
static constexpr int SCAN_TIMEOUT = 10;
|
||||
static constexpr int HOST_START = 1;
|
||||
static constexpr int HOST_END = 254;
|
||||
};
|
||||
|
||||
#endif // DEVICESCANNER_H
|
||||
|
||||
@@ -16,6 +16,9 @@ NetworkManager::NetworkManager(QObject *parent)
|
||||
connect(m_dataSocket, &QUdpSocket::readyRead, this, &NetworkManager::onReadyRead);
|
||||
connect(m_dataSocket, &QUdpSocket::errorOccurred, this, &NetworkManager::onError);
|
||||
|
||||
// 连接控制socket接收信号(用于接收相机回复,如曝光值)
|
||||
connect(m_controlSocket, &QUdpSocket::readyRead, this, &NetworkManager::onControlReadyRead);
|
||||
|
||||
// 连接GVSP解析器信号
|
||||
connect(m_gvspParser, &GVSPParser::imageReceived, this, &NetworkManager::imageReceived);
|
||||
connect(m_gvspParser, &GVSPParser::leftImageReceived, this, &NetworkManager::leftImageReceived);
|
||||
@@ -67,6 +70,10 @@ bool NetworkManager::connectToCamera(const QString &ip, int controlPort, int dat
|
||||
m_isConnected = true;
|
||||
qDebug() << "Connected to camera:" << m_cameraIp << "Control port:" << m_controlPort << "Data port:" << m_dataPort;
|
||||
|
||||
// Send DISCOVER to get camera's current exposure value
|
||||
sendCommand("DISCOVER");
|
||||
qDebug() << "Sent DISCOVER to fetch camera exposure";
|
||||
|
||||
// Send STOP command to register client IP on camera
|
||||
sendStopCommand();
|
||||
qDebug() << "Sent STOP command to register client IP";
|
||||
@@ -213,6 +220,27 @@ void NetworkManager::onReadyRead()
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkManager::onControlReadyRead()
|
||||
{
|
||||
while (m_controlSocket->hasPendingDatagrams()) {
|
||||
QByteArray datagram;
|
||||
datagram.resize(m_controlSocket->pendingDatagramSize());
|
||||
m_controlSocket->readDatagram(datagram.data(), datagram.size());
|
||||
|
||||
QString response = QString::fromUtf8(datagram);
|
||||
qDebug() << "[NetworkManager] Control response:" << response;
|
||||
|
||||
if (response.startsWith("EXPOSURE:")) {
|
||||
bool ok;
|
||||
int exposure = response.mid(9).trimmed().toInt(&ok);
|
||||
if (ok && exposure > 0) {
|
||||
qDebug() << "[NetworkManager] Camera exposure:" << exposure << "us";
|
||||
emit exposureReceived(exposure);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkManager::onError(QAbstractSocket::SocketError socketError)
|
||||
{
|
||||
QString error = QString("Socket error: %1").arg(m_dataSocket->errorString());
|
||||
|
||||
@@ -43,6 +43,7 @@ public:
|
||||
signals:
|
||||
void connected();
|
||||
void disconnected();
|
||||
void exposureReceived(int exposureUs);
|
||||
void errorOccurred(const QString &error);
|
||||
void dataReceived(const QByteArray &data);
|
||||
void imageReceived(const QImage &image, uint32_t blockId);
|
||||
@@ -54,6 +55,7 @@ signals:
|
||||
|
||||
private slots:
|
||||
void onReadyRead();
|
||||
void onControlReadyRead();
|
||||
void onError(QAbstractSocket::SocketError socketError);
|
||||
|
||||
private:
|
||||
|
||||
@@ -106,9 +106,8 @@ bool PointCloudProcessor::initializeOpenCL()
|
||||
"int y = idx / width; "
|
||||
"int x = idx % width; "
|
||||
"float z = depth[idx] * z_scale; "
|
||||
// 完全平面的圆柱投影:X和Y直接使用像素坐标,缩放到合适的范围
|
||||
"xyz[idx*3] = (x - cx) * 2.0f; " // X坐标,缩放系数2.0
|
||||
"xyz[idx*3+1] = -(y - cy) * 2.0f; " // Y坐标取反,修正上下颠倒
|
||||
"xyz[idx*3] = (x - cx) * z * inv_fx; "
|
||||
"xyz[idx*3+1] = (y - cy) * z * inv_fy; "
|
||||
"xyz[idx*3+2] = z; "
|
||||
"}";
|
||||
|
||||
@@ -283,31 +282,30 @@ void PointCloudProcessor::processPointCloudData(const QByteArray &cloudData, uin
|
||||
// 从int16_t数组读取点云数据
|
||||
const int16_t* cloudShort = reinterpret_cast<const int16_t*>(cloudData.constData());
|
||||
|
||||
float inv_fx = 1.0f / m_fx;
|
||||
float inv_fy = 1.0f / m_fy;
|
||||
|
||||
if (isZOnly) {
|
||||
// Z-only格式:转换为正交投影(柱形)
|
||||
// Z-only格式:标准针孔模型反投影
|
||||
for (size_t i = 0; i < m_totalPoints; i++) {
|
||||
int row = i / m_imageWidth;
|
||||
int col = i % m_imageWidth;
|
||||
|
||||
// 读取深度值(单位:毫米)
|
||||
float z = static_cast<float>(cloudShort[i]) * m_zScale;
|
||||
|
||||
// 正交投影:X、Y使用像素坐标(Y轴翻转以修正镜像)
|
||||
cloud->points[i].x = static_cast<float>(col);
|
||||
cloud->points[i].y = static_cast<float>(m_imageHeight - 1 - row);
|
||||
cloud->points[i].x = (col - m_cx) * z * inv_fx;
|
||||
cloud->points[i].y = (row - m_cy) * z * inv_fy;
|
||||
cloud->points[i].z = z;
|
||||
}
|
||||
} else {
|
||||
// XYZ格式:完整的三维坐标
|
||||
// 转换为正交投影(柱形),使用像素坐标作为X、Y
|
||||
// XYZ格式:使用Z值进行针孔模型反投影
|
||||
for (size_t i = 0; i < m_totalPoints; i++) {
|
||||
int row = i / m_imageWidth;
|
||||
int col = i % m_imageWidth;
|
||||
|
||||
// 正交投影:X、Y使用像素坐标(Y轴翻转以修正镜像),Z使用深度值
|
||||
cloud->points[i].x = static_cast<float>(col);
|
||||
cloud->points[i].y = static_cast<float>(m_imageHeight - 1 - row);
|
||||
cloud->points[i].z = static_cast<float>(cloudShort[i * 3 + 2]) * m_zScale;
|
||||
float z = static_cast<float>(cloudShort[i * 3 + 2]) * m_zScale;
|
||||
cloud->points[i].x = (col - m_cx) * z * inv_fx;
|
||||
cloud->points[i].y = (row - m_cy) * z * inv_fy;
|
||||
cloud->points[i].z = z;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -377,11 +377,11 @@ void MainWindow::setupUI()
|
||||
QHBoxLayout *exposureLayout = new QHBoxLayout();
|
||||
m_exposureSlider = new QSlider(Qt::Horizontal, exposureGroup);
|
||||
m_exposureSlider->setRange(100, 100000);
|
||||
m_exposureSlider->setValue(1000);
|
||||
m_exposureSlider->setValue(5980);
|
||||
|
||||
m_exposureSpinBox = new QSpinBox(exposureGroup);
|
||||
m_exposureSpinBox->setRange(100, 100000);
|
||||
m_exposureSpinBox->setValue(1000);
|
||||
m_exposureSpinBox->setValue(5980);
|
||||
m_exposureSpinBox->setMinimumWidth(80);
|
||||
|
||||
exposureLayout->addWidget(m_exposureSlider, 3);
|
||||
@@ -639,6 +639,18 @@ void MainWindow::setupConnections()
|
||||
connect(m_networkManager.get(), &NetworkManager::disconnected, this, &MainWindow::onNetworkDisconnected);
|
||||
connect(m_networkManager.get(), &NetworkManager::dataReceived, this, &MainWindow::onDataReceived);
|
||||
|
||||
// 接收下位机曝光值并同步到UI(连接时触发)
|
||||
connect(m_networkManager.get(), &NetworkManager::exposureReceived, this, [this](int exposureUs) {
|
||||
qDebug() << "[MainWindow] 收到曝光值同步:" << exposureUs << "us";
|
||||
m_exposureSlider->blockSignals(true);
|
||||
m_exposureSpinBox->blockSignals(true);
|
||||
m_exposureSlider->setValue(exposureUs);
|
||||
m_exposureSpinBox->setValue(exposureUs);
|
||||
m_exposureSlider->blockSignals(false);
|
||||
m_exposureSpinBox->blockSignals(false);
|
||||
addLog(QString("同步相机曝光值: %1 μs").arg(exposureUs), "INFO");
|
||||
});
|
||||
|
||||
// GVSP数据信号连接(从NetworkManager)
|
||||
connect(m_networkManager.get(), &NetworkManager::imageReceived, this, &MainWindow::onImageReceived);
|
||||
connect(m_networkManager.get(), &NetworkManager::leftImageReceived, this, &MainWindow::onLeftImageReceived);
|
||||
@@ -658,6 +670,17 @@ void MainWindow::setupConnections()
|
||||
connect(m_deviceScanner.get(), &DeviceScanner::scanProgress, this, &MainWindow::onScanProgress);
|
||||
connect(m_deviceScanner.get(), &DeviceScanner::scanFinished, this, &MainWindow::onScanFinished);
|
||||
|
||||
// 接收下位机曝光值并同步到UI
|
||||
connect(m_deviceScanner.get(), &DeviceScanner::exposureReceived, this, [this](int exposureUs) {
|
||||
m_exposureSlider->blockSignals(true);
|
||||
m_exposureSpinBox->blockSignals(true);
|
||||
m_exposureSlider->setValue(exposureUs);
|
||||
m_exposureSpinBox->setValue(exposureUs);
|
||||
m_exposureSlider->blockSignals(false);
|
||||
m_exposureSpinBox->blockSignals(false);
|
||||
addLog(QString("同步相机曝光值: %1 μs").arg(exposureUs), "INFO");
|
||||
});
|
||||
|
||||
// 设备列表选择连接
|
||||
connect(m_deviceList, &QListWidget::itemClicked, this, &MainWindow::onDeviceSelected);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "gui/PointCloudGLWidget.h"
|
||||
#include <QDebug>
|
||||
#include <QPushButton>
|
||||
#include <cmath>
|
||||
#include <cfloat>
|
||||
|
||||
@@ -24,6 +25,17 @@ PointCloudGLWidget::PointCloudGLWidget(QWidget *parent)
|
||||
, m_colorMode(0) // 默认黑白模式
|
||||
{
|
||||
setMinimumSize(400, 400);
|
||||
|
||||
// 添加重置视角按钮
|
||||
QPushButton *resetBtn = new QPushButton("重置", this);
|
||||
resetBtn->setFixedSize(60, 30);
|
||||
resetBtn->move(10, 10);
|
||||
resetBtn->setStyleSheet(
|
||||
"QPushButton { background-color: rgba(50, 50, 50, 180); color: white; border: 1px solid #555; border-radius: 4px; }"
|
||||
"QPushButton:hover { background-color: rgba(70, 70, 70, 200); }"
|
||||
"QPushButton:pressed { background-color: rgba(40, 40, 40, 220); }"
|
||||
);
|
||||
connect(resetBtn, &QPushButton::clicked, this, &PointCloudGLWidget::resetView);
|
||||
}
|
||||
|
||||
PointCloudGLWidget::~PointCloudGLWidget()
|
||||
@@ -125,9 +137,12 @@ void PointCloudGLWidget::resizeGL(int w, int h)
|
||||
{
|
||||
m_projection.setToIdentity();
|
||||
|
||||
// 使用透视投影,从相机原点看向Z轴正方向
|
||||
// 使用正交投影,避免透视变形
|
||||
float aspect = float(w) / float(h);
|
||||
m_projection.perspective(m_fov, aspect, 1.0f, 50000.0f);
|
||||
float orthoSize = m_viewDistance * 0.5f / m_zoom;
|
||||
m_projection.ortho(-orthoSize * aspect, orthoSize * aspect,
|
||||
-orthoSize, orthoSize,
|
||||
-50000.0f, 50000.0f);
|
||||
}
|
||||
|
||||
void PointCloudGLWidget::paintGL()
|
||||
@@ -138,10 +153,13 @@ void PointCloudGLWidget::paintGL()
|
||||
return;
|
||||
}
|
||||
|
||||
// 重新计算透视投影矩阵
|
||||
// 重新计算正交投影矩阵
|
||||
m_projection.setToIdentity();
|
||||
float aspect = float(width()) / float(height());
|
||||
m_projection.perspective(m_fov / m_zoom, aspect, 1.0f, 50000.0f);
|
||||
float orthoSize = m_viewDistance * 0.5f / m_zoom;
|
||||
m_projection.ortho(-orthoSize * aspect, orthoSize * aspect,
|
||||
-orthoSize, orthoSize,
|
||||
-50000.0f, 50000.0f);
|
||||
|
||||
// 设置view矩阵 - 轨道相机模式(围绕点云中心旋转)
|
||||
m_view.setToIdentity();
|
||||
@@ -238,9 +256,9 @@ void PointCloudGLWidget::updatePointCloud(pcl::PointCloud<pcl::PointXYZ>::Ptr cl
|
||||
float minZ = FLT_MAX, maxZ = -FLT_MAX;
|
||||
|
||||
for (const auto& point : cloud->points) {
|
||||
if (point.z > 0.01f) {
|
||||
if (point.z > 0.01f) { // 过滤掉无效的零点
|
||||
m_vertices.push_back(point.x);
|
||||
m_vertices.push_back(point.y);
|
||||
m_vertices.push_back(-point.y);
|
||||
m_vertices.push_back(point.z);
|
||||
|
||||
// 更新包围盒
|
||||
@@ -297,6 +315,19 @@ void PointCloudGLWidget::updatePointCloud(pcl::PointCloud<pcl::PointXYZ>::Ptr cl
|
||||
update();
|
||||
}
|
||||
|
||||
void PointCloudGLWidget::resetView()
|
||||
{
|
||||
// 重置所有视角参数到初始状态
|
||||
m_rotationX = 0.0f;
|
||||
m_rotationY = 0.0f;
|
||||
m_panOffset = QVector3D(0.0f, 0.0f, 0.0f);
|
||||
m_zoom = 1.0f;
|
||||
m_firstFrame = true; // 标记为首帧,下次更新时会重新计算视角
|
||||
|
||||
update();
|
||||
qDebug() << "[PointCloudGLWidget] 视角已重置";
|
||||
}
|
||||
|
||||
void PointCloudGLWidget::updateBuffers()
|
||||
{
|
||||
if (m_vertices.empty() || !m_vao || !m_vertexBuffer) {
|
||||
|
||||
@@ -33,7 +33,7 @@ int main(int argc, char *argv[])
|
||||
// 设置应用程序信息
|
||||
app.setOrganizationName("Viewer");
|
||||
app.setApplicationName("Viewer");
|
||||
app.setApplicationVersion("0.3.0");
|
||||
app.setApplicationVersion("0.3.2");
|
||||
|
||||
// 初始化Logger(在可执行文件同目录下)
|
||||
QString logPath = QCoreApplication::applicationDirPath() + "/viewer.log";
|
||||
|
||||
Reference in New Issue
Block a user