Merge branch 'master' of http://gitea.mnutil.com/YangOne/GrabBag
This commit is contained in:
commit
588b42932d
@ -8,11 +8,22 @@ ParkingSpaceGuide 使用一台平面相机、一台雷达和一块显示屏完
|
||||
- 雷达:`Device/RsLidarDevice`
|
||||
- 显示屏:`Device/LedDisplayDevice`
|
||||
- 组播:`Nets/UDPClient`
|
||||
- 2D 机型识别:`AppAlgo/AAPGS_model`(RK3588 Linux AArch64)
|
||||
|
||||
算法入口当前预留在:
|
||||
算法入口位于:
|
||||
|
||||
`ParkingSpaceGuideApp/Presenter/Src/DetectPresenter.cpp`
|
||||
|
||||
机型识别由雷达算法按距离触发,相机软触发取图后调用 AAPGS 分类模型,
|
||||
当前支持 `A320` 和 `B737`。应用默认从可执行文件同级的
|
||||
`AAPGS_model/models/model.yaml` 加载模型;也可通过环境变量覆盖:
|
||||
|
||||
- `AAPGS_MODEL_CONFIG`:模型 YAML 的完整路径
|
||||
- `AAPGS_MODEL_ROOT`:AAPGS SDK 根目录
|
||||
- `AAPGS_RUNTIME_LIBRARY`:`libyolo_runtime.so.2` 的完整路径
|
||||
|
||||
Windows 构建保留界面与业务流程,但不执行仅支持 RK3588 的 NPU 推理。
|
||||
|
||||
文档:
|
||||
|
||||
- 详细设计:`详细设计.md`
|
||||
|
||||
@ -37,6 +37,9 @@ INCLUDEPATH += $$PWD/../../../Nets/UDPClient/Inc
|
||||
INCLUDEPATH += $$PWD/../../../Utils/VrCommon/Inc
|
||||
INCLUDEPATH += $$PWD/../../../Utils/VrUtils/Inc
|
||||
|
||||
AAPGS_ROOT = $$clean_path($$PWD/../../../AppAlgo/AAPGS_model)
|
||||
INCLUDEPATH += $$AAPGS_ROOT/include
|
||||
|
||||
win32 {
|
||||
POCO_ROOT = $$PWD/../../../SDK/poco/Windows
|
||||
} else:unix:!macx {
|
||||
@ -61,6 +64,7 @@ win32 {
|
||||
SOURCES += \
|
||||
Presenter/Src/ConfigManager.cpp \
|
||||
Presenter/Src/ParkingSpaceGuidePresenter.cpp \
|
||||
Presenter/Src/AapgsModelClassifier.cpp \
|
||||
Presenter/Src/DetectPresenter.cpp \
|
||||
Presenter/Src/ParkingStatusHttpServer.cpp \
|
||||
main.cpp \
|
||||
@ -70,6 +74,7 @@ SOURCES += \
|
||||
HEADERS += \
|
||||
Presenter/Inc/ConfigManager.h \
|
||||
Presenter/Inc/ParkingSpaceGuidePresenter.h \
|
||||
Presenter/Inc/AapgsModelClassifier.h \
|
||||
Presenter/Inc/DetectPresenter.h \
|
||||
Presenter/Inc/ParkingStatusHttpServer.h \
|
||||
mainwindow.h \
|
||||
@ -156,6 +161,20 @@ win32 {
|
||||
|
||||
unix {
|
||||
target.path = /usr/lib
|
||||
|
||||
aapgsRuntime.path = /usr/lib/AAPGS_model/lib
|
||||
aapgsRuntime.files = \
|
||||
$$AAPGS_ROOT/lib/libyolo_runtime.so.2 \
|
||||
$$AAPGS_ROOT/lib/libyolo_backend_cls.so \
|
||||
$$AAPGS_ROOT/lib/librknnrt.so \
|
||||
$$AAPGS_ROOT/lib/librga.so
|
||||
|
||||
aapgsModels.path = /usr/lib/AAPGS_model/models
|
||||
aapgsModels.files = \
|
||||
$$AAPGS_ROOT/models/model.yaml \
|
||||
$$AAPGS_ROOT/models/model.rknn
|
||||
|
||||
INSTALLS += aapgsRuntime aapgsModels
|
||||
}
|
||||
!isEmpty(target.path): INSTALLS += target
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
#ifndef PARKINGSPACEGUIDE_DETECTPRESENTER_H
|
||||
#define PARKINGSPACEGUIDE_DETECTPRESENTER_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QImage>
|
||||
#include <QString>
|
||||
@ -8,6 +10,8 @@
|
||||
#include "IRsLidarDevice.h"
|
||||
#include "IYParkingSpaceGuideStatus.h"
|
||||
|
||||
class AapgsModelClassifier;
|
||||
|
||||
enum class AirplanePresenceState
|
||||
{
|
||||
NotDetected,
|
||||
@ -51,8 +55,11 @@ struct ModelRecognitionResult
|
||||
class DetectPresenter
|
||||
{
|
||||
public:
|
||||
DetectPresenter() = default;
|
||||
~DetectPresenter() = default;
|
||||
DetectPresenter();
|
||||
~DetectPresenter();
|
||||
|
||||
DetectPresenter(const DetectPresenter&) = delete;
|
||||
DetectPresenter& operator=(const DetectPresenter&) = delete;
|
||||
|
||||
static QString GetAlgoVersion();
|
||||
|
||||
@ -69,6 +76,9 @@ public:
|
||||
int RecognizeModel2D(const QImage& frame,
|
||||
const QByteArray& recognitionContext,
|
||||
ModelRecognitionResult& result);
|
||||
|
||||
private:
|
||||
std::unique_ptr<AapgsModelClassifier> m_modelClassifier;
|
||||
};
|
||||
|
||||
#endif // PARKINGSPACEGUIDE_DETECTPRESENTER_H
|
||||
|
||||
@ -3,6 +3,15 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "AapgsModelClassifier.h"
|
||||
|
||||
DetectPresenter::DetectPresenter()
|
||||
: m_modelClassifier(std::make_unique<AapgsModelClassifier>())
|
||||
{
|
||||
}
|
||||
|
||||
DetectPresenter::~DetectPresenter() = default;
|
||||
|
||||
bool ModelRecognitionResult::IsVerified(double minimumConfidence) const
|
||||
{
|
||||
if (!std::isfinite(minimumConfidence)) {
|
||||
@ -22,7 +31,7 @@ bool ModelRecognitionResult::IsVerified(double minimumConfidence) const
|
||||
|
||||
QString DetectPresenter::GetAlgoVersion()
|
||||
{
|
||||
return QStringLiteral("停车引导预留算法接口 版本0.1");
|
||||
return QStringLiteral("停机引导算法接口 0.1 / AAPGS_model 1.0.0");
|
||||
}
|
||||
|
||||
int DetectPresenter::DetectAirplanePresence(const RsCloudData& cloud,
|
||||
@ -81,7 +90,7 @@ int DetectPresenter::DetectParkingSpaceGuide(const RsCloudData& cloud,
|
||||
info.guideStateCode = 0;
|
||||
info.guideText = QStringLiteral("预留算法接口");
|
||||
result.parkingSpaceInfoList.push_back(info);
|
||||
result.message = QStringLiteral("停车引导预留算法接口");
|
||||
result.message = QStringLiteral("停机引导预留算法接口");
|
||||
|
||||
const double modelVerifyDistance = algorithmParams.calibrationParam.modelVerifyDistance;
|
||||
const bool isInModelVerifyRange = modelVerifyDistance >= 0.0 &&
|
||||
@ -110,10 +119,25 @@ int DetectPresenter::RecognizeModel2D(const QImage& frame,
|
||||
const QByteArray& recognitionContext,
|
||||
ModelRecognitionResult& result)
|
||||
{
|
||||
Q_UNUSED(frame);
|
||||
Q_UNUSED(recognitionContext);
|
||||
|
||||
result = ModelRecognitionResult();
|
||||
result.message = QStringLiteral("机型识别算法接口未实现");
|
||||
if (!m_modelClassifier) {
|
||||
result.message = QStringLiteral("AAPGS机型识别器未初始化");
|
||||
return -1;
|
||||
}
|
||||
|
||||
AapgsModelClassifier::Classification classification;
|
||||
QString errorMessage;
|
||||
if (!m_modelClassifier->Classify(frame, classification, errorMessage)) {
|
||||
result.message = errorMessage.trimmed().isEmpty()
|
||||
? QStringLiteral("AAPGS机型识别失败")
|
||||
: errorMessage.trimmed();
|
||||
return -1;
|
||||
}
|
||||
|
||||
result.modelType = classification.modelType;
|
||||
result.confidence = classification.confidence;
|
||||
result.verified = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -137,7 +137,7 @@ int ParkingSpaceGuidePresenter::Init()
|
||||
{
|
||||
m_reconfiguring.store(false);
|
||||
NotifyWorkStatus(WorkStatus::InitIng);
|
||||
NotifyStatus("停车引导初始化中");
|
||||
NotifyStatus("停机引导初始化中");
|
||||
|
||||
int ret = InitConfig();
|
||||
if (ret != 0) {
|
||||
@ -192,7 +192,7 @@ int ParkingSpaceGuidePresenter::Init()
|
||||
|
||||
m_initialized = true;
|
||||
NotifyWorkStatus(WorkStatus::Ready);
|
||||
NotifyStatus("停车引导初始化完成");
|
||||
NotifyStatus("停机引导初始化完成");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -229,7 +229,7 @@ bool ParkingSpaceGuidePresenter::StartDetection(int cameraIndex)
|
||||
}
|
||||
|
||||
if (m_detectionRunning.load()) {
|
||||
NotifyStatus("停车引导实时检测已在运行");
|
||||
NotifyStatus("停机引导实时检测已在运行");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -259,12 +259,12 @@ bool ParkingSpaceGuidePresenter::StartDetection(int cameraIndex)
|
||||
m_detectionRunning.store(false);
|
||||
m_stopDetectionRequested.store(true);
|
||||
NotifyWorkStatus(WorkStatus::Error);
|
||||
NotifyStatus("启动停车引导实时检测线程失败");
|
||||
NotifyStatus("启动停机引导实时检测线程失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
NotifyWorkStatus(WorkStatus::Working);
|
||||
NotifyStatus("停车引导实时检测已开始");
|
||||
NotifyStatus("停机引导实时检测已开始");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -298,19 +298,19 @@ bool ParkingSpaceGuidePresenter::TriggerDetection(int cameraIndex)
|
||||
}
|
||||
|
||||
NotifyWorkStatus(WorkStatus::Detecting);
|
||||
NotifyStatus("停车引导单次检测已开始");
|
||||
NotifyStatus("停机引导单次检测已开始");
|
||||
bool success = false;
|
||||
ParkingGuideAlgorithmState algorithmState;
|
||||
try {
|
||||
success = DetectOnce(cloudFrame->cloud, algorithmState);
|
||||
} catch (const std::exception& e) {
|
||||
NotifyStatus(std::string("停车引导单次检测异常:") + e.what());
|
||||
NotifyStatus(std::string("停机引导单次检测异常:") + e.what());
|
||||
} catch (...) {
|
||||
NotifyStatus("停车引导单次检测发生未知异常");
|
||||
NotifyStatus("停机引导单次检测发生未知异常");
|
||||
}
|
||||
NotifyWorkStatus(success ? WorkStatus::Completed : WorkStatus::Error);
|
||||
NotifyStatus(success ? "停车引导单次检测完成"
|
||||
: "停车引导单次检测失败");
|
||||
NotifyStatus(success ? "停机引导单次检测完成"
|
||||
: "停机引导单次检测失败");
|
||||
return success;
|
||||
}
|
||||
|
||||
@ -335,11 +335,11 @@ bool ParkingSpaceGuidePresenter::DetectOnce(const RsCloudData& cloud,
|
||||
if (ret != 0) {
|
||||
result.errorCode = ret;
|
||||
if (result.message.isEmpty()) {
|
||||
result.message = QStringLiteral("停车引导检测失败");
|
||||
result.message = QStringLiteral("停机引导检测失败");
|
||||
}
|
||||
} else {
|
||||
if (result.parkingSpaceInfoList.empty() && result.message.isEmpty()) {
|
||||
result.message = QStringLiteral("无停车引导结果");
|
||||
result.message = QStringLiteral("无停机引导结果");
|
||||
}
|
||||
|
||||
if (control.needModelRecognition) {
|
||||
@ -385,9 +385,17 @@ bool ParkingSpaceGuidePresenter::DetectOnce(const RsCloudData& cloud,
|
||||
info.confidence = control.nextState.modelConfidence;
|
||||
}
|
||||
} else {
|
||||
const QString reason = modelResult.message.trimmed().isEmpty()
|
||||
? QStringLiteral("机型识别结果未通过验证")
|
||||
: modelResult.message;
|
||||
QString reason = modelResult.message.trimmed();
|
||||
if (reason.isEmpty() && modelRet == 0) {
|
||||
reason = QStringLiteral(
|
||||
"机型识别置信度不足:%1,实际%2,阈值%3")
|
||||
.arg(modelResult.modelType)
|
||||
.arg(modelResult.confidence, 0, 'f', 3)
|
||||
.arg(minimumConfidence, 0, 'f', 3);
|
||||
}
|
||||
if (reason.isEmpty()) {
|
||||
reason = QStringLiteral("机型识别结果未通过验证");
|
||||
}
|
||||
appendRecognitionFailure(reason);
|
||||
}
|
||||
}
|
||||
@ -608,10 +616,10 @@ void ParkingSpaceGuidePresenter::DetectionLoop()
|
||||
DetectOnce(cloudFrame->cloud, algorithmState);
|
||||
} catch (const std::exception& e) {
|
||||
PublishErrorResult(-1,
|
||||
QStringLiteral("停车引导检测异常:%1")
|
||||
QStringLiteral("停机引导检测异常:%1")
|
||||
.arg(QString::fromUtf8(e.what())));
|
||||
} catch (...) {
|
||||
PublishErrorResult(-1, QStringLiteral("停车引导检测发生未知异常"));
|
||||
PublishErrorResult(-1, QStringLiteral("停机引导检测发生未知异常"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -639,7 +647,7 @@ int ParkingSpaceGuidePresenter::StopDetection()
|
||||
|
||||
if (wasRunning) {
|
||||
NotifyWorkStatus(WorkStatus::Ready);
|
||||
NotifyStatus("停车引导实时检测已停止");
|
||||
NotifyStatus("停机引导实时检测已停止");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#ifndef PARKINGSPACEGUIDE_VERSION_H
|
||||
#define PARKINGSPACEGUIDE_VERSION_H
|
||||
|
||||
#define PARKINGSPACEGUIDE_APP_NAME "停车引导"
|
||||
#define PARKINGSPACEGUIDE_APP_NAME "停机引导"
|
||||
#define PARKINGSPACEGUIDE_VERSION_STRING "0.1.0"
|
||||
#define PARKINGSPACEGUIDE_BUILD_STRING "1"
|
||||
#define PARKINGSPACEGUIDE_FULL_VERSION_STRING "版本" PARKINGSPACEGUIDE_VERSION_STRING "_" PARKINGSPACEGUIDE_BUILD_STRING
|
||||
|
||||
@ -23,9 +23,9 @@ DialogAlgoArg::DialogAlgoArg(QWidget* parent)
|
||||
, ui(new Ui::DialogAlgoArg)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setWindowTitle(QStringLiteral("停车引导参数设置"));
|
||||
setWindowTitle(QStringLiteral("停机引导参数设置"));
|
||||
if (ui->label_title) {
|
||||
ui->label_title->setText(QStringLiteral("停车引导参数设置"));
|
||||
ui->label_title->setText(QStringLiteral("停机引导参数设置"));
|
||||
}
|
||||
BuildUi();
|
||||
connect(ui->tabWidget, &QTabWidget::currentChanged, this, [this](int index) {
|
||||
@ -101,7 +101,7 @@ void DialogAlgoArg::ShowTabGroup(ConfigPage page)
|
||||
|
||||
void DialogAlgoArg::UpdateTitle(ConfigPage page)
|
||||
{
|
||||
QString title = QStringLiteral("停车引导参数设置");
|
||||
QString title = QStringLiteral("停机引导参数设置");
|
||||
switch (page) {
|
||||
case ConfigPage::Detection:
|
||||
title = QStringLiteral("检测参数设置");
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
<string notr="true">color: rgb(221, 225, 233);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>停车引导参数设置</string>
|
||||
<string>停机引导参数设置</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
|
||||
@ -17,7 +17,7 @@ int main(int argc, char* argv[])
|
||||
if (instanceManager.isAnotherInstanceRunning()) {
|
||||
QMessageBox messageBox;
|
||||
messageBox.setWindowTitle(QObject::tr("应用已运行"));
|
||||
messageBox.setText(QObject::tr("停车引导程序已在运行。"));
|
||||
messageBox.setText(QObject::tr("停机引导程序已在运行。"));
|
||||
messageBox.addButton(QObject::tr("确定"), QMessageBox::AcceptRole);
|
||||
messageBox.exec();
|
||||
return 0;
|
||||
|
||||
@ -8,7 +8,9 @@
|
||||
#include <QFileDialog>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsView>
|
||||
#include <QHBoxLayout>
|
||||
#include <QIcon>
|
||||
#include <QLabel>
|
||||
#include <QListWidgetItem>
|
||||
#include <QMetaObject>
|
||||
#include <QPixmap>
|
||||
@ -107,6 +109,39 @@ QString FormatNumber(double value, const QString& unit, int precision = 2)
|
||||
return QStringLiteral("%1%2").arg(value, 0, 'f', precision).arg(unit);
|
||||
}
|
||||
|
||||
void ConfigureResultWidget(SingleTargetResultWidget* resultWidget)
|
||||
{
|
||||
if (!resultWidget) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep this presentation local to ParkingSpaceGuide; UICommon defaults are shared by other apps.
|
||||
auto* mainLayout = qobject_cast<QVBoxLayout*>(resultWidget->layout());
|
||||
if (mainLayout && mainLayout->count() > 0) {
|
||||
auto* headerLayout = qobject_cast<QHBoxLayout*>(mainLayout->itemAt(0)->layout());
|
||||
if (headerLayout && headerLayout->count() == 2) {
|
||||
headerLayout->setSpacing(12);
|
||||
headerLayout->setStretch(0, 0);
|
||||
headerLayout->setStretch(1, 0);
|
||||
headerLayout->addStretch(1);
|
||||
}
|
||||
}
|
||||
|
||||
resultWidget->setStyleSheet(
|
||||
resultWidget->styleSheet()
|
||||
+ QStringLiteral(
|
||||
"#singleTargetTitle { font-size: 28px; }"
|
||||
"#singleTargetStatus { font-size: 34px; font-weight: 700; padding: 0; }"
|
||||
"#singleTargetEmpty { font-size: 28px; }"
|
||||
"#singleTargetFieldName { font-size: 24px; }"
|
||||
"#singleTargetFieldValue { font-size: 26px; }"));
|
||||
|
||||
auto* statusLabel = resultWidget->findChild<QLabel*>(QStringLiteral("singleTargetStatus"));
|
||||
if (statusLabel) {
|
||||
statusLabel->setStyleSheet(QStringLiteral("background-color: transparent;"));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MainWindow::MainWindow(QWidget* parent)
|
||||
@ -114,9 +149,9 @@ MainWindow::MainWindow(QWidget* parent)
|
||||
, ui(new Ui::MainWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setWindowTitle(QStringLiteral("停车引导"));
|
||||
setWindowTitle(QStringLiteral("停机引导"));
|
||||
if (ui->label) {
|
||||
ui->label->setText(QStringLiteral("停车引导"));
|
||||
ui->label->setText(QStringLiteral("停机引导"));
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -288,6 +323,7 @@ void MainWindow::addDetectionResult(const DetectionResult& result)
|
||||
resultWidget->setTitle(tr("当前状态"));
|
||||
resultWidget->setStatus(GuideStateName(ParkingGuideState::Unknown), false);
|
||||
resultWidget->setEmptyText(tr("暂无停机状态结果"));
|
||||
ConfigureResultWidget(resultWidget);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -305,6 +341,7 @@ void MainWindow::addDetectionResult(const DetectionResult& result)
|
||||
{tr("偏转角"), FormatNumber(info.angle, tr("度"))},
|
||||
{tr("速度"), FormatNumber(info.aircraftSpeed, tr("米/秒"), 2)}
|
||||
});
|
||||
ConfigureResultWidget(resultWidget);
|
||||
}
|
||||
|
||||
void MainWindow::OnStatusUpdate(const std::string& statusMessage)
|
||||
@ -452,7 +489,7 @@ void MainWindow::on_btn_start_clicked()
|
||||
}
|
||||
|
||||
clearDetectionLog();
|
||||
updateStatusLog(tr("开始停车引导检测"));
|
||||
updateStatusLog(tr("开始停机引导检测"));
|
||||
if (!m_presenter->StartDetection(-1)) {
|
||||
updateStatusLog(tr("启动检测失败"));
|
||||
}
|
||||
@ -686,7 +723,7 @@ void MainWindow::onSaveDetectionData()
|
||||
}
|
||||
|
||||
const std::string timeStamp = CVrDateUtils::GetNowTime();
|
||||
const QString fileName = QString("停车引导_%1_%2.txt")
|
||||
const QString fileName = QString("停机引导_%1_%2.txt")
|
||||
.arg(m_presenter->GetDetectIndex())
|
||||
.arg(QString::fromStdString(timeStamp));
|
||||
const QString fullPath = QDir(dirPath).filePath(fileName);
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>停车引导</string>
|
||||
<string>停机引导</string>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
@ -75,7 +75,7 @@ color: rgb(239, 241, 245);</string>
|
||||
background-color: rgba(255, 255, 255, 0);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>停车引导</string>
|
||||
<string>停机引导</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QListWidget" name="detect_result_list">
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 89 KiB |
@ -5,6 +5,7 @@
|
||||
<COM Name="COM11" Remarks="" Baund="9600" Parity="0" StopBit="0" DataBit="0" FlowCtrl="0" TransMode="0" CharType="0" BreakTime="10" />
|
||||
<COM Name="COM13" Remarks="" Baund="9600" Parity="0" StopBit="0" DataBit="0" FlowCtrl="0" TransMode="0" CharType="0" BreakTime="10" />
|
||||
<COM Name="COM9" Remarks="" Baund="9600" Parity="0" StopBit="0" DataBit="0" FlowCtrl="0" TransMode="0" CharType="0" BreakTime="10" />
|
||||
<COM Name="COM1" Remarks="" Baund="9600" Parity="0" StopBit="0" DataBit="0" FlowCtrl="0" TransMode="0" CharType="0" BreakTime="10" />
|
||||
<NET Name="PLC_NET" Remarks="" DesIP="127.0.0.1" LocalPortID="502" DesPortID="0" LinkMode="1" LinkResetTime="0" LinkHoldTime="6000" TransMode="2" CharType="0" MaxAsynNum="15" />
|
||||
</PORT_LIST>
|
||||
<DEVICE_LIST>
|
||||
@ -13,7 +14,7 @@
|
||||
<DATA Name="拍照" BLOCK="2" PrtcTYPE="1" Unit="--" ShowType="0" Addr="1001" Gain="1" Size="1" BitOffset="0" BitNum="16" Range="--" Color="" ByteOder="0" WordOder="0" IntervalTime="0" IsShow="1" IsBRead="1" IsBWrite="0">
|
||||
<TIMEOUT Time="2000" ResendCount="0" />
|
||||
<CURVE IsShow="0" />
|
||||
<VALUE Stategy="0" TimeBngrWay="0" Coef_a="101" Coef_b="0" Coef_c="0" Coef_d="0" Coef_e="0" ResetStep="0" XInvl="1000" MaxRadom="0" MinRadom="0" MaxValue="0" MinValue="0" />
|
||||
<VALUE Stategy="0" TimeBngrWay="0" Coef_a="201" Coef_b="0" Coef_c="0" Coef_d="0" Coef_e="0" ResetStep="0" XInvl="1000" MaxRadom="0" MinRadom="0" MaxValue="0" MinValue="0" />
|
||||
</DATA>
|
||||
<DATA Name="完成" BLOCK="2" PrtcTYPE="1" Unit="--" ShowType="0" Addr="1003" Gain="1" Size="1" BitOffset="0" BitNum="16" Range="--" Color="" ByteOder="0" WordOder="0" IntervalTime="0" IsShow="1" IsBRead="1" IsBWrite="0">
|
||||
<TIMEOUT Time="2000" ResendCount="0" />
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB |
@ -42,15 +42,17 @@
|
||||
|
||||
### 4.2 产品型号与拍照触发值(D1000)
|
||||
|
||||
| 序号 | 产品型号 | 触发相机1 | 触发相机2 | 当前状态 |
|
||||
|------|----------|-----------|-----------|----------|
|
||||
| 1 | 4WE6 | 1 | 2 | 已实现 |
|
||||
| 2 | Z2S6 | 101 | 102 | 已实现 |
|
||||
| 3 | Z2FS6_30_2 | 201 | 202 | 已实现 |
|
||||
| 4 | DB10 | 301 | 302 | 已实现 |
|
||||
| 5 | DB20 | 401 | 402 | 已实现 |
|
||||
|序号| 产品型号 | 算法接口 | 触发相机1 | 触发相机2 | 高度 |
|
||||
|----|------|--------------|-----------|-----------|------|
|
||||
| 1 | 4WE6 | `API_1` | 1 | 2 | 47 |
|
||||
| 2 | Z2S6 | `API_2` | 101 | 102 | 47 |
|
||||
| 3 | Z2FS6_30_2 | `API_2` | 201 | 202 | 28 |
|
||||
| 4 | DB10 | `API_3` | 301 | 302 | 60 |
|
||||
| 5 | DB20 | `API_3` | 401 | 402 | 60 |
|
||||
|
||||
五种工件分别读取各自独立配置的检测参数。
|
||||
> **编号说明**:产品序号即软件工件编号,配置、界面、触发协议和算法分派统一使用 1-5,不再转换为算法 test 的历史工件编号。`API_1`、`API_2`、`API_3` 分别对应三套工件孔定位接口。
|
||||
|
||||
五种工件分别读取其软件工件编号对应的独立检测参数和工具参数。
|
||||
|
||||
### 4.3 检测结果状态码(D1002)
|
||||
|
||||
|
||||
@ -4,14 +4,24 @@
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <QImage>
|
||||
#include <QMetaType>
|
||||
|
||||
// 使用 AppCommon 提供的通用接口和类型
|
||||
#include "IVisionApplicationStatus.h"
|
||||
|
||||
// 算法 workpieceType 的高16位表示方向:1=反向,0=正向。
|
||||
inline bool IsReversedWorkpieceType(int workpieceType)
|
||||
{
|
||||
const std::uint32_t packedType = static_cast<std::uint32_t>(workpieceType);
|
||||
return ((packedType >> 16) & 0xFFFFu) == 1u;
|
||||
}
|
||||
|
||||
// 孔位置结构体
|
||||
struct HolePosition : public PositionData<double> {
|
||||
int workpieceType = 0; // 算法原始工件类型:高16位为方向,1=反向,0=正向
|
||||
|
||||
// 默认构造函数
|
||||
HolePosition() : PositionData<double>() {}
|
||||
|
||||
@ -30,7 +40,7 @@ struct HolePosition : public PositionData<double> {
|
||||
|
||||
// 工件孔检测结果结构体
|
||||
struct WorkpieceHoleDetectionResult : public DetectionResultData<HolePosition> {
|
||||
int workpieceType = 0; // 工件类型
|
||||
int workpieceType = 0; // 第一个工件的算法原始类型(包含高16位方向)
|
||||
|
||||
// 默认构造函数
|
||||
WorkpieceHoleDetectionResult() = default;
|
||||
|
||||
@ -33,7 +33,6 @@ public:
|
||||
const VrDebugParam& debugParam,
|
||||
LaserDataLoader& dataLoader,
|
||||
const double clibMatrix[16],
|
||||
int eulerOrder,
|
||||
int dirVectorInvert,
|
||||
int poseOutputOrder,
|
||||
WorkpieceHoleDetectionResult& detectionResult);
|
||||
|
||||
@ -4,21 +4,30 @@
|
||||
namespace WorkpieceProduct
|
||||
{
|
||||
|
||||
enum class AlgorithmApi
|
||||
{
|
||||
Api1,
|
||||
Api2,
|
||||
Api3
|
||||
};
|
||||
|
||||
struct ProductInfo
|
||||
{
|
||||
int number;
|
||||
const char* model;
|
||||
bool detectionSupported;
|
||||
AlgorithmApi algorithmApi;
|
||||
int triggerBase; // D1000 触发值 = triggerBase + cameraIndex
|
||||
};
|
||||
|
||||
inline const ProductInfo* Products()
|
||||
{
|
||||
static const ProductInfo products[] = {
|
||||
{1, "4WE6", true},
|
||||
{2, "Z2S6", true},
|
||||
{3, "Z2FS6_30_2", true},
|
||||
{4, "DB10", true},
|
||||
{5, "DB20", true}
|
||||
{1, "4WE6", true, AlgorithmApi::Api1, 0},
|
||||
{2, "Z2S6", true, AlgorithmApi::Api2, 100},
|
||||
{3, "Z2FS6_30_2", true, AlgorithmApi::Api2, 200},
|
||||
{4, "DB10", true, AlgorithmApi::Api3, 300},
|
||||
{5, "DB20", true, AlgorithmApi::Api3, 400}
|
||||
};
|
||||
return products;
|
||||
}
|
||||
@ -41,7 +50,11 @@ inline const ProductInfo* Find(int workpieceNumber)
|
||||
|
||||
inline int TriggerValue(int workpieceNumber, int cameraIndex)
|
||||
{
|
||||
return (workpieceNumber - 1) * 100 + cameraIndex;
|
||||
const ProductInfo* product = Find(workpieceNumber);
|
||||
if (!product || cameraIndex < 1 || cameraIndex > 2) {
|
||||
return -1;
|
||||
}
|
||||
return product->triggerBase + cameraIndex;
|
||||
}
|
||||
|
||||
inline bool DecodeTriggerValue(int triggerValue, int& workpieceNumber, int& cameraIndex)
|
||||
|
||||
@ -1,13 +1,57 @@
|
||||
#include "DetectPresenter.h"
|
||||
#include "workpieceHolePositioning_Export.h"
|
||||
#include "SG_baseAlgo_Export.h"
|
||||
#include "SG_errCode.h"
|
||||
#include "AlgoParamConverter.h"
|
||||
#include "IHandEyeCalib.h"
|
||||
#include "WorkpieceProduct.h"
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
|
||||
namespace {
|
||||
|
||||
HECEulerOrder ToHandEyeEulerOrder(int eulerOrder)
|
||||
{
|
||||
switch (eulerOrder) {
|
||||
case 10: return HECEulerOrder::XYZ;
|
||||
case 11: return HECEulerOrder::ZYX;
|
||||
case 12: return HECEulerOrder::ZXY;
|
||||
case 13: return HECEulerOrder::YXZ;
|
||||
case 14: return HECEulerOrder::YZX;
|
||||
case 15: return HECEulerOrder::XZY;
|
||||
default:
|
||||
LOG_WARNING("Unsupported tool euler order %d, fallback to 11 (ZYX)\n", eulerOrder);
|
||||
return HECEulerOrder::ZYX;
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyToolRotationToEyeAxes(IHandEyeCalib& handEyeCalib,
|
||||
HECEulerOrder eulerOrder,
|
||||
const VrToolParam& toolParam,
|
||||
std::vector<HECPoint3D>& axes)
|
||||
{
|
||||
if (axes.size() != 3 ||
|
||||
(std::fabs(toolParam.rotX) < 1e-9 &&
|
||||
std::fabs(toolParam.rotY) < 1e-9 &&
|
||||
std::fabs(toolParam.rotZ) < 1e-9)) {
|
||||
return;
|
||||
}
|
||||
|
||||
HECRotationMatrix toolRotation;
|
||||
handEyeCalib.EulerToRotationMatrix(
|
||||
HECEulerAngles::fromDegrees(toolParam.rotX, toolParam.rotY, toolParam.rotZ),
|
||||
eulerOrder,
|
||||
toolRotation);
|
||||
|
||||
const std::vector<HECPoint3D> originalAxes = axes;
|
||||
for (int column = 0; column < 3; ++column) {
|
||||
axes[column] = (originalAxes[0] * toolRotation.at(0, column)
|
||||
+ originalAxes[1] * toolRotation.at(1, column)
|
||||
+ originalAxes[2] * toolRotation.at(2, column)).normalized();
|
||||
}
|
||||
}
|
||||
|
||||
bool HasValidZPoint(const std::vector<SVzNL3DPosition>& line)
|
||||
{
|
||||
for (const SVzNL3DPosition& point : line) {
|
||||
@ -64,7 +108,6 @@ int DetectPresenter::DetectWorkpieceHole(
|
||||
const VrDebugParam& debugParam,
|
||||
LaserDataLoader& dataLoader,
|
||||
const double clibMatrix[16],
|
||||
int eulerOrder,
|
||||
int dirVectorInvert,
|
||||
int poseOutputOrder,
|
||||
WorkpieceHoleDetectionResult& detectionResult)
|
||||
@ -80,6 +123,8 @@ int DetectPresenter::DetectWorkpieceHole(
|
||||
LOG_ERROR("Algorithm profile is missing for workpiece %d\n", workpieceNumber);
|
||||
return ERR_CODE(DEV_CONFIG_ERR);
|
||||
}
|
||||
const VrToolParam& toolParam = profile->toolParam;
|
||||
const HECEulerOrder toolEulerOrder = ToHandEyeEulerOrder(toolParam.eulerOrder);
|
||||
|
||||
// 获取当前相机的校准参数
|
||||
VrCameraPlaneCalibParam cameraCalibParamValue;
|
||||
@ -136,6 +181,10 @@ int DetectPresenter::DetectWorkpieceHole(
|
||||
growParam.maxLineSkipNum, growParam.yDeviation_max,
|
||||
growParam.maxSkipDistance, growParam.zDeviation_max,
|
||||
growParam.minLTypeTreeLen, growParam.minVTypeTreeLen);
|
||||
LOG_INFO("Workpiece %d tool params: eulerOrder=%d, rot=(%.3f,%.3f,%.3f), offset=(%.3f,%.3f,%.3f)\n",
|
||||
workpieceNumber, toolParam.eulerOrder,
|
||||
toolParam.rotX, toolParam.rotY, toolParam.rotZ,
|
||||
toolParam.offsetX, toolParam.offsetY, toolParam.offsetZ);
|
||||
|
||||
// 准备平面校准参数
|
||||
SSG_planeCalibPara groundCalibPara = AlgoParamConverter::ToAlgoPlaneCalibParam(cameraCalibParam);
|
||||
@ -162,22 +211,26 @@ int DetectPresenter::DetectWorkpieceHole(
|
||||
|
||||
std::vector<WD_workpieceInfo> workpiecePositioning;
|
||||
const char* algorithmName = nullptr;
|
||||
switch (workpieceNumber) {
|
||||
case 1:
|
||||
const WorkpieceProduct::ProductInfo* product = WorkpieceProduct::Find(workpieceNumber);
|
||||
if (!product || !product->detectionSupported) {
|
||||
LOG_ERROR("No product or detection algorithm for software workpiece %d\n", workpieceNumber);
|
||||
return ERR_CODE(FUN_UNSUPPORT);
|
||||
}
|
||||
|
||||
switch (product->algorithmApi) {
|
||||
case WorkpieceProduct::AlgorithmApi::Api1:
|
||||
algorithmName = "wd_workpieceHolePositioning";
|
||||
wd_workpieceHolePositioning(
|
||||
algorithmData, workpiecePara, lineSegPara, filterParam, growParam,
|
||||
groundCalibPara, workpiecePositioning, &errCode);
|
||||
break;
|
||||
case 2:
|
||||
case 5:
|
||||
case WorkpieceProduct::AlgorithmApi::Api2:
|
||||
algorithmName = "wd_workpieceHolePositioning_2";
|
||||
wd_workpieceHolePositioning_2(
|
||||
algorithmData, workpiecePara, lineSegPara, filterParam, growParam,
|
||||
groundCalibPara, workpiecePositioning, &errCode);
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
case WorkpieceProduct::AlgorithmApi::Api3:
|
||||
algorithmName = "wd_workpieceHolePositioning_3";
|
||||
wd_workpieceHolePositioning_3(
|
||||
algorithmData, workpiecePara, workpieceParam.bigHoleDiameter,
|
||||
@ -185,7 +238,7 @@ int DetectPresenter::DetectWorkpieceHole(
|
||||
workpiecePositioning, &errCode);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("No detection algorithm for workpiece %d\n", workpieceNumber);
|
||||
LOG_ERROR("Unsupported algorithm API for software workpiece %d\n", workpieceNumber);
|
||||
return ERR_CODE(FUN_UNSUPPORT);
|
||||
}
|
||||
|
||||
@ -213,6 +266,12 @@ int DetectPresenter::DetectWorkpieceHole(
|
||||
if (i == 0) {
|
||||
detectionResult.workpieceType = workpiece.workpieceType;
|
||||
}
|
||||
if (IsReversedWorkpieceType(workpiece.workpieceType)) {
|
||||
// 算法执行成功,但反向工件属于异常物料;保留结果继续生成可视化图片。
|
||||
detectionResult.errorCode = SX_ERR_UNKNOWN_OBJECT;
|
||||
LOG_WARNING("Detected reversed workpiece %zu (workpieceType=%d)\n",
|
||||
i + 1, workpiece.workpieceType);
|
||||
}
|
||||
|
||||
// 六轴转换:位置+姿态(转换到机械臂坐标系)
|
||||
HECPoint3D eyeCenter(workpiece.center.x, workpiece.center.y, workpiece.center.z);
|
||||
@ -242,6 +301,9 @@ int DetectPresenter::DetectWorkpieceHole(
|
||||
break;
|
||||
}
|
||||
|
||||
// 工具姿态补偿在 Eye 坐标系内合成,再进入手眼旋转链。
|
||||
ApplyToolRotationToEyeAxes(*handEyeCalib, toolEulerOrder, toolParam, dirVectors_eye);
|
||||
|
||||
// 1. 位置转换:R * P_eye + T
|
||||
HECPoint3D ptRobot;
|
||||
handEyeCalib->TransformPoint(calibResult.R, calibResult.T, eyeCenter, ptRobot);
|
||||
@ -264,15 +326,17 @@ int DetectPresenter::DetectWorkpieceHole(
|
||||
R_pose[2][1] = dirVectors_robot[1].z;
|
||||
R_pose[2][2] = dirVectors_robot[2].z;
|
||||
|
||||
// 4. 使用算法库的 rotationMatrixToEulerZYX 转换为欧拉角(返回角度)
|
||||
SSG_EulerAngles robotRpy = rotationMatrixToEulerZYX(R_pose);
|
||||
// 4. 保持既有 ZYX 输出分解约定,确保零工具补偿时输出完全兼容。
|
||||
// toolParam.eulerOrder 只控制上面的工具补偿旋转合成顺序。
|
||||
const SSG_EulerAngles robotRpy = rotationMatrixToEulerZYX(R_pose);
|
||||
|
||||
// 将机器人坐标系下的位姿添加到positions列表
|
||||
// 根据 poseOutputOrder 重映射欧拉角输出顺序
|
||||
HolePosition centerRobotPos;
|
||||
centerRobotPos.x = ptRobot.x;
|
||||
centerRobotPos.y = ptRobot.y;
|
||||
centerRobotPos.z = ptRobot.z;
|
||||
centerRobotPos.workpieceType = workpiece.workpieceType;
|
||||
centerRobotPos.x = ptRobot.x + toolParam.offsetX;
|
||||
centerRobotPos.y = ptRobot.y + toolParam.offsetY;
|
||||
centerRobotPos.z = ptRobot.z + toolParam.offsetZ;
|
||||
switch (poseOutputOrder) {
|
||||
case POSE_ORDER_RPY:
|
||||
centerRobotPos.roll = robotRpy.roll;
|
||||
@ -315,9 +379,9 @@ int DetectPresenter::DetectWorkpieceHole(
|
||||
|
||||
if(debugParam.enableDebug && debugParam.printDetailLog){
|
||||
LOG_INFO("[Algo Thread] Direction vectors (eye): X=(%.3f,%.3f,%.3f), Y=(%.3f,%.3f,%.3f), Z=(%.3f,%.3f,%.3f)\n",
|
||||
workpiece.x_dir.x, workpiece.x_dir.y, workpiece.x_dir.z,
|
||||
workpiece.y_dir.x, workpiece.y_dir.y, workpiece.y_dir.z,
|
||||
workpiece.z_dir.x, workpiece.z_dir.y, workpiece.z_dir.z);
|
||||
dirVectors_eye[0].x, dirVectors_eye[0].y, dirVectors_eye[0].z,
|
||||
dirVectors_eye[1].x, dirVectors_eye[1].y, dirVectors_eye[1].z,
|
||||
dirVectors_eye[2].x, dirVectors_eye[2].y, dirVectors_eye[2].z);
|
||||
LOG_INFO("[Algo Thread] Direction vectors (robot): X=(%.3f,%.3f,%.3f), Y=(%.3f,%.3f,%.3f), Z=(%.3f,%.3f,%.3f)\n",
|
||||
dirVectors_robot[0].x, dirVectors_robot[0].y, dirVectors_robot[0].z,
|
||||
dirVectors_robot[1].x, dirVectors_robot[1].y, dirVectors_robot[1].z,
|
||||
@ -325,22 +389,25 @@ int DetectPresenter::DetectWorkpieceHole(
|
||||
LOG_INFO("[Algo Thread] Center Eye Coords: X=%.3f, Y=%.3f, Z=%.3f\n",
|
||||
workpiece.center.x, workpiece.center.y, workpiece.center.z);
|
||||
LOG_INFO("[Algo Thread] Center Robot Coords: X=%.3f, Y=%.3f, Z=%.3f, R=%.4f, P=%.4f, Y=%.4f\n",
|
||||
ptRobot.x, ptRobot.y, ptRobot.z,
|
||||
centerRobotPos.x, centerRobotPos.y, centerRobotPos.z,
|
||||
centerRobotPos.roll, centerRobotPos.pitch, centerRobotPos.yaw);
|
||||
}
|
||||
}
|
||||
|
||||
// 使用 PointCloudCanvas 生成点云图像(灰色底图 + 红色孔位标记 + 中心点编号)
|
||||
// 使用 PointCloudCanvas 生成点云图像(正向红点、反向蓝点 + 中心点编号)
|
||||
{
|
||||
PointCloudCanvas canvas = PointCloudCanvas::Create(xyzData);
|
||||
for (size_t i = 0; i < workpiecePositioning.size(); i++) {
|
||||
const WD_workpieceInfo& workpiece = workpiecePositioning[i];
|
||||
// 红色小圆点标记每个孔
|
||||
const QColor markerColor = IsReversedWorkpieceType(workpiece.workpieceType)
|
||||
? QColor(0, 0, 255)
|
||||
: QColor(255, 0, 0);
|
||||
// 小圆点标记每个孔
|
||||
for (size_t j = 0; j < workpiece.holes.size(); j++) {
|
||||
canvas.drawPoint(workpiece.holes[j].x, workpiece.holes[j].y, QColor(255, 0, 0), 4);
|
||||
canvas.drawPoint(workpiece.holes[j].x, workpiece.holes[j].y, markerColor, 4);
|
||||
}
|
||||
// 中心点标记 + 白色编号
|
||||
canvas.drawPoint(workpiece.center.x, workpiece.center.y, QColor(255, 0, 0), 4);
|
||||
canvas.drawPoint(workpiece.center.x, workpiece.center.y, markerColor, 4);
|
||||
canvas.drawText(workpiece.center.x, workpiece.center.y,
|
||||
QString("%1").arg(i + 1), Qt::white, 14);
|
||||
}
|
||||
|
||||
@ -62,6 +62,14 @@ const char* GetPLCStatusDescription(int statusCode)
|
||||
}
|
||||
}
|
||||
|
||||
bool ContainsReversedWorkpiece(const WorkpieceHoleDetectionResult& result)
|
||||
{
|
||||
return std::any_of(result.positions.begin(), result.positions.end(),
|
||||
[](const HolePosition& position) {
|
||||
return IsReversedWorkpieceType(position.workpieceType);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ConfigChangeListenerProxy::OnSystemConfigChanged(const SystemConfig& config)
|
||||
@ -390,16 +398,6 @@ int WorkpieceHolePresenter::ProcessAlgoDetection(std::vector<std::pair<EVzResult
|
||||
ConfigResult configResult = m_pConfigManager->GetConfigResult();
|
||||
VrDebugParam debugParam = configResult.debugParam;
|
||||
|
||||
// 获取旋转顺序配置(从当前相机对应的标定矩阵获取)
|
||||
int eulerOrder = 11; // 默认外旋ZYX
|
||||
int matrixIdx = m_currentCameraIndex - 1;
|
||||
if (matrixIdx >= 0 && matrixIdx < static_cast<int>(configResult.handEyeCalibMatrixList.size())) {
|
||||
eulerOrder = configResult.handEyeCalibMatrixList[matrixIdx].eulerOrder;
|
||||
} else if (!configResult.handEyeCalibMatrixList.empty()) {
|
||||
eulerOrder = configResult.handEyeCalibMatrixList[0].eulerOrder;
|
||||
}
|
||||
LOG_INFO("[Algo Thread] Using euler order: %d\n", eulerOrder);
|
||||
|
||||
// 获取方向向量反向配置
|
||||
int dirVectorInvert = configResult.plcRobotServerConfig.dirVectorInvert;
|
||||
LOG_INFO("[Algo Thread] Using dir vector invert: %d\n", dirVectorInvert);
|
||||
@ -412,12 +410,22 @@ int WorkpieceHolePresenter::ProcessAlgoDetection(std::vector<std::pair<EVzResult
|
||||
|
||||
int nRet = m_pDetectPresenter->DetectWorkpieceHole(workpieceNumber, m_currentCameraIndex, detectionDataCache,
|
||||
algorithmParams, debugParam, m_dataLoader,
|
||||
currentClibMatrix.clibMatrix, eulerOrder, dirVectorInvert,
|
||||
currentClibMatrix.clibMatrix, dirVectorInvert,
|
||||
poseOutputOrder, detectionResult);
|
||||
const bool hasReversedWorkpiece = ContainsReversedWorkpiece(detectionResult);
|
||||
// 根据项目类型选择处理方式
|
||||
if (GetStatusCallback<IYWorkpieceHoleStatus>()) {
|
||||
QString err = QString("错误:%1").arg(nRet);
|
||||
if (auto pStatus = GetStatusCallback<IYWorkpieceHoleStatus>()) pStatus->OnStatusUpdate(QString("检测%1").arg(SUCCESS == nRet ? "成功": err).toStdString());
|
||||
QString detectionStatus;
|
||||
if (nRet != SUCCESS) {
|
||||
detectionStatus = QString("检测错误:%1").arg(nRet);
|
||||
} else if (hasReversedWorkpiece) {
|
||||
detectionStatus = "检测异常:工件反向";
|
||||
} else {
|
||||
detectionStatus = "检测成功";
|
||||
}
|
||||
if (auto pStatus = GetStatusCallback<IYWorkpieceHoleStatus>()) {
|
||||
pStatus->OnStatusUpdate(detectionStatus.toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO("[Algo Thread] wd_workpieceHolePositioning detected %zu workpieces time : %.2f ms\n", detectionResult.positions.size(), oTimeUtils.GetElapsedTimeInMilliSec());
|
||||
@ -435,7 +443,9 @@ int WorkpieceHolePresenter::ProcessAlgoDetection(std::vector<std::pair<EVzResult
|
||||
}
|
||||
|
||||
// 更新状态
|
||||
QString statusMsg = QString("检测完成,发现%1个工件").arg(detectionResult.positions.size());
|
||||
QString statusMsg = hasReversedWorkpiece
|
||||
? QString("检测完成,发现%1个工件,其中包含反向工件").arg(detectionResult.positions.size())
|
||||
: QString("检测完成,发现%1个工件").arg(detectionResult.positions.size());
|
||||
if (auto pStatus = GetStatusCallback<IYWorkpieceHoleStatus>()) pStatus->OnStatusUpdate(statusMsg.toStdString());
|
||||
|
||||
// 发送检测结果给TCP客户端
|
||||
@ -895,7 +905,9 @@ void WorkpieceHolePresenter::SendCoordinateDataToPLC(const WorkpieceHoleDetectio
|
||||
if (auto pStatus = GetStatusCallback<IYWorkpieceHoleStatus>()) {
|
||||
switch (plcStatus) {
|
||||
case PLCModbusClient::RESULT_ABNORMAL_MATERIAL:
|
||||
pStatus->OnStatusUpdate("检测结果为异常物料,已通知PLC");
|
||||
pStatus->OnStatusUpdate(ContainsReversedWorkpiece(result)
|
||||
? "检测到反向工件,已按异常物料通知PLC"
|
||||
: "检测结果为异常物料,已通知PLC");
|
||||
break;
|
||||
case PLCModbusClient::RESULT_NO_PRODUCT:
|
||||
pStatus->OnStatusUpdate("检测结果为无产品,已通知PLC");
|
||||
@ -926,7 +938,7 @@ void WorkpieceHolePresenter::SendCoordinateDataToPLC(const WorkpieceHoleDetectio
|
||||
int poseOutputOrder = configResult.plcRobotServerConfig.poseOutputOrder;
|
||||
LOG_INFO("Using pose output order: %d\n", poseOutputOrder);
|
||||
|
||||
// 协议单点输出改为最后一个工件。
|
||||
// 协议单点输出使用最后一个工件。
|
||||
const auto& pos = result.positions.back();
|
||||
PLCModbusClient::CoordinateData coord;
|
||||
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
#define WORKPIECEHOLE_APP_NAME "工件孔定位"
|
||||
|
||||
// 版本字符串
|
||||
#define WORKPIECEHOLE_VERSION_STRING "1.2.1"
|
||||
#define WORKPIECEHOLE_BUILD_STRING "1"
|
||||
#define WORKPIECEHOLE_VERSION_STRING "1.2.2"
|
||||
#define WORKPIECEHOLE_BUILD_STRING "2"
|
||||
#define WORKPIECEHOLE_FULL_VERSION_STRING "V" WORKPIECEHOLE_VERSION_STRING "_" WORKPIECEHOLE_BUILD_STRING
|
||||
|
||||
// 构建日期
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
# 1.2.2
|
||||
## build_1 2026-07-14
|
||||
1. 工件3和工件5 反了。
|
||||
2. 每个工件有独立的工具参数调整
|
||||
|
||||
# 1.2.1
|
||||
## build_1 2026-07-14
|
||||
1. 更新算法包含1-5工件算法
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#include "PathManager.h"
|
||||
#include "StyledMessageBox.h"
|
||||
#include "HandEyeCalibWidget.h"
|
||||
#include "ToolExtrinsicWidget.h"
|
||||
#include "WorkpieceProduct.h"
|
||||
|
||||
#include "VrLog.h"
|
||||
@ -24,6 +25,7 @@ DialogAlgoarg::DialogAlgoarg(ConfigManager* configManager, QWidget *parent)
|
||||
, m_currentWorkpieceNumber(1)
|
||||
, m_isSwitchingWorkpieceProfile(true)
|
||||
, m_handEyeCalibWidget(nullptr)
|
||||
, m_toolExtrinsicWidget(nullptr)
|
||||
{
|
||||
try {
|
||||
ui->setupUi(this);
|
||||
@ -52,6 +54,9 @@ DialogAlgoarg::DialogAlgoarg(ConfigManager* configManager, QWidget *parent)
|
||||
// 初始化工件参数组下拉框
|
||||
InitWorkpieceProfileComboBox();
|
||||
|
||||
// 初始化工具参数 tab;工具参数由顶部工件选择器驱动
|
||||
InitToolExtrinsicTab();
|
||||
|
||||
// 从配置文件加载数据到界面
|
||||
LoadConfigToUI();
|
||||
m_isSwitchingWorkpieceProfile = false;
|
||||
@ -156,17 +161,39 @@ void DialogAlgoarg::LoadCurrentWorkpieceProfileToUI()
|
||||
LoadLineSegParamToUI(profile.lineSegParam);
|
||||
LoadFilterParamToUI(profile.filterParam);
|
||||
LoadGrowParamToUI(profile.growParam);
|
||||
|
||||
if (m_toolExtrinsicWidget) {
|
||||
m_toolExtrinsicWidget->setData(
|
||||
profile.toolParam.eulerOrder,
|
||||
profile.toolParam.rotX, profile.toolParam.rotY, profile.toolParam.rotZ,
|
||||
profile.toolParam.offsetX, profile.toolParam.offsetY, profile.toolParam.offsetZ);
|
||||
}
|
||||
}
|
||||
|
||||
bool DialogAlgoarg::SaveCurrentWorkpieceProfileFromUI()
|
||||
{
|
||||
if (m_toolExtrinsicWidget && !m_toolExtrinsicWidget->hasAcceptableInput()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VrWorkpieceAlgorithmProfile& profile =
|
||||
m_algorithmParams.GetOrCreateWorkpieceProfile(m_currentWorkpieceNumber);
|
||||
|
||||
return SaveWorkpieceHoleParamFromUI(profile.workpieceHoleParam) &&
|
||||
SaveLineSegParamFromUI(profile.lineSegParam) &&
|
||||
SaveFilterParamFromUI(profile.filterParam) &&
|
||||
SaveGrowParamFromUI(profile.growParam);
|
||||
if (!SaveWorkpieceHoleParamFromUI(profile.workpieceHoleParam) ||
|
||||
!SaveLineSegParamFromUI(profile.lineSegParam) ||
|
||||
!SaveFilterParamFromUI(profile.filterParam) ||
|
||||
!SaveGrowParamFromUI(profile.growParam)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_toolExtrinsicWidget) {
|
||||
m_toolExtrinsicWidget->getData(
|
||||
profile.toolParam.eulerOrder,
|
||||
profile.toolParam.rotX, profile.toolParam.rotY, profile.toolParam.rotZ,
|
||||
profile.toolParam.offsetX, profile.toolParam.offsetY, profile.toolParam.offsetZ);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DialogAlgoarg::on_comboBox_workpieceProfile_currentIndexChanged(int index)
|
||||
@ -243,8 +270,9 @@ bool DialogAlgoarg::SaveConfigFromUI()
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取当前配置
|
||||
SystemConfig systemConfig = m_pConfigManager->GetConfig();
|
||||
// 基于当前配置构造候选值;落盘失败时恢复运行时配置。
|
||||
const SystemConfig previousSystemConfig = m_pConfigManager->GetConfig();
|
||||
SystemConfig systemConfig = previousSystemConfig;
|
||||
VrAlgorithmParams& algoParams = systemConfig.configResult.algorithmParams;
|
||||
|
||||
// 先暂存当前工件,再一次性保存所有工件参数组。
|
||||
@ -300,7 +328,12 @@ bool DialogAlgoarg::SaveConfigFromUI()
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_pConfigManager->SaveConfigToFile(m_configFilePath.toStdString());
|
||||
if (!m_pConfigManager->SaveConfigToFile(m_configFilePath.toStdString())) {
|
||||
m_pConfigManager->UpdateFullConfig(previousSystemConfig);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
return false;
|
||||
@ -407,6 +440,21 @@ void DialogAlgoarg::on_btn_camer_cancel_clicked()
|
||||
reject();
|
||||
}
|
||||
|
||||
// ========== 工具参数相关实现 ==========
|
||||
|
||||
void DialogAlgoarg::InitToolExtrinsicTab()
|
||||
{
|
||||
if (!ui || !ui->verticalLayout_toolExtrinsicHost || m_toolExtrinsicWidget) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_toolExtrinsicWidget = new ToolExtrinsicWidget(this);
|
||||
m_toolExtrinsicWidget->setCameraSelectorVisible(false);
|
||||
m_toolExtrinsicWidget->setTargetOffsetVisible(true);
|
||||
m_toolExtrinsicWidget->setAxialAngleRangeVisible(false);
|
||||
ui->verticalLayout_toolExtrinsicHost->addWidget(m_toolExtrinsicWidget);
|
||||
}
|
||||
|
||||
// ========== 手眼标定相关实现 ==========
|
||||
|
||||
void DialogAlgoarg::InitHandEyeCalibTab()
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#include "ConfigManager.h"
|
||||
|
||||
class HandEyeCalibWidget;
|
||||
class ToolExtrinsicWidget;
|
||||
|
||||
namespace Ui {
|
||||
class DialogAlgoarg;
|
||||
@ -59,6 +60,9 @@ private:
|
||||
void InitHandEyeCalibTab();
|
||||
void InitEulerOrderComboBox();
|
||||
|
||||
// 工具参数
|
||||
void InitToolExtrinsicTab();
|
||||
|
||||
private:
|
||||
Ui::DialogAlgoarg *ui;
|
||||
ConfigManager* m_pConfigManager;
|
||||
@ -69,6 +73,9 @@ private:
|
||||
|
||||
// 手眼标定共享控件
|
||||
HandEyeCalibWidget* m_handEyeCalibWidget;
|
||||
|
||||
// 工具参数共享控件
|
||||
ToolExtrinsicWidget* m_toolExtrinsicWidget;
|
||||
};
|
||||
|
||||
#endif // DIALOGALGOARG_H
|
||||
|
||||
@ -774,6 +774,12 @@ QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px;
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_toolExtrinsic">
|
||||
<attribute name="title">
|
||||
<string>工具参数</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_toolExtrinsicHost"/>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_network">
|
||||
<attribute name="title">
|
||||
<string>网络配置</string>
|
||||
|
||||
@ -434,7 +434,7 @@ int MainWindow::selectWorkpieceForDetection()
|
||||
layout.setContentsMargins(20, 20, 20, 20);
|
||||
layout.setSpacing(12);
|
||||
|
||||
QLabel header(tr("序号 产品型号"), &dialog);
|
||||
QLabel header(tr("显示序号 - 产品型号"), &dialog);
|
||||
layout.addWidget(&header);
|
||||
|
||||
QListWidget productList(&dialog);
|
||||
@ -446,7 +446,7 @@ int MainWindow::selectWorkpieceForDetection()
|
||||
const WorkpieceProduct::ProductInfo* products = WorkpieceProduct::Products();
|
||||
for (int i = 0; i < WorkpieceProduct::ProductCount(); ++i) {
|
||||
const WorkpieceProduct::ProductInfo& product = products[i];
|
||||
QString text = QString("%1 %2")
|
||||
QString text = tr("%1 - %2")
|
||||
.arg(product.number)
|
||||
.arg(QString::fromLatin1(product.model));
|
||||
if (!product.detectionSupported) {
|
||||
|
||||
@ -26,7 +26,7 @@ struct VrOutlierFilterParam
|
||||
*/
|
||||
struct VrLineSegParam
|
||||
{
|
||||
double distScale = 3.0; // 计算方向角的窗口比例因子
|
||||
double distScale = 10.0; // 计算方向角的窗口比例因子
|
||||
double segGapTh_y = 15.0; // Y方向间隔阈值,大于此值认为是分段
|
||||
double segGapTh_z = 0.0; // Z方向间隔阈值,大于此值认为是分段
|
||||
};
|
||||
@ -51,7 +51,7 @@ struct VrWorkpieceHoleParam
|
||||
{
|
||||
int workpieceType = 0; // 工件类型
|
||||
double holeDiameter = 6.0; // 孔直径 (mm)
|
||||
double bigHoleDiameter = 33.0; // 大孔直径 (mm),工件3算法使用
|
||||
double bigHoleDiameter = 33.0; // 大孔直径 (mm),API_3 算法使用
|
||||
double holeDist_L = 40.0; // 孔间距_长 (mm)
|
||||
double holeDist_W = 32.0; // 孔间距_宽 (mm)
|
||||
double xLen = 44.0; // x轴方向长度,用于绘制平面 (mm)
|
||||
@ -59,6 +59,22 @@ struct VrWorkpieceHoleParam
|
||||
double H = 47.0; // 高度 (mm)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 工具参数
|
||||
*
|
||||
* 每个工件独立保存一套工具姿态补偿和目标点偏移参数。
|
||||
*/
|
||||
struct VrToolParam
|
||||
{
|
||||
int eulerOrder = 11; // 欧拉角旋转顺序,默认外旋 ZYX
|
||||
double rotX = 0.0; // 工具姿态 X 轴补偿 (deg)
|
||||
double rotY = 0.0; // 工具姿态 Y 轴补偿 (deg)
|
||||
double rotZ = 0.0; // 工具姿态 Z 轴补偿 (deg)
|
||||
double offsetX = 0.0; // 目标点 X 方向偏移 (mm)
|
||||
double offsetY = 0.0; // 目标点 Y 方向偏移 (mm)
|
||||
double offsetZ = 0.0; // 目标点 Z 方向偏移 (mm)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief PLC Modbus 寄存器地址配置
|
||||
*
|
||||
@ -198,6 +214,7 @@ struct VrWorkpieceAlgorithmProfile
|
||||
VrLineSegParam lineSegParam;
|
||||
VrOutlierFilterParam filterParam;
|
||||
VrTreeGrowParam growParam;
|
||||
VrToolParam toolParam;
|
||||
|
||||
VrWorkpieceAlgorithmProfile() = default;
|
||||
|
||||
@ -220,7 +237,7 @@ struct VrWorkpieceAlgorithmProfile
|
||||
workpieceHoleParam.yLen = 70.0;
|
||||
workpieceHoleParam.H = 47.0;
|
||||
|
||||
lineSegParam.distScale = 3.0;
|
||||
lineSegParam.distScale = 10.0;
|
||||
lineSegParam.segGapTh_y = 15.0;
|
||||
lineSegParam.segGapTh_z = 0.0;
|
||||
|
||||
@ -234,13 +251,29 @@ struct VrWorkpieceAlgorithmProfile
|
||||
growParam.minLTypeTreeLen = 2.0;
|
||||
growParam.minVTypeTreeLen = 2.0;
|
||||
|
||||
if (number == 3 || number == 4) {
|
||||
toolParam = VrToolParam();
|
||||
|
||||
// 参数编号始终采用软件产品顺序,不依赖算法 test 的历史工件编号。
|
||||
switch (number) {
|
||||
case 1: // 4WE6 / API_1 / 原 test1
|
||||
break;
|
||||
case 2: // Z2S6 / API_2 / 原 test2
|
||||
workpieceHoleParam.holeDiameter = 8.0;
|
||||
break;
|
||||
case 3: // Z2FS6_30_2 / API_2 / 原 test5
|
||||
workpieceHoleParam.H = 28.0;
|
||||
lineSegParam.distScale = 14.0;
|
||||
growParam.minLTypeTreeLen = 1.5;
|
||||
growParam.minVTypeTreeLen = 1.5;
|
||||
break;
|
||||
case 4: // DB10 / API_3 / 原 test3
|
||||
case 5: // DB20 / API_3 / 原 test4
|
||||
workpieceHoleParam.H = 60.0;
|
||||
lineSegParam.distScale = 30.0;
|
||||
lineSegParam.segGapTh_y = 40.0;
|
||||
} else if (number == 5) {
|
||||
workpieceHoleParam.holeDiameter = 10.0;
|
||||
workpieceHoleParam.H = 28.0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -10,6 +10,23 @@ using namespace tinyxml2;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kSoftwareProductNumberingVersion = 2;
|
||||
|
||||
int ToSoftwareWorkpieceNumber(int storedNumber, bool usesSoftwareProductNumbering)
|
||||
{
|
||||
if (usesSoftwareProductNumbering) {
|
||||
return storedNumber;
|
||||
}
|
||||
|
||||
// 旧配置按算法 test 编号保存;运行时统一转换为软件产品编号。
|
||||
switch (storedNumber) {
|
||||
case 3: return 4;
|
||||
case 4: return 5;
|
||||
case 5: return 3;
|
||||
default: return storedNumber;
|
||||
}
|
||||
}
|
||||
|
||||
void LoadWorkpieceProfileParams(
|
||||
XMLElement* parentElement,
|
||||
VrWorkpieceAlgorithmProfile& profile)
|
||||
@ -75,6 +92,25 @@ void LoadWorkpieceProfileParams(
|
||||
if (growParamElement->Attribute("minVTypeTreeLen"))
|
||||
profile.growParam.minVTypeTreeLen = growParamElement->DoubleAttribute("minVTypeTreeLen");
|
||||
}
|
||||
|
||||
XMLElement* toolParamElement = parentElement->FirstChildElement("ToolParam");
|
||||
if (toolParamElement)
|
||||
{
|
||||
if (toolParamElement->Attribute("eulerOrder"))
|
||||
profile.toolParam.eulerOrder = toolParamElement->IntAttribute("eulerOrder");
|
||||
if (toolParamElement->Attribute("rotX"))
|
||||
profile.toolParam.rotX = toolParamElement->DoubleAttribute("rotX");
|
||||
if (toolParamElement->Attribute("rotY"))
|
||||
profile.toolParam.rotY = toolParamElement->DoubleAttribute("rotY");
|
||||
if (toolParamElement->Attribute("rotZ"))
|
||||
profile.toolParam.rotZ = toolParamElement->DoubleAttribute("rotZ");
|
||||
if (toolParamElement->Attribute("offsetX"))
|
||||
profile.toolParam.offsetX = toolParamElement->DoubleAttribute("offsetX");
|
||||
if (toolParamElement->Attribute("offsetY"))
|
||||
profile.toolParam.offsetY = toolParamElement->DoubleAttribute("offsetY");
|
||||
if (toolParamElement->Attribute("offsetZ"))
|
||||
profile.toolParam.offsetZ = toolParamElement->DoubleAttribute("offsetZ");
|
||||
}
|
||||
}
|
||||
|
||||
void SaveWorkpieceProfileParams(
|
||||
@ -116,6 +152,16 @@ void SaveWorkpieceProfileParams(
|
||||
growParamElement->SetAttribute("minLTypeTreeLen", profile.growParam.minLTypeTreeLen);
|
||||
growParamElement->SetAttribute("minVTypeTreeLen", profile.growParam.minVTypeTreeLen);
|
||||
profileElement->InsertEndChild(growParamElement);
|
||||
|
||||
XMLElement* toolParamElement = doc.NewElement("ToolParam");
|
||||
toolParamElement->SetAttribute("eulerOrder", profile.toolParam.eulerOrder);
|
||||
toolParamElement->SetAttribute("rotX", profile.toolParam.rotX);
|
||||
toolParamElement->SetAttribute("rotY", profile.toolParam.rotY);
|
||||
toolParamElement->SetAttribute("rotZ", profile.toolParam.rotZ);
|
||||
toolParamElement->SetAttribute("offsetX", profile.toolParam.offsetX);
|
||||
toolParamElement->SetAttribute("offsetY", profile.toolParam.offsetY);
|
||||
toolParamElement->SetAttribute("offsetZ", profile.toolParam.offsetZ);
|
||||
profileElement->InsertEndChild(toolParamElement);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -149,6 +195,14 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
|
||||
return LOAD_CONFIG_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
const int productNumberingVersion =
|
||||
root->IntAttribute("productNumberingVersion", 1);
|
||||
const bool usesSoftwareProductNumbering =
|
||||
productNumberingVersion >= kSoftwareProductNumberingVersion;
|
||||
if (!usesSoftwareProductNumbering) {
|
||||
LOG_INFO("Migrating legacy workpiece profiles to software product numbering\n");
|
||||
}
|
||||
|
||||
// 解析摄像头列表
|
||||
ConfigXmlUtils::LoadCameraList(root, configResult.cameraList);
|
||||
|
||||
@ -181,7 +235,10 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
|
||||
XMLElement* profileElement = algoParamsElement->FirstChildElement("WorkpieceParamGroup");
|
||||
if (profileElement) {
|
||||
while (profileElement) {
|
||||
const int workpieceNumber = profileElement->IntAttribute("workpieceNumber", 0);
|
||||
const int storedWorkpieceNumber =
|
||||
profileElement->IntAttribute("workpieceNumber", 0);
|
||||
const int workpieceNumber = ToSoftwareWorkpieceNumber(
|
||||
storedWorkpieceNumber, usesSoftwareProductNumbering);
|
||||
VrWorkpieceAlgorithmProfile* workpieceProfile =
|
||||
configResult.algorithmParams.FindWorkpieceProfile(workpieceNumber);
|
||||
if (workpieceProfile) {
|
||||
@ -265,6 +322,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
|
||||
|
||||
// 创建根元素
|
||||
XMLElement* root = doc.NewElement("WorkpieceHoleConfig");
|
||||
root->SetAttribute("productNumberingVersion", kSoftwareProductNumberingVersion);
|
||||
doc.InsertEndChild(root);
|
||||
|
||||
// 添加摄像头列表
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<WorkpieceHoleConfig>
|
||||
<WorkpieceHoleConfig productNumberingVersion="2">
|
||||
<Cameras>
|
||||
<Camera name="Camera1" ip="192.168.1.100"/>
|
||||
</Cameras>
|
||||
@ -10,32 +10,37 @@
|
||||
<WorkpieceParamGroup workpieceNumber="1">
|
||||
<WorkpieceHoleParam workpieceType="0" holeDiameter="6.0" bigHoleDiameter="33.0" holeDist_L="40.0" holeDist_W="32.0" xLen="44.0" yLen="70.0" H="47.0"/>
|
||||
<FilterParam continuityTh="20.0" outlierTh="5.0"/>
|
||||
<LineSegParam distScale="3.0" segGapTh_y="15.0" segGapTh_z="0.0"/>
|
||||
<LineSegParam distScale="10.0" segGapTh_y="15.0" segGapTh_z="0.0"/>
|
||||
<GrowParam maxLineSkipNum="2" yDeviation_max="4.0" maxSkipDistance="0.0" zDeviation_max="10.0" minLTypeTreeLen="2.0" minVTypeTreeLen="2.0"/>
|
||||
<ToolParam eulerOrder="11" rotX="0.0" rotY="0.0" rotZ="0.0" offsetX="0.0" offsetY="0.0" offsetZ="0.0"/>
|
||||
</WorkpieceParamGroup>
|
||||
<WorkpieceParamGroup workpieceNumber="2">
|
||||
<WorkpieceHoleParam workpieceType="0" holeDiameter="6.0" bigHoleDiameter="33.0" holeDist_L="40.0" holeDist_W="32.0" xLen="44.0" yLen="70.0" H="47.0"/>
|
||||
<WorkpieceHoleParam workpieceType="0" holeDiameter="8.0" bigHoleDiameter="33.0" holeDist_L="40.0" holeDist_W="32.0" xLen="44.0" yLen="70.0" H="47.0"/>
|
||||
<FilterParam continuityTh="5.0" outlierTh="5.0"/>
|
||||
<LineSegParam distScale="3.0" segGapTh_y="15.0" segGapTh_z="0.0"/>
|
||||
<LineSegParam distScale="10.0" segGapTh_y="15.0" segGapTh_z="0.0"/>
|
||||
<GrowParam maxLineSkipNum="2" yDeviation_max="4.0" maxSkipDistance="0.0" zDeviation_max="10.0" minLTypeTreeLen="2.0" minVTypeTreeLen="2.0"/>
|
||||
<ToolParam eulerOrder="11" rotX="0.0" rotY="0.0" rotZ="0.0" offsetX="0.0" offsetY="0.0" offsetZ="0.0"/>
|
||||
</WorkpieceParamGroup>
|
||||
<WorkpieceParamGroup workpieceNumber="3">
|
||||
<WorkpieceHoleParam workpieceType="0" holeDiameter="6.0" bigHoleDiameter="33.0" holeDist_L="40.0" holeDist_W="32.0" xLen="44.0" yLen="70.0" H="60.0"/>
|
||||
<WorkpieceHoleParam workpieceType="0" holeDiameter="6.0" bigHoleDiameter="33.0" holeDist_L="40.0" holeDist_W="32.0" xLen="44.0" yLen="70.0" H="28.0"/>
|
||||
<FilterParam continuityTh="5.0" outlierTh="5.0"/>
|
||||
<LineSegParam distScale="30.0" segGapTh_y="40.0" segGapTh_z="0.0"/>
|
||||
<GrowParam maxLineSkipNum="2" yDeviation_max="4.0" maxSkipDistance="0.0" zDeviation_max="10.0" minLTypeTreeLen="2.0" minVTypeTreeLen="2.0"/>
|
||||
<LineSegParam distScale="14.0" segGapTh_y="15.0" segGapTh_z="0.0"/>
|
||||
<GrowParam maxLineSkipNum="2" yDeviation_max="4.0" maxSkipDistance="0.0" zDeviation_max="10.0" minLTypeTreeLen="1.5" minVTypeTreeLen="1.5"/>
|
||||
<ToolParam eulerOrder="11" rotX="0.0" rotY="0.0" rotZ="0.0" offsetX="0.0" offsetY="0.0" offsetZ="0.0"/>
|
||||
</WorkpieceParamGroup>
|
||||
<WorkpieceParamGroup workpieceNumber="4">
|
||||
<WorkpieceHoleParam workpieceType="0" holeDiameter="6.0" bigHoleDiameter="33.0" holeDist_L="40.0" holeDist_W="32.0" xLen="44.0" yLen="70.0" H="60.0"/>
|
||||
<FilterParam continuityTh="5.0" outlierTh="5.0"/>
|
||||
<LineSegParam distScale="30.0" segGapTh_y="40.0" segGapTh_z="0.0"/>
|
||||
<GrowParam maxLineSkipNum="2" yDeviation_max="4.0" maxSkipDistance="0.0" zDeviation_max="10.0" minLTypeTreeLen="2.0" minVTypeTreeLen="2.0"/>
|
||||
<ToolParam eulerOrder="11" rotX="0.0" rotY="0.0" rotZ="0.0" offsetX="0.0" offsetY="0.0" offsetZ="0.0"/>
|
||||
</WorkpieceParamGroup>
|
||||
<WorkpieceParamGroup workpieceNumber="5">
|
||||
<WorkpieceHoleParam workpieceType="0" holeDiameter="10.0" bigHoleDiameter="33.0" holeDist_L="40.0" holeDist_W="32.0" xLen="44.0" yLen="70.0" H="28.0"/>
|
||||
<WorkpieceHoleParam workpieceType="0" holeDiameter="6.0" bigHoleDiameter="33.0" holeDist_L="40.0" holeDist_W="32.0" xLen="44.0" yLen="70.0" H="60.0"/>
|
||||
<FilterParam continuityTh="5.0" outlierTh="5.0"/>
|
||||
<LineSegParam distScale="3.0" segGapTh_y="15.0" segGapTh_z="0.0"/>
|
||||
<LineSegParam distScale="30.0" segGapTh_y="40.0" segGapTh_z="0.0"/>
|
||||
<GrowParam maxLineSkipNum="2" yDeviation_max="4.0" maxSkipDistance="0.0" zDeviation_max="10.0" minLTypeTreeLen="2.0" minVTypeTreeLen="2.0"/>
|
||||
<ToolParam eulerOrder="11" rotX="0.0" rotY="0.0" rotZ="0.0" offsetX="0.0" offsetY="0.0" offsetZ="0.0"/>
|
||||
</WorkpieceParamGroup>
|
||||
<PlaneCalibParams>
|
||||
<CameraCalibParam cameraIndex="1" cameraName="Camera1" isCalibrated="false" planeHeight="-1.0"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -12,6 +12,7 @@
|
||||
#define SG_ERR_SCAN_DIR_NOT_SUPPORTED -1009
|
||||
#define SX_ERR_ZERO_OBJECTS -1010
|
||||
#define SX_ERR_NULL_GROUND_PLANE -1011
|
||||
#define SX_ERR_ZERO_2D_OBJECTS -1012
|
||||
|
||||
//BQ_workpiece
|
||||
#define SX_ERR_INVLD_VTREE_NUM -2001
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -189,7 +189,7 @@ void _outputWorkpieceInfo(char* fileName, std::vector< WD_workpieceInfo>& workpi
|
||||
int number = (int)workpiecePositioning.size();
|
||||
for (int i = 0; i < number; i++)
|
||||
{
|
||||
sprintf_s(dataStr, 250, "工件_%d", i + 1);
|
||||
sprintf_s(dataStr, 250, "工件_%d: type=%x", i + 1, workpiecePositioning[i].workpieceType);
|
||||
sw << dataStr << std::endl;
|
||||
int holeNumber = (int)workpiecePositioning[i].holes.size();
|
||||
for (int j = 0; j < holeNumber; j++)
|
||||
@ -947,11 +947,15 @@ void _XOYprojection(
|
||||
if (i == 0)
|
||||
{
|
||||
rgb = { 255, 0, 0 };
|
||||
if((holes[i].workpieceType&0xffff0000) > 0)//方向为反
|
||||
rgb = { 255, 0, 255 };
|
||||
size = int(3.0/scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
rgb = { 255, 255, 0 };
|
||||
if ((holes[i].workpieceType & 0xffff0000) > 0)//方向为反
|
||||
rgb = { 0, 255, 255 };
|
||||
size = int(3.0 / scale);
|
||||
}
|
||||
WD_workpieceInfo& a_hole = holes[i];
|
||||
@ -1569,7 +1573,7 @@ void TuoPuFa_holePosition_test3(void)
|
||||
};
|
||||
|
||||
SVzNLRange fileIdx[TPF3_TEST_GROUP] = {
|
||||
{1,10}, {1, 10}, {1,10}, {1,10}, {1, 10},
|
||||
{1,11}, {1, 10}, {1,10}, {1,10}, {1, 10},
|
||||
{1,10}, {1, 10},
|
||||
};
|
||||
|
||||
@ -1635,7 +1639,7 @@ void TuoPuFa_holePosition_test3(void)
|
||||
|
||||
for (int fidx = fileIdx[grp].nMin; fidx <= fileIdx[grp].nMax; fidx++)
|
||||
{
|
||||
//fidx = 2;
|
||||
//fidx = 3;
|
||||
char _scan_file[256];
|
||||
sprintf_s(_scan_file, "%s%d_LaserData_Jh26B074.txt", dataPath[grp], fidx);
|
||||
std::vector<std::vector< SVzNL3DPosition>> scanLines;
|
||||
@ -2736,11 +2740,11 @@ int main()
|
||||
TuoPuFa_holePosition_test5();
|
||||
else if (keSG_拓普发孔定位_工件全测试 == testMode)
|
||||
{
|
||||
TuoPuFa_holePosition_test();
|
||||
TuoPuFa_holePosition_test2();
|
||||
//TuoPuFa_holePosition_test();
|
||||
//TuoPuFa_holePosition_test2();
|
||||
TuoPuFa_holePosition_test3();
|
||||
TuoPuFa_holePosition_test4();
|
||||
TuoPuFa_holePosition_test5();
|
||||
//TuoPuFa_holePosition_test5();
|
||||
}
|
||||
else if (keSG_华航孔定位 == testMode)
|
||||
HuaHang_holePosition_test();
|
||||
|
||||
@ -90,6 +90,9 @@ public:
|
||||
double& axialAngleMin, double& axialAngleMax) const;
|
||||
|
||||
int currentCameraIndex() const;
|
||||
bool hasAcceptableInput() const;
|
||||
// 单条目编辑场景可隐藏控件内置选择器,由外部选择器驱动 setData/getData。
|
||||
void setCameraSelectorVisible(bool visible);
|
||||
void setTargetOffsetVisible(bool visible);
|
||||
void setAxialAngleRangeVisible(bool visible);
|
||||
|
||||
@ -108,6 +111,7 @@ private:
|
||||
Ui::ToolExtrinsicWidget* ui = nullptr;
|
||||
|
||||
QGroupBox* m_groupExtrinsic;
|
||||
QLabel* m_labelCamera;
|
||||
QComboBox* m_comboCamera;
|
||||
QComboBox* m_comboEulerOrder;
|
||||
QLineEdit* m_editRotX;
|
||||
|
||||
@ -10,6 +10,7 @@ ToolExtrinsicWidget::ToolExtrinsicWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, ui(new Ui::ToolExtrinsicWidget)
|
||||
, m_groupExtrinsic(nullptr)
|
||||
, m_labelCamera(nullptr)
|
||||
, m_comboCamera(nullptr)
|
||||
, m_comboEulerOrder(nullptr)
|
||||
, m_editRotX(nullptr)
|
||||
@ -206,6 +207,29 @@ int ToolExtrinsicWidget::currentCameraIndex() const
|
||||
return m_comboCamera->currentData().toInt();
|
||||
}
|
||||
|
||||
bool ToolExtrinsicWidget::hasAcceptableInput() const
|
||||
{
|
||||
const auto isAcceptable = [](const QLineEdit* edit) {
|
||||
return !edit || edit->hasAcceptableInput();
|
||||
};
|
||||
|
||||
return m_comboEulerOrder && m_comboEulerOrder->currentIndex() >= 0
|
||||
&& isAcceptable(m_editRotX)
|
||||
&& isAcceptable(m_editRotY)
|
||||
&& isAcceptable(m_editRotZ)
|
||||
&& isAcceptable(m_editAxialAngleMin)
|
||||
&& isAcceptable(m_editAxialAngleMax)
|
||||
&& isAcceptable(m_editOffsetX)
|
||||
&& isAcceptable(m_editOffsetY)
|
||||
&& isAcceptable(m_editOffsetZ);
|
||||
}
|
||||
|
||||
void ToolExtrinsicWidget::setCameraSelectorVisible(bool visible)
|
||||
{
|
||||
if (m_labelCamera) m_labelCamera->setVisible(visible);
|
||||
if (m_comboCamera) m_comboCamera->setVisible(visible);
|
||||
}
|
||||
|
||||
void ToolExtrinsicWidget::setTargetOffsetVisible(bool visible)
|
||||
{
|
||||
if (m_labelTargetOffset) m_labelTargetOffset->setVisible(visible);
|
||||
@ -332,6 +356,7 @@ void ToolExtrinsicWidget::setupUI()
|
||||
}
|
||||
|
||||
m_groupExtrinsic = ui->groupExtrinsic;
|
||||
m_labelCamera = ui->labelCamera;
|
||||
m_comboCamera = ui->comboCamera;
|
||||
m_comboEulerOrder = ui->comboEulerOrder;
|
||||
m_editRotX = ui->editRotX;
|
||||
|
||||
2
Device
2
Device
@ -1 +1 @@
|
||||
Subproject commit 76d33caae885f1b47eec264f65f8e5f76cc131d0
|
||||
Subproject commit 1136a52250ae2595291f231492c770b29fe3354f
|
||||
@ -17,7 +17,7 @@
|
||||
| 11 | 铁路隧道槽道测量 | TunnelChannel | 1.0.0.3 |
|
||||
| 12 | 螺杆定位 | ScrewPosition | 1.2.4.1 |
|
||||
| 13 | 包裹拆线位置定位 | BagThreadPosition | 1.0.0.4 |
|
||||
| 14 | 工件孔定位 | WorkpieceHole | 1.2.1.1 |
|
||||
| 14 | 工件孔定位 | WorkpieceHole | 1.2.2.2 |
|
||||
| 15 | 轮胎孔定位 | TireHolePose | 1.0.2.1 |
|
||||
| 16 | 坑孔定位 | HolePitPosition | 无 |
|
||||
| 17 | 钢筋焊缝定位 | RodWeldSeam | 1.0.0.1 |
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user