feat:WorkpieceRotor

-增加了logo

  -修改了软件名称
This commit is contained in:
YangSn 2026-10-08 11:10:11 +08:00
parent 5548168418
commit 15ba37d83c
57 changed files with 5721 additions and 915 deletions

View File

@ -194,6 +194,7 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
if (debugElement)
{
debugElement->QueryBoolAttribute("enableDebug", &configResult.debugParam.enableDebug);
debugElement->QueryBoolAttribute("crash", &configResult.debugParam.crash);
debugElement->QueryBoolAttribute("saveDebugImage", &configResult.debugParam.saveDebugImage);
debugElement->QueryBoolAttribute("savePointCloud", &configResult.debugParam.savePointCloud);
debugElement->QueryBoolAttribute("printDetailLog", &configResult.debugParam.printDetailLog);
@ -385,6 +386,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
// 3. 保存调试参数
XMLElement* debugElement = doc.NewElement("DebugParam");
debugElement->SetAttribute("enableDebug", configResult.debugParam.enableDebug);
debugElement->SetAttribute("crash", configResult.debugParam.crash);
debugElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud);
debugElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage);
debugElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog);

View File

@ -1,407 +1,200 @@
#include "VrBeltTearingConfig.h"
#include "IVrBeltTearingConfig.h"
#include <algorithm>
#include <sstream>
#include "VrLog.h"
#include <QFile>
#include <QTextStream>
#include <QString>
#include <QTextCodec>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include "tinyxml2.h"
VrBeltTearingConfig::VrBeltTearingConfig()
: m_notify(nullptr)
using namespace tinyxml2;
namespace
{
const char* attr(const XMLElement* node, const char* name, const char* fallback = "")
{
const char* value = node ? node->Attribute(name) : nullptr;
return value ? value : fallback;
}
}
VrBeltTearingConfig::~VrBeltTearingConfig()
VrBeltTearingConfig::VrBeltTearingConfig() : m_notify(nullptr) {}
VrBeltTearingConfig::~VrBeltTearingConfig() = default;
bool IVrBeltTearingConfig::CreateInstance(IVrBeltTearingConfig** output)
{
}
// Also add the static CreateInstance method to the interface class
bool IVrBeltTearingConfig::CreateInstance(IVrBeltTearingConfig** ppVrConfig)
{
if (!ppVrConfig) {
return false;
}
*ppVrConfig = new VrBeltTearingConfig();
if (!output) return false;
*output = new VrBeltTearingConfig();
return true;
}
BeltTearingConfigResult VrBeltTearingConfig::LoadConfig(const std::string& filePath)
{
BeltTearingConfigResult result;
// 使用QString处理可能包含中文的路径
QString qFilePath = QString::fromStdString(filePath);
QFile file(qFilePath);
// 检查文件是否存在并可读
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
LOG_DEBUG("Failed to open file: %s\n", filePath.c_str());
return result;
XMLDocument document;
if (document.LoadFile(filePath.c_str()) != XML_SUCCESS) return result;
const XMLElement* root = document.FirstChildElement("BeltTearingConfig");
if (!root) return BeltTearingConfigResult();
const XMLElement* servers = root->FirstChildElement("ClientServers");
for (const XMLElement* node = servers ? servers->FirstChildElement("Server") : nullptr; node; node = node->NextSiblingElement("Server"))
{
ServerInfo server;
server.name = attr(node, "name");
server.ip = attr(node, "ip");
server.port = node->IntAttribute("port", 5900);
if (server.port <= 0 || server.port > 65535) server.port = 5900;
result.servers.push_back(server);
}
// 使用QXmlStreamReader解析XML内容
QXmlStreamReader xml(&file);
// 读取到根元素
if (xml.readNextStartElement()) {
if (xml.name() != "BeltTearingConfig") {
xml.raiseError(QObject::tr("Not a BeltTearingConfig file"));
}
} else {
xml.raiseError(QObject::tr("Failed to read root element"));
const XMLElement* algorithm = root->FirstChildElement("AlgorithmParams");
const XMLElement* tearing = algorithm ? algorithm->FirstChildElement("BeltTearingParam") : nullptr;
if (tearing)
{
BeltTearingParam& params = result.algorithmParams.beltTearingParam;
params.scanXScale = tearing->DoubleAttribute("ScanXScale", params.scanXScale);
params.scanYScale = tearing->DoubleAttribute("ScanYScale", params.scanYScale);
params.differnceBinTh = tearing->DoubleAttribute("DiffernceBinTh", params.differnceBinTh);
params.tearingMinLen = tearing->DoubleAttribute("TearingMinLen", params.tearingMinLen);
params.tearingMinWidthY = tearing->DoubleAttribute("TearingMinWidthY", params.tearingMinWidthY);
params.tearingMinDepthZ = tearing->DoubleAttribute("TearingMinDepthZ", params.tearingMinDepthZ);
params.tearingMinGap = tearing->DoubleAttribute("TearingMinGap", params.tearingMinGap);
params.sameGapTh = tearing->DoubleAttribute("SameGapTh", params.sameGapTh);
params.gapChkWin = tearing->IntAttribute("GapChkWin", params.gapChkWin);
}
// 解析XML内容
while (!xml.atEnd() && !xml.hasError()) {
xml.readNext();
// 解析服务器配置
if (xml.isStartElement() && xml.name() == "ClientServers") {
while (xml.readNextStartElement()) {
if (xml.name() == "Server") {
ServerInfo server;
server.name = xml.attributes().value("name").toString().toStdString(); server.ip = xml.attributes().value("ip").toString().toStdString();
server.port = xml.attributes().value("port").toInt();
if (server.port <= 0 || server.port > 65535) {
server.port = 5900;
}
result.servers.push_back(server);
xml.skipCurrentElement();
}
}
}
// 解析算法参数
else if (xml.isStartElement() && xml.name() == "AlgorithmParams") {
while (xml.readNextStartElement()) {
// 皮带撕裂参数
if (xml.name() == "BeltTearingParam") {
// 读取简化的SDK兼容参数
result.algorithmParams.beltTearingParam.scanXScale =
xml.attributes().value("ScanXScale").toDouble();
result.algorithmParams.beltTearingParam.scanYScale =
xml.attributes().value("ScanYScale").toDouble();
result.algorithmParams.beltTearingParam.differnceBinTh =
xml.attributes().value("DiffernceBinTh").toDouble();
result.algorithmParams.beltTearingParam.tearingMinLen =
xml.attributes().value("TearingMinLen").toDouble();
result.algorithmParams.beltTearingParam.tearingMinWidthY =
xml.attributes().value("TearingMinWidthY").toDouble();
result.algorithmParams.beltTearingParam.tearingMinDepthZ =
xml.attributes().value("TearingMinDepthZ").toDouble();
result.algorithmParams.beltTearingParam.tearingMinGap =
xml.attributes().value("TearingMinGap").toDouble();
result.algorithmParams.beltTearingParam.sameGapTh =
xml.attributes().value("SameGapTh").toDouble();
result.algorithmParams.beltTearingParam.gapChkWin =
xml.attributes().value("GapChkWin").toInt();
// 设置默认值(如果参数不存在)
if (result.algorithmParams.beltTearingParam.scanXScale == 0.0) {
result.algorithmParams.beltTearingParam.scanXScale = 1.0;
}
if (result.algorithmParams.beltTearingParam.scanYScale == 0.0) {
result.algorithmParams.beltTearingParam.scanYScale = 1.0;
}
if (result.algorithmParams.beltTearingParam.differnceBinTh == 0.0) {
result.algorithmParams.beltTearingParam.differnceBinTh = 1.0;
}
if (result.algorithmParams.beltTearingParam.tearingMinLen == 0.0) {
result.algorithmParams.beltTearingParam.tearingMinLen = 5.0;
}
if (result.algorithmParams.beltTearingParam.tearingMinGap == 0.0) {
result.algorithmParams.beltTearingParam.tearingMinGap = 2.0;
}
if (result.algorithmParams.beltTearingParam.sameGapTh == 0.0) {
result.algorithmParams.beltTearingParam.sameGapTh = 2.0;
}
if (result.algorithmParams.beltTearingParam.gapChkWin == 0) {
result.algorithmParams.beltTearingParam.gapChkWin = 5;
}
xml.skipCurrentElement();
}
// 监控参数
else if (xml.name() == "MonitoringParam") {
result.algorithmParams.monitoringParam.checkInterval =
xml.attributes().value("CheckInterval").toInt();
result.algorithmParams.monitoringParam.alertThreshold =
xml.attributes().value("AlertThreshold").toDouble();
// 向后兼容:忽略不再使用的参数
// maxHistorySize 已移除,不再读取
xml.skipCurrentElement();
}
// 跳过不再使用的图像处理参数
else if (xml.name() == "ImageProcessingParam") {
// 向后兼容:跳过图像处理参数,不再使用
xml.skipCurrentElement();
}
else {
xml.skipCurrentElement();
}
}
}
// 解析调试参数
else if (xml.isStartElement() && xml.name() == "DebugParam") {
result.debugParam.enableDebug = xml.attributes().value("enableDebug").toInt();
result.debugParam.saveDebugImage = xml.attributes().value("saveDebugImage").toInt();
result.debugParam.printDetailLog = xml.attributes().value("printDetailLog").toInt();
result.debugParam.debugOutputPath = xml.attributes().value("debugOutputPath").toString().toStdString();
xml.skipCurrentElement();
}
// 解析队列处理参数
else if (xml.isStartElement() && xml.name() == "QueueProcessParam") {
int maxQueueSize = xml.attributes().value("maxQueueSize").toInt();
int generationInterval = xml.attributes().value("generationInterval").toInt();
int imageLineCount = xml.attributes().value("imageLineCount").toInt();
// 设置默认值(如果参数不存在或为0)
result.queueProcessParam.maxQueueSize = (maxQueueSize > 0) ? maxQueueSize : 300;
result.queueProcessParam.generationInterval = (generationInterval > 0) ? generationInterval : 100;
result.queueProcessParam.imageLineCount = (imageLineCount > 0) ? imageLineCount : 300;
xml.skipCurrentElement();
}
// 解析TCP点云发送参数。抽样仅用于发送,不影响算法和成像队列。
else if (xml.isStartElement() && xml.name() == "PointCloudSendParam") {
const QString enabled = xml.attributes().value("enabled").toString().toLower();
const int lineStep = xml.attributes().value("lineStep").toInt();
const int pointStep = xml.attributes().value("pointStep").toInt();
result.pointCloudSendParam.enabled = enabled.isEmpty() || enabled == "true" || enabled == "1";
result.pointCloudSendParam.lineStep = (lineStep > 0) ? lineStep : 1;
result.pointCloudSendParam.pointStep = (pointStep > 0) ? pointStep : 1;
xml.skipCurrentElement();
}
// 解析项目类型
else if (xml.isStartElement() && xml.name() == "ProjectType") {
QString typeStr = xml.attributes().value("type").toString();
if (typeStr == "BeltMonitoring") {
result.projectType = BeltTearingProjectType::BeltMonitoring;
} else {
result.projectType = BeltTearingProjectType::BeltTearing;
}
xml.skipCurrentElement();
}
// 解析服务端配置
else if (xml.isStartElement() && xml.name() == "LocalServerConfig") {
while (xml.readNextStartElement()) {
if (xml.name() == "ServerPort") {
result.serverPort = xml.attributes().value("port").toInt();
xml.skipCurrentElement();
} else if (xml.name() == "TcpPort") {
result.tcpPort = xml.attributes().value("port").toInt();
if (result.tcpPort == 0) {
result.tcpPort = 5800; // 默认值
}
xml.skipCurrentElement();
} else if (xml.name() == "ModbusTCPProtocol") {
// 解析ModbusTCP协议类型,默认为简化协议
QString protocolStr = xml.attributes().value("type").toString().toLower();
if (protocolStr == "standard") {
result.modbusTCPProtocol = ModbusTCPProtocolType::Standard;
} else {
result.modbusTCPProtocol = ModbusTCPProtocolType::Simplified; // 默认简化协议
}
xml.skipCurrentElement();
} else {
xml.skipCurrentElement();
}
}
}
// 解析相机配置
else if (xml.isStartElement() && xml.name() == "Cameras") {
while (xml.readNextStartElement()) {
if (xml.name() == "Camera") {
CameraParam camera;
camera.name = xml.attributes().value("name").toString().toStdString();
camera.cameraIP = xml.attributes().value("ip").toString().toStdString();
result.cameras.push_back(camera);
xml.skipCurrentElement();
}
}
}
// 解析串口配置(用于 Modbus RTU)
else if (xml.isStartElement() && xml.name() == "SerialPort") {
QString portName = xml.attributes().value("portName").toString();
int baudRate = xml.attributes().value("baudRate").toInt();
QString parity = xml.attributes().value("parity").toString();
int dataBits = xml.attributes().value("dataBits").toInt();
int stopBits = xml.attributes().value("stopBits").toInt();
// 如果配置文件中有串口配置,则使用配置值
if (!portName.isEmpty()) {
result.serialPortParam.portName = portName.toStdString();
}
if (baudRate > 0) {
result.serialPortParam.baudRate = baudRate;
}
if (!parity.isEmpty() && parity.length() > 0) {
result.serialPortParam.parity = parity.at(0).toLatin1();
}
if (dataBits > 0) {
result.serialPortParam.dataBits = dataBits;
}
if (stopBits > 0) {
result.serialPortParam.stopBits = stopBits;
}
xml.skipCurrentElement();
}
const XMLElement* monitoring = algorithm ? algorithm->FirstChildElement("MonitoringParam") : nullptr;
if (monitoring)
{
result.algorithmParams.monitoringParam.checkInterval = monitoring->IntAttribute("CheckInterval", result.algorithmParams.monitoringParam.checkInterval);
result.algorithmParams.monitoringParam.alertThreshold = monitoring->DoubleAttribute("AlertThreshold", result.algorithmParams.monitoringParam.alertThreshold);
}
file.close();
// 检查解析错误
if (xml.hasError()) {
LOG_ERROR("XML parsing error: %s\n", xml.errorString().toStdString().c_str());
return BeltTearingConfigResult(); // 返回空结果
const XMLElement* debug = root->FirstChildElement("DebugParam");
if (debug)
{
result.debugParam.enableDebug = debug->BoolAttribute("enableDebug", result.debugParam.enableDebug);
result.debugParam.saveDebugImage = debug->BoolAttribute("saveDebugImage", result.debugParam.saveDebugImage);
result.debugParam.printDetailLog = debug->BoolAttribute("printDetailLog", result.debugParam.printDetailLog);
result.debugParam.debugOutputPath = attr(debug, "debugOutputPath");
}
const XMLElement* queue = root->FirstChildElement("QueueProcessParam");
if (queue)
{
result.queueProcessParam.maxQueueSize = queue->IntAttribute("maxQueueSize", result.queueProcessParam.maxQueueSize);
result.queueProcessParam.generationInterval = queue->IntAttribute("generationInterval", result.queueProcessParam.generationInterval);
result.queueProcessParam.imageLineCount = queue->IntAttribute("imageLineCount", result.queueProcessParam.imageLineCount);
}
const XMLElement* pointCloud = root->FirstChildElement("PointCloudSendParam");
if (pointCloud)
{
result.pointCloudSendParam.enabled = pointCloud->BoolAttribute("enabled", result.pointCloudSendParam.enabled);
result.pointCloudSendParam.lineStep = pointCloud->IntAttribute("lineStep", result.pointCloudSendParam.lineStep);
result.pointCloudSendParam.pointStep = pointCloud->IntAttribute("pointStep", result.pointCloudSendParam.pointStep);
}
const XMLElement* project = root->FirstChildElement("ProjectType");
if (project && std::string(attr(project, "type")) == "BeltMonitoring") result.projectType = BeltTearingProjectType::BeltMonitoring;
const XMLElement* local = root->FirstChildElement("LocalServerConfig");
if (local)
{
const XMLElement* serverPort = local->FirstChildElement("ServerPort");
const XMLElement* tcpPort = local->FirstChildElement("TcpPort");
const XMLElement* protocol = local->FirstChildElement("ModbusTCPProtocol");
if (serverPort) result.serverPort = serverPort->IntAttribute("port", result.serverPort);
if (tcpPort) result.tcpPort = tcpPort->IntAttribute("port", result.tcpPort);
if (protocol && std::string(attr(protocol, "type")) == "standard") result.modbusTCPProtocol = ModbusTCPProtocolType::Standard;
}
const XMLElement* cameras = root->FirstChildElement("Cameras");
for (const XMLElement* node = cameras ? cameras->FirstChildElement("Camera") : nullptr; node; node = node->NextSiblingElement("Camera"))
{
CameraParam camera;
camera.name = attr(node, "name");
camera.cameraIP = attr(node, "ip");
result.cameras.push_back(camera);
}
const XMLElement* serial = root->FirstChildElement("SerialPort");
if (serial)
{
result.serialPortParam.portName = attr(serial, "portName", result.serialPortParam.portName.c_str());
result.serialPortParam.baudRate = serial->IntAttribute("baudRate", result.serialPortParam.baudRate);
const char* parity = serial->Attribute("parity");
if (parity && parity[0]) result.serialPortParam.parity = parity[0];
result.serialPortParam.dataBits = serial->IntAttribute("dataBits", result.serialPortParam.dataBits);
result.serialPortParam.stopBits = serial->IntAttribute("stopBits", result.serialPortParam.stopBits);
}
return result;
}
bool VrBeltTearingConfig::SaveConfig(const std::string& filePath, BeltTearingConfigResult& configResult)
bool VrBeltTearingConfig::SaveConfig(const std::string& filePath, BeltTearingConfigResult& config)
{
// 使用QString处理可能包含中文的路径
QString qFilePath = QString::fromStdString(filePath);
QFile file(qFilePath);
// 打开文件进行写入
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
LOG_DEBUG("Failed to open file for writing: %s\n", filePath.c_str());
return false;
}
// 使用QXmlStreamWriter写入XML内容
QXmlStreamWriter xml(&file);
xml.setAutoFormatting(true);
xml.setCodec("UTF-8"); // Explicitly set UTF-8 encoding to handle Chinese characters correctly
xml.writeStartDocument();
// 设置编码为UTF-8以正确处理中文
xml.writeStartElement("BeltTearingConfig");
// 保存服务器配置
if (!configResult.servers.empty()) {
xml.writeStartElement("ClientServers");
for (const auto& server : configResult.servers) {
xml.writeStartElement("Server");
xml.writeAttribute("name", QString::fromStdString(server.name));
xml.writeAttribute("ip", QString::fromStdString(server.ip));
xml.writeAttribute("port", QString::number(server.port));
xml.writeEndElement(); // Server
XMLDocument document;
document.InsertEndChild(document.NewDeclaration("xml version=\"1.0\" encoding=\"UTF-8\""));
XMLElement* root = document.NewElement("BeltTearingConfig");
document.InsertEndChild(root);
if (!config.servers.empty())
{
XMLElement* servers = document.NewElement("ClientServers");
root->InsertEndChild(servers);
for (const ServerInfo& server : config.servers)
{
XMLElement* node = document.NewElement("Server");
node->SetAttribute("name", server.name.c_str());
node->SetAttribute("ip", server.ip.c_str());
node->SetAttribute("port", server.port);
servers->InsertEndChild(node);
}
xml.writeEndElement(); // ClientServers
}
// 保存算法参数
xml.writeStartElement("AlgorithmParams");
// 皮带撕裂参数 - 简化版本
xml.writeStartElement("BeltTearingParam");
xml.writeAttribute("ScanXScale", QString::number(configResult.algorithmParams.beltTearingParam.scanXScale));
xml.writeAttribute("ScanYScale", QString::number(configResult.algorithmParams.beltTearingParam.scanYScale));
xml.writeAttribute("DiffernceBinTh", QString::number(configResult.algorithmParams.beltTearingParam.differnceBinTh));
xml.writeAttribute("TearingMinLen", QString::number(configResult.algorithmParams.beltTearingParam.tearingMinLen));
xml.writeAttribute("TearingMinWidthY", QString::number(configResult.algorithmParams.beltTearingParam.tearingMinWidthY));
xml.writeAttribute("TearingMinDepthZ", QString::number(configResult.algorithmParams.beltTearingParam.tearingMinDepthZ));
xml.writeAttribute("TearingMinGap", QString::number(configResult.algorithmParams.beltTearingParam.tearingMinGap));
xml.writeAttribute("SameGapTh", QString::number(configResult.algorithmParams.beltTearingParam.sameGapTh));
xml.writeAttribute("GapChkWin", QString::number(configResult.algorithmParams.beltTearingParam.gapChkWin));
xml.writeEndElement(); // BeltTearingParam
// 监控参数
xml.writeStartElement("MonitoringParam");
xml.writeAttribute("CheckInterval", QString::number(configResult.algorithmParams.monitoringParam.checkInterval));
xml.writeAttribute("AlertThreshold", QString::number(configResult.algorithmParams.monitoringParam.alertThreshold));
xml.writeEndElement(); // MonitoringParam
xml.writeEndElement(); // AlgorithmParams
// 保存调试参数
xml.writeStartElement("DebugParam");
xml.writeAttribute("enableDebug", QString::number(configResult.debugParam.enableDebug));
xml.writeAttribute("saveDebugImage", QString::number(configResult.debugParam.saveDebugImage));
xml.writeAttribute("printDetailLog", QString::number(configResult.debugParam.printDetailLog));
xml.writeAttribute("debugOutputPath", QString::fromStdString(configResult.debugParam.debugOutputPath));
xml.writeEndElement(); // DebugParam
// 保存队列处理参数
xml.writeStartElement("QueueProcessParam");
xml.writeAttribute("maxQueueSize", QString::number(configResult.queueProcessParam.maxQueueSize));
xml.writeAttribute("generationInterval", QString::number(configResult.queueProcessParam.generationInterval));
xml.writeAttribute("imageLineCount", QString::number(configResult.queueProcessParam.imageLineCount));
xml.writeEndElement(); // QueueProcessParam
// 保存TCP点云发送参数
xml.writeStartElement("PointCloudSendParam");
xml.writeAttribute("enabled", configResult.pointCloudSendParam.enabled ? "true" : "false");
xml.writeAttribute("lineStep", QString::number(configResult.pointCloudSendParam.lineStep));
xml.writeAttribute("pointStep", QString::number(configResult.pointCloudSendParam.pointStep));
xml.writeEndElement(); // PointCloudSendParam
// 保存项目类型
xml.writeStartElement("ProjectType");
QString typeStr = (configResult.projectType == BeltTearingProjectType::BeltMonitoring) ? "BeltMonitoring" : "BeltTearing";
xml.writeAttribute("type", typeStr);
xml.writeEndElement(); // ProjectType
// 保存服务端配置
xml.writeStartElement("LocalServerConfig");
xml.writeStartElement("ServerPort");
xml.writeAttribute("port", QString::number(configResult.serverPort));
xml.writeEndElement(); // ServerPort
xml.writeStartElement("TcpPort");
xml.writeAttribute("port", QString::number(configResult.tcpPort));
xml.writeEndElement(); // TcpPort
// 保存ModbusTCP协议类型
xml.writeStartElement("ModbusTCPProtocol");
QString protocolTypeStr = (configResult.modbusTCPProtocol == ModbusTCPProtocolType::Standard) ? "standard" : "simplified";
xml.writeAttribute("type", protocolTypeStr);
xml.writeEndElement(); // ModbusTCPProtocol
xml.writeEndElement(); // LocalServerConfig
// 保存相机配置
xml.writeStartElement("Cameras");
for (const auto& camera : configResult.cameras) {
xml.writeStartElement("Camera");
xml.writeAttribute("name", QString::fromStdString(camera.name));
xml.writeAttribute("ip", QString::fromStdString(camera.cameraIP));
xml.writeEndElement(); // Camera
XMLElement* algorithm = document.NewElement("AlgorithmParams");
root->InsertEndChild(algorithm);
XMLElement* tearing = document.NewElement("BeltTearingParam");
algorithm->InsertEndChild(tearing);
const BeltTearingParam& params = config.algorithmParams.beltTearingParam;
tearing->SetAttribute("ScanXScale", params.scanXScale);
tearing->SetAttribute("ScanYScale", params.scanYScale);
tearing->SetAttribute("DiffernceBinTh", params.differnceBinTh);
tearing->SetAttribute("TearingMinLen", params.tearingMinLen);
tearing->SetAttribute("TearingMinWidthY", params.tearingMinWidthY);
tearing->SetAttribute("TearingMinDepthZ", params.tearingMinDepthZ);
tearing->SetAttribute("TearingMinGap", params.tearingMinGap);
tearing->SetAttribute("SameGapTh", params.sameGapTh);
tearing->SetAttribute("GapChkWin", params.gapChkWin);
XMLElement* monitoring = document.NewElement("MonitoringParam");
algorithm->InsertEndChild(monitoring);
monitoring->SetAttribute("CheckInterval", config.algorithmParams.monitoringParam.checkInterval);
monitoring->SetAttribute("AlertThreshold", config.algorithmParams.monitoringParam.alertThreshold);
XMLElement* debug = document.NewElement("DebugParam");
root->InsertEndChild(debug);
debug->SetAttribute("enableDebug", config.debugParam.enableDebug);
debug->SetAttribute("saveDebugImage", config.debugParam.saveDebugImage);
debug->SetAttribute("printDetailLog", config.debugParam.printDetailLog);
debug->SetAttribute("debugOutputPath", config.debugParam.debugOutputPath.c_str());
XMLElement* queue = document.NewElement("QueueProcessParam");
root->InsertEndChild(queue);
queue->SetAttribute("maxQueueSize", config.queueProcessParam.maxQueueSize);
queue->SetAttribute("generationInterval", config.queueProcessParam.generationInterval);
queue->SetAttribute("imageLineCount", config.queueProcessParam.imageLineCount);
XMLElement* pointCloud = document.NewElement("PointCloudSendParam");
root->InsertEndChild(pointCloud);
pointCloud->SetAttribute("enabled", config.pointCloudSendParam.enabled ? "true" : "false");
pointCloud->SetAttribute("lineStep", config.pointCloudSendParam.lineStep);
pointCloud->SetAttribute("pointStep", config.pointCloudSendParam.pointStep);
XMLElement* project = document.NewElement("ProjectType");
root->InsertEndChild(project);
project->SetAttribute("type", config.projectType == BeltTearingProjectType::BeltMonitoring ? "BeltMonitoring" : "BeltTearing");
XMLElement* local = document.NewElement("LocalServerConfig");
root->InsertEndChild(local);
XMLElement* serverPort = document.NewElement("ServerPort"); serverPort->SetAttribute("port", config.serverPort); local->InsertEndChild(serverPort);
XMLElement* tcpPort = document.NewElement("TcpPort"); tcpPort->SetAttribute("port", config.tcpPort); local->InsertEndChild(tcpPort);
XMLElement* protocol = document.NewElement("ModbusTCPProtocol"); protocol->SetAttribute("type", config.modbusTCPProtocol == ModbusTCPProtocolType::Standard ? "standard" : "simplified"); local->InsertEndChild(protocol);
XMLElement* cameras = document.NewElement("Cameras"); root->InsertEndChild(cameras);
for (const CameraParam& camera : config.cameras)
{
XMLElement* node = document.NewElement("Camera"); node->SetAttribute("name", camera.name.c_str()); node->SetAttribute("ip", camera.cameraIP.c_str()); cameras->InsertEndChild(node);
}
xml.writeEndElement(); // Cameras
// 保存串口配置(用于 Modbus RTU)
xml.writeStartElement("SerialPort");
xml.writeAttribute("portName", QString::fromStdString(configResult.serialPortParam.portName));
xml.writeAttribute("baudRate", QString::number(configResult.serialPortParam.baudRate));
xml.writeAttribute("parity", QString(QChar(configResult.serialPortParam.parity)));
xml.writeAttribute("dataBits", QString::number(configResult.serialPortParam.dataBits));
xml.writeAttribute("stopBits", QString::number(configResult.serialPortParam.stopBits));
xml.writeEndElement(); // SerialPort
xml.writeEndElement(); // BeltTearingConfig
xml.writeEndDocument();
file.close();
// 通知配置改变
if (m_notify) {
m_notify->OnConfigChanged(configResult);
}
XMLElement* serial = document.NewElement("SerialPort");
root->InsertEndChild(serial);
serial->SetAttribute("portName", config.serialPortParam.portName.c_str());
serial->SetAttribute("baudRate", config.serialPortParam.baudRate);
serial->SetAttribute("parity", std::string(1, config.serialPortParam.parity).c_str());
serial->SetAttribute("dataBits", config.serialPortParam.dataBits);
serial->SetAttribute("stopBits", config.serialPortParam.stopBits);
if (document.SaveFile(filePath.c_str()) != XML_SUCCESS) return false;
if (m_notify) m_notify->OnConfigChanged(config);
return true;
}

View File

@ -389,6 +389,10 @@ ConfigResult CVrConfig::LoadConfig(const std::string& filePath)
result.debugParam.saveDebugImage = debugParamElement->BoolAttribute("saveDebugImage");
if (debugParamElement->Attribute("printDetailLog"))
result.debugParam.printDetailLog = debugParamElement->BoolAttribute("printDetailLog");
if (debugParamElement->Attribute("crash"))
{
result.debugParam.crash = debugParamElement->BoolAttribute("crash");
}
if (debugParamElement->Attribute("debugOutputPath"))
result.debugParam.debugOutputPath = debugParamElement->Attribute("debugOutputPath");
}
@ -740,6 +744,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
debugParamElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud);
debugParamElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage);
debugParamElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog);
debugParamElement->SetAttribute("crash", configResult.debugParam.crash);
debugParamElement->SetAttribute("debugOutputPath", configResult.debugParam.debugOutputPath.c_str());
root->InsertEndChild(debugParamElement);

View File

@ -247,6 +247,10 @@ ConfigResult CVrConfig::LoadConfig(const std::string& filePath)
XMLElement* debugParamElement = root->FirstChildElement("DebugParam");
if (debugParamElement)
{
if (debugParamElement->Attribute("crash"))
{
result.debugParam.crash = debugParamElement->BoolAttribute("crash");
}
if (debugParamElement->Attribute("enableDebug"))
result.debugParam.enableDebug = debugParamElement->BoolAttribute("enableDebug");
if (debugParamElement->Attribute("savePointCloud"))
@ -404,6 +408,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
// 添加调试参数(在AlgorithmParams外面)
XMLElement* debugParamElement = doc.NewElement("DebugParam");
debugParamElement->SetAttribute("enableDebug", configResult.debugParam.enableDebug);
debugParamElement->SetAttribute("crash", configResult.debugParam.crash);
debugParamElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud);
debugParamElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage);
debugParamElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog);

View File

@ -710,6 +710,7 @@ void LoadDebugParam(XMLElement* root, VrDebugParam& param)
}
param.enableDebug = element->BoolAttribute("enableDebug", param.enableDebug);
param.crash = element->BoolAttribute("crash", param.crash);
param.savePointCloud = element->BoolAttribute("savePointCloud", param.savePointCloud);
param.saveDebugImage = element->BoolAttribute("saveDebugImage", param.saveDebugImage);
param.printDetailLog = element->BoolAttribute("printDetailLog", param.printDetailLog);
@ -720,6 +721,7 @@ void SaveDebugParam(XMLDocument& doc, XMLElement* root, const VrDebugParam& para
{
XMLElement* element = doc.NewElement("DebugParam");
element->SetAttribute("enableDebug", param.enableDebug);
element->SetAttribute("crash", param.crash);
element->SetAttribute("savePointCloud", param.savePointCloud);
element->SetAttribute("saveDebugImage", param.saveDebugImage);
element->SetAttribute("printDetailLog", param.printDetailLog);

View File

@ -281,6 +281,10 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
XMLElement* debugParamElement = root->FirstChildElement("DebugParam");
if (debugParamElement)
{
if (debugParamElement->Attribute("crash"))
{
configResult.debugParam.crash = debugParamElement->BoolAttribute("crash");
}
if (debugParamElement->Attribute("enableDebug"))
configResult.debugParam.enableDebug = debugParamElement->BoolAttribute("enableDebug");
if (debugParamElement->Attribute("savePointCloud"))
@ -443,6 +447,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
// 添加调试参数(在AlgorithmParams外面)
XMLElement* debugParamElement = doc.NewElement("DebugParam");
debugParamElement->SetAttribute("enableDebug", configResult.debugParam.enableDebug);
debugParamElement->SetAttribute("crash", configResult.debugParam.crash);
debugParamElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud);
debugParamElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage);
debugParamElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog);

View File

@ -214,6 +214,10 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
XMLElement* debugParamElement = root->FirstChildElement("DebugParam");
if (debugParamElement)
{
if (debugParamElement->Attribute("crash"))
{
configResult.debugParam.crash = debugParamElement->BoolAttribute("crash");
}
if (debugParamElement->Attribute("enableDebug"))
configResult.debugParam.enableDebug = debugParamElement->BoolAttribute("enableDebug");
if (debugParamElement->Attribute("savePointCloud"))
@ -425,6 +429,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
// 添加调试参数(在AlgorithmParams外面)
XMLElement* debugParamElement = doc.NewElement("DebugParam");
debugParamElement->SetAttribute("enableDebug", configResult.debugParam.enableDebug);
debugParamElement->SetAttribute("crash", configResult.debugParam.crash);
debugParamElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud);
debugParamElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage);
debugParamElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog);

View File

@ -327,6 +327,7 @@ struct VrAlgorithmParams
VrFullImageCaptureParam fullImageCaptureParam; // 在线全图二维采集参数
VrBinParam binParam; // 料框检测/默认料框参数
VrRotorCoreParam rotorCoreParam; // 转子钢芯算法参数
std::vector<std::pair<int, VrToolParam>> toolParamsByCamera;
std::vector<VrPointCloudFilterParam> pointCloudFilterParams;
// 显式赋值构造函数,确保正确的深拷贝
@ -341,6 +342,7 @@ struct VrAlgorithmParams
fullImageCaptureParam = other.fullImageCaptureParam;
binParam = other.binParam;
rotorCoreParam = other.rotorCoreParam;
toolParamsByCamera = other.toolParamsByCamera;
pointCloudFilterParams = other.pointCloudFilterParams;
}
return *this;
@ -357,6 +359,7 @@ struct VrAlgorithmParams
, fullImageCaptureParam(other.fullImageCaptureParam)
, binParam(other.binParam)
, rotorCoreParam(other.rotorCoreParam)
, toolParamsByCamera(other.toolParamsByCamera)
, pointCloudFilterParams(other.pointCloudFilterParams)
{
}
@ -370,6 +373,7 @@ struct VrAlgorithmParams
*/
struct ConfigResult
{
std::vector<std::pair<int, VrHandEyeCalibMatrix>> handEyeCalibMatricesByCamera;
std::vector<DeviceInfo> cameraList;
std::vector<DeviceInfo> deviceList;
VrAlgorithmParams algorithmParams; // 算法参数
@ -381,6 +385,7 @@ struct ConfigResult
// 显式赋值构造函数,确保正确的深拷贝
ConfigResult& operator=(const ConfigResult& other) {
if (this != &other) {
handEyeCalibMatricesByCamera = other.handEyeCalibMatricesByCamera;
cameraList = other.cameraList;
deviceList = other.deviceList;
algorithmParams = other.algorithmParams;
@ -394,7 +399,8 @@ struct ConfigResult
// 显式复制构造函数
ConfigResult(const ConfigResult& other)
: cameraList(other.cameraList)
: handEyeCalibMatricesByCamera(other.handEyeCalibMatricesByCamera)
, cameraList(other.cameraList)
, deviceList(other.deviceList)
, algorithmParams(other.algorithmParams)
, handEyeCalibMatrix(other.handEyeCalibMatrix)

View File

@ -55,6 +55,13 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
return LOAD_CONFIG_INVALID_FORMAT;
}
// 重读时清除按相机索引保存的数据,避免旧条目改变相机编号或覆盖新配置。
configResult.cameraList.clear();
configResult.algorithmParams.toolParamsByCamera.clear();
configResult.handEyeCalibMatricesByCamera.clear();
configResult.algorithmParams.toolParam = VrToolParam();
configResult.handEyeCalibMatrix = VrHandEyeCalibMatrix();
// 解析摄像头列表
XMLElement* camerasElement = root->FirstChildElement("Cameras");
if (camerasElement)
@ -138,6 +145,26 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
if (toolParamElement->Attribute("offsetZ"))
configResult.algorithmParams.toolParam.offsetZ = toolParamElement->DoubleAttribute("offsetZ");
}
XMLElement* toolParamsElement = algoParamsElement->FirstChildElement("ToolParamsByCamera");
if (toolParamsElement)
{
for (XMLElement* item = toolParamsElement->FirstChildElement("ToolParam"); item; item = item->NextSiblingElement("ToolParam"))
{
const int cameraIndex = item->IntAttribute("cameraIndex", 0);
VrToolParam value = cameraIndex == 1 ? configResult.algorithmParams.toolParam : VrToolParam();
value.eulerOrder = item->IntAttribute("eulerOrder", value.eulerOrder);
value.rotX = item->DoubleAttribute("rotX", value.rotX);
value.rotY = item->DoubleAttribute("rotY", value.rotY);
value.rotZ = item->DoubleAttribute("rotZ", value.rotZ);
value.offsetX = item->DoubleAttribute("offsetX", value.offsetX);
value.offsetY = item->DoubleAttribute("offsetY", value.offsetY);
value.offsetZ = item->DoubleAttribute("offsetZ", value.offsetZ);
if (cameraIndex > 0)
{
configResult.algorithmParams.toolParamsByCamera.push_back(std::make_pair(cameraIndex, value));
}
}
}
// 类别映射独立于有序的曝光/增益/阈值参数列表。
XMLElement* modelParamElement = algoParamsElement->FirstChildElement("WorkpieceClassMapping");
@ -398,6 +425,10 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
XMLElement* debugParamElement = root->FirstChildElement("DebugParam");
if (debugParamElement)
{
if (debugParamElement->Attribute("crash"))
{
configResult.debugParam.crash = debugParamElement->BoolAttribute("crash");
}
if (debugParamElement->Attribute("enableDebug"))
configResult.debugParam.enableDebug = debugParamElement->BoolAttribute("enableDebug");
if (debugParamElement->Attribute("savePointCloud"))
@ -449,6 +480,32 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
else
configResult.handEyeCalibMatrix.eulerOrder = 11; // 默认外旋ZYX
}
XMLElement* handEyeMatricesElement = root->FirstChildElement("HandEyeCalibMatrices");
if (handEyeMatricesElement)
{
for (XMLElement* item = handEyeMatricesElement->FirstChildElement("HandEyeCalibMatrix");
item;
item = item->NextSiblingElement("HandEyeCalibMatrix"))
{
const int cameraIndex = item->IntAttribute("cameraIndex", 0);
if (cameraIndex <= 0)
{
continue;
}
VrHandEyeCalibMatrix value;
for (int i = 0; i < 16; ++i)
{
char attrName[32];
sprintf(attrName, "m%d", i);
if (item->Attribute(attrName))
{
value.matrix[i] = item->DoubleAttribute(attrName);
}
}
value.eulerOrder = item->IntAttribute("eulerOrder", 11);
configResult.handEyeCalibMatricesByCamera.push_back(std::make_pair(cameraIndex, value));
}
}
// 解析 TCP/IP 文本服务端端口和 Modbus 寄存器配置
XMLElement* tcpServerConfigElement = root->FirstChildElement("TcpServerConfig");
@ -553,6 +610,24 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
toolParamElement->SetAttribute("offsetY", configResult.algorithmParams.toolParam.offsetY);
toolParamElement->SetAttribute("offsetZ", configResult.algorithmParams.toolParam.offsetZ);
algoParamsElement->InsertEndChild(toolParamElement);
if (!configResult.algorithmParams.toolParamsByCamera.empty())
{
XMLElement* list = doc.NewElement("ToolParamsByCamera");
for (const auto& entry : configResult.algorithmParams.toolParamsByCamera)
{
XMLElement* item = doc.NewElement("ToolParam");
item->SetAttribute("cameraIndex", entry.first);
item->SetAttribute("eulerOrder", entry.second.eulerOrder);
item->SetAttribute("rotX", entry.second.rotX);
item->SetAttribute("rotY", entry.second.rotY);
item->SetAttribute("rotZ", entry.second.rotZ);
item->SetAttribute("offsetX", entry.second.offsetX);
item->SetAttribute("offsetY", entry.second.offsetY);
item->SetAttribute("offsetZ", entry.second.offsetZ);
list->InsertEndChild(item);
}
algoParamsElement->InsertEndChild(list);
}
const auto& modelParams = configResult.algorithmParams.modelParams;
const VrWorkpieceModelParam& legacyModelParam = configResult.algorithmParams.modelParam;
@ -689,6 +764,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
// 添加调试参数(在AlgorithmParams外面)
XMLElement* debugParamElement = doc.NewElement("DebugParam");
debugParamElement->SetAttribute("enableDebug", configResult.debugParam.enableDebug);
debugParamElement->SetAttribute("crash", configResult.debugParam.crash);
debugParamElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud);
debugParamElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage);
debugParamElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog);
@ -718,6 +794,24 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
// 保存旋转顺序
handEyeElement->SetAttribute("eulerOrder", configResult.handEyeCalibMatrix.eulerOrder);
root->InsertEndChild(handEyeElement);
if (!configResult.handEyeCalibMatricesByCamera.empty())
{
XMLElement* handEyeMatricesElement = doc.NewElement("HandEyeCalibMatrices");
for (const auto& entry : configResult.handEyeCalibMatricesByCamera)
{
XMLElement* item = doc.NewElement("HandEyeCalibMatrix");
item->SetAttribute("cameraIndex", entry.first);
for (int i = 0; i < 16; ++i)
{
char attrName[32];
sprintf(attrName, "m%d", i);
item->SetAttribute(attrName, entry.second.matrix[i]);
}
item->SetAttribute("eulerOrder", entry.second.eulerOrder);
handEyeMatricesElement->InsertEndChild(item);
}
root->InsertEndChild(handEyeMatricesElement);
}
// 添加 TCP/IP 文本服务端端口和 Modbus 寄存器配置
XMLElement* tcpServerConfigElement = doc.NewElement("TcpServerConfig");

View File

@ -336,16 +336,15 @@ void LogRotorAlgorithmInput(
algorithmParams.toolParam.offsetZ);
LOG_INFO("[Rotor Debug] planeCalib: planeHeight=%.6f\n", groundCalibPara.planeHeight);
LOG_INFO("[Rotor Debug] planeCalib=[%.9f %.9f %.9f; %.9f %.9f %.9f; %.9f %.9f %.9f]\n",
LOG_INFO("[Rotor Debug] planeCalib=[%.6f %.6f %.6f; %.6f %.6f %.6f; %.6f %.6f %.6f]\n",
groundCalibPara.planeCalib[0], groundCalibPara.planeCalib[1], groundCalibPara.planeCalib[2],
groundCalibPara.planeCalib[3], groundCalibPara.planeCalib[4], groundCalibPara.planeCalib[5],
groundCalibPara.planeCalib[6], groundCalibPara.planeCalib[7], groundCalibPara.planeCalib[8]);
LOG_INFO("[Rotor Debug] invRMatrix=[%.9f %.9f %.9f; %.9f %.9f %.9f; %.9f %.9f %.9f]\n",
LOG_INFO("[Rotor Debug] invRMatrix=[%.6f %.6f %.6f; %.6f %.6f %.6f; %.6f %.6f %.6f]\n",
groundCalibPara.invRMatrix[0], groundCalibPara.invRMatrix[1], groundCalibPara.invRMatrix[2],
groundCalibPara.invRMatrix[3], groundCalibPara.invRMatrix[4], groundCalibPara.invRMatrix[5],
groundCalibPara.invRMatrix[6], groundCalibPara.invRMatrix[7], groundCalibPara.invRMatrix[8]);
LOG_INFO("[Rotor Debug] handEye=[%.9f %.9f %.9f %.9f; %.9f %.9f %.9f %.9f; "
"%.9f %.9f %.9f %.9f; %.9f %.9f %.9f %.9f]\n",
LOG_INFO("[Rotor Debug] handEye=[%.6f %.6f %.6f %.6f; %.6f %.6f %.6f %.6f %.6f %.6f %.6f]\n",
clibMatrix[0], clibMatrix[1], clibMatrix[2], clibMatrix[3],
clibMatrix[4], clibMatrix[5], clibMatrix[6], clibMatrix[7],
clibMatrix[8], clibMatrix[9], clibMatrix[10], clibMatrix[11],
@ -714,15 +713,19 @@ int DetectPresenter::DetectStatorWorkpiecePosition(
#ifdef _OUTPUT_DEBUG_DATA
std::vector<std::vector<SVzNL3DPosition>> debugScanLines;
#endif
size_t totalPoints = 0;
for (const auto& line : algorithmData) {
totalPoints += line.size();
if (debugParam.enableDebug && debugParam.printDetailLog)
{
size_t totalPoints = 0;
for (const auto& line : algorithmData)
{
totalPoints += line.size();
}
LOG_INFO("[Rotor] entering wd_HRM_RotorCorePositioning: lines=%zu points=%zu objROIs=%zu "
"spacerLength=%.3f spacerWidth=%.3f radius=%.3f height=%.3f useGroundCalibNormal=%d\n",
algorithmData.size(), totalPoints, objROIs.size(),
spacerParam.dWidth, spacerParam.dHeight,
workpieceParam.radius, workpieceParam.height, algorithmParams.rotorCoreParam.useGroundCalibNormal);
}
LOG_INFO("[Rotor] entering wd_HRM_RotorCorePositioning: lines=%zu points=%zu objROIs=%zu "
"spacerLength=%.3f spacerWidth=%.3f radius=%.3f height=%.3f useGroundCalibNormal=%d\n",
algorithmData.size(), totalPoints, objROIs.size(),
spacerParam.dWidth, spacerParam.dHeight,
workpieceParam.radius, workpieceParam.height, algorithmParams.rotorCoreParam.useGroundCalibNormal);
#if 1
try {
@ -756,9 +759,9 @@ int DetectPresenter::DetectStatorWorkpiecePosition(
}
#endif
if (debugParam.enableDebug && debugParam.printDetailLog) {
LogRawRotorOutputs(workpiecePositions);
}
// if (debugParam.enableDebug && debugParam.printDetailLog) {
// LogRawRotorOutputs(workpiecePositions);
// }
if (algorithmResult != SUCCESS) {
detectionResult.errorCode = algorithmResult;
return algorithmResult;
@ -843,8 +846,11 @@ int DetectPresenter::DetectStatorWorkpiecePosition(
position.hasLeftImagePoint = true;
position.leftImageX = workpiece.imgCenter.x;
position.leftImageY = workpiece.imgCenter.y;
LOG_INFO("Rotor output[%zu] algorithm 2D point: left2D=(%.3f,%.3f)\n",
index, position.leftImageX, position.leftImageY);
if (debugParam.enableDebug && debugParam.printDetailLog)
{
LOG_INFO("Rotor output[%zu] algorithm 2D point: left2D=(%.3f,%.3f)\n",
index, position.leftImageX, position.leftImageY);
}
}
else if (workpiece.workpieceType == kSpacerWorkpieceType)
{
@ -935,9 +941,9 @@ int DetectPresenter::DetectStatorWorkpiecePosition(
position.yaw = robotRpy.yaw;
if (debugParam.enableDebug && debugParam.printDetailLog) {
LOG_INFO("[Rotor Debug] finalOutput[%02zu]: id=%d -> %d, type=%d, value=%.3f, "
"centerRobot=(%.3f,%.3f,%.3f), targetXYZ=(%.3f,%.3f,%.3f), "
"RPY=(%.3f,%.3f,%.3f)\n",
LOG_INFO("[Rotor Debug] final[%02zu]: id=%d->%d,type=%d,value=%.3f, "
"robot=(%.3f,%.3f,%.3f),offset=(%.3f,%.3f,%.3f), "
"rpy=(%.3f,%.3f,%.3f)\n",
index,
workpiece.id,
position.sourceDetectionIndex,

View File

@ -45,6 +45,39 @@ const uint16_t kWorkStatusError = 2;
const int kModbusRegsPerPoint = 14; // workpieceType + reserved + x/y/z/r/p/y
const int kSpacerWorkpieceType = 9;
bool resolveCameraParameters(const ConfigResult& config, int cameraIndex, VrToolParam& toolParam, VrHandEyeCalibMatrix& handEyeMatrix)
{
if (cameraIndex < 1 || cameraIndex > std::max(1, static_cast<int>(config.cameraList.size())))
{
return false;
}
// 旧单份配置只属于相机 1,其他相机必须具备独立的工具参数和手眼矩阵。
bool hasToolParam = cameraIndex == 1;
bool hasHandEyeMatrix = cameraIndex == 1;
if (cameraIndex == 1)
{
toolParam = config.algorithmParams.toolParam;
handEyeMatrix = config.handEyeCalibMatrix;
}
for (const auto& item : config.algorithmParams.toolParamsByCamera)
{
if (item.first == cameraIndex)
{
toolParam = item.second;
hasToolParam = true;
}
}
for (const auto& item : config.handEyeCalibMatricesByCamera)
{
if (item.first == cameraIndex)
{
handEyeMatrix = item.second;
hasHandEyeMatrix = true;
}
}
return hasToolParam && hasHandEyeMatrix;
}
struct ModbusCoordinateData
{
uint16_t workpieceType = 0;
@ -647,6 +680,15 @@ int StatorWorkpiecePositionPresenter::StartWorkpieceDetection(int cameraIndex, b
if (resolvedCameraIndex <= 0) {
resolvedCameraIndex = 1;
}
VrToolParam toolParam;
VrHandEyeCalibMatrix handEyeMatrix;
if (!resolveCameraParameters(configResult, resolvedCameraIndex, toolParam, handEyeMatrix))
{
OnStatusUpdate(QString("相机 %1 的工具参数或手眼标定配置缺失,请先配置并保存。")
.arg(resolvedCameraIndex).toStdString());
FinishDetectionCycle();
return ERR_CODE(DEV_ARG_INVAILD);
}
m_detectionCameraIndex.store(resolvedCameraIndex);
m_autoStopHandled.store(false);
@ -748,6 +790,20 @@ int StatorWorkpiecePositionPresenter::LoadDebugImageAndDataAndDetect(
return ERR_CODE(DEV_ARG_INVAILD);
}
int cameraIndex = GetDetectIndex();
if (cameraIndex <= 0)
{
cameraIndex = 1;
}
VrToolParam toolParam;
VrHandEyeCalibMatrix handEyeMatrix;
if (!resolveCameraParameters(configResult, cameraIndex, toolParam, handEyeMatrix))
{
OnStatusUpdate(QString("相机 %1 的工具参数或手眼标定配置缺失,请先配置并保存。")
.arg(cameraIndex).toStdString());
return ERR_CODE(DEV_ARG_INVAILD);
}
if (!detectBin) {
QImage sourceImage(QString::fromStdString(imageFilePath));
if (sourceImage.isNull()) {
@ -799,10 +855,6 @@ int StatorWorkpiecePositionPresenter::LoadDebugImageAndDataAndDetect(
.arg(rgbImage.height()).toStdString());
}
int cameraIndex = GetDetectIndex();
if (cameraIndex <= 0) {
cameraIndex = 1;
}
m_detectionCameraIndex.store(cameraIndex);
m_debugDataSaveContext = CreateDebugDataSaveContext(
m_pConfigManager->GetConfigResult().debugParam, cameraIndex);
@ -1359,82 +1411,27 @@ int StatorWorkpiecePositionPresenter::InitApp()
// 初始化算法参数(实现BasePresenter纯虚函数)
int StatorWorkpiecePositionPresenter::InitAlgoParams()
{
LOG_DEBUG("initializing algorithm parameters\n");
QString exePath = QCoreApplication::applicationFilePath();
// 从 ConfigManager 获取配置结果(包含手眼标定矩阵)
ConfigResult configResult = m_pConfigManager->GetConfigResult();
// 从 config.xml 加载手眼标定矩阵
SetDebugParam(configResult.debugParam);
CalibMatrix calibMatrix;
memcpy(calibMatrix.clibMatrix, configResult.handEyeCalibMatrix.matrix, sizeof(double) * 16);
const int cameraCount = std::max(1, static_cast<int>(configResult.cameraList.size()));
{
std::lock_guard<std::mutex> lock(m_clibMatrixMutex);
m_clibMatrixList.clear();
m_clibMatrixList.push_back(calibMatrix);
}
LOG_INFO("Loaded hand-eye calibration matrix from config.xml:\n");
QString clibMatrixStr;
for (int i = 0; i < 4; ++i) {
clibMatrixStr.clear();
for (int j = 0; j < 4; ++j) {
clibMatrixStr += QString::asprintf("%8.4f ", calibMatrix.clibMatrix[i * 4 + j]);
for (int cameraIndex = 1; cameraIndex <= cameraCount; ++cameraIndex)
{
VrToolParam toolParam;
VrHandEyeCalibMatrix handEyeMatrix;
resolveCameraParameters(configResult, cameraIndex, toolParam, handEyeMatrix);
CalibMatrix calibMatrix;
memcpy(calibMatrix.clibMatrix, handEyeMatrix.matrix, sizeof(calibMatrix.clibMatrix));
m_clibMatrixList.push_back(calibMatrix);
}
LOG_INFO(" %s\n", clibMatrixStr.toStdString().c_str());
}
LOG_INFO("Total loaded %d hand-eye calibration matrices\n", 1);
const VrAlgorithmParams& xmlParams = configResult.algorithmParams;
LOG_INFO("Loaded XML params - Tool: eulerOrder=%d, rot=(%.3f,%.3f,%.3f), "
"offset=(%.3f,%.3f,%.3f)\n",
xmlParams.toolParam.eulerOrder,
xmlParams.toolParam.rotX, xmlParams.toolParam.rotY, xmlParams.toolParam.rotZ,
xmlParams.toolParam.offsetX, xmlParams.toolParam.offsetY, xmlParams.toolParam.offsetZ);
LOG_INFO("Loaded 2D model params - minScore=%.3f, "
"classToWorkpieceType={0:%d,1:%d,2:%d}; "
"model package is resolved from the fixed platform directory\n",
xmlParams.modelParam.minimumScore,
xmlParams.modelParam.class0WorkpieceType,
xmlParams.modelParam.class1WorkpieceType,
xmlParams.modelParam.class2WorkpieceType);
LOG_INFO("Algorithm parameters initialized successfully\n");
// 循环打印所有相机的调平参数(添加安全检查)
LOG_INFO("Loading plane calibration parameters for all cameras:\n");
if (!xmlParams.planeCalibParam.cameraCalibParams.empty()) {
for (const auto& cameraParam : xmlParams.planeCalibParam.cameraCalibParams) {
try {
LOG_INFO("Camera %d (%s) calibration parameters:\n",
cameraParam.cameraIndex, cameraParam.cameraName.c_str());
LOG_INFO(" Is calibrated: %s\n", cameraParam.isCalibrated ? "YES" : "NO");
LOG_INFO(" Plane height: %.3f\n", cameraParam.planeHeight);
LOG_INFO(" Plane calibration matrix:\n");
LOG_INFO(" [%.3f, %.3f, %.3f]\n", cameraParam.planeCalib[0], cameraParam.planeCalib[1], cameraParam.planeCalib[2]);
LOG_INFO(" [%.3f, %.3f, %.3f]\n", cameraParam.planeCalib[3], cameraParam.planeCalib[4], cameraParam.planeCalib[5]);
LOG_INFO(" [%.3f, %.3f, %.3f]\n", cameraParam.planeCalib[6], cameraParam.planeCalib[7], cameraParam.planeCalib[8]);
LOG_INFO(" Inverse rotation matrix:\n");
LOG_INFO(" [%.3f, %.3f, %.3f]\n", cameraParam.invRMatrix[0], cameraParam.invRMatrix[1], cameraParam.invRMatrix[2]);
LOG_INFO(" [%.3f, %.3f, %.3f]\n", cameraParam.invRMatrix[3], cameraParam.invRMatrix[4], cameraParam.invRMatrix[5]);
LOG_INFO(" [%.3f, %.3f, %.3f]\n", cameraParam.invRMatrix[6], cameraParam.invRMatrix[7], cameraParam.invRMatrix[8]);
LOG_INFO(" --------------------------------\n");
} catch (const std::exception& e) {
LOG_ERROR("Exception while printing camera calibration parameters: %s\n", e.what());
} catch (...) {
LOG_ERROR("Unknown exception while printing camera calibration parameters\n");
}
}
} else {
LOG_WARNING("No plane calibration parameters found in configuration\n");
}
LOG_INFO("Algorithm parameters loaded: cameras=%d\n", cameraCount);
m_initializationComplete.store(true);
return SUCCESS;
}
@ -1510,8 +1507,6 @@ int StatorWorkpiecePositionPresenter::ProcessAlgoDetection(std::vector<std::pair
}
ScopedAlgorithmProcessing processingGuard(m_algorithmProcessing);
LOG_INFO("[Algo Thread] Start real detection task using algorithm\n");
// 检查检测处理器是否已初始化
if (!m_pDetectPresenter) {
LOG_ERROR("DetectPresenter is null, cannot proceed with detection\n");
@ -1526,25 +1521,24 @@ int StatorWorkpiecePositionPresenter::ProcessAlgoDetection(std::vector<std::pair
// 本次处理使用配置快照;工件尺寸及公共转子参数使用触发时固定的副本。
const ConfigResult configResult = m_pConfigManager->GetConfigResult();
VrAlgorithmParams algorithmParams = configResult.algorithmParams;
// 同一配置快照、同一相机编号解析整套参数,禁止回退到另一相机。
VrHandEyeCalibMatrix handEyeMatrix;
if (!resolveCameraParameters(configResult, detectionCameraIndex, algorithmParams.toolParam, handEyeMatrix))
{
LOG_ERROR("Missing tool or hand-eye parameters for camera %d\n", detectionCameraIndex);
OnStatusUpdate(QString("相机 %1 的工具参数或手眼标定配置缺失。").arg(detectionCameraIndex).toStdString());
return ERR_CODE(DEV_ARG_INVAILD);
}
// 工件类别映射属于公共配置,不能随二维曝光/增益/阈值列表切换。
{
std::lock_guard<std::mutex> sizeLock(m_workpieceSizeMutex);
algorithmParams.rotorCoreParam = m_detectionRotorCoreParam;
LOG_INFO("[Rotor] algorithm workpiece=%d radius=%.6f height=%.6f\n",
m_detectionWorkpieceIndex, algorithmParams.rotorCoreParam.radius, algorithmParams.rotorCoreParam.height);
}
CalibMatrix currentClibMatrix;
std::memcpy(currentClibMatrix.clibMatrix,
configResult.handEyeCalibMatrix.matrix,
handEyeMatrix.matrix,
sizeof(currentClibMatrix.clibMatrix));
// 打印手眼标定矩阵
LOG_INFO("[Algo Thread] Hand-Eye Calibration Matrix (Camera %d):\n", detectionCameraIndex);
LOG_INFO(" [%.4f, %.4f, %.4f, %.4f]\n", currentClibMatrix.clibMatrix[0], currentClibMatrix.clibMatrix[1], currentClibMatrix.clibMatrix[2], currentClibMatrix.clibMatrix[3]);
LOG_INFO(" [%.4f, %.4f, %.4f, %.4f]\n", currentClibMatrix.clibMatrix[4], currentClibMatrix.clibMatrix[5], currentClibMatrix.clibMatrix[6], currentClibMatrix.clibMatrix[7]);
LOG_INFO(" [%.4f, %.4f, %.4f, %.4f]\n", currentClibMatrix.clibMatrix[8], currentClibMatrix.clibMatrix[9], currentClibMatrix.clibMatrix[10], currentClibMatrix.clibMatrix[11]);
LOG_INFO(" [%.4f, %.4f, %.4f, %.4f]\n", currentClibMatrix.clibMatrix[12], currentClibMatrix.clibMatrix[13], currentClibMatrix.clibMatrix[14], currentClibMatrix.clibMatrix[15]);
const VrDebugParam debugParam = configResult.debugParam;
// 与参数页采用相同的相机配置查找规则,关闭时直接旁路整个过滤模块。
@ -1561,11 +1555,14 @@ int StatorWorkpiecePositionPresenter::ProcessAlgoDetection(std::vector<std::pair
}
}
const bool filterEnabled = cloudFilter && cloudFilter->enabled;
LOG_INFO("[Rotor Cloud] raw input: camera=%d lines=%zu firstFrame=%llu lastFrame=%llu filterEnabled=%d\n",
detectionCameraIndex, detectionDataCache.size(),
detectionDataCache.empty() ? 0ULL : detectionDataCache.front().second.llFrameIdx,
detectionDataCache.empty() ? 0ULL : detectionDataCache.back().second.llFrameIdx,
filterEnabled ? 1 : 0);
if (debugParam.enableDebug && debugParam.printDetailLog)
{
LOG_INFO("[Rotor Cloud] raw input: camera=%d lines=%zu firstFrame=%llu lastFrame=%llu filterEnabled=%d\n",
detectionCameraIndex, detectionDataCache.size(),
detectionDataCache.empty() ? 0ULL : detectionDataCache.front().second.llFrameIdx,
detectionDataCache.empty() ? 0ULL : detectionDataCache.back().second.llFrameIdx,
filterEnabled ? 1 : 0);
}
if (filterEnabled)
{
const int filterResult = m_pDetectPresenter->FilterPointCloud(detectionCameraIndex, detectionDataCache, *cloudFilter);
@ -1581,12 +1578,10 @@ int StatorWorkpiecePositionPresenter::ProcessAlgoDetection(std::vector<std::pair
OnStatusUpdate("扫描线数:" + std::to_string(detectionDataCache.size()) + ",正在算法检测...");
// 获取旋转顺序配置
int eulerOrder = configResult.handEyeCalibMatrix.eulerOrder;
LOG_INFO("[Algo Thread] Using euler order: %d\n", eulerOrder);
int eulerOrder = handEyeMatrix.eulerOrder;
// 获取方向向量反向配置
int dirVectorInvert = configResult.plcRobotServerConfig.dirVectorInvert;
LOG_INFO("[Algo Thread] Using dir vector invert: %d\n", dirVectorInvert);
m_pendingDetectionTask.store(DETECTION_TASK_WORKPIECE);
StatorWorkpiecePositionDetectionResult detectionResult;
@ -1753,18 +1748,13 @@ int StatorWorkpiecePositionPresenter::ProcessAlgoDetection(std::vector<std::pair
SaveDebug2DFinalImage(m_debugDataSaveContext.filePrefix, debugParam, detectionResult.image,
!offlineImageBuffer.empty());
}
// 根据项目类型选择处理方式
if (GetStatusCallback<IYStatorWorkpiecePositionStatus>()) {
QString err = QString("错误:%1").arg(nRet);
if (auto pStatus = GetStatusCallback<IYStatorWorkpiecePositionStatus>()) pStatus->OnStatusUpdate(QString("检测%1").arg(SUCCESS == nRet ? "成功": err).toStdString());
}
LOG_INFO("[Algo Thread] rotor core positioning detected %zu workpieces time : %.2f ms\n",
detectionResult.positions.size(), oTimeUtils.GetElapsedTimeInMilliSec());
LOG_INFO("[Rotor] camera=%d result=%d workpieces=%zu time=%.2f ms\n",
detectionCameraIndex, nRet, detectionResult.positions.size(), oTimeUtils.GetElapsedTimeInMilliSec());
if (nRet != SUCCESS) {
m_pendingDetectionTask.store(DETECTION_TASK_WORKPIECE);
LOG_ERROR("[Algo Thread] Detection failed, error: %d\n", nRet);
OnStatusUpdate(QString("相机 %1 检测失败,错误码:%2").arg(detectionCameraIndex).arg(nRet).toStdString());
detectionResult.cameraIndex = detectionCameraIndex;
detectionResult.errorCode = nRet;
if (auto pStatus = GetStatusCallback<IYStatorWorkpiecePositionStatus>()) {
@ -1796,7 +1786,7 @@ int StatorWorkpiecePositionPresenter::ProcessAlgoDetection(std::vector<std::pair
}
// 更新状态
QString statusMsg = QString("检测完成,发现%1个工件").arg(detectionResult.positions.size());
QString statusMsg = QString("相机 %1 检测完成,发现%2个工件").arg(detectionCameraIndex).arg(detectionResult.positions.size());
if (auto pStatus = GetStatusCallback<IYStatorWorkpiecePositionStatus>()) pStatus->OnStatusUpdate(statusMsg.toStdString());
// 发送检测结果给TCP客户端

View File

@ -4,7 +4,7 @@
// 版本字符串
#define STATORWORKPIECEPOSITION_APP_NAME "转子钢芯定位"
#define STATORWORKPIECEPOSITION_APP_KEY "WorkpieceRotorApp"
#define STATORWORKPIECEPOSITION_VERSION_STRING "1.0.7"
#define STATORWORKPIECEPOSITION_VERSION_STRING "1.2.0"
#define STATORWORKPIECEPOSITION_BUILD_STRING "1"
#define STATORWORKPIECEPOSITION_FULL_VERSION_STRING "V" STATORWORKPIECEPOSITION_VERSION_STRING "_" STATORWORKPIECEPOSITION_BUILD_STRING

View File

@ -1,5 +1,22 @@
# 1.2.0
## build_1 20261008
1. 增加了logo
2. 修改了软件名称
# 1.1.2
## build_1 20261007
1. 更新了算法:会检测出下一层的工件
2. 更新了模型
# 1.1.1
## build_1 20261006
1. 更新了模型
# 1.1.0
## build_1 20261003
1. 双相机方案:为了适配更换工具
# 1.0.7
## build_1
## build_1 20260930
1. 更新算法:优化隔板检测

View File

@ -72,8 +72,7 @@ SOURCES += \
main.cpp \
mainwindow.cpp \
dialogalgoarg.cpp \
workpiecesizeactions.cpp \
resultitem.cpp
workpiecesizeactions.cpp
# 头文件
HEADERS += \
@ -89,15 +88,13 @@ HEADERS += \
dialogalgoarg.h \
rotornetworkconfigwidget.h \
workpiecesizeactions.h \
resultitem.h \
Version.h
# UI文件
FORMS += \
mainwindow.ui \
dialogalgoarg.ui \
workpiecesizeactions.ui \
resultitem.ui
workpiecesizeactions.ui
# 资源文件
RESOURCES += \

View File

@ -25,8 +25,6 @@
namespace
{
const int kHandEyeConfigIndex = 1;
bool isIdentityMatrix(const double matrix[16])
{
for (int index = 0; index < 16; ++index)
@ -121,10 +119,20 @@ void DialogAlgoarg::LoadConfigToUI()
LoadBinDetectionParamsToUI(configData.algorithmParams.binParam);
LoadRotorCoreParamsToUI(configData.algorithmParams.rotorCoreParam);
if (m_toolExtrinsicWidget) {
m_toolExtrinsicWidget->setData(
toolParam.eulerOrder,
toolParam.rotX, toolParam.rotY, toolParam.rotZ,
toolParam.offsetX, toolParam.offsetY, toolParam.offsetZ);
const int count = std::max(1, static_cast<int>(configData.cameraList.size()));
for (int cameraIndex = 1; cameraIndex <= count; ++cameraIndex)
{
VrToolParam value = cameraIndex == 1 ? toolParam : VrToolParam();
for (const auto& item : configData.algorithmParams.toolParamsByCamera)
{
if (item.first == cameraIndex)
{
value = item.second;
}
}
m_toolExtrinsicWidget->setData(cameraIndex, value.eulerOrder, value.rotX, value.rotY,
value.rotZ, value.offsetX, value.offsetY, value.offsetZ);
}
}
// 加载网络配置(JSON TCP 端口 + Modbus 寄存器地址)
@ -176,7 +184,22 @@ void DialogAlgoarg::InitToolExtrinsicTab()
}
m_toolExtrinsicWidget = ui->toolExtrinsicWidget;
m_toolExtrinsicWidget->setCameraSelectorVisible(false);
m_toolExtrinsicWidget->setCameraSelectorVisible(true);
QVector<ToolExtrinsicCameraInfo> cameras;
const ConfigResult config = m_pConfigManager ? m_pConfigManager->GetConfigResult() : ConfigResult();
const int cameraCount = std::max(1, static_cast<int>(config.cameraList.size()));
for (int cameraIndex = 1; cameraIndex <= cameraCount; ++cameraIndex)
{
ToolExtrinsicCameraInfo camera;
camera.cameraIndex = cameraIndex;
camera.displayName = QStringLiteral("相机 %1").arg(cameraIndex);
if (cameraIndex <= static_cast<int>(config.cameraList.size()) && !config.cameraList[cameraIndex - 1].name.empty())
{
camera.displayName = QString::fromStdString(config.cameraList[cameraIndex - 1].name);
}
cameras.append(camera);
}
m_toolExtrinsicWidget->setCameraList(cameras);
m_toolExtrinsicWidget->setTargetOffsetVisible(true);
m_toolExtrinsicWidget->setAxialAngleRangeVisible(false);
}
@ -195,12 +218,21 @@ void DialogAlgoarg::InitHandEyeCalibTab()
PathManager::GetInstance().GetAppConfigDirectory());
QVector<HandEyeCalibCameraInfo> cameraList;
HandEyeCalibCameraInfo cameraInfo;
cameraInfo.cameraIndex = kHandEyeConfigIndex;
cameraInfo.displayName = QStringLiteral("手眼标定");
cameraList.append(cameraInfo);
const ConfigResult cameraConfig = m_pConfigManager ? m_pConfigManager->GetConfigResult() : ConfigResult();
const int cameraCount = std::max(1, static_cast<int>(cameraConfig.cameraList.size()));
for (int cameraIndex = 1; cameraIndex <= cameraCount; ++cameraIndex)
{
HandEyeCalibCameraInfo info;
info.cameraIndex = cameraIndex;
info.displayName = QStringLiteral("相机 %1").arg(cameraIndex);
if (cameraIndex <= static_cast<int>(cameraConfig.cameraList.size()) && !cameraConfig.cameraList[cameraIndex - 1].name.empty())
{
info.displayName = QString::fromStdString(cameraConfig.cameraList[cameraIndex - 1].name);
}
cameraList.append(info);
}
m_handEyeCalibWidget->setCameraList(cameraList);
m_handEyeCalibWidget->setCalibType(kHandEyeConfigIndex, 0);
m_handEyeCalibWidget->setCalibType(1, 0);
connect(m_handEyeCalibWidget, &HandEyeCalibWidget::calibMatrixLoaded,
this, &DialogAlgoarg::onCalibMatrixLoaded);
@ -349,10 +381,23 @@ bool DialogAlgoarg::SaveConfigFromUI()
StyledMessageBox::warning(this, "错误", "工具参数输入有误,请检查!");
return false;
}
m_toolExtrinsicWidget->getData(
toolParam.eulerOrder,
toolParam.rotX, toolParam.rotY, toolParam.rotZ,
toolParam.offsetX, toolParam.offsetY, toolParam.offsetZ);
systemConfig.configResult.algorithmParams.toolParamsByCamera.clear();
const int cameraCount = std::max(1, static_cast<int>(systemConfig.configResult.cameraList.size()));
for (int cameraIndex = 1; cameraIndex <= cameraCount; ++cameraIndex)
{
VrToolParam value;
if (!m_toolExtrinsicWidget->getData(cameraIndex, value.eulerOrder, value.rotX, value.rotY,
value.rotZ, value.offsetX, value.offsetY, value.offsetZ))
{
StyledMessageBox::warning(this, tr("错误"), tr("相机 %1 的工具参数输入有误,请检查。").arg(cameraIndex));
return false;
}
systemConfig.configResult.algorithmParams.toolParamsByCamera.push_back(std::make_pair(cameraIndex, value));
if (cameraIndex == 1)
{
toolParam = value;
}
}
// 保存网络配置(JSON TCP 端口 + Modbus 寄存器地址)
if (!SavePlcRobotServerConfigFromUI(systemConfig.configResult.plcRobotServerConfig)) {
@ -360,11 +405,32 @@ bool DialogAlgoarg::SaveConfigFromUI()
return false;
}
// 保存手眼标定矩阵到配置
if (!SaveCalibMatrixToConfig(systemConfig.configResult.handEyeCalibMatrix)) {
StyledMessageBox::warning(this, "错误", "手眼标定矩阵输入有误,请检查!");
return false;
// 工具参数与手眼矩阵按同一相机编号成对保存,旧单份字段仅镜像相机 1。
std::vector<std::pair<int, VrHandEyeCalibMatrix>> matrices;
for (int cameraIndex = 1; cameraIndex <= cameraCount; ++cameraIndex)
{
VrHandEyeCalibMatrix matrix = cameraIndex == 1
? systemConfig.configResult.handEyeCalibMatrix : VrHandEyeCalibMatrix();
for (const auto& item : systemConfig.configResult.handEyeCalibMatricesByCamera)
{
if (item.first == cameraIndex)
{
matrix = item.second;
}
}
bool calibrated = false;
if (!m_handEyeCalibWidget || !m_handEyeCalibWidget->getCalibData(cameraIndex, matrix.matrix, calibrated))
{
StyledMessageBox::warning(this, tr("错误"), tr("相机 %1 的手眼标定矩阵输入有误,请检查。").arg(cameraIndex));
return false;
}
matrices.push_back(std::make_pair(cameraIndex, matrix));
if (cameraIndex == 1)
{
systemConfig.configResult.handEyeCalibMatrix = matrix;
}
}
systemConfig.configResult.handEyeCalibMatricesByCamera = matrices;
// 更新并保存配置到文件
if (!m_pConfigManager->UpdateFullConfig(systemConfig)) {
@ -828,23 +894,21 @@ void DialogAlgoarg::LoadHandEyeCalibConfig()
}
const ConfigResult configData = m_pConfigManager->GetConfigResult();
const VrHandEyeCalibMatrix& calibMatrix = configData.handEyeCalibMatrix;
m_handEyeCalibWidget->setCalibData(
kHandEyeConfigIndex,
calibMatrix.matrix,
!isIdentityMatrix(calibMatrix.matrix));
}
bool DialogAlgoarg::SaveCalibMatrixToConfig(VrHandEyeCalibMatrix& calibMatrix)
{
if (!m_handEyeCalibWidget)
const int count = std::max(1, static_cast<int>(configData.cameraList.size()));
for (int cameraIndex = 1; cameraIndex <= count; ++cameraIndex)
{
return false;
const VrHandEyeCalibMatrix defaultMatrix;
const VrHandEyeCalibMatrix* calibMatrix = cameraIndex == 1 ? &configData.handEyeCalibMatrix : &defaultMatrix;
for (const auto& item : configData.handEyeCalibMatricesByCamera)
{
if (item.first == cameraIndex)
{
calibMatrix = &item.second;
}
}
m_handEyeCalibWidget->setCalibData(cameraIndex, calibMatrix->matrix,
!isIdentityMatrix(calibMatrix->matrix));
}
bool isCalibrated = false;
return m_handEyeCalibWidget->getCalibData(
kHandEyeConfigIndex, calibMatrix.matrix, isCalibrated);
}
void DialogAlgoarg::LoadPlcRobotServerConfigToUI(const VrPlcRobotServerConfig& config)

View File

@ -64,7 +64,6 @@ private:
bool SavePlcRobotServerConfigFromUI(VrPlcRobotServerConfig& config);
void LoadHandEyeCalibConfig();
bool SaveCalibMatrixToConfig(VrHandEyeCalibMatrix& calibMatrix);
private:
Ui::DialogAlgoarg *ui;

View File

@ -459,6 +459,13 @@ QDoubleSpinBox { color: rgb(221, 225, 233); background-color: rgb(47, 48, 52); b
<property name="font"><font><pointsize>16</pointsize></font></property>
<property name="styleSheet">
<string notr="true">QGroupBox, QLabel { color: rgb(221, 225, 233); }
QGroupBox#groupBox_modelDetectionRegion { border: 1px solid rgb(100, 100, 100); border-radius: 4px; margin-top: 20px; padding-top: 12px; spacing: 10px; }
QGroupBox#groupBox_modelDetectionRegion::title { subcontrol-origin: margin; subcontrol-position: top left; left: 10px; padding: 0px 8px; }
QGroupBox#groupBox_modelDetectionRegion::indicator { width: 28px; height: 28px; border: 2px solid rgb(180, 190, 205); border-radius: 3px; background-color: rgb(47, 48, 52); }
QGroupBox#groupBox_modelDetectionRegion::indicator:unchecked { image: none; }
QGroupBox#groupBox_modelDetectionRegion::indicator:checked { background-color: rgb(40, 120, 180); border-color: rgb(130, 190, 240); image: url(:/common/resource/checkbox_check.png); }
QGroupBox#groupBox_modelDetectionRegion::indicator:hover { border-color: rgb(230, 240, 255); }
QGroupBox#groupBox_modelDetectionRegion::indicator:disabled { background-color: rgb(60, 65, 75); border-color: rgb(110, 120, 135); }
QSpinBox { color: rgb(221, 225, 233); background-color: rgb(47, 48, 52); border: 1px solid rgb(100, 100, 100); padding: 4px; }</string>
</property>
<property name="title"><string>2D采图区域(左目全图像素)</string></property>

View File

@ -1,6 +1,7 @@
#include "mainwindow.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "resultitem.h"
#include "XyzRpyItem.h"
#include "ResultListLayoutHelper.h"
#include <QStandardItemModel>
#include <QDebug>
#include <QPainter>
@ -15,6 +16,7 @@
#include <QStringListModel>
#include <QScreen>
#include <QApplication>
#include <QStatusBar>
#include "VrLog.h"
#include "VrDateUtils.h"
#include <QIcon>
@ -43,6 +45,41 @@
#include "Version.h"
#include "IVrUtils.h"
namespace
{
// 将算法返回的位置编号转换为结果卡片上的中文名称。
QString workpiecePositionText(int workpieceType)
{
switch (workpieceType)
{
case 0:
return QStringLiteral("中央");
case 1:
return QStringLiteral("上边");
case 2:
return QStringLiteral("下边");
case 3:
return QStringLiteral("左边");
case 4:
return QStringLiteral("右边");
case 5:
return QStringLiteral("左上角");
case 6:
return QStringLiteral("右上角");
case 7:
return QStringLiteral("左下角");
case 8:
return QStringLiteral("右下角");
case 9:
return QStringLiteral("隔板");
default:
return QStringLiteral("未知");
}
}
} // namespace
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
@ -115,7 +152,6 @@ MainWindow::MainWindow(QWidget *parent)
// 初始化右键菜单
setupContextMenu();
updateStatusLog(tr("设备开始初始化..."));
// 初始化模块
Init();
@ -155,9 +191,7 @@ bool MainWindow::eventFilter(QObject* obj, QEvent* event)
if (obj == m_versionLabel && event->type() == QEvent::MouseButtonPress) {
const QString algoVersion = m_presenter ? m_presenter->GetAlgoVersion() : QString();
AboutDialog dlg(STATORWORKPIECEPOSITION_APP_NAME,
m_softwareVersionText,
algoVersion,
this);
m_softwareVersionText, algoVersion, this);
dlg.exec();
return true;
}
@ -255,13 +289,16 @@ void MainWindow::addDetectionResult(const StatorWorkpiecePositionDetectionResult
// 显示每个工件的中心点坐标
for (size_t i = 0; i < result.positions.size(); i++) {
ResultItem* resultWidget = new ResultItem();
XyzRpyItem* resultWidget = new XyzRpyItem();
resultWidget->setAutoFillBackground(true);
// 设置中心点数据
const WorkpiecePosition& workpiece = result.positions[i];
resultWidget->setResultData(
result.positions[i].workpieceId,
result.positions[i],
workpiece.workpieceId,
QStringLiteral("工件"),
workpiecePositionText(workpiece.workpieceType),
static_cast<const PositionData<double>&>(workpiece),
i == 0);
// 创建QListWidgetItem
@ -335,9 +372,7 @@ void MainWindow::OnCameraCountChanged(int cameraCount)
// 根据相机个数调整页面布局
adjustLayoutForCameraCount(cameraCount);
updateStatusLog(cameraCount < 2
? tr("系统使用单相机模式")
: tr("系统使用双相机模式"));
updateStatusLog(cameraCount < 2 ? tr("单相机模式") : tr("双相机模式"));
}, Qt::QueuedConnection);
}
@ -370,6 +405,7 @@ void MainWindow::OnWorkStatusChanged(WorkStatus status)
void MainWindow::updateWorkStatusLabel(WorkStatus status)
{
const bool statusChanged = m_workStatus != status;
// 如果状态变为Working,清空检测日志(表示开始新的检测)
if (status == WorkStatus::Working && m_workStatus != WorkStatus::Working)
{
@ -387,7 +423,10 @@ void MainWindow::updateWorkStatusLabel(WorkStatus status)
ui->label_work->setText(statusText);
statusText = "【工作状态】" + statusText;
updateStatusLog(statusText);
if (statusChanged)
{
updateStatusLog(statusText);
}
// 根据不同状态设置不同的样式和按钮启用状态
switch (status) {
@ -429,17 +468,15 @@ void MainWindow::updateDetectionResultDisplay(const StatorWorkpiecePositionDetec
// 根据相机索引决定显示在哪个图像视图上
if (!result.image.isNull()) {
if (result.cameraIndex == 2) {
// 第二个相机的结果显示在detect_image_2上
updateStatusLog(tr("相机2检测图像更新"));
// 第二个相机的结果显示在detect_image_2
displayImageInSecondView(result.image);
} else {
// 第一个相机或默认显示在detect_image上
updateStatusLog(tr("相机1检测图像更新"));
// 第一个相机或默认显示在detect_image
displayImage(result.image);
}
}
// 2D模型检测完成后会先推送图像结果,此时没有3D位姿,不刷新结果列表。
// 2D模型检测完成后会先推送图像结果,此时没有3D位姿,不刷新结果列表
if (!result.positions.empty()) {
addDetectionResult(result);
} else if (result.errorCode != 0) {
@ -465,7 +502,7 @@ void MainWindow::on_btn_start_clicked()
updateWorkStatusLabel(WorkStatus::Working);
updateStatusLog(tr("开始工件检测,正在准备二维采集..."));
// 取图、二维推理、保存调试图和启动扫描均在后台执行,UI 只处理排队的通知。
// 取图、二维推理、保存调试图和启动扫描均在后台执行,UI 只处理排队的通知
try
{
m_startDetectionThread = std::thread([this]()
@ -492,7 +529,7 @@ void MainWindow::on_btn_start_clicked()
QMetaObject::invokeMethod(this, [this, errorMessage]()
{
// 耗时操作已经结束,回收后才允许下一次按钮启动复用线程对象。
// 耗时操作已经结束,回收后才允许下一次按钮启动复用线程对象
m_startDetectionThread.join();
m_startDetectionPending = false;
if (!errorMessage.isEmpty())
@ -520,12 +557,12 @@ void MainWindow::on_btn_stop_clicked()
{
if (m_startDetectionPending)
{
updateStatusLog(tr("正在准备检测,请等待启动完成"));
updateStatusLog(tr("正在准备检测,请等待启动完成。"));
return;
}
// 检查Presenter是否已初始化
if (!m_presenter) {
updateStatusLog(tr("系统未初始化,请等待初始化完成"));
updateStatusLog(tr("系统未初始化,请等待初始化完成。"));
return;
}
@ -537,13 +574,13 @@ void MainWindow::on_btn_camera_clicked()
{
// 检查是否有其他按钮已被选中
if (m_selectedButton != nullptr && m_selectedButton != ui->btn_camera) {
updateStatusLog(tr("请先关闭当前打开的配置窗口"));
updateStatusLog(tr("请先关闭当前打开的配置窗口。"));
return;
}
// 检查Presenter是否已初始化
if (!m_presenter) {
updateStatusLog(tr("系统未初始化,请等待初始化完成"));
updateStatusLog(tr("系统未初始化,请等待初始化完成。"));
return;
}
@ -570,13 +607,13 @@ void MainWindow::on_btn_algo_config_clicked()
{
// 检查是否有其他按钮已被选中
if (m_selectedButton != nullptr && m_selectedButton != ui->btn_algo_config) {
updateStatusLog(tr("请先关闭当前打开的配置窗口"));
updateStatusLog(tr("请先关闭当前打开的配置窗口。"));
return;
}
// 检查Presenter是否已初始化
if (!m_presenter) {
updateStatusLog(tr("系统未初始化,请等待初始化完成"));
updateStatusLog(tr("系统未初始化,请等待初始化完成。"));
return;
}
@ -635,13 +672,13 @@ void MainWindow::on_btn_camera_levelling_clicked()
{
// 检查是否有其他按钮已被选中
if (m_selectedButton != nullptr && m_selectedButton != ui->btn_camera_levelling) {
updateStatusLog(tr("请先关闭当前打开的配置窗口"));
updateStatusLog(tr("请先关闭当前打开的配置窗口。"));
return;
}
// 检查Presenter是否已初始化
if (!m_presenter) {
updateStatusLog(tr("系统未初始化,请等待初始化完成"));
updateStatusLog(tr("系统未初始化,请等待初始化完成。"));
return;
}
@ -685,7 +722,7 @@ void MainWindow::on_btn_test_clicked()
}
// 检查是否有其他按钮已被选中
if (m_selectedButton != nullptr && m_selectedButton != ui->btn_test) {
updateStatusLog(tr("请先关闭当前打开的配置窗口"));
updateStatusLog(tr("请先关闭当前打开的配置窗口。"));
return;
}
@ -949,41 +986,15 @@ void MainWindow::setupSingleCameraLayout()
// 获取可用高度(已排除任务栏和20px边距)
int availableHeight = getAvailableHeight();
// 单相机模式下的原始布局
// detect_image: 占据左侧大部分空间,动态计算高度
int detectImageTop = 140;
int detectImageHeight = availableHeight - detectImageTop;
ui->detect_image->setGeometry(20, detectImageTop, 1311, detectImageHeight);
// detect_image_2: 隐藏
ui->detect_image_2->setVisible(false);
// 设备状态框架: 位于右上方,恢复原始尺寸
ui->frame_dev->setGeometry(1344, 140, 556, 64);
// 检测结果列表: 位于右侧中间
ui->detect_result_list->setGeometry(1344, 216, 556, 622);
// 单相机模式下恢复原有的垂直滚动设置
ui->detect_result_list->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
ui->detect_result_list->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->detect_result_list->setFlow(QListView::TopToBottom); // 从上到下排列
ui->detect_result_list->setWrapping(false); // 不换行,每行一个
// 单相机模式:设置gridSize,每行显示1个目标
int gridWidth = 550;
int gridHeight = 65;
ui->detect_result_list->setGridSize(QSize(gridWidth, gridHeight));
ResultListLayoutHelper::setupSingleCameraMode(
ui->detect_result_list,
ui->detect_image, ui->detect_image_2,
ui->frame_dev, ui->detect_log,
availableHeight);
// 恢复设备状态widget的原始布局
m_deviceStatusWidget->setCameraCount(1);
// 日志: 位于右下方,动态计算高度
int detectLogTop = 850;
int detectLogHeight = availableHeight - detectLogTop;
ui->detect_log->setGeometry(1344, detectLogTop, 556, detectLogHeight);
updateStatusLog(tr("页面布局已调整为单相机模式"));
}
// 设置多相机模式布局
@ -992,39 +1003,14 @@ void MainWindow::setupMultiCameraLayout()
// 获取可用高度(已排除任务栏和20px边距)
int availableHeight = getAvailableHeight();
// 多相机模式下的布局调整
// detect_image: 缩小到左侧一半空间,动态计算高度
int detectImageTop = 140;
int detectImageHeight = availableHeight - detectImageTop;
ui->detect_image->setGeometry(10, detectImageTop, 940, detectImageHeight);
// detect_image_2: 显示在右侧一半空间,动态计算高度
ui->detect_image_2->setVisible(true);
ui->detect_image_2->setGeometry(970, detectImageTop, 940, detectImageHeight);
// 设备状态框架: 移动到下方左侧,调整为更高的尺寸以适应纵向排布
ui->frame_dev->setGeometry(10, 806, 180, 200);
// 检测结果列表: 移动到下方中间
ui->detect_result_list->setGeometry(196, 806, 1380, 200);
// 多相机模式:设置gridSize,每行显示1个目标,水平排列
ui->detect_result_list->setFlow(QListView::LeftToRight); // 水平排列
ui->detect_result_list->setWrapping(false); // 不换行
ui->detect_result_list->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->detect_result_list->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
int gridWidth = 540;
int gridHeight = 65;
ui->detect_result_list->setGridSize(QSize(gridWidth, gridHeight));
ResultListLayoutHelper::setupMultiCameraMode(
ui->detect_result_list,
ui->detect_image, ui->detect_image_2,
ui->frame_dev, ui->detect_log,
availableHeight);
m_deviceStatusWidget->setCameraCount(2);
// 日志: 移动到下方右侧,动态计算高度
int detectLogTop = 806;
int detectLogHeight = availableHeight - detectLogTop;
ui->detect_log->setGeometry(1582, detectLogTop, 328, detectLogHeight);
updateStatusLog(tr("页面布局已调整为多相机模式"));
}
// 处理相机点击事件
@ -1081,7 +1067,7 @@ void MainWindow::showContextMenu(const QPoint& pos, QGraphicsView* view)
{
// 检查是否有检测数据可以保存
if (!m_presenter) {
updateStatusLog(tr("系统未初始化,无法保存数据"));
updateStatusLog(tr("系统未初始化,无法保存数据。"));
return;
}
@ -1090,12 +1076,12 @@ void MainWindow::showContextMenu(const QPoint& pos, QGraphicsView* view)
// 检查是否是上次检测的相机
if (cameraIndex != m_presenter->GetDetectIndex()) {
updateStatusLog(tr("当前视图不是上次检测的相机,无法保存数据"));
updateStatusLog(tr("当前视图不是上次检测的相机,无法保存数据。"));
return;
}
if (m_presenter->GetDetectionDataCacheSize() == 0) {
updateStatusLog(tr("没有检测数据可以保存"));
updateStatusLog(tr("没有检测数据可以保存。"));
return;
}
@ -1107,14 +1093,14 @@ void MainWindow::showContextMenu(const QPoint& pos, QGraphicsView* view)
void MainWindow::onSaveDetectionData()
{
if (!m_presenter) {
updateStatusLog(tr("系统未初始化,无法保存数据"));
updateStatusLog(tr("系统未初始化,无法保存数据。"));
return;
}
ConfigManager* config = m_presenter->GetConfigManager();
if (!config)
{
updateStatusLog(tr("配置模块未初始化,无法保存数据"));
updateStatusLog(tr("配置模块未初始化,无法保存数据。"));
return;
}
@ -1138,8 +1124,6 @@ void MainWindow::onSaveDetectionData()
// 保存数据
if (saveDetectionDataToFile(fullPath, m_presenter->GetDetectIndex())) {
updateStatusLog(tr("检测数据和2D图像已保存,基础文件:%1").arg(fileName.c_str()));
} else {
updateStatusLog(tr("保存检测数据失败"));
}
}
@ -1151,7 +1135,6 @@ bool MainWindow::saveDetectionDataToFile(const QString& filePath, int cameraInde
int result = m_presenter->SaveDetectionDataToFile(filePath.toStdString());
if (result == 0) {
updateStatusLog(tr("检测数据保存成功"));
return true;
} else {
updateStatusLog(tr("保存数据失败,错误码:%1").arg(result));
@ -1237,4 +1220,3 @@ void MainWindow::setupCPUSerialDisplay()
LOG_INFO("CPU Serial Number: %s\n", cpuSerial.toStdString().c_str());
}

View File

@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
<string>转子钢芯定位系统</string>
<string>定子转子3D无序抓取</string>
</property>
<property name="autoFillBackground">
<bool>false</bool>
@ -55,29 +55,6 @@ color: rgb(239, 241, 245);</string>
<set>QAbstractItemView::NoEditTriggers</set>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>40</x>
<y>14</y>
<width>431</width>
<height>91</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>36</pointsize>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(239, 241, 245);
background-color: rgba(255, 255, 255, 0);</string>
</property>
<property name="text">
<string>转子钢芯定位系统</string>
</property>
</widget>
<widget class="QListWidget" name="detect_result_list">
<property name="geometry">
<rect>
@ -97,7 +74,7 @@ background-color: rgba(255, 255, 255, 0);</string>
<widget class="QPushButton" name="btn_test">
<property name="geometry">
<rect>
<x>1218</x>
<x>1305</x>
<y>21</y>
<width>220</width>
<height>80</height>
@ -120,7 +97,7 @@ border: none;</string>
<widget class="QPushButton" name="btn_algo_config">
<property name="geometry">
<rect>
<x>728</x>
<x>835</x>
<y>21</y>
<width>220</width>
<height>80</height>
@ -143,7 +120,7 @@ border: none;</string>
<widget class="QPushButton" name="btn_camera_levelling">
<property name="geometry">
<rect>
<x>973</x>
<x>1070</x>
<y>21</y>
<width>220</width>
<height>80</height>
@ -166,7 +143,7 @@ border: none;</string>
<widget class="QPushButton" name="btn_camera">
<property name="geometry">
<rect>
<x>483</x>
<x>600</x>
<y>21</y>
<width>220</width>
<height>80</height>
@ -204,9 +181,9 @@ border: none;</string>
<widget class="QLabel" name="label_work">
<property name="geometry">
<rect>
<x>1450</x>
<x>1570</x>
<y>20</y>
<width>311</width>
<width>191</width>
<height>81</height>
</rect>
</property>
@ -262,8 +239,8 @@ background-color: rgba(255, 255, 255, 0);</string>
<widget class="QPushButton" name="btn_start">
<property name="geometry">
<rect>
<x>1525</x>
<y>21</y>
<x>1550</x>
<y>22</y>
<width>80</width>
<height>80</height>
</rect>
@ -284,7 +261,7 @@ background-color: rgba(255, 255, 255, 0);</string>
<widget class="QPushButton" name="btn_stop">
<property name="geometry">
<rect>
<x>1645</x>
<x>1650</x>
<y>21</y>
<width>80</width>
<height>80</height>
@ -303,6 +280,64 @@ background-color: rgba(255, 255, 255, 0);</string>
<string/>
</property>
</widget>
<widget class="QToolButton" name="title_logo">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>120</width>
<height>120</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: transparent;
border: none;
padding: 0px;
margin: 0px;</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/resource/hrm.png</normaloff>:/resource/hrm.png</iconset>
</property>
<property name="iconSize">
<size>
<width>150</width>
<height>80</height>
</size>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonIconOnly</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>120</x>
<y>21</y>
<width>461</width>
<height>80</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>36</pointsize>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(239, 241, 245);
background-color: rgba(255, 255, 255, 0);</string>
</property>
<property name="text">
<string>定子转子3D无序抓取</string>
</property>
</widget>
</widget>
<widget class="QFrame" name="frame_dev">
<property name="geometry">
@ -340,7 +375,6 @@ background-color: rgba(255, 255, 255, 0);</string>
<zorder>groupBox</zorder>
<zorder>detect_image</zorder>
<zorder>detect_log</zorder>
<zorder>label</zorder>
<zorder>detect_result_list</zorder>
<zorder>btn_test</zorder>
<zorder>btn_algo_config</zorder>
@ -383,7 +417,6 @@ background-color: rgba(255, 255, 255, 0);</string>
</property>
</action>
</widget>
<resources/>
<customwidgets>
<customwidget>
<class>ImageView</class>
@ -391,5 +424,8 @@ background-color: rgba(255, 255, 255, 0);</string>
<header>ImageView.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="resources.qrc"/>
</resources>
<connections/>
</ui>

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

View File

@ -1,11 +1,7 @@
<RCC>
<!-- 应用特定资源 -->
<qresource prefix="/">
<file>resource/logo.png</file>
<file>resource/logo.ico</file>
<file>resource/hrm.png</file>
</qresource>
<!-- 公用资源(从 UICommon 引入) -->
<!-- 注意:公用资源通过 UICommon 库自动引入,无需在此重复定义 -->
<!-- 使用方式::/common/resource/xxx.png -->
</RCC>

View File

@ -1,74 +0,0 @@
#include "resultitem.h"
#include "ui_resultitem.h"
namespace {
QString WorkpiecePositionText(int workpieceType)
{
switch (workpieceType) {
case 0: return QStringLiteral("中央");
case 1: return QStringLiteral("上边");
case 2: return QStringLiteral("下边");
case 3: return QStringLiteral("左边");
case 4: return QStringLiteral("右边");
case 5: return QStringLiteral("左上角");
case 6: return QStringLiteral("右上角");
case 7: return QStringLiteral("左下角");
case 8: return QStringLiteral("右下角");
case 9: return QStringLiteral("隔板");
default: return QStringLiteral("未知");
}
}
} // namespace
ResultItem::ResultItem(QWidget *parent)
: QWidget(parent)
, ui(new Ui::ResultItem)
{
ui->setupUi(this);
// 强制应用样式表
this->setAttribute(Qt::WA_StyledBackground, true);
// 初始化时设置样式
setItemStyle();
}
ResultItem::~ResultItem()
{
delete ui;
}
void ResultItem::setResultData(
int workpieceIndex,
const WorkpiecePosition& position,
bool isCurrentPickup)
{
ui->result_id->setStyleSheet(isCurrentPickup
? QStringLiteral("color: rgb(255, 0, 0); background-color: transparent;")
: QStringLiteral("color: rgb(239, 241, 245); background-color: transparent;"));
ui->result_id->setText(QString("工件 %1 位置:%2")
.arg(workpieceIndex)
.arg(WorkpiecePositionText(position.workpieceType)));
ui->result_xyz_rpy->setText(QString(
"X:%1 Y:%2 Z:%3 R:%4 P:%5 Y:%6")
.arg(position.x, 0, 'f', 3)
.arg(position.y, 0, 'f', 3)
.arg(position.z, 0, 'f', 3)
.arg(position.roll, 0, 'f', 3)
.arg(position.pitch, 0, 'f', 3)
.arg(position.yaw, 0, 'f', 3));
}
void ResultItem::setItemStyle()
{
// 设置边框作为分隔线
this->setStyleSheet(
"ResultItem { "
" border: 1px solid #191A1C; "
" border-radius: 0px; "
"} "
);
}

View File

@ -1,29 +0,0 @@
#ifndef RESULTITEM_H
#define RESULTITEM_H
#include <QWidget>
#include "IYStatorWorkpiecePositionStatus.h"
namespace Ui {
class ResultItem;
}
class ResultItem : public QWidget
{
Q_OBJECT
public:
explicit ResultItem(QWidget *parent = nullptr);
~ResultItem();
// 设置检测结果数据
void setResultData(int workpieceIndex, const WorkpiecePosition& position, bool isCurrentPickup);
// 设置样式
void setItemStyle();
private:
Ui::ResultItem *ui;
};
#endif // RESULTITEM_H

View File

@ -1,130 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ResultItem</class>
<widget class="QWidget" name="ResultItem">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>520</width>
<height>60</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>520</width>
<height>60</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(37, 38, 42);
</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_top">
<property name="spacing">
<number>8</number>
</property>
<item>
<widget class="QWidget" name="iconWidget" native="true">
<property name="minimumSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">image: url(:/common/resource/result_icon.png);
background-color: rgba(255, 255, 255, 0);</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="result_id">
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(239, 241, 245);
background-color: transparent;</string>
</property>
<property name="text">
<string>工件 1</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_bottom">
<property name="leftMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="result_xyz_rpy">
<property name="font">
<font>
<family>Consolas</family>
<pointsize>11</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(200, 200, 200);
background-color: transparent;</string>
</property>
<property name="text">
<string>X:0.000 Y:0.000 Z:0.000 R:0.000 P:0.000 Y:0.000</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View File

@ -243,6 +243,10 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
XMLElement* debugParamElement = root->FirstChildElement("DebugParam");
if (debugParamElement)
{
if (debugParamElement->Attribute("crash"))
{
configResult.debugParam.crash = debugParamElement->BoolAttribute("crash");
}
if (debugParamElement->Attribute("enableDebug"))
configResult.debugParam.enableDebug = debugParamElement->BoolAttribute("enableDebug");
if (debugParamElement->Attribute("savePointCloud"))
@ -423,6 +427,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
// 添加调试参数
XMLElement* debugParamElement = doc.NewElement("DebugParam");
debugParamElement->SetAttribute("enableDebug", configResult.debugParam.enableDebug);
debugParamElement->SetAttribute("crash", configResult.debugParam.crash);
debugParamElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud);
debugParamElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage);
debugParamElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog);

View File

@ -258,6 +258,11 @@ WheelMeasureConfigResult VrWheelMeasureConfig::LoadConfig(const std::string& fil
// 解析调试参数
else if (xml.isStartElement() && xml.name() == "DebugParam") {
result.debugParam.enableDebug = xml.attributes().value("enableDebug").toInt();
if (xml.attributes().hasAttribute("crash"))
{
const QString crashValue = xml.attributes().value("crash").toString();
result.debugParam.crash = crashValue == "1" || crashValue.compare("true", Qt::CaseInsensitive) == 0;
}
result.debugParam.savePointCloud = xml.attributes().value("savePointCloud").toInt();
result.debugParam.saveDebugImage = xml.attributes().value("saveDebugImage").toInt();
result.debugParam.printDetailLog = xml.attributes().value("printDetailLog").toInt();
@ -475,6 +480,7 @@ bool VrWheelMeasureConfig::SaveConfig(const std::string& filePath, WheelMeasureC
// 保存调试参数
xml.writeStartElement("DebugParam");
xml.writeAttribute("enableDebug", QString::number(configResult.debugParam.enableDebug));
xml.writeAttribute("crash", QString::number(configResult.debugParam.crash));
xml.writeAttribute("savePointCloud", QString::number(configResult.debugParam.savePointCloud));
xml.writeAttribute("saveDebugImage", QString::number(configResult.debugParam.saveDebugImage));
xml.writeAttribute("printDetailLog", QString::number(configResult.debugParam.printDetailLog));

View File

@ -296,6 +296,10 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
XMLElement* debugParamElement = root->FirstChildElement("DebugParam");
if (debugParamElement)
{
if (debugParamElement->Attribute("crash"))
{
configResult.debugParam.crash = debugParamElement->BoolAttribute("crash");
}
if (debugParamElement->Attribute("enableDebug"))
configResult.debugParam.enableDebug = debugParamElement->BoolAttribute("enableDebug");
if (debugParamElement->Attribute("savePointCloud"))
@ -470,6 +474,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
// 添加调试参数(在AlgorithmParams外面)
XMLElement* debugParamElement = doc.NewElement("DebugParam");
debugParamElement->SetAttribute("enableDebug", configResult.debugParam.enableDebug);
debugParamElement->SetAttribute("crash", configResult.debugParam.crash);
debugParamElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud);
debugParamElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage);
debugParamElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog);

View File

@ -82,6 +82,7 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
if (debugElement)
{
debugElement->QueryBoolAttribute("enableDebug", &configResult.debugParam.enableDebug);
debugElement->QueryBoolAttribute("crash", &configResult.debugParam.crash);
debugElement->QueryBoolAttribute("saveDebugImage", &configResult.debugParam.saveDebugImage);
debugElement->QueryBoolAttribute("savePointCloud", &configResult.debugParam.savePointCloud);
debugElement->QueryBoolAttribute("printDetailLog", &configResult.debugParam.printDetailLog);
@ -157,6 +158,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
// 3. 保存调试参数
XMLElement* debugElement = doc.NewElement("Debug");
debugElement->SetAttribute("enableDebug", configResult.debugParam.enableDebug);
debugElement->SetAttribute("crash", configResult.debugParam.crash);
debugElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage);
debugElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud);
debugElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog);

View File

@ -7,7 +7,7 @@ target_platform: "windows-x64"
model_family: "yolo8"
output_contract: "task_specific"
runtime:
backend_library: "../../lib/yolo_backend_onnx_cpu.dll"
backend_library: "../lib/yolo_backend_onnx_cpu.dll"
input:
width: 1280
height: 1280