diff --git a/App/DroneScrewbolt/DroneScrewCtrlApp/Presenter/Inc/DroneScrewCtrlPresenter.h b/App/DroneScrewbolt/DroneScrewCtrlApp/Presenter/Inc/DroneScrewCtrlPresenter.h index ccc14bdd..e8901d89 100644 --- a/App/DroneScrewbolt/DroneScrewCtrlApp/Presenter/Inc/DroneScrewCtrlPresenter.h +++ b/App/DroneScrewbolt/DroneScrewCtrlApp/Presenter/Inc/DroneScrewCtrlPresenter.h @@ -39,6 +39,7 @@ public: void SetStatusReceiver(IYDroneScrewCtrlStatus* receiver) { m_pReceiver = receiver; } void ApplyConfig(const DroneScrewCtrlConfigResult& cfg); + void UpdateDisplayOptions(const DroneScrewDisplayOption& options); int InitApp(); void DeinitApp(); diff --git a/App/DroneScrewbolt/DroneScrewCtrlApp/Presenter/Src/DroneScrewCtrlPresenter.cpp b/App/DroneScrewbolt/DroneScrewCtrlApp/Presenter/Src/DroneScrewCtrlPresenter.cpp index 64243407..16c96757 100644 --- a/App/DroneScrewbolt/DroneScrewCtrlApp/Presenter/Src/DroneScrewCtrlPresenter.cpp +++ b/App/DroneScrewbolt/DroneScrewCtrlApp/Presenter/Src/DroneScrewCtrlPresenter.cpp @@ -181,6 +181,11 @@ void DroneScrewCtrlPresenter::ApplyConfig(const DroneScrewCtrlConfigResult& cfg) } } +void DroneScrewCtrlPresenter::UpdateDisplayOptions(const DroneScrewDisplayOption& options) +{ + m_cfg.display = options; +} + int DroneScrewCtrlPresenter::InitApp() { if (m_bRunning.load()) return 0; diff --git a/App/DroneScrewbolt/DroneScrewCtrlApp/Version.h b/App/DroneScrewbolt/DroneScrewCtrlApp/Version.h index fc6a5b54..b03ce064 100644 --- a/App/DroneScrewbolt/DroneScrewCtrlApp/Version.h +++ b/App/DroneScrewbolt/DroneScrewCtrlApp/Version.h @@ -3,8 +3,8 @@ #define DRONESCREWCTRL_APP_NAME "无人机螺杆控制端" #define DRONESCREWCTRL_VERSION_STRING "1.0.0" -#define DRONESCREWCTRL_BUILD_STRING "2" -#define DRONESCREWCTRL_FULL_VERSION_STRING "V1.0.0_2" +#define DRONESCREWCTRL_BUILD_STRING "3" +#define DRONESCREWCTRL_FULL_VERSION_STRING "V1.0.0_3" inline const char* GetDroneScrewCtrlFullVersion() { return DRONESCREWCTRL_FULL_VERSION_STRING; } diff --git a/App/DroneScrewbolt/DroneScrewCtrlApp/dialogalgoarg.cpp b/App/DroneScrewbolt/DroneScrewCtrlApp/dialogalgoarg.cpp index bb7969a1..d2d4b1dc 100644 --- a/App/DroneScrewbolt/DroneScrewCtrlApp/dialogalgoarg.cpp +++ b/App/DroneScrewbolt/DroneScrewCtrlApp/dialogalgoarg.cpp @@ -1,8 +1,11 @@ #include "dialogalgoarg.h" #include "ui_dialogalgoarg.h" +#include #include +#include #include +#include DialogAlgoArg::DialogAlgoArg(QWidget* parent) : QDialog(parent) @@ -17,6 +20,7 @@ DialogAlgoArg::DialogAlgoArg(QWidget* parent) ui->edit_width->setValidator(new QIntValidator(0, 8192, this)); ui->edit_height->setValidator(new QIntValidator(0, 8192, this)); ui->edit_modelType->setValidator(new QIntValidator(0, 10, this)); + createDisplayRows(); } DialogAlgoArg::~DialogAlgoArg() @@ -35,6 +39,17 @@ DroneScrewAlgoUiParams DialogAlgoArg::GetParams() const return m_params; } +void DialogAlgoArg::SetDisplayOptions(const DroneScrewDisplayOption& options) +{ + m_displayOptions = options; + loadDisplayToUi(options); +} + +DroneScrewDisplayOption DialogAlgoArg::GetDisplayOptions() const +{ + return m_displayOptions; +} + void DialogAlgoArg::loadToUi(const DroneScrewAlgoUiParams& p) { if (ui->edit_score) ui->edit_score->setText(QString::number(p.scoreThreshold, 'f', 2)); @@ -57,9 +72,39 @@ DroneScrewAlgoUiParams DialogAlgoArg::collectFromUi() const return p; } +void DialogAlgoArg::createDisplayRows() +{ + if (!ui->fl_main) + return; + + m_checkDrawBoxes = new QCheckBox(QStringLiteral("显示"), this); + m_checkDrawBoxes->setObjectName(QStringLiteral("check_drawBoxes")); + m_checkDrawBoxes->setMinimumHeight(40); + m_checkDrawBoxes->setStyleSheet(QStringLiteral( + "QCheckBox { color: rgb(239, 241, 245); font-size: 18px; background: none; }")); + + ui->fl_main->addRow(new QLabel(QStringLiteral("显示结果框"), this), + m_checkDrawBoxes); +} + +void DialogAlgoArg::loadDisplayToUi(const DroneScrewDisplayOption& options) +{ + if (m_checkDrawBoxes) + m_checkDrawBoxes->setChecked(options.drawBoxes); +} + +DroneScrewDisplayOption DialogAlgoArg::collectDisplayFromUi() const +{ + DroneScrewDisplayOption options = m_displayOptions; + if (m_checkDrawBoxes) + options.drawBoxes = m_checkDrawBoxes->isChecked(); + return options; +} + void DialogAlgoArg::on_btn_ok_clicked() { m_params = collectFromUi(); + m_displayOptions = collectDisplayFromUi(); accept(); } @@ -71,10 +116,14 @@ void DialogAlgoArg::on_btn_cancel_clicked() void DialogAlgoArg::on_btn_apply_clicked() { m_params = collectFromUi(); + m_displayOptions = collectDisplayFromUi(); } void DialogAlgoArg::on_btn_reset_clicked() { DroneScrewAlgoUiParams p; // 默认值 loadToUi(p); + DroneScrewDisplayOption options = m_displayOptions; + options.drawBoxes = true; + loadDisplayToUi(options); } diff --git a/App/DroneScrewbolt/DroneScrewCtrlApp/dialogalgoarg.h b/App/DroneScrewbolt/DroneScrewCtrlApp/dialogalgoarg.h index 9505c6d9..9edad02d 100644 --- a/App/DroneScrewbolt/DroneScrewCtrlApp/dialogalgoarg.h +++ b/App/DroneScrewbolt/DroneScrewCtrlApp/dialogalgoarg.h @@ -5,6 +5,7 @@ #include "IVrConfig.h" namespace Ui { class DialogAlgoArg; } +class QCheckBox; class DialogAlgoArg : public QDialog { @@ -16,6 +17,8 @@ public: void SetParams(const DroneScrewAlgoUiParams& params); DroneScrewAlgoUiParams GetParams() const; + void SetDisplayOptions(const DroneScrewDisplayOption& options); + DroneScrewDisplayOption GetDisplayOptions() const; private slots: void on_btn_ok_clicked(); @@ -26,10 +29,15 @@ private slots: private: void loadToUi(const DroneScrewAlgoUiParams& p); DroneScrewAlgoUiParams collectFromUi() const; + void createDisplayRows(); + void loadDisplayToUi(const DroneScrewDisplayOption& options); + DroneScrewDisplayOption collectDisplayFromUi() const; private: Ui::DialogAlgoArg* ui{nullptr}; DroneScrewAlgoUiParams m_params; + DroneScrewDisplayOption m_displayOptions; + QCheckBox* m_checkDrawBoxes{nullptr}; }; #endif // DRONESCREWCTRL_DIALOG_ALGOARG_H diff --git a/App/DroneScrewbolt/DroneScrewCtrlApp/mainwindow.cpp b/App/DroneScrewbolt/DroneScrewCtrlApp/mainwindow.cpp index 77de336b..9f522106 100644 --- a/App/DroneScrewbolt/DroneScrewCtrlApp/mainwindow.cpp +++ b/App/DroneScrewbolt/DroneScrewCtrlApp/mainwindow.cpp @@ -671,12 +671,16 @@ void MainWindow::on_btn_algo_config_clicked() { dlg.SetParams(cfg.algo); } + dlg.SetDisplayOptions(cfg.display); if (dlg.exec() == QDialog::Accepted) { cfg.algo = dlg.GetParams(); + cfg.display = dlg.GetDisplayOptions(); ConfigManager::Instance().SetConfig(cfg); ConfigManager::Instance().Save(); + m_maxResultItems = cfg.display.maxResultListItems; + if (m_pPresenter) m_pPresenter->UpdateDisplayOptions(cfg.display); if (m_pPresenter) m_pPresenter->PushAlgoParams(cfg.algo); appendLog(QStringLiteral("已应用算法参数")); } diff --git a/App/DroneScrewbolt/DroneScrewCtrlApp/resultitem.cpp b/App/DroneScrewbolt/DroneScrewCtrlApp/resultitem.cpp index 687f73cf..f524f60c 100644 --- a/App/DroneScrewbolt/DroneScrewCtrlApp/resultitem.cpp +++ b/App/DroneScrewbolt/DroneScrewCtrlApp/resultitem.cpp @@ -9,9 +9,9 @@ namespace void setResultTextStyle(Ui::ResultItem* ui) { const QString titleStyle = - QStringLiteral("color: rgb(239, 241, 245); background-color: rgb(37, 38, 42); font-size: 16px; font-weight: 600;"); + QStringLiteral("color: rgb(239, 241, 245); background-color: rgb(37, 38, 42); font-size: 20px; font-weight: 600;"); const QString valueStyle = - QStringLiteral("color: rgb(255, 255, 255); background-color: rgb(37, 38, 42); font-size: 20px; font-weight: 700;"); + QStringLiteral("color: rgb(255, 255, 255); background-color: rgb(37, 38, 42); font-size: 26px; font-weight: 700;"); if (ui->lb_id_t) ui->lb_id_t->setStyleSheet(titleStyle); if (ui->lb_time_t) ui->lb_time_t->setStyleSheet(titleStyle); @@ -34,9 +34,7 @@ QString failedText(const CtrlDetectionFrame& frame) QString oneDistanceText(const CtrlDetectionDistance& d) { const double meters = d.distanceMm / 1000.0; - if (d.fromId < 0) - return QStringLiteral("整体距离: %1m").arg(meters, 0, 'f', 3); - return QStringLiteral("目标%1距离: %2m").arg(d.fromId + 1).arg(meters, 0, 'f', 3); + return QStringLiteral("%1m").arg(meters, 0, 'f', 3); } QString distanceText(const CtrlDetectionFrame& frame, int targetIndex) @@ -58,7 +56,7 @@ QString distanceIndexText(const CtrlDetectionFrame& frame, int targetIndex) if (targetIndex >= 0 && targetIndex < total) { const CtrlDetectionDistance& d = frame.distances[static_cast(targetIndex)]; - return QString::number(d.toId); + return QString::number(d.toId + 1); } return QStringLiteral("-"); } @@ -113,7 +111,14 @@ void ResultItem::setResultData(int targetIndex, { const bool precisionMode = (mode == DisplayMode::Precision); setResultTextStyle(ui); - setMinimumHeight(precisionMode ? 92 : 82); + setMinimumHeight(precisionMode ? 104 : 108); + + if (ui->lb_id_t) + ui->lb_id_t->setText(QStringLiteral("ID")); + if (ui->lb_status_t) + ui->lb_status_t->setText(mode == DisplayMode::Distance + ? QStringLiteral("距离") + : QStringLiteral("状态")); if (ui->label_id) { diff --git a/App/DroneScrewbolt/DroneScrewServer/Algo/DroneScrewAlgoStub.cpp b/App/DroneScrewbolt/DroneScrewServer/Algo/DroneScrewAlgoStub.cpp index d87c83fc..2cd21361 100644 --- a/App/DroneScrewbolt/DroneScrewServer/Algo/DroneScrewAlgoStub.cpp +++ b/App/DroneScrewbolt/DroneScrewServer/Algo/DroneScrewAlgoStub.cpp @@ -15,9 +15,13 @@ #include #include #include +#include +#include #include #ifdef DRONESCREW_STEREO_BOLT_DIRECT_LINK +#include +#include namespace { @@ -28,6 +32,65 @@ constexpr int kDefaultYoloImgSize = 960; constexpr int kDefaultExpectedBoltCount = 8; constexpr int kMaxRangeBolts = 64; +cv::Mat mat64(int rows, int cols, const double* values) +{ + cv::Mat m(rows, cols, CV_64F); + std::memcpy(m.ptr(0), values, sizeof(double) * rows * cols); + return m; +} + +int roiSampleCount(double span) +{ + if (span <= 1.0) + return 2; + return std::max(3, std::min(17, static_cast(std::ceil(span / 64.0)) + 1)); +} + +bool sampleMapBilinear(const std::vector& mapX, + const std::vector& mapY, + int mapWidth, + int mapHeight, + double rectX, + double rectY, + double& rawX, + double& rawY) +{ + if (mapWidth <= 0 || mapHeight <= 0 || + mapX.size() != static_cast(mapWidth) * static_cast(mapHeight) || + mapY.size() != mapX.size()) + { + return false; + } + + rectX = std::max(0.0, std::min(rectX, static_cast(mapWidth - 1))); + rectY = std::max(0.0, std::min(rectY, static_cast(mapHeight - 1))); + + const int x0 = static_cast(std::floor(rectX)); + const int y0 = static_cast(std::floor(rectY)); + const int x1 = std::min(x0 + 1, mapWidth - 1); + const int y1 = std::min(y0 + 1, mapHeight - 1); + const double tx = rectX - x0; + const double ty = rectY - y0; + + const size_t i00 = static_cast(y0) * mapWidth + x0; + const size_t i10 = static_cast(y0) * mapWidth + x1; + const size_t i01 = static_cast(y1) * mapWidth + x0; + const size_t i11 = static_cast(y1) * mapWidth + x1; + + const double xTop = static_cast(mapX[i00]) * (1.0 - tx) + + static_cast(mapX[i10]) * tx; + const double xBottom = static_cast(mapX[i01]) * (1.0 - tx) + + static_cast(mapX[i11]) * tx; + const double yTop = static_cast(mapY[i00]) * (1.0 - tx) + + static_cast(mapY[i10]) * tx; + const double yBottom = static_cast(mapY[i01]) * (1.0 - tx) + + static_cast(mapY[i11]) * tx; + + rawX = xTop * (1.0 - ty) + xBottom * ty; + rawY = yTop * (1.0 - ty) + yBottom * ty; + return std::isfinite(rawX) && std::isfinite(rawY); +} + void appendUniqueDir(QStringList& dirs, const QString& dir) { if (dir.isEmpty()) @@ -325,18 +388,6 @@ bool validateInput(const DroneScrewInputImage& leftImage, return true; } -void appendRoiBox(const StereoBoltModuleRoiC& roi, DroneScrewResult& result) -{ - DroneScrewBox box; - box.classId = roi.class_id; - box.score = static_cast(roi.score); - box.x = roi.x; - box.y = roi.y; - box.width = roi.width; - box.height = roi.height; - result.boxes.push_back(box); -} - void appendDistance(const StereoBoltModuleDistanceC& distance, DroneScrewResult& result) { DroneScrewDistance d; @@ -452,6 +503,203 @@ DroneScrewAlgoStub::~DroneScrewAlgoStub() UnInit(); } +bool DroneScrewAlgoStub::buildRectifiedLeftToRawMap(const StereoBoltCalibC& calib, + double rectAlpha, + std::string& error) +{ + m_rectMapWidth = 0; + m_rectMapHeight = 0; + m_leftRectToRawX.clear(); + m_leftRectToRawY.clear(); + + if (calib.image_width <= 0 || calib.image_height <= 0) + { + error = "invalid calibration image size for rectified-to-raw map"; + return false; + } + if (!std::isfinite(rectAlpha)) + { + error = "invalid rectification alpha for rectified-to-raw map"; + return false; + } + + try + { + const cv::Size imageSize(calib.image_width, calib.image_height); + const cv::Mat K1 = mat64(3, 3, calib.left_K); + const cv::Mat D1 = mat64(1, 5, calib.left_D); + const cv::Mat K2 = mat64(3, 3, calib.right_K); + const cv::Mat D2 = mat64(1, 5, calib.right_D); + const cv::Mat R = mat64(3, 3, calib.R); + const cv::Mat T = mat64(3, 1, calib.T); + + cv::Mat R1; + cv::Mat R2; + cv::Mat P1; + cv::Mat P2; + cv::Mat Q; + cv::Rect validRoi1; + cv::Rect validRoi2; + cv::stereoRectify(K1, D1, K2, D2, imageSize, R, T, + R1, R2, P1, P2, Q, + cv::CALIB_ZERO_DISPARITY, + rectAlpha, + imageSize, + &validRoi1, + &validRoi2); + + cv::Mat mapX; + cv::Mat mapY; + cv::initUndistortRectifyMap(K1, D1, R1, P1, imageSize, + CV_32FC1, mapX, mapY); + + if (mapX.empty() || mapY.empty() || + mapX.cols != imageSize.width || mapX.rows != imageSize.height || + mapY.cols != imageSize.width || mapY.rows != imageSize.height) + { + error = "OpenCV returned invalid rectified-to-raw map"; + return false; + } + + const size_t total = static_cast(imageSize.width) * + static_cast(imageSize.height); + m_leftRectToRawX.resize(total); + m_leftRectToRawY.resize(total); + for (int y = 0; y < imageSize.height; ++y) + { + const float* srcX = mapX.ptr(y); + const float* srcY = mapY.ptr(y); + const size_t offset = static_cast(y) * imageSize.width; + std::copy(srcX, srcX + imageSize.width, m_leftRectToRawX.begin() + offset); + std::copy(srcY, srcY + imageSize.width, m_leftRectToRawY.begin() + offset); + } + + m_calib = calib; + m_rectAlpha = rectAlpha; + m_rectMapWidth = imageSize.width; + m_rectMapHeight = imageSize.height; + return true; + } + catch (const cv::Exception& e) + { + error = std::string("OpenCV rectified-to-raw map failed: ") + e.what(); + } + catch (const std::exception& e) + { + error = std::string("rectified-to-raw map failed: ") + e.what(); + } + catch (...) + { + error = "rectified-to-raw map failed: unknown exception"; + } + + m_rectMapWidth = 0; + m_rectMapHeight = 0; + m_leftRectToRawX.clear(); + m_leftRectToRawY.clear(); + return false; +} + +bool DroneScrewAlgoStub::mapRectifiedLeftRoi(const StereoBoltModuleRoiC& roi, + int rawWidth, + int rawHeight, + DroneScrewBox& box) const +{ + if (roi.width <= 0 || roi.height <= 0) + return false; + if (m_rectMapWidth <= 0 || m_rectMapHeight <= 0 || + m_leftRectToRawX.empty() || m_leftRectToRawY.empty()) + { + return false; + } + + const int dstWidth = rawWidth > 0 ? rawWidth : m_rectMapWidth; + const int dstHeight = rawHeight > 0 ? rawHeight : m_rectMapHeight; + if (dstWidth <= 0 || dstHeight <= 0) + return false; + + const double rectLeft = static_cast(roi.x); + const double rectTop = static_cast(roi.y); + const double rectRight = static_cast(roi.x) + static_cast(roi.width) - 1.0; + const double rectBottom = static_cast(roi.y) + static_cast(roi.height) - 1.0; + if (rectRight < 0.0 || rectBottom < 0.0 || + rectLeft > static_cast(m_rectMapWidth - 1) || + rectTop > static_cast(m_rectMapHeight - 1)) + { + return false; + } + + double minRawX = std::numeric_limits::infinity(); + double minRawY = std::numeric_limits::infinity(); + double maxRawX = -std::numeric_limits::infinity(); + double maxRawY = -std::numeric_limits::infinity(); + bool hasSample = false; + + const int samplesX = roiSampleCount(rectRight - rectLeft); + const int samplesY = roiSampleCount(rectBottom - rectTop); + for (int sy = 0; sy < samplesY; ++sy) + { + const double ty = (samplesY <= 1) ? 0.0 : static_cast(sy) / (samplesY - 1); + const double rectY = rectTop + (rectBottom - rectTop) * ty; + for (int sx = 0; sx < samplesX; ++sx) + { + const double tx = (samplesX <= 1) ? 0.0 : static_cast(sx) / (samplesX - 1); + const double rectX = rectLeft + (rectRight - rectLeft) * tx; + + double rawX = 0.0; + double rawY = 0.0; + if (!sampleMapBilinear(m_leftRectToRawX, m_leftRectToRawY, + m_rectMapWidth, m_rectMapHeight, + rectX, rectY, rawX, rawY)) + { + continue; + } + + minRawX = std::min(minRawX, rawX); + minRawY = std::min(minRawY, rawY); + maxRawX = std::max(maxRawX, rawX); + maxRawY = std::max(maxRawY, rawY); + hasSample = true; + } + } + + if (!hasSample) + return false; + if (maxRawX < 0.0 || maxRawY < 0.0 || + minRawX > static_cast(dstWidth - 1) || + minRawY > static_cast(dstHeight - 1)) + { + return false; + } + + const int left = std::max(0, std::min(dstWidth - 1, + static_cast(std::floor(minRawX)))); + const int top = std::max(0, std::min(dstHeight - 1, + static_cast(std::floor(minRawY)))); + const int right = std::max(0, std::min(dstWidth - 1, + static_cast(std::ceil(maxRawX)))); + const int bottom = std::max(0, std::min(dstHeight - 1, + static_cast(std::ceil(maxRawY)))); + if (right < left || bottom < top) + return false; + + box.classId = roi.class_id; + box.score = static_cast(roi.score); + box.x = left; + box.y = top; + box.width = right - left + 1; + box.height = bottom - top + 1; + return true; +} + +void DroneScrewAlgoStub::appendRoiBox(const StereoBoltModuleRoiC& roi, + DroneScrewResult& result) const +{ + DroneScrewBox box; + if (mapRectifiedLeftRoi(roi, result.imageWidth, result.imageHeight, box)) + result.boxes.push_back(box); +} + int DroneScrewAlgoStub::Init(const DroneScrewAlgoParams& params) { std::lock_guard lk(m_mutex); @@ -464,6 +712,10 @@ int DroneScrewAlgoStub::Init(const DroneScrewAlgoParams& params) sb_destroy(m_ctx); m_ctx = nullptr; } + m_rectMapWidth = 0; + m_rectMapHeight = 0; + m_leftRectToRawX.clear(); + m_leftRectToRawY.clear(); QStringList configBases; appendUniqueDir(configBases, QDir::currentPath()); @@ -536,10 +788,22 @@ int DroneScrewAlgoStub::Init(const DroneScrewAlgoParams& params) algoParams.rng_lrc_trusted_mm = yamlDouble(yaml, "ranging.lrc_trusted_mm", algoParams.rng_lrc_trusted_mm); + if (!buildRectifiedLeftToRawMap(calib, algoParams.rect_alpha, loadError)) + { + m_initError = std::string("stereo_bolt build bbox rectified-to-raw map failed calib=") + + calibPath.toStdString() + " err=" + loadError; + LOG_ERROR("[ALGO] %s\n", m_initError.c_str()); + return ERR_CODE(DEV_OPEN_ERR); + } + m_ctx = sb_create_ex(&calib, &model, &algoParams); if (!m_ctx) { + m_rectMapWidth = 0; + m_rectMapHeight = 0; + m_leftRectToRawX.clear(); + m_leftRectToRawY.clear(); const char* err = sb_last_error(nullptr); m_initError = std::string("stereo_bolt create_ex failed config=") + configPath.toStdString() + @@ -578,6 +842,10 @@ int DroneScrewAlgoStub::UnInit() sb_destroy(m_ctx); m_ctx = nullptr; } + m_rectMapWidth = 0; + m_rectMapHeight = 0; + m_leftRectToRawX.clear(); + m_leftRectToRawY.clear(); return 0; } diff --git a/App/DroneScrewbolt/DroneScrewServer/Algo/DroneScrewAlgoStub.h b/App/DroneScrewbolt/DroneScrewServer/Algo/DroneScrewAlgoStub.h index a4d47b09..f904fedf 100644 --- a/App/DroneScrewbolt/DroneScrewServer/Algo/DroneScrewAlgoStub.h +++ b/App/DroneScrewbolt/DroneScrewServer/Algo/DroneScrewAlgoStub.h @@ -37,12 +37,32 @@ public: std::string GetVersion() const override; private: +#ifdef DRONESCREW_STEREO_BOLT_DIRECT_LINK + bool buildRectifiedLeftToRawMap(const StereoBoltCalibC& calib, + double rectAlpha, + std::string& error); + bool mapRectifiedLeftRoi(const StereoBoltModuleRoiC& roi, + int rawWidth, + int rawHeight, + DroneScrewBox& box) const; + void appendRoiBox(const StereoBoltModuleRoiC& roi, + DroneScrewResult& result) const; +#endif + DroneScrewAlgoParams m_params; StereoBoltCtx* m_ctx{nullptr}; std::string m_configPath; std::string m_calibPath; std::string m_modelPath; std::string m_initError; +#ifdef DRONESCREW_STEREO_BOLT_DIRECT_LINK + StereoBoltCalibC m_calib{}; + double m_rectAlpha{1.0}; + int m_rectMapWidth{0}; + int m_rectMapHeight{0}; + std::vector m_leftRectToRawX; + std::vector m_leftRectToRawY; +#endif mutable std::mutex m_mutex; std::atomic m_bInited{false}; }; diff --git a/App/DroneScrewbolt/DroneScrewServer/DroneScrewServer.pro b/App/DroneScrewbolt/DroneScrewServer/DroneScrewServer.pro index 4069d4e8..4d9c377c 100644 --- a/App/DroneScrewbolt/DroneScrewServer/DroneScrewServer.pro +++ b/App/DroneScrewbolt/DroneScrewServer/DroneScrewServer.pro @@ -103,7 +103,7 @@ win32:CONFIG(debug, debug|release) { OPENCV450_LIB_DIR = $$PWD/../../../SDK/OpenCV450/Arm/aarch64/lib INCLUDEPATH += $$PWD/../../../SDK/OpenCV450/Arm/aarch64/include/opencv4 LIBS += -L$${OPENCV450_LIB_DIR} - LIBS += -lopencv_imgcodecs -lopencv_imgproc -lopencv_core + LIBS += -lopencv_imgcodecs -lopencv_imgproc -lopencv_calib3d -lopencv_core QMAKE_LFLAGS += -Wl,-rpath-link,$${OPENCV450_LIB_DIR} # MVS SDK diff --git a/App/DroneScrewbolt/DroneScrewServer/DroneScrewServerPresenter.cpp b/App/DroneScrewbolt/DroneScrewServer/DroneScrewServerPresenter.cpp index e264f07a..37bef26a 100644 --- a/App/DroneScrewbolt/DroneScrewServer/DroneScrewServerPresenter.cpp +++ b/App/DroneScrewbolt/DroneScrewServer/DroneScrewServerPresenter.cpp @@ -67,7 +67,9 @@ constexpr int64_t kDistanceDecimationVertical = 4; constexpr int kDetectPipelinePrecision = 0; constexpr int kDetectPipelineDistance = 1; constexpr const char* kImageSaveRootDir = "/home/cat"; -constexpr size_t kMaxImageSaveQueueDepth = 16; +constexpr size_t kMaxImageSaveQueueDepth = 100; +constexpr size_t kImageSaveWorkerCount = 4; +constexpr int kDistanceImageSaveStride = 3; const char* mvsSdkErrorName(int code) { @@ -1812,9 +1814,9 @@ int DroneScrewServerPresenter::stopDetectionWork() stopGpioTriggerLoop(); m_bThreadExit = true; + stopImageSaveThread(); if (m_detectThread.joinable()) m_detectThread.join(); - stopImageSaveThread(); m_bIsDetecting = false; m_detectPipelineMode = kDetectPipelinePrecision; m_activeTriggerFps = static_cast(kPrecisionFrameRate); @@ -1933,9 +1935,9 @@ int DroneScrewServerPresenter::stopLiveStream() m_bRtspStarted = false; stopGpioTriggerLoop(); m_bThreadExit = true; + stopImageSaveThread(); if (m_detectThread.joinable()) m_detectThread.join(); - stopImageSaveThread(); m_bIsDetecting = false; m_detectPipelineMode = kDetectPipelinePrecision; m_activeTriggerFps = static_cast(kPrecisionFrameRate); @@ -2356,28 +2358,51 @@ void DroneScrewServerPresenter::startImageSaveThread(const QString& modeName) m_imageSaveThreadExit = false; } - m_imageSaveThread = std::thread(&DroneScrewServerPresenter::imageSaveThreadFunc, this); - LOG_INFO("[SAVE] image save thread started mode=%s dir=%s\n", - modeName.toStdString().c_str(), sessionDir.toStdString().c_str()); + m_imageSaveThreads.reserve(kImageSaveWorkerCount); + for (size_t i = 0; i < kImageSaveWorkerCount; ++i) + { + m_imageSaveThreads.emplace_back(&DroneScrewServerPresenter::imageSaveThreadFunc, + this, + static_cast(i)); + } + LOG_INFO("[SAVE] image save workers started count=%zu mode=%s dir=%s\n", + m_imageSaveThreads.size(), + modeName.toStdString().c_str(), + sessionDir.toStdString().c_str()); } void DroneScrewServerPresenter::stopImageSaveThread() { - const bool shouldJoin = m_imageSaveThread.joinable(); + const bool shouldJoin = !m_imageSaveThreads.empty(); + size_t droppedPending = 0; { std::lock_guard lk(m_imageSaveMutex); - m_imageSaveQueue.clear(); if (!shouldJoin) { + m_imageSaveQueue.clear(); m_imageSaveSessionDir.clear(); m_imageSaveThreadExit = false; return; } + droppedPending = m_imageSaveQueue.size(); + if (droppedPending > 0) + m_imageSaveQueue.clear(); m_imageSaveThreadExit = true; } + if (droppedPending > 0) + { + LOG_WARN("[SAVE] stop requested, drop pending save jobs=%zu\n", + droppedPending); + } + m_imageSaveCv.notify_all(); - m_imageSaveThread.join(); + for (std::thread& worker : m_imageSaveThreads) + { + if (worker.joinable()) + worker.join(); + } + m_imageSaveThreads.clear(); { std::lock_guard lk(m_imageSaveMutex); @@ -2457,9 +2482,9 @@ void DroneScrewServerPresenter::enqueueImageSave(const MvsImageData& leftImg, m_imageSaveCv.notify_one(); } -void DroneScrewServerPresenter::imageSaveThreadFunc() +void DroneScrewServerPresenter::imageSaveThreadFunc(int workerIndex) { - LOG_DEBUG("[SAVE] worker entered\n"); + LOG_DEBUG("[SAVE] worker #%d entered\n", workerIndex); for (;;) { ImageSaveJob job; @@ -2470,10 +2495,15 @@ void DroneScrewServerPresenter::imageSaveThreadFunc() }); if (m_imageSaveThreadExit.load()) + { + m_imageSaveQueue.clear(); break; + } if (m_imageSaveQueue.empty()) + { continue; + } job = std::move(m_imageSaveQueue.front()); m_imageSaveQueue.pop_front(); @@ -2502,9 +2532,11 @@ void DroneScrewServerPresenter::imageSaveThreadFunc() }; saveMono8(job.left, QStringLiteral("left")); + if (m_imageSaveThreadExit.load()) + break; saveMono8(job.right, QStringLiteral("right")); } - LOG_DEBUG("[SAVE] worker stopped\n"); + LOG_DEBUG("[SAVE] worker #%d stopped\n", workerIndex); } DroneScrewResult DroneScrewServerPresenter::runSingleDetection() @@ -2800,6 +2832,7 @@ void DroneScrewServerPresenter::detectThreadFunc() } int frameCnt = 0; + unsigned long long savedFrameCnt = 0; bool haveProcessedFrame = false; unsigned long long lastProcessedLeftFrameId = 0; unsigned long long lastProcessedRightFrameId = 0; @@ -2875,7 +2908,12 @@ void DroneScrewServerPresenter::detectThreadFunc() haveProcessedFrame = true; lastProcessedLeftFrameId = leftImg.frameID; lastProcessedRightFrameId = rightImg.frameID; - enqueueImageSave(leftImg, rightImg, static_cast(frameCnt + 1)); + const bool distanceSaveMode = + (m_detectPipelineMode.load() == kDetectPipelineDistance); + const bool shouldSaveFrame = + !distanceSaveMode || ((frameCnt % kDistanceImageSaveStride) == 0); + if (shouldSaveFrame) + enqueueImageSave(leftImg, rightImg, ++savedFrameCnt); // Publish the display frame before detection work. detectMode only controls raw transport. if (m_bRawPubEnabled.load()) diff --git a/App/DroneScrewbolt/DroneScrewServer/DroneScrewServerPresenter.h b/App/DroneScrewbolt/DroneScrewServer/DroneScrewServerPresenter.h index 0cb507fe..6f4ce51e 100644 --- a/App/DroneScrewbolt/DroneScrewServer/DroneScrewServerPresenter.h +++ b/App/DroneScrewbolt/DroneScrewServer/DroneScrewServerPresenter.h @@ -236,7 +236,7 @@ private: unsigned int timeoutMs); void startImageSaveThread(const QString& modeName); void stopImageSaveThread(); - void imageSaveThreadFunc(); + void imageSaveThreadFunc(int workerIndex); void enqueueImageSave(const MvsImageData& leftImg, const MvsImageData& rightImg, unsigned long long index); @@ -344,7 +344,7 @@ private: bool m_bRightImageReady{false}; // 异步图像保存 - std::thread m_imageSaveThread; + std::vector m_imageSaveThreads; std::atomic m_imageSaveThreadExit{false}; std::mutex m_imageSaveMutex; std::condition_variable m_imageSaveCv; diff --git a/App/DroneScrewbolt/DroneScrewServer/Version.h b/App/DroneScrewbolt/DroneScrewServer/Version.h index 1f4f7a7c..ddbd32ae 100644 --- a/App/DroneScrewbolt/DroneScrewServer/Version.h +++ b/App/DroneScrewbolt/DroneScrewServer/Version.h @@ -8,6 +8,6 @@ #define DRONESCREWSERVER_COMPANY_NAME "VisionTech" #define DRONESCREWSERVER_COPYRIGHT "Copyright (C) 2026" #define DRONESCREWSERVER_VERSION_STRING "1.0.0" -#define DRONESCREWSERVER_VERSION_BUILD "2" +#define DRONESCREWSERVER_VERSION_BUILD "3" #endif // DRONESCREW_VERSION_H diff --git a/AppAlgo/stereo_bolt_delivery/ABI_FINGERPRINT.txt b/AppAlgo/stereo_bolt_delivery/ABI_FINGERPRINT.txt index 16a0d5e2..01510689 100644 --- a/AppAlgo/stereo_bolt_delivery/ABI_FINGERPRINT.txt +++ b/AppAlgo/stereo_bolt_delivery/ABI_FINGERPRINT.txt @@ -1,13 +1,38 @@ -# ABI fingerprint — self-contained delivery -# OpenCV 4.5.0 (source build, standard SONAME — no Debian 'd' suffix) -# -# Target: aarch64, Ubuntu 22.04, glibc >= 2.32, RK3588 NPU driver -# Bundled: OpenCV 4.5.0 + librknnrt 2.3.2, total 9 libs in lib/ -# SONAME: libopencv_core.so.4.5 (NOT .4.5d) -# System: libc / libpthread / libm / libdl / libstdc++ / libgcc_s -# -# Install: -# tar xzf stereo_bolt_delivery_selfcontained.tar.gz -# cd stereo_bolt_delivery -# export LD_LIBRARY_PATH=/e/02_Modules/StereoVisionBoltMeasurement_cpp/lib: -# ldd lib/libstereo_bolt.so | grep "not found" # should be empty +# ABI fingerprint of libstereo_bolt.so +# Your board MUST match these or the .so will not load / not run. +# generated: 2026-07-01 (rebuilt on RK3588; OBB-axis ranging stability fix) +# libstereo_bolt.so SHA256: +# 67d9406c1ab69c36b8f3b363fe6cf38567fdac97c25fa160e4c80a4d63b172ae + +[arch] +AArch64 + +[exported symbols] (9 total: lifecycle + precise + ranging) + # --- lifecycle / construction --- + sb_create # create from a config.yaml path (reads files from disk) + sb_create_ex # NEW: create from in-memory structs (zero file IO / no path / no perms) + sb_default_params # NEW: prefilled StereoBoltParamsC for sb_create_ex + sb_destroy + sb_last_error + sb_free_bolt_module_result + # --- precise measurement (12MP) --- + sb_process_bolt_module_files # precise, file variant + sb_process_bolt_module_buffers # precise, in-memory buffer variant + # --- binned ranging (3MP) --- + sb_range_bolt_binned # binned ranging, in-memory buffer + +[min glibc required] (your board's glibc must be >= this) +GLIBC_2.32 + +[NEEDED shared libraries] (provide matching sonames at runtime) + librknnrt.so + libopencv_calib3d.so.4.5 + libopencv_imgcodecs.so.4.5 + libopencv_dnn.so.4.5 + libopencv_imgproc.so.4.5 + libopencv_core.so.4.5 + libstdc++.so.6 + libm.so.6 + libgcc_s.so.1 + libc.so.6 + ld-linux-aarch64.so.1 diff --git a/AppAlgo/stereo_bolt_delivery/CHANGELOG.txt b/AppAlgo/stereo_bolt_delivery/CHANGELOG.txt new file mode 100644 index 00000000..c7a56003 --- /dev/null +++ b/AppAlgo/stereo_bolt_delivery/CHANGELOG.txt @@ -0,0 +1,67 @@ +CHANGELOG — libstereo_bolt delivery package +============================================ + +## 2026-07-01 v3 — stable OBB-axis ranging summary + +Current delivery contents: +- Model: best_obb_1024.rknn (OBB, imgsz 1024, RKNN) +- Precise measurement and ranging share the same OBB model. +- Runtime libraries are bundled in lib/: libstereo_bolt.so, OpenCV 4.5.0, librknnrt.so. +- No target-board apt install is required for runtime OpenCV. + +Algorithm: +- Ranging now uses left/right OBB-axis pairing before the NCC fallback. +- Even-count OBB candidates no longer average two different pair hypotheses. + The summary distance chooses the center candidate with the more stable sampled + axis disparity, removing the RK3588 1498/1652 mm jump. + +Performance / validation: +- RK3588, distance_153304_403, 21 binned frames: + found=21/21, distance mean=1652.360 mm, min=1652.091, max=1652.582, + std=0.149 mm, elapsed mean=153.146 ms. +- Windows ctest: 22/22 pass. + +## 2026-06-30 v2 — OBB model + plane skip optimization + +Changes vs previous delivery (2026-06-24, v1): + +### Model +- Previous package used an HBB detector at imgsz 960. +- Current package uses best_obb_1024.rknn (OBB, imgsz 1024, YOLOv8 oriented bbox). +- OBB model trained on mixed 5MP + 12MP data (2026-06-29) +- Auto-detection: library detects OBB vs HBB from RKNN output shape + +### Config +- yolo.task: "auto" (auto-detect OBB/HBB from model output) +- yolo.imgsz: 960 -> 1024 +- yolo.conf: 0.5 (unchanged) +- measurement.height_from_plane: false (height = top-to-foot along axis) +- working_distance.z_min_mm: 1200 -> 900 +- working_distance.z_max_mm: 2800 -> 4000 +- ranging.z_min_mm: 1300 -> 900 +- ranging.z_max_mm: 3600 -> 4000 + +### Algorithm +- Plane computation skipped when height_from_plane=false + (saves 300-550ms per frame on 8-bolt scenes) +- OBB auto-detection in RKNN backend +- OBB centerline rescue + peer height consensus clamp + +### Calibration +- Unchanged: stereo_calib_20260621_202857_ascii.xml (baseline 482mm) + +### C API +- ABI unchanged: c_api.h has zero changes +- All function signatures identical +- Struct layouts identical +- Drop-in .so replacement, no recompile needed for downstream + +### Performance (RK3588 board, 8-bolt scene, height_from_plane=false) +- Precise: ~400ms/frame (was ~800ms with plane enabled) +- Ranging: superseded by v3 benchmark above + +### Verified on +- RK3588 board (linaro): 0621 dataset 21 frames, 12/21 success +- 0627 dataset 10 frames: 10/10 success +- 0628 dataset 10 frames: 10/10 success +- Windows ctest at the time: 20/20 pass diff --git a/AppAlgo/stereo_bolt_delivery/DELIVERY.md b/AppAlgo/stereo_bolt_delivery/DELIVERY.md index a7fc03a4..d6c12e74 100644 --- a/AppAlgo/stereo_bolt_delivery/DELIVERY.md +++ b/AppAlgo/stereo_bolt_delivery/DELIVERY.md @@ -1,166 +1,226 @@ # libstereo_bolt 交付说明(.so + 头文件) -你拿到的是**预编译的 aarch64 动态库 + C 头文件**,不含算法源码。 -OpenCV 4.5.4、RKNN runtime 及全部传递依赖(约 131 个 `.so`)**已随包内置于 `lib/`**, -无需在目标板上安装任何软件包(NPU 内核驱动由固件提供,不在用户态包内)。 +你拿到的是**预编译的 aarch64 动态库 + C 头文件 + 模型 + 标定 + 示例**,不含算法源码。 +运行时依赖中 **`libstereo_bolt.so`、OpenCV 4.5.0 `.so`、`librknnrt.so` 均已随包 `lib/`**;无需 apt 安装 OpenCV。NPU 驱动由板子系统提供。 -> **本包标定 = 12MP 生产相机(4096×3000)。** 喂进来的左右图必须是该相机的 **4096×3000** 帧 -> (`config` 里 `image.width=4096 / height=3000` 已配好,指向 `calib/stereo_calib_20260607_211135.xml`)。 -> **包内不含测试图** —— 请用你自己这套 12MP 相机拍的左右图。 -> ✅ **已在板上实测**:本 12MP 标定**加载 + rectify 通过**(4096×3000,stereo_rms=0.373、baseline=200.19、f_rect=3330)。 -> ⚠️ 但**检测 + 测量在该相机上还没用真实螺杆图验过**(包内无螺杆图)。 -> ⚠️ 本标定单相机 RMS=**0.373**(5MP 那套是 0.19),已把 `calibration.max_single_rms` **放宽到 0.40** 才放行; -> 0.373 偏松会折损测量精度,建议后续**重标到 <0.20** 再收回此闸。首次上机务必用真实螺杆图核对结果。 +> **本库导出两个独立的产品接口**(同一个 `.so`、同一个 `StereoBoltCtx`): +> 1. **精测(PRECISE)** `sb_process_bolt_module_buffers` / `_files` —— 12MP 高精度测量, +> 每颗螺栓高度 / 相邻间距 / 地平面 / 3D 中心轴 / 顶底 3D 坐标。**触发式"测得准"路径(板上约 400ms/帧,依输入和 QC 耗时波动)。** +> 2. **测距(RANGING)** `sb_range_bolt_binned` —— 2×2 binned(3MP) 实时测距, +> 输出相机→螺栓代表直线距离。**无人机实时路径(统一用 OBB@1024,当前 distance_153304_403 复测约 153ms/帧)。** +> +> **创建上下文推荐用内存接口 `sb_create_ex`**(见下节):标定、模型、参数全部经接口/buffer 传入, +> 库**零文件 IO、无路径假设、无安装目录读权限问题**。旧的 `sb_create(config.yaml)` 仍保留作向后兼容。 + +--- + +## ★ 创建上下文:`sb_create_ex`(推荐)vs `sb_create`(兼容) + +两个产品接口共用同一个 `StereoBoltCtx`。创建它有两种方式: + +### 推荐:`sb_create_ex` —— 全参数经接口传入,零文件 IO +```c +StereoBoltCtx* sb_create_ex(const StereoBoltCalibC* calib, + const StereoBoltModelC* model, + const StereoBoltParamsC* params); +StereoBoltParamsC sb_default_params(void); /* 预填默认值,再覆盖你关心的 */ +``` +- `StereoBoltCalibC`:标定 —— 左右 `K[9]`/`D[5]` + `R[9]`/`T[3]` + `baseline_mm` + 图像尺寸。**替代标定 XML。** +- `StereoBoltModelC`:模型 —— `.rknn` 字节流 `data`+`size` + `backend`(rk3588 用 `SB_YOLO_BACKEND_RKNN`) + `imgsz`/`conf`/`iou`。**替代 weights 文件路径。** +- `StereoBoltParamsC`:算法数值参数 —— 视差带(`wd_z_min/max_mm` 物理带,**或** `sgbm_min/num_disparities` 固定带,二选一必填)、`rectification`、`measurement.foot_rim`、`ranging.*`。**替代 config.yaml 数值键。** 带 `struct_size` 做前向兼容。 + +**这条路径库不碰磁盘**:标定/模型从哪来由你决定(你的标定流程、资源文件、相机 SDK);本包提供的 `calib/*.xml` 与 `weights/*.rknn` 只是**给你读进来填结构体**的现成数据。 + +### 兼容:`sb_create(const char* config_yaml_path)` +读 `config.rk3588.12mp.yaml`(其内部再按相对路径找标定 XML + 模型)。**有两文件依赖 + 工作目录/读权限要求**——正是 `sb_create_ex` 要规避的。仅在离线/快速测试时用。 + +> `sb_create` / `sb_create_ex` **只调一次**(载配置+标定+模型进 NPU,慢),之后每帧复用同一个 `ctx`。 +> `sb_destroy` / `sb_last_error` 两接口共用。线程安全:同一 `ctx` 别多线程并发。 + +--- + +## 交付边界(职责划分) + +**本交付物是一个"算法模块",不是完整软件系统。** 软件总体集成由你(接入方)负责。 + +| 项 | 由谁负责 | +|---|---| +| 双目螺栓**测量/测距算法**(`.so` + `.h` + 模型 + 标定 + 示例 + 文档) | **算法方(本包提供)** | +| 板子**运行时环境**:aarch64 + glibc、NPU 驱动;算法依赖的 OpenCV 与 `librknnrt.so` 已随包 `lib/` | **接入方** | +| **取图**(相机驱动 / Mono8 buffer 喂入)、上层业务、UI、调度、与上位机/无人机通信 | **接入方** | +| **标定数据来源**(填入 `StereoBoltCalibC`)、按 `c_api.h` 调用、对 height/distance/confidence 做业务决策 | **接入方** | + +> 算法方只交付"喂进左右图 → 返回螺栓高度/距离"的纯算法能力(C ABI)。本库不碰相机、不碰网络、 +> **不碰磁盘(`sb_create_ex` + buffer 接口)**、不做悬停/对中等业务决策。逐颗 `confidence`(trusted/suspect) + `status` 由接入方取舍。 ## 包内容 | 路径 | 说明 | |---|---| -| `lib/libstereo_bolt.so` | 预编译共享库(aarch64) | -| `lib/librknnrt.so` 等(共 ~131 个) | OpenCV 4.5.4 + 全部传递依赖(**已随包,无需 apt**) | +| `lib/libstereo_bolt.so` | 预编译共享库(aarch64,含 `sb_create_ex` 内存接口) | | `include/stereo_bolt/c_api.h` | C ABI 头文件(唯一对外接口) | -| `weights/best_1280_20260621.rknn` | NPU 模型(精测 + 测距共用,imgsz 1280) | -| `calib/stereo_calib_20260621_202857_ascii.xml` | 出厂标定(0621 装配,baseline 482 mm) | -| `config/config.rk3588.12mp.yaml` | 配置文件(兼容 `sb_create` 接口用) | -| `example/` | 最小 C 示例(精测 + 测距) + CMakeLists | -| `INSTALL_3588.md` | 安装与运行说明(**从这里开始**) | -| `DEPENDENCIES.md` | 依赖库详细清单及说明 | -| `ABI_FINGERPRINT.txt` | 二进制 ABI 指纹 | +| `weights/best_obb_1024.rknn` | NPU 模型 —— OBB 检测(精测+测距共用,imgsz 1024,FP16) | +| `calib/stereo_calib_20260621_202857_ascii.xml` | 12MP 双目标定(**0621 装配 baseline 482mm**,Halcon 位姿已修正) | +| `config/config.rk3588.12mp.yaml` | 仅 `sb_create`(兼容)用 + 作参数参考;**`sb_create_ex` 不需要它** | +| `lib/*.so` | 算法库、OpenCV 4.5.0 运行库、RKNN 运行时(aarch64 2.3.2),运行时通过 `LD_LIBRARY_PATH=./lib` 加载 | +| `INSTALL_3588.md` / `ABI_FINGERPRINT.txt` | 板上装环境说明 / .so 二进制指纹(**你的环境必须匹配**) | +| `example/` | C 调用示例(`main.c` 精测,**sb_create + buffer 接口**)+ CMakeLists | --- -## 你什么时候需要什么(编译期 vs 运行期) +## 编译期 vs 运行期 -**重点:你不需要重编这个库**(它是预编译好的成品,你也没有源码)。你只编译*你自己* -调用它的小程序,那一步几乎零依赖。OpenCV / librknnrt 是**运行那一刻**才需要的。 +**你不需要重编这个库**(预编译成品,无源码)。你只编译*你自己*调用它的程序。 -| 东西 | 编译你自己的调用程序 | 在板上**运行** | +| 东西 | 编译你的调用程序 | 板上运行 | |---|:---:|:---:| | `c_api.h` | ✅ | — | | `libstereo_bolt.so` | ✅(链接,带 `--allow-shlib-undefined`) | ✅ | -| OpenCV 4.5.4 | ❌ 不需要(C ABI 不暴露 cv 类型) | ✅ **已随包 `lib/`,无需安装** | -| `librknnrt.so`(RKNN 运行时) | ❌ | ✅ **已随包 `lib/`** | +| OpenCV | 你的程序:❌ 不需要(C ABI 不暴露 cv 类型,相机直接给 Mono8 buffer)
本包 example:❌ 运行不需要系统 OpenCV | ✅ 已随包 `lib/`,无需 apt | +| `librknnrt.so` | ❌ | ✅ 已随包 `lib/`,`LD_LIBRARY_PATH=./lib` | | NPU 驱动 + `/dev/rknpu` | ❌ | ✅ 必须(内核态) | -| `.rknn` 模型 + 标定 XML + `config` | ❌ | ✅ 必须(**已在本包** weights/ config/ calib/) | +| 模型 / 标定 | ❌ | ✅(已在本包;`sb_create_ex` 由你读进来传,`sb_create` 由库读) | -> **“YOLO 依赖”在板上很轻**:就是 `librknnrt.so` + NPU 驱动 + `.rknn` 文件。 -> **不需要** Python / PyTorch / ultralytics / ONNXRuntime / rknn-toolkit2 —— 那些是 PC 上把 -> `.onnx` 转成 `.rknn` 用的,转完板上就用不到。 +> example 用 OpenCV 只是为了独立跑(读本包的标定 XML + 测试图)。**你的产品里相机直接给 Mono8 buffer、 +> 标定来自你自己的来源,调用端无需 OpenCV,库也始终零文件 IO。** --- -## ⚠️ 必须匹配的 ABI 指纹(动态链接的硬约束) +## ⚠️ 必须匹配的 ABI 指纹 -这个 `.so` 在某个具体环境里编出来,二进制里**写死了**它依赖的 so 名字和 glibc 版本。 -**“自己准备 OpenCV” 不等于随便哪个 OpenCV 都行——必须是指纹里那个 major.minor。** +`.so` 二进制里写死了依赖的 so 名字和 glibc 版本。打开 `ABI_FINGERPRINT.txt` 确保板子满足: +- CPU = **aarch64**;**glibc ≥ 指纹版本**(GLIBC_2.32); +- **运行时优先使用本包 `lib/`**(内含标准 SONAME `libopencv_*.so.4.5`,无需系统 OpenCV); +- **librknnrt 版本** = 转 `.rknn` 的 toolkit 版本(**2.3.x**)。 +- 指纹应列出 9 个导出符号,含 **`sb_create_ex` / `sb_default_params`**。 -打开 `ABI_FINGERPRINT.txt`,确保你的板子满足: - -- CPU 架构 = **aarch64** -- **glibc ≥ 指纹里的版本**(低于它,加载即 `GLIBC_x.xx not found`) -- **OpenCV soname 与指纹一致**(本包指纹 = `libopencv_core.so.4.5d` ⇒ 装 **OpenCV 4.5.x**;其它版本 soname 不通用) -- **librknnrt 版本** = 转换 `.rknn` 所用 toolkit 版本(**2.3.x**) - -一条命令自检(应当**没有任何** `not found`): - -```bash -ldd lib/libstereo_bolt.so | grep "not found" -``` +自检(应无任何 `not found`):`ldd lib/libstereo_bolt.so | grep "not found"` --- -## 板子上需要准备的 +## 公共错误码 -| 依赖 | 状态 | -|---|---| -| OpenCV 4.5.4 + 传递依赖(131 个 .so) | ✅ **已随包 `lib/`**,无需 apt | -| `librknnrt.so`(RKNN 运行时 2.3.2) | ✅ **已随包 `lib/`** | -| `.rknn` 模型 + 标定 XML + config | ✅ **已随包** `weights/` `calib/` `config/` | -| NPU 内核驱动 + `/dev/rknpu` | ⚠️ **板子固件提供**,出厂 RK3588 一般已有 | -| glibc ≥ 2.32(Ubuntu 22.04 自带 2.35) | ✅ 系统自带,无需操作 | - -> 完整依赖说明见 `DEPENDENCIES.md`。 +`SB_OK=0` / `SB_ERR_INVALID_CONFIG=-1` / `SB_ERR_IO=-2` / `SB_ERR_ALGORITHM_REJECTED=-3` / `SB_ERR_INTERNAL=-99`。 +> `SB_ERR_ALGORITHM_REJECTED` 不是崩溃,是算法判定该帧不满足 `expected` 颗(仅精测返回);**仍返回已测出的部分螺栓**(见下)。 --- -## 怎么用(C ABI) +## 接口 1 —— 精测(PRECISE) -完整示例见 `example/main.c`。有**两个**喂帧入口,生产用内存版: +**用途**:12MP 逐颗高精度测量。触发式"测得准"路径,板上 ~2.5Hz@1024。 +**输入**:内存 Mono8 buffer(生产首选)或文件路径(离线)。buffer 尺寸**必须等于标定尺寸**,否则 `SB_ERR_IO`。 -**① 生产路径 —— 内存版(不落盘,直接喂相机帧):** ```c -StereoBoltCtx* ctx = sb_create("config/config.rk3588.yaml"); // 一次:载配置/标定/RKNN 模型 -while (抓帧) { - const uint8_t* L = camera_left_frame(); // 相机 SDK 给的 Mono8 缓冲(W*H 字节,在内存) - const uint8_t* R = camera_right_frame(); - StereoBoltModuleResultC out; - sb_process_bolt_module_buffers(ctx, - L, W, H, 0, // stride=0 表示行紧凑排列(每行 W 字节) - R, W, H, 0, - 1, // channels: 1=Mono8(灰度), 3=BGR 也接受(内部转灰度) - 8, &out); // 8 = 期望螺栓数 - /* out.success==1: out.data.bolts[i].height_mm / foot_xyz / top_xyz + adjacent_distances + ground_plane - out.success==0: out.failure.reason + left_rois/right_rois/pairs(诊断用) */ - sb_free_bolt_module_result(&out); -} -sb_destroy(ctx); // 一次 +sb_status_t sb_process_bolt_module_buffers( + StereoBoltCtx* ctx, + const uint8_t* left_data, int left_width, int left_height, int left_stride_bytes, + const uint8_t* right_data, int right_width, int right_height, int right_stride_bytes, + int channels, int expected_bolt_count, StereoBoltModuleResultC* out); +sb_status_t sb_process_bolt_module_files(StereoBoltCtx*, const char* left, const char* right, + int expected_bolt_count, StereoBoltModuleResultC* out); +void sb_free_bolt_module_result(StereoBoltModuleResultC* out); /* 用完必须释放 */ ``` -> `W`/`H` **必须等于标定分辨率**(`config` 的 `image.width`/`image.height`),否则返回 `SB_ERR_IO`。 -> 内存版**全程不碰磁盘**(已实测:删掉源文件后内存版照常出结果)。 -**② 离线/测试路径 —— 文件版(读磁盘图,用你自己的 12MP 左右图):** +**输出 `StereoBoltModuleResultC`** —— 每颗 `data.bolts[i]`: +- **`height_mm`**(高度) +- **`top_xyz` / `foot_xyz`**(顶 / 底 3D 坐标) +- **`centerline`**(3D 中心轴:`point` + `direction`) +- `left_roi` / `right_roi`(左右图 2D 框 x,y,w,h,score) +- **`confidence`**(0=trusted / 1=suspect)+ `status` +另含 `adjacent_distances[]`(相邻 `distance_mm`)、`ground_plane`(`model[4]`/`rms_mm`/`inlier_count`)。 + +**坐标系/单位**:高度、间距、3D 坐标 = **mm**,**校正后左相机坐标系**(x 右 / y 下 / z=深度,典型 z≈1600–2200mm)。 + +**★ 部分输出契约(去 all-or-nothing)**:即使 `success==0`(数量不符 `count_mismatch`,无论检到的多还是少于 `expected`), +`data.bolts[]` 仍含**所有干净测出的螺栓**(各带 height/top/foot/confidence);`failure`(reason + 完整 ROI + 配对状态)始终填充,能看出缺哪颗、为何缺。**别只看返回码就丢结果——照样遍历 `data.bolts[]`,按 `confidence` 自行取舍。** + +**最简用法(`sb_create_ex`)**: ```c -sb_process_bolt_module_files(ctx, "your_left.bmp", "your_right.bmp", 8, &out); +StereoBoltCalibC calib = {...}; /* 填 K/D/R/T/size/baseline */ +StereoBoltModelC model = {.data=rknn_bytes, .size=n, .backend=SB_YOLO_BACKEND_RKNN, .imgsz=1024, .conf=0.5f}; +StereoBoltParamsC p = sb_default_params(); p.wd_z_min_mm=1200; p.wd_z_max_mm=2800; +StereoBoltCtx* ctx = sb_create_ex(&calib, &model, &p); /* 一次 */ +StereoBoltModuleResultC out; +sb_process_bolt_module_buffers(ctx, L,w,h,0, R,w,h,0, 1, /*expected=*/6, &out); +/* 遍历 out.data.bolts[i].height_mm / top_xyz / foot_xyz ... */ +sb_free_bolt_module_result(&out); sb_destroy(ctx); ``` -> 图必须是本套 12MP 相机的 4096×3000(灰度或彩色均可),格式 .bmp/.tiff/.png。本包不含测试图。 - -错误码:`SB_OK=0` / `SB_ERR_INVALID_CONFIG=-1` / `SB_ERR_IO=-2` / -`SB_ERR_ALGORITHM_REJECTED=-3` / `SB_ERR_INTERNAL=-99`。 -> `SB_ERR_ALGORITHM_REJECTED` 不是崩溃,是算法判定该帧不可测,`out.failure` 里有原因 + ROI + 配对状态。 +完整示例见 `example/main.c`。**imgsz 首选 1024**(OBB 模型,自动检测旋转框);交付权重 FP16(INT8 检出全废,不可用)。 --- -## 输入 / 输出(契约) +## 高度测量模式(`measurement.height_from_plane`) -**输入** -- 一次性(`sb_create`):`config/config.rk3588.yaml`(内部指向 `calib/*.xml` 与 `weights/best.rknn`)。 -- 每帧(生产用 `sb_process_bolt_module_buffers`):左/右 Mono8 内存缓冲 + 宽/高/stride、通道数、期望螺栓数(int)、`&out`。 -- 每帧(离线用 `sb_process_bolt_module_files`):左图路径、右图路径、期望螺栓数(int)、`&out`。 +精测接口支持两种高度计算方式,通过 config 中 `measurement.height_from_plane` 切换: -**输出**(`StereoBoltModuleResultC out` + 返回码) -- 成功 `out.success==1` → `out.data`: - - `bolts[]` 每颗:`bolt_id` / `left_roi`,`right_roi`(x,y,width,height,score) / **`height_mm`** / - **`foot_xyz`,`top_xyz`**(底/顶 3D 点) / `centerline`(3D 轴 = point+direction) - - `adjacent_distances[]`:`bolt_id_i`,`bolt_id_j`,**`distance_mm`** - - `ground_plane`:`model[4]`(ax+by+cz+d=0)、`rms_mm`、`inlier_count` -- 失败 `out.success==0` → `out.failure`:`reason` + `left_rois[]`/`right_rois[]` + `pairs[]`(配对诊断) +| 模式 | config 值 | 高度定义 | 是否需要平面拟合 | 板上耗时(8颗场景) | +|---|---|---|---|---| +| **顶到底**(默认) | `height_from_plane: false` | 螺杆顶端到可见底端沿拟合轴的长度 | **不需要**(自动跳过) | ~400ms | +| **顶到平面** | `height_from_plane: true` | 螺杆顶端到支撑平面的法向距离 | 需要(StereoBM 拟合) | ~800ms | -**单位 / 坐标系**:高度、间距 = **mm**;3D 坐标(foot/top/centerline) = **mm,校正后左相机坐标系** -(x 右 / y 下 / z=深度,典型 z≈1600–2200mm)。`score`=YOLO 置信度。 +**默认关闭平面测量**(`height_from_plane: false`),此时平面计算完全跳过,节省 300-550ms。 -输出格式示例(**仅示意字段结构**,数值来自早先 5MP 验证机,非本包 12MP 相机): - -``` -success=1 bolts=8 distances=8 -bolt 0 height=151.2mm top=(-191.9,-122.9,1995.3) foot=(-204.3,2.9,2078.3) -... -dist 0-1=268.3mm dist 1-2=254.4mm ... dist 7-0=335.8mm +**开启平面测量**:在 `config.rk3588.12mp.yaml` 中修改: +```yaml +measurement: + height_from_plane: true # 开启:高度 = 螺杆顶端到支撑平面的法向距离 ``` -> 一句话:**入 = 一对左右图 + 期望螺栓数(配置/标定/模型初始化时给一次); -> 出 = 每颗螺栓 高度 + 顶/底 3D + 3D 中心轴 + 相邻间距 + 地平面,或失败原因 + 诊断。** +`sb_create_ex` 用户:当前 `StereoBoltParamsC` 不含此字段,默认为 false(顶到底)。如需平面模式,使用 `sb_create(config.yaml)` 并在 config 中设置。 --- -## 编译你自己的调用程序 + 运行(**都在包根目录执行**) +## 接口 2 —— 测距(RANGING)`sb_range_bolt_binned` +**用途**:实时回答"左目光心→螺栓多远"。容忍部分可见:**绝不要求螺栓数**,≥1 颗即给距离。当前板上约 153ms/帧(~6.5Hz)@1024。 +**输入**:2×2 binned 内存 buffer(12MP→3MP=2048×1500),Mono8/BGR。binned 内参**内部推导**,无需单独 binned 标定。 + +```c +typedef struct { double distance_mm; int left_x,left_y,right_x,right_y; double ncc_score,lrc_px; int confidence; } StereoBoltRangeBoltC; +typedef struct { int found; double distance_mm; int bolt_count; char status[32]; } StereoBoltRangeSummaryC; +sb_status_t sb_range_bolt_binned( + StereoBoltCtx* ctx, + const uint8_t* left_data, int left_width, int left_height, int left_stride_bytes, + const uint8_t* right_data, int right_width, int right_height, int right_stride_bytes, + int channels, StereoBoltRangeSummaryC* out, StereoBoltRangeBoltC* bolts, int max_bolts); +``` +**输出**:主用 `out.distance_mm`(可见杆代表距离);`out.found` 每帧轮询;`bolts[]` 给出每颗候选的左右匹配点。OBB 轴路线下 `ncc_score` 为检测/匹配分数、`lrc_px` 为轴视差自洽度(字段名保留用于 ABI 兼容);NCC 回退路线下为 NCC/LRC 指标。精度目标 **±50mm**(稳定区 1.3–2.4m)。 +**可选 `ranging.*` 参数**(`StereoBoltParamsC` 里 `rng_*`,都有默认):`z_min/max_mm` 视差带、`lrc_max_mm`/`lrc_trusted_mm` 物理同根门 等。完整示例见 `example/range_example.cpp`。 + +> 注:`sb_create_ex` 即使**只做测距**也要求提供精测视差带(`wd_z_*` 或 `sgbm_*`),因为同一 `ctx` 两接口共用。 + +--- + +## 该用哪个 + 板上实测吞吐 + +| | 精测 `sb_process_bolt_module_*` | 测距 `sb_range_bolt_binned` | +|---|---|---| +| 问题 | 每颗螺栓高度/间距/地平面/3D 坐标 | 相机离螺栓多远(代表距离) | +| 输入 | 12MP 原图(Mono8 buffer / 文件) | 2×2 binned 3MP buffer | +| 螺栓数 | 要 `expected`(不符=部分输出+`count_mismatch`) | **不要**,≥1 颗即 `found` | +| 板上吞吐@1024 | **约 400ms/帧** | **约 153ms/帧 ≈ 6.5Hz** | +| 定位 | 触发式"测得准" | 无人机实时定位反馈 | + +> 测距实测于 RK3588(best_obb_1024.rknn,distance_153304_403,21 帧):均值 153.146ms,距离均值 1652.360mm,跨度 0.491mm。 +> 本交付为单模型 OBB@1024(精测+测距共用)。 + +--- + +## 编译你的调用程序 + 运行 + +示例用 C/C++,运行时库从本包 `lib/` 加载: ```bash -# 在包根目录(与 lib/ config/ calib/ 同级)编译 -cmake -S example -B example/build -DSB_ROOT="$(pwd)" -cmake --build example/build +cd example +cmake -S . -B build -DSB_ROOT="$(cd .. && pwd)" +cmake --build build # -> bolt_demo(精测)+ range_demo(测距) -# 在包根目录运行(config 里 calib/、weights/ 是相对路径,按当前目录解析) -# example 走【内存接口】: 读 raw Mono8 裸数据(W*H 字节)喂 sb_process_bolt_module_buffers, -# 库全程不见文件路径。your_left/right.raw = 你 12MP 相机帧 dump 的 Mono8(4096*3000 字节) -LD_LIBRARY_PATH=lib:/usr/lib:$LD_LIBRARY_PATH \ - ./example/build/bolt_demo config/config.rk3588.yaml your_left.raw your_right.raw 4096 3000 8 +# ../lib 含 libstereo_bolt.so + OpenCV 4.5.0 + librknnrt.so +# 精测:传标定 XML + 模型 + 左右图 + 期望螺栓数 +LD_LIBRARY_PATH=../lib:$LD_LIBRARY_PATH \ + ./build/bolt_demo ../calib/stereo_calib_20260621_202857_ascii.xml \ + ../weights/best_obb_1024.rknn L.bmp R.bmp 6 + +# 测距:传标定 + 模型 + 左右图(demo 内部 2×2 binning;生产直接喂相机 binned buffer) +LD_LIBRARY_PATH=../lib:$LD_LIBRARY_PATH \ + ./build/range_demo ../calib/stereo_calib_20260621_202857_ascii.xml \ + ../weights/best_obb_1024.rknn L.bmp R.bmp ``` diff --git a/AppAlgo/stereo_bolt_delivery/DEPENDENCIES.md b/AppAlgo/stereo_bolt_delivery/DEPENDENCIES.md index 406cca26..84ef038b 100644 --- a/AppAlgo/stereo_bolt_delivery/DEPENDENCIES.md +++ b/AppAlgo/stereo_bolt_delivery/DEPENDENCIES.md @@ -5,74 +5,25 @@ --- -## 一、包含哪些库(lib/ 目录) +## 一、包含哪些库(lib/ 目录,共 9 个) -### 核心算法库(本包提供) | 库 | 说明 | |---|---| | `libstereo_bolt.so` | 本算法库(主体) | | `librknnrt.so` | RKNN NPU 推理运行时(Rockchip 2.3.2) | +| `libopencv_core.so.4.5` | OpenCV 4.5.0 — 基础矩阵/图像容器 | +| `libopencv_imgproc.so.4.5` | OpenCV 4.5.0 — 图像处理 | +| `libopencv_calib3d.so.4.5` | OpenCV 4.5.0 — 立体标定、rectify、三角测量 | +| `libopencv_imgcodecs.so.4.5` | OpenCV 4.5.0 — 图像文件读写 | +| `libopencv_dnn.so.4.5` | OpenCV 4.5.0 — DNN 模块 | +| `libopencv_features2d.so.4.5` | OpenCV 4.5.0 — 特征点(calib3d 依赖) | +| `libopencv_flann.so.4.5` | OpenCV 4.5.0 — FLANN 索引(features2d 依赖) | -### OpenCV 4.5.4(Ubuntu 22.04 release,aarch64) -| 库 | 用途 | -|---|---| -| `libopencv_core.so.4.5d` | 基础矩阵/图像容器 | -| `libopencv_imgproc.so.4.5d` | 图像处理(滤波、边缘、几何变换) | -| `libopencv_calib3d.so.4.5d` | 立体标定、rectify、三角测量 | -| `libopencv_imgcodecs.so.4.5d` | 图像文件读写(bmp/png/tiff) | -| `libopencv_dnn.so.4.5d` | DNN 模块(YOLO ONNX 推理备用后端) | -| `libopencv_features2d.so.4.5d` | 特征点(calib3d 依赖) | -| `libopencv_flann.so.4.5d` | FLANN 索引(features2d 依赖) | - -### 图像编解码层(OpenCV 依赖) -| 库 | 用途 | -|---|---| -| `libjpeg.so.8` | JPEG | -| `libpng16.so.16` | PNG | -| `libtiff.so.5` | TIFF(含 libjbig/libdeflate/liblzma/libzstd/liblz4) | -| `libwebp.so.7` | WebP | -| `libopenjp2.so.7` | JPEG2000 | -| `libIlmImf-2_5.so.25` 等 | OpenEXR(IlmImf/Imath/Iex/IlmThread/Half) | -| `libheif.so.1` 等 | HEIF/HEVC(libaom/libde265/libx265/libdav1d) | -| `libgif.so.7` | GIF | -| `libcharls.so.2` | JPEG-LS | -| `libz.so.1` | zlib | - -### 并行计算(OpenCV 依赖) -| 库 | 用途 | -|---|---| -| `libtbb.so.2` | Intel TBB 线程并行 | - -### DNN / 模型序列化(OpenCV DNN 依赖) -| 库 | 用途 | -|---|---| -| `libprotobuf.so.23` | Protobuf(ONNX 模型解析) | - -### GDAL / 地理空间(OpenCV imgcodecs 依赖,随包但运行中不主动调用) -libgdal、libgdcm\*、libgeotiff、libproj、libgeos\*、libogdi、libspatialite、 -libnetcdf、libhdf5\*、libpq(PostgreSQL)、libmysqlclient、libsqlite3、 -libcfitsio、libkml\*、libminizip、liburiparser、libxerces-c、libfreexl、 -libarmadillo、libblas/liblapack/libgfortran、libarpack、libsuperlu、 -libqhull\_r、libfyba/libfyut/libfygm、libblosc、libsnappy、libsz/libaec、 -libmfhdfalt/libdfalt、librttopo、libxml2、libgif - -### 字体 / PDF(libgdal → libpoppler 链) -libpoppler、libfreetype、libbrotli\*、libfontconfig、liblcms2 - -### 网络 / 安全(libgdal → libcurl 链,随包但不主动使用) -libcurl、libcurl-gnutls、libssl/libcrypto(OpenSSL 3)、libgnutls、 -libnettle/libhogweed/libgmp、libp11-kit、libffi、libtasn1、 -libgssapi\_krb5/libkrb5/libk5crypto/libkrb5support(Kerberos)、 -libcom\_err、libkeyutils、libnghttp2、libidn2、libunistring、 -librtmp、libssh、libpsl、libldap/liblber、libsasl2 - -### 其他系统级(随包) -libicuuc/libicudata(Unicode)、libnss\*/libnspr4/libplc4/libplds4/libsmime3(NSS)、 -libuuid、libjson-c、libexpat、libpcre2-8、libltdl、libodbc/libodbcinst、 -libnuma、libresolv、libtirpc - -> 上述库来自 Ubuntu 22.04 apt(OpenCV 4.5.4 的官方传递依赖闭包),均为 **release/优化版本**, -> 非 debug 版。库数量约 **131 个**,打包后约 **70 MB**。 +> OpenCV 从源码编译(非 apt 包),**SONAME 为标准的 `libopencv_*.so.4.5`** +> (不带 Debian/Ubuntu 的 `d` 后缀),与自行源码编译 OpenCV 4.5.x 的环境兼容。 +> +> 图像编解码(libjpeg/libpng/libtiff/libwebp/openjpeg)、protobuf、zlib +> 均已**静态链入** OpenCV,不再作为独立 `.so` 出现。 --- @@ -86,7 +37,6 @@ libnuma、libresolv、libtirpc | `libpthread.so.0` | 同上(已并入 glibc 2.34+) | | `libm.so.6` | 同上 | | `libdl.so.2` | 同上 | -| `librt.so.1` | 同上 | | `libstdc++.so.6` | GCC 11+(Ubuntu 22.04 自带) | | `libgcc_s.so.1` | 同上 | | `ld-linux-aarch64.so.1` | 动态链接器,系统自带 | @@ -95,47 +45,48 @@ libnuma、libresolv、libtirpc ## 三、为什么这么设计 -**问题:** OpenCV 在 Ubuntu 22.04 拖着约 131 个传递依赖(包括 GDAL、curl、Kerberos、 -OpenSSL 等)。如果只靠目标板 apt 安装,新板子需要联网、可能遇到镜像源不可达、 -版本不一致等问题,交付不可控。 +**问题:** 如果目标板通过 apt 安装 OpenCV,Debian/Ubuntu 会给 SONAME 加私有后缀 +(如 `libopencv_core.so.4.5d` 而非标准的 `…4.5`),导致与自行编译的 OpenCV 不兼容; +另外 apt 安装需要联网,新板子部署不可控。 -**解法:** 把整个依赖闭包随包发布。运行时通过 `LD_LIBRARY_PATH` 优先搜索 `lib/`, -所有依赖在包内自洽。这些库只对本进程生效,**不替换系统库**,不影响系统 -ssl/网络栈/其他程序。 +**解法:** 从 OpenCV 4.5.0 官方源码编译,只构建算法实际需要的 7 个模块 +(core/imgproc/calib3d/imgcodecs/dnn/features2d/flann),图像编解码等第三方库 +全部静态链入。最终 `lib/` 只有 9 个 `.so`(含本库 + librknnrt),约 **22 MB**, +依赖关系极简,无需联网、无需 apt。 --- ## 四、运行时加载方式 ```bash -# 解包后,设一次环境变量,之后所有调用本库的程序都能找到这些依赖 +# 解包后,设一次环境变量 export LD_LIBRARY_PATH="$(pwd)/lib:$LD_LIBRARY_PATH" # 验证:应当没有任何 "not found" 输出 ldd lib/libstereo_bolt.so | grep "not found" ``` -`LD_LIBRARY_PATH` 对所有传递依赖生效——当 `libopencv_core` 加载 `libjpeg` 时, -链接器同样优先搜索 `lib/`,无需对每个 `.so` 单独处理。 +--- + +## 五、SONAME 兼容性说明 + +| 编译来源 | SONAME | 与本包兼容? | +|---|---|---| +| **本包(源码编译 4.5.0)** | `libopencv_core.so.4.5` | ✅ | +| **自行源码编译 4.5.x** | `libopencv_core.so.4.5` | ✅ | +| **Ubuntu apt `libopencv-dev`** | `libopencv_core.so.4.5d` | ❌ SONAME 不匹配 | + +本包的 `libstereo_bolt.so` 链接的 NEEDED 是标准 `libopencv_*.so.4.5`(无 `d`), +与任何从源码编译的 OpenCV 4.5.x 兼容。如果目标板上碰巧装了 apt 的 OpenCV(带 `d`), +只要 `LD_LIBRARY_PATH` 优先指向 `lib/`,就会用本包内的库,不会冲突。 --- -## 五、编译调用程序时注意 +## 六、编译调用程序时注意 -运行不需要 OpenCV,但**编译** `example/` 示例代码时需要 OpenCV **头文件** -(`.h`),而 `lib/` 里只有 `.so`(运行库),没有头文件。 +**生产路径**:你自己的代码只用 `include/stereo_bolt/c_api.h`,不引用任何 OpenCV 头, +相机 buffer 直接传给 `sb_process_bolt_module_buffers`,**完全不需要 OpenCV 头文件**。 -两种方案: - -**A. 目标板上 apt 装头文件(推荐,只需一次)** -```bash -# 只装 -dev 包获取头文件;运行库用包内 lib/ 的,不用 apt 装的 .so -sudo apt install libopencv-dev -``` - -**B. 仅使用 C ABI,无需 OpenCV 头(生产路径)** - -你自己的产品代码只用 `include/stereo_bolt/c_api.h`,不引用任何 OpenCV 头, -相机 buffer 直接传给 `sb_process_bolt_module_buffers`。这是生产路径, -**完全不需要 OpenCV 头文件**,也不需要 apt。 -`example/` 仅作集成验证用,生产端不需要编译它。 +**编译 `example/` 示例**(可选,仅用于集成验证):当前示例是纯 C, +只读 headerless Mono8 raw 文件并调用 C ABI,不引用 OpenCV 头文件。 +运行时仍用本包 `lib/` 里的库。 diff --git a/AppAlgo/stereo_bolt_delivery/INSTALL_3588.md b/AppAlgo/stereo_bolt_delivery/INSTALL_3588.md index d8feffdd..4e80f8a1 100644 --- a/AppAlgo/stereo_bolt_delivery/INSTALL_3588.md +++ b/AppAlgo/stereo_bolt_delivery/INSTALL_3588.md @@ -32,9 +32,9 @@ cd stereo_bolt_delivery ``` stereo_bolt_delivery/ -├── lib/ ← 算法库 + OpenCV 4.5.4 + 全部传递依赖(131 个 .so) +├── lib/ ← 算法库 + OpenCV 4.5.0 + librknnrt(共 9 个 .so) ├── include/stereo_bolt/ ← c_api.h(唯一对外头文件) -├── weights/ ← best_1280_20260621.rknn(NPU 模型) +├── weights/ ← best_obb_1024.rknn(NPU OBB 模型) ├── calib/ ← 出厂标定 XML(0621 装配,baseline 482mm) ├── config/ ← config.rk3588.12mp.yaml(兼容接口用) ├── example/ ← 最小 C 示例 + CMakeLists @@ -82,20 +82,31 @@ StereoBoltModelC model = { .data = rknn_bytes, // weights/*.rknn 的原始字节 .size = rknn_size, .backend = SB_YOLO_BACKEND_RKNN, - .imgsz = 1280, + .imgsz = 1024, .conf = 0.5f }; StereoBoltParamsC p = sb_default_params(); -p.wd_z_min_mm = 1200; // 工作距离下限(mm) -p.wd_z_max_mm = 2800; // 工作距离上限(mm) +p.wd_z_min_mm = 900; // 工作距离下限(mm) +p.wd_z_max_mm = 4000; // 工作距离上限(mm) StereoBoltCtx* ctx = sb_create_ex(&calib, &model, &p); // 只调一次(慢,载模型进 NPU) ``` > 兼容方式:`sb_create("config/config.rk3588.12mp.yaml")`(读文件,需工作目录正确)。 -> 模型:`weights/best_1280_20260621.rknn`(精测 + 测距共用,imgsz 1280)。 +> 模型:`weights/best_obb_1024.rknn`(精测 + 测距共用,OBB imgsz 1024)。 > 标定:`calib/stereo_calib_20260621_202857_ascii.xml`(0621 装配,baseline 482 mm)。 +### 高度测量模式 + +默认 `height_from_plane: false`:高度 = 螺杆顶端到可见底端(**不跑平面拟合**,快 ~400ms/帧)。 + +如需平面模式(高度 = 顶端到支撑平面法向距离),在 config 中改为: +```yaml +measurement: + height_from_plane: true +``` +开启后每帧增加 300-550ms 平面拟合耗时。 + --- ## 6. 每帧调用 @@ -118,7 +129,7 @@ sb_free_bolt_module_result(&m); // 必须 free StereoBoltRangeSummaryC s; StereoBoltRangeBoltC bolts[64]; sb_range_bolt_binned(ctx, lb, bw, bh, 0, rb, bw, bh, 0, 1, &s, bolts, 64); -// s.distance_mm:最近螺栓到相机的距离(mm) +// s.distance_mm:相机到可见螺栓的代表距离(mm) // ── 退出 ───────────────────────────────────────────────────────────────── sb_destroy(ctx); // 只调一次 @@ -131,19 +142,15 @@ sb_destroy(ctx); // 只调一次 ## 7. 编译示例程序(可选,用于集成验证) -示例代码读图用了 OpenCV,编译时需要 **OpenCV 头文件**(`lib/` 里只有 `.so`,没有 `.h`)。 +示例代码 `main.c` 是纯 C,**不依赖 OpenCV**(读 raw Mono8 文件 + C ABI 调用)。 ```bash -# 安装 OpenCV 头文件(只需 -dev 包;运行库用 lib/ 里的,不用 apt 的 .so) -sudo apt install libopencv-dev - -# 编译 cd example cmake -S . -B build -DSB_ROOT="$(cd .. && pwd)" cmake --build build ``` -> **生产代码不需要此步骤**:你自己的调用端只用 `c_api.h`,不引用任何 OpenCV 头, +> **生产代码同理**:你自己的调用端只用 `c_api.h`, > 相机 buffer 直接喂给 `sb_process_bolt_module_buffers`,全程无 OpenCV 依赖。 --- @@ -154,20 +161,13 @@ cmake --build build # 在 stereo_bolt_delivery/ 根目录执行(使 lib/ 路径正确) export LD_LIBRARY_PATH="$(pwd)/lib:$LD_LIBRARY_PATH" -# 精测 demo(需要左右 12MP 图,4096×3000,与标定分辨率一致) +# 精测 demo(需要左右 12MP raw Mono8,4096×3000) ./example/build/bolt_demo \ - calib/stereo_calib_20260621_202857_ascii.xml \ - weights/best_1280_20260621.rknn \ - /path/to/left.bmp /path/to/right.bmp 6 - -# 测距 demo -./example/build/range_demo \ - calib/stereo_calib_20260621_202857_ascii.xml \ - weights/best_1280_20260621.rknn \ - /path/to/left.bmp /path/to/right.bmp + config/config.rk3588.12mp.yaml \ + /path/to/left.raw /path/to/right.raw 4096 3000 8 ``` -板上实测吞吐:**精测 ~582 ms/帧 ≈ 1.7 Hz**、**测距 ~238 ms/帧 ≈ 4.2 Hz**(0621 装配,帧 1)。 +板上实测吞吐:**精测 ~400 ms/帧 ≈ 2.5 Hz**(height_from_plane=false)、**测距 ~238 ms/帧 ≈ 4.2 Hz**(0621 装配)。 --- diff --git a/AppAlgo/stereo_bolt_delivery/config/config.rk3588.yaml b/AppAlgo/stereo_bolt_delivery/config/config.rk3588.yaml index 8a27a2c7..8857b58c 100644 --- a/AppAlgo/stereo_bolt_delivery/config/config.rk3588.yaml +++ b/AppAlgo/stereo_bolt_delivery/config/config.rk3588.yaml @@ -1,12 +1,14 @@ # ============================================================ -# (C++ port) -# -# Python e:/02_Modules/StereoVisionBoltMeasurement/config.yaml -# -# Python paths +# RK3588 delivery config — OBB model, baseline 482mm +# Only used by sb_create(); sb_create_ex() ignores this file. # ============================================================ -# ---------- ---------- +# ---------- working distance ---------- +working_distance: + z_min_mm: 900 + z_max_mm: 4000 + +# ---------- image ---------- image: width: 4096 height: 3000 @@ -14,35 +16,32 @@ image: expected_dtype: "uint8" allowed_extensions: [".tiff", ".tif", ".bmp", ".png"] -# ---------- ---------- +# ---------- image validation ---------- image_validation: min_max_value: 10 max_saturation_ratio: 0.20 -# ---------- ChArUco ---------- +# ---------- ChArUco ---------- charuco: rows: 9 cols: 9 square_length_mm: 50.0 marker_length_mm: 37.0 - dict_id: 11 # DICT_6X6_1000 + dict_id: 11 min_corners_per_image: 30 -# ---------- ---------- +# ---------- calibration ---------- calibration: source: "xml" - xml_path: "calib/stereo_calib.xml" # 0621 新装配 baseline 482mm (ChArUco) + xml_path: "calib/stereo_calib.xml" min_image_pairs: 15 max_skip_ratio: 0.30 - max_single_rms: 0.40 # : 12MP RMS=0.373(5MP 0.19) - # 0.40 ;0.373 0.1px ,, - # <0.20 + max_single_rms: 0.40 max_stereo_rms: 0.80 expected_baseline_range: [80, 600] use_fix_intrinsic: true -# ---------- ---------- -# alpha=1.0 CLAUDE.md #1 +# ---------- rectification ---------- rectification: alpha: 1.0 interpolation: "cubic" @@ -53,9 +52,7 @@ rectification: max_epipolar_error_pixel: 5.0 min_valid_roi_ratio: 0.25 -# ---------- SGBM ---------- -# min_disparity / num_disparities f_rectB -# f_rect3190, B235mmalpha=1.0 +# ---------- SGBM ---------- sgbm: min_disparity: 288 num_disparities: 736 @@ -69,24 +66,18 @@ sgbm: pre_filter_cap: 63 mode: "SGBM_3WAY" -# ---------- WLS ---------- +# ---------- WLS ---------- wls: lambda_value: 8000.0 sigma_color: 1.5 -# ---------- ---------- +# ---------- disparity validation ---------- disparity_validation: min_valid_pixel_ratio: 0.10 min_disparity_range: 50 max_nan_ratio: 0.90 -# ---------- ---------- -# z_min_mm / z_max_mm / expected_wd_range are FALLBACK only when run_pipeline / -# c_api are wired (the standard path) SceneRuntime derives the real reachable -# Z range from (f_rect, baseline, sgbm.min_disparity, sgbm.num_disparities) and -# overrides these values per frame. See docs/L_100_SCENE_HANDOFF.md 6.2 and -# include/stereo_bolt/scene_runtime.hpp. Keep the fallback values conservative -# enough to cover every supported workpiece distance. +# ---------- pointcloud ---------- pointcloud: z_min_mm: 600 z_max_mm: 3000 @@ -108,13 +99,7 @@ pointcloud: dilate_px: 100 min_area_ratio: 0.02 -# ---------- 2D ---------- -# mask_method: foam_mask( mask) -# "brightness" + (; -# rectify ROI 0, mask ROI ) -# "ransac" dense xyz_map RANSAC , -# " < ransac.distance_thresh_mm" mask -# "brightness" parity_tests , "ransac" +# ---------- 2D detection ---------- bolt_detect_2d: mask_method: "ransac" foam_brightness_threshold: 70 @@ -125,99 +110,75 @@ bolt_detect_2d: bolt_min_area_px: 800 min_bolts: 1 max_bolts: 0 - bolt_exclude_dilate_px: 0 # 0 CLAUDE.md #5 + bolt_exclude_dilate_px: 0 ransac: - distance_thresh_mm: 8.0 # < inlier(3 - # frame 100 residual_std2.6 mm) + distance_thresh_mm: 8.0 max_iterations: 200 - min_inlier_ratio: 0.15 # inlier - sample_stride: 4 # xyz_map ( 44 1 RANSAC ) - morph_close_kernel_px: 9 # mask + + min_inlier_ratio: 0.15 + sample_stride: 4 + morph_close_kernel_px: 9 -# ---------- (SVD on foam mask) ---------- +# ---------- plane ---------- plane: max_residual_std_mm: 15.0 max_normal_to_axis_deg: 60 outlier_rejection_iterations: 5 outlier_mad_k: 2.5 -# ---------- ---------- -# plane fit bolt extract bolt 3D -# inlier OBB, OBB margin_mm WARN -# margin_mm bolt.expected_diameter_mm, -# / +# ---------- support gate ---------- support_gate: enabled: true margin_mm: 10.0 -# ---------- ---------- -# (run_pipeline / c_api) bolt bbox_projection h_est, -# extract height max(h_est 1.3, h_est + 20 mm), -# validate_measurements |measured - h_est| < bolt.height_tolerance_mm -# max_height_mm / extract_height_filter_mm fallback -# bbox_projection h_est -# (expected_diameter_mm), +# ---------- bolt ---------- bolt: min_height_mm: 5.0 - max_height_mm: 250.0 # fallback ceiling (used only when h_est is unavailable) - extract_height_filter_mm: 200.0 # fallback filter (same condition) - height_tolerance_mm: 60.0 # |measured - h_est| ; validate_measurements + max_height_mm: 250.0 + extract_height_filter_mm: 200.0 + height_tolerance_mm: 60.0 expected_diameter_mm: 16.0 min_points_per_bolt: 50 max_axis_plane_angle_deg: 30 - extract_fallback: "bbox_projection" # 'none' | 'bbox_projection'; YOLO bbox + , SGBM / + extract_fallback: "bbox_projection" -# ---------- ---------- +# ---------- measurement ---------- measurement: top_extraction_method: "percentile_on_axis" top_percentile: 0.99 - foot_rim_correction: false # 0621 决定: 关 rim(圆顶无领圈杆上 rim 修正会过冲) + height_from_plane: false + plane_use_sgbm: true + plane_sgbm_scale: 0.5 + plane_max_samples: 40000 -# ---------- Diagnostics ---------- +# ---------- diagnostics ---------- diagnostics: enabled: true blind_strip_warn_ratio: 0.25 min_roi_pointcloud_coverage_ratio: 0.001 -# ---------- YOLO ---------- -# C++ ONNX Python best_v4_finetune.pt .onnx -# dilate (50/30), -# scene_runtime.cpp pixels_per_mm(= f / Z_repr), -# / +# ---------- YOLO ---------- yolo: enabled: true - # 统一模型: 精测(sb_process_bolt_module_*)与测距(sb_range_bolt_binned)共用 weights/best.rknn。 - # 当前交付模型为 960 输入;文件名保持稳定,模型更新时只替换 weights/best.rknn。 model_path: "weights/best.rknn" backend: "rknn" - imgsz: 960 + task: "auto" + imgsz: 1024 conf: 0.5 dead_zone_min_valid_pts: 50 - dead_zone_bbox_dilate_mm: 24.0 # = expected_diameter_mm 1.5; valid_xyz bbox ,/ - foam_bbox_dilate_mm: 16.0 # = expected_diameter_mm 1.0; foam_mask YOLO bbox - - # Experimental: keep sparse-global YOLO bolts only when right YOLO + local LR check pass. + dead_zone_bbox_dilate_mm: 24.0 + foam_bbox_dilate_mm: 16.0 local_stereo_fallback: enabled: true min_valid_points: 50 -# ---------- working distance (physical, rig-level) ---------- -# 0621 新装配 B=482mm: 精测视差带从 WD + 标定自推导 (disp = f*B/Z), 换基线无需重调像素。 -# 旧固定 sgbm 像素带(288/736)是 200mm 装配的, 对 482mm 会 NEAR-clip 近处螺柱。 -# 注意: 需 .so 含视差带解耦(commit dc696d8 起); 旧 .so 不读此块仍用固定带。 -working_distance: - z_min_mm: 1200 - z_max_mm: 2800 - # ---------- ranging (binned) ---------- -# 测距视差带也按 0621 rig 的 WD; 物理 LRC 门(mm)随距离自紧。 ranging: - z_min_mm: 1300 - z_max_mm: 3600 + z_min_mm: 900 + z_max_mm: 4000 lrc_max_mm: 2.5 lrc_trusted_mm: 1.2 -# ---------- ---------- +# ---------- evaluation ---------- evaluation: height_max_mm: 2.0 height_p95_mm: 1.5 @@ -226,24 +187,23 @@ evaluation: repeatability_std_max_mm: 1.0 matching_max_distance_mm: 50.0 -# ---------- ---------- +# ---------- pipeline ---------- pipeline: fail_fast: true resume_from_checkpoint: true parallel_frames: false save_intermediate: false -# ---------- ---------- +# ---------- logging ---------- logging: level: "INFO" log_to_file: true log_file: "outputs/logs/pipeline.log" -# ---------- ---------- -# dataset/ , Python +# ---------- paths ---------- paths: calibration_dir: "calib" test_dir: "dataset/test" groundtruth_csv: "dataset/groundtruth.csv" - output_dir: "outputs_macro257" - stereo_params_yaml: "outputs_macro257/stereo_params.yaml" + output_dir: "outputs" + stereo_params_yaml: "outputs/stereo_params.yaml" diff --git a/AppAlgo/stereo_bolt_delivery/example/CMakeLists.txt b/AppAlgo/stereo_bolt_delivery/example/CMakeLists.txt index 7ee0bcba..a6adb44d 100644 --- a/AppAlgo/stereo_bolt_delivery/example/CMakeLists.txt +++ b/AppAlgo/stereo_bolt_delivery/example/CMakeLists.txt @@ -1,21 +1,20 @@ -# Minimal downstream build: link the prebuilt libstereo_bolt.so via c_api.h only. -# No OpenCV / RKNN headers needed here — the C ABI hides all C++/OpenCV types. +# Minimal downstream build — links the prebuilt libstereo_bolt.so. +# bolt_demo (main.c) -> sb_create + sb_process_bolt_module_buffers +# +# OpenCV here is NOT required by the demo — it uses only C ABI + raw Mono8 +# buffers. OpenCV is a transitive runtime dep of libstereo_bolt.so only. cmake_minimum_required(VERSION 3.16) project(bolt_demo LANGUAGES C) -# Point at the unpacked delivery dir (contains include/ and lib/). set(SB_ROOT "${CMAKE_CURRENT_LIST_DIR}/.." CACHE PATH "unpacked libstereo_bolt delivery root") add_executable(bolt_demo main.c) target_include_directories(bolt_demo PRIVATE "${SB_ROOT}/include") target_link_directories(bolt_demo PRIVATE "${SB_ROOT}/lib") target_link_libraries(bolt_demo PRIVATE stereo_bolt) +target_link_options(bolt_demo PRIVATE "LINKER:--allow-shlib-undefined") -# libstereo_bolt.so pulls in OpenCV + librknnrt transitively. You do NOT need -# those at LINK time — defer their resolution to runtime so you can link with -# just -lstereo_bolt. (Without this, ld follows the NEEDED chain and fails with -# "undefined reference to rknn_*/cv::*".) -target_link_options(bolt_demo PRIVATE "LINKER:--allow-shlib-undefined") - -# Runtime: LD_LIBRARY_PATH must include the dirs holding -# libstereo_bolt.so + libopencv_*.so (matching ABI_FINGERPRINT.txt) + librknnrt.so +# Runtime: LD_LIBRARY_PATH must include ../lib (holds libstereo_bolt.so + +# OpenCV 4.5.0 + librknnrt.so). Example: +# LD_LIBRARY_PATH=../lib ./build/bolt_demo \ +# ../config/config.rk3588.12mp.yaml L.raw R.raw 4096 3000 8 diff --git a/AppAlgo/stereo_bolt_delivery/include/stereo_bolt/c_api.h b/AppAlgo/stereo_bolt_delivery/include/stereo_bolt/c_api.h index 3ce21b67..5f8e8d22 100644 --- a/AppAlgo/stereo_bolt_delivery/include/stereo_bolt/c_api.h +++ b/AppAlgo/stereo_bolt_delivery/include/stereo_bolt/c_api.h @@ -184,7 +184,7 @@ typedef struct { double wd_z_max_mm; double sgbm_min_disparity; /* fixed pixel band (used when wd_* unset) */ double sgbm_num_disparities; - int foot_rim_correction; /* -1 = built-in default, 0 = off, 1 = on */ + int foot_rim_correction; /* -1 = built-in default (off), 0 = off, 1 = on */ /* --- binned ranging path (sb_range_bolt_binned) --- */ double rng_z_min_mm; @@ -259,16 +259,16 @@ typedef struct { double distance_mm; /* straight-line distance, left optical centre -> bolt */ int left_x; /* bbox centre in the rectified-left (binned) image */ int left_y; - int right_x; /* NCC correspondence in the rectified-right (binned) image */ - int right_y; /* -> the "same bolt" evidence; mark to verify */ - double ncc_score; /* match score */ - double lrc_px; /* left-right consistency residual (px); small == same point */ + int right_x; /* matched point in the rectified-right (binned) image */ + int right_y; /* OBB-axis route: right OBB axis point; NCC fallback: correspondence */ + double ncc_score; /* detector/match score; ABI name kept for compatibility */ + double lrc_px; /* OBB disparity stddev or NCC LRC residual; smaller is better */ int confidence; /* 0 = trusted (strong + self-consistent), 1 = suspect */ } StereoBoltRangeBoltC; typedef struct { - int found; /* 1 if >= 1 bolt was detected and NCC/LRC-verified */ - double distance_mm; /* MEDIAN straight-line distance over visible bolts */ + int found; /* 1 if >= 1 bolt was detected and paired/matched */ + double distance_mm; /* representative straight-line distance over visible bolts */ int bolt_count; /* total visible bolts (may exceed the number written to bolts[]) */ char status[32]; /* "ok" | "no_detection" | "no_valid_match" (why found==0) */ } StereoBoltRangeSummaryC; @@ -281,7 +281,7 @@ typedef struct { * - The binned size may be ANY clean downscale of the calibrated size * (config image.width/height); the binned intrinsics are derived internally * (no separate binned calibration needed). 2x2 binning is the intended case. - * - out : summary (found / median distance / visible count). Required. + * - out : summary (found / representative distance / visible count). Required. * - bolts : optional caller-allocated array (capacity max_bolts); filled with * per-bolt detail (distance + left/right match points), near -> far. * Pass NULL / max_bolts=0 to skip. The caller owns it; nothing to free. diff --git a/AppAlgo/stereo_bolt_delivery/lib/libstereo_bolt.so b/AppAlgo/stereo_bolt_delivery/lib/libstereo_bolt.so index 046cdc75..0949f766 100644 Binary files a/AppAlgo/stereo_bolt_delivery/lib/libstereo_bolt.so and b/AppAlgo/stereo_bolt_delivery/lib/libstereo_bolt.so differ diff --git a/AppAlgo/stereo_bolt_delivery/weights/best.rknn b/AppAlgo/stereo_bolt_delivery/weights/best.rknn index 575b8a94..71e738c6 100644 Binary files a/AppAlgo/stereo_bolt_delivery/weights/best.rknn and b/AppAlgo/stereo_bolt_delivery/weights/best.rknn differ diff --git a/Device/Device.pro b/Device/Device.pro index 5187d4e9..33ffbe6e 100644 --- a/Device/Device.pro +++ b/Device/Device.pro @@ -4,17 +4,53 @@ TEMPLATE = subdirs # SDK(需要先编译) -SUBDIRS += ../SDK/Device/EpicEye/EpicEyeSDK.pro +isEmpty(TARGET_APP) { + SUBDIRS += ../SDK/Device/EpicEye/EpicEyeSDK.pro -SUBDIRS += \ - VrEyeDevice/VrEyeDevice.pro \ - EpicEyeDevice/EpicEyeDevice.pro \ - GalaxyDevice/GalaxyDevice.pro \ - HikDevice/HikDevice.pro \ - MvsDevice/MvsDevice.pro \ - GlLineLaserDevice/GlLineLaserDevice.pro + SUBDIRS += \ + VrEyeDevice/VrEyeDevice.pro \ + EpicEyeDevice/EpicEyeDevice.pro \ + GalaxyDevice/GalaxyDevice.pro \ + HikDevice/HikDevice.pro \ + MvsDevice/MvsDevice.pro \ + GlLineLaserDevice/GlLineLaserDevice.pro +} else { + !equals(TARGET_APP, "BeltTearing") { + SUBDIRS += VrEyeDevice/VrEyeDevice.pro + } + + contains(TARGET_APP, "^(WorkpiecePosition|WorkpieceProject)$") { + SUBDIRS += ../SDK/Device/EpicEye/EpicEyeSDK.pro + SUBDIRS += EpicEyeDevice/EpicEyeDevice.pro + SUBDIRS += VrEyeDevice/VrEyeDevice.pro + } + + equals(TARGET_APP, "BinocularMark") { + SUBDIRS += GalaxyDevice/GalaxyDevice.pro + } + + contains(TARGET_APP, "^(DroneScrewServer|DroneScrewbolt)$") { + SUBDIRS += MvsDevice/MvsDevice.pro + } + + equals(TARGET_APP, "BagThreadPosition") { + SUBDIRS += VrEyeDevice/VrEyeDevice.pro + SUBDIRS += GlLineLaserDevice/GlLineLaserDevice.pro + } + + equals(TARGET_APP, "TunnelChannel") { + SUBDIRS += VrEyeDevice/VrEyeDevice.pro + SUBDIRS += HikDevice/HikDevice.pro + } + + contains(TARGET_APP, "^(BeltTearing|WorkpieceSplice|GrabBag|LapWeld|Workpiece|ParticleSize|ScrewPosition|WorkpieceHole|DiscHolePose|TireHolePose|StatorPosition|RodAndBarPosition|RodWeldSeam|HoleDetection|HolePitPosition|WheelMeasure)$") { + SUBDIRS += VrEyeDevice/VrEyeDevice.pro + } +} win32-msvc { SUBDIRS += RsLidarDevice/RsLidarDevice.pro } + +SUBDIRS = $$unique(SUBDIRS)