update: 扩大ip搜索范围,修改点云可视化逻辑,添加上位机与下位机曝光值同步

This commit is contained in:
2026-02-06 14:47:42 +08:00
parent 03c7cb58da
commit 04a5ec269f
12 changed files with 204 additions and 59 deletions

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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());

View File

@@ -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:

View File

@@ -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;
}
}