add,计划采集18,上海农科院3D植物表型:

1、任务类型ObtainingDepthInformation兼容功能:获取植被和升降台的平均深度信息并写入文件:3DPlantPhenotypeScenario\plant_depth_values.txt和3DPlantPhenotypeScenario\LiftingPlatform_depth_values.txt
This commit is contained in:
tangchao0503
2026-08-11 16:52:32 +08:00
parent 64cdc7591d
commit abdb27b228
12 changed files with 552 additions and 4 deletions

138
HPPA/DepthValueLogger.cpp Normal file
View File

@ -0,0 +1,138 @@
#include "stdafx.h"
#include "DepthValueLogger.h"
#include "AppSettings.h"
#include "fileOperation.h"
#include <QDir>
DepthValueLogger& DepthValueLogger::instance()
{
static DepthValueLogger instance;
return instance;
}
DepthValueLogger::DepthValueLogger()
{
}
DepthValueLogger::~DepthValueLogger()
{
}
QString DepthValueLogger::getLogFilePath(DepthValueType type) const
{
FileOperation* fileOperation = new FileOperation();
QString directory = QString::fromStdString(fileOperation->getDirectoryOfExe());
QString basePath = directory + QDir::separator() + "3DPlantPhenotypeScenario";
switch (type)
{
case DepthValueType::Plant:
return basePath + QDir::separator() + "plant_depth_values.txt";
case DepthValueType::LiftingPlatform:
return basePath + QDir::separator() + "LiftingPlatform_depth_values.txt";
default:
return basePath + QDir::separator() + "plant_depth_values.txt";
}
}
void DepthValueLogger::appendDepthValue(double depthValue, DepthValueType type)
{
QString filePath = getLogFilePath(type);
QFileInfo fileInfo(filePath);
QDir dir = fileInfo.absoluteDir();
if (!dir.exists())
{
dir.mkpath(".");
}
QFile file(filePath);
if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
{
QTextStream out(&file);
QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss.zzz");
out << timestamp << "\t" << QString::number(depthValue, 'f', 6) << "\n";
file.close();
emit depthValueLogged(depthValue, type);
}
}
double DepthValueLogger::readLatestDepthValue(DepthValueType type) const
{
QString filePath = getLogFilePath(type);
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
return 0.0;
}
double latestValue = 0.0;
QTextStream in(&file);
while (!in.atEnd())
{
QString line = in.readLine().trimmed();
if (line.isEmpty())
{
continue;
}
QStringList parts = line.split("\t");
if (parts.size() >= 2)
{
latestValue = parts.last().toDouble();
}
}
file.close();
return latestValue;
}
bool DepthValueLogger::hasValidDepthValue(DepthValueType type) const
{
QString filePath = getLogFilePath(type);
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
return false;
}
bool hasValue = false;
QTextStream in(&file);
while (!in.atEnd())
{
QString line = in.readLine().trimmed();
if (!line.isEmpty())
{
hasValue = true;
break;
}
}
file.close();
return hasValue;
}
void DepthValueLogger::appendPlantDepthValue(double depthValue)
{
appendDepthValue(depthValue, DepthValueType::Plant);
}
void DepthValueLogger::appendLiftingPlatformDepthValue(double depthValue)
{
appendDepthValue(depthValue, DepthValueType::LiftingPlatform);
}
double DepthValueLogger::readLatestPlantDepthValue() const
{
return readLatestDepthValue(DepthValueType::Plant);
}
double DepthValueLogger::readLatestLiftingPlatformDepthValue() const
{
return readLatestDepthValue(DepthValueType::LiftingPlatform);
}

41
HPPA/DepthValueLogger.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef DEPTH_VALUE_LOGGER_H
#define DEPTH_VALUE_LOGGER_H
#include <QObject>
#include <QString>
enum class DepthValueType
{
Plant,
LiftingPlatform
};
class DepthValueLogger : public QObject
{
Q_OBJECT
public:
static DepthValueLogger& instance();
void appendDepthValue(double depthValue, DepthValueType type);
double readLatestDepthValue(DepthValueType type) const;
bool hasValidDepthValue(DepthValueType type) const;
void appendPlantDepthValue(double depthValue);
void appendLiftingPlatformDepthValue(double depthValue);
double readLatestPlantDepthValue() const;
double readLatestLiftingPlatformDepthValue() const;
signals:
void depthValueLogged(double value, DepthValueType type);
private:
DepthValueLogger();
~DepthValueLogger();
DepthValueLogger(const DepthValueLogger&) = delete;
DepthValueLogger& operator=(const DepthValueLogger&) = delete;
QString getLogFilePath(DepthValueType type) const;
};
#endif

View File

@ -776,7 +776,7 @@ void HPPA::onStartTimedDataCollection(int camType)
void HPPA::onObtainTargetDepthInformation(SubTask subTaskParams)
{
m_tmc->run4_ObtainTargetDepthInfo(m_depthCameraWindow, subTaskParams.depthInfoX, subTaskParams.depthInfoY, subTaskParams.averageNumberOfTimes, subTaskParams.percentageOfEffectiveArea);
m_tmc->run4_ObtainTargetDepthInfo(m_depthCameraWindow, subTaskParams.depthType, subTaskParams.depthInfoX, subTaskParams.depthInfoY, subTaskParams.averageNumberOfTimes, subTaskParams.percentageOfEffectiveArea);
}
void HPPA::onTimedDataCollection()
@ -1057,6 +1057,11 @@ void HPPA::initControlTabwidget()
m_omc->setWindowFlags(Qt::Widget);
ui.controlTabWidget->addTab(m_omc, QString::fromLocal8Bit("1轴马达控制"));
//1轴马达控制,上海3D植物表情,白板/调焦纸升降
m_omc_LiftingPlatform = new OneMotorControl_LiftingPlatform();
m_omc_LiftingPlatform->setWindowFlags(Qt::Widget);
ui.controlTabWidget->addTab(m_omc_LiftingPlatform, QString::fromLocal8Bit("白板/调焦升降台"));
//2轴马达控制
m_tmc = new TwoMotorControl(this);
//connect(m_tmc, SIGNAL(startLineNumSignal(int)), this, SLOT(onCreateTab(int)));
@ -1679,6 +1684,7 @@ void HPPA::create3DPlantPhenotypeScenario()
//m_tabManager->showTab(m_pc);
m_tabManager->showTab(m_pc3D);
m_tabManager->showTab(m_tmc);
m_tabManager->showTab(m_omc_LiftingPlatform);
m_view3DModelManager->switchScenario(View3DModelManager::ScenarioType::PlantPhenotype);

View File

@ -321,6 +321,7 @@ private:
PowerControl3D* m_pc3D;
RobotArmControl* m_rac;
OneMotorControl* m_omc;
OneMotorControl_LiftingPlatform* m_omc_LiftingPlatform;
TwoMotorControl* m_tmc;
FodisWindow* m_fodisWindow;
GonggaShanRecordCtl* m_gonggaShanRecordCtl;

View File

@ -186,6 +186,7 @@
<QtUic Include="gonggashanCtl.ui" />
<QtUic Include="HPPA.ui" />
<QtMoc Include="HPPA.h" />
<ClCompile Include="DepthValueLogger.cpp" />
<ClCompile Include="fileOperation.cpp" />
<ClCompile Include="focusWindow.cpp" />
<ClCompile Include="HPPA.cpp" />
@ -212,6 +213,7 @@
<QtUic Include="twoMotorControl.ui" />
</ItemGroup>
<ItemGroup>
<QtMoc Include="DepthValueLogger.h" />
<QtMoc Include="fileOperation.h" />
</ItemGroup>
<ItemGroup>

View File

@ -283,6 +283,9 @@
<ClCompile Include="ResononNirImager.cpp">
<Filter>Source Files\hyperImagerCtl</Filter>
</ClCompile>
<ClCompile Include="DepthValueLogger.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<QtMoc Include="fileOperation.h">
@ -453,6 +456,9 @@
<QtMoc Include="resononImager.h">
<Filter>Header Files\hyperImagerCtl</Filter>
</QtMoc>
<QtMoc Include="DepthValueLogger.h">
<Filter>Header Files</Filter>
</QtMoc>
</ItemGroup>
<ItemGroup>
<ClInclude Include="imageProcessor.h">

View File

@ -267,3 +267,276 @@ bool OneMotorControl::getMotorsConnectionStatus()
{
return m_xMotorConnectionStatus;
}
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
OneMotorControl_LiftingPlatform::OneMotorControl_LiftingPlatform(QWidget* parent) : QDialog(parent)
{
ui.setupUi(this);
connect(this->ui.connect_btn, SIGNAL(pressed()), this, SLOT(onConnectMotor()));
connect(this->ui.right_btn, SIGNAL(pressed()), this, SLOT(onxMotorRight()));
connect(this->ui.right_btn, SIGNAL(released()), this, SLOT(onxMotorStop()));
connect(this->ui.left_btn, SIGNAL(pressed()), this, SLOT(onxMotorLeft()));
connect(this->ui.left_btn, SIGNAL(released()), this, SLOT(onxMotorStop()));
connect(this->ui.move2loc_pushButton, SIGNAL(pressed()), this, SLOT(onxMove2Loc()));
connect(this->ui.zero_start_btn, SIGNAL(released()), this, SLOT(zeroStart()));
connect(this->ui.rangeMeasurement_btn, SIGNAL(pressed()), this, SLOT(onx_rangeMeasurement()));
// 从 AppSettings 读取速度参数
AppSettings& settings = AppSettings::instance();
ui.speed_lineEdit->setText(QString::number(settings.scanSpeed()));
ui.return_speed_lineEdit->setText(QString::number(settings.returnSpeed()));
// 连接信号,当控件数值变化时保存到 AppSettings
connect(ui.speed_lineEdit, &QLineEdit::editingFinished, [this]() {
AppSettings::instance().setScanSpeed(ui.speed_lineEdit->text().toDouble());
});
connect(ui.return_speed_lineEdit, &QLineEdit::editingFinished, [this]() {
AppSettings::instance().setReturnSpeed(ui.return_speed_lineEdit->text().toDouble());
});
}
OneMotorControl_LiftingPlatform::~OneMotorControl_LiftingPlatform()
{
m_motorThread.quit();
m_motorThread.wait();
}
void OneMotorControl_LiftingPlatform::onConnectMotor()
{
connectMotor(true);
}
void OneMotorControl_LiftingPlatform::connectMotor(bool isNotification)
{
if (getMotorsConnectionStatus())
{
if (isNotification)
{
QMessageBox msgBox;
msgBox.setText(QString::fromLocal8Bit("马达已连接!"));
msgBox.exec();
}
return;
}
if (m_multiAxisController != nullptr)
{
disconnect(m_multiAxisController, SIGNAL(broadcastLocationSignal(std::vector<double>)), this, SLOT(display_x_loc(std::vector<double>)));
disconnect(this, SIGNAL(moveSignal(int, bool, double, int)), m_multiAxisController, SLOT(move(int, bool, double, int)));
disconnect(this, SIGNAL(move2LocSignal(int, double, double, int)), m_multiAxisController, SLOT(moveTo(int, double, double, int)));
disconnect(this, SIGNAL(stopSignal(int)), m_multiAxisController, SLOT(stop(int)));
disconnect(this, SIGNAL(zeroStartSignal(int)), m_multiAxisController, SLOT(zeroStart(int)));
disconnect(this, SIGNAL(rangeMeasurement(int, double, int)), m_multiAxisController, SLOT(rangeMeasurement(int, double, int)));
disconnect(this, SIGNAL(testConnectivitySignal(int, int)), m_multiAxisController, SLOT(testConnectivity(int, int)));
disconnect(m_multiAxisController, SIGNAL(broadcastConnectivity(std::vector<int>)), this, SLOT(display_motors_connectivity(std::vector<int>)));
m_motorThread.quit();
m_motorThread.wait();
m_multiAxisController = nullptr;
}
try
{
FileOperation* fileOperation = new FileOperation();
string directory = fileOperation->getDirectoryOfExe();
QString configFilePath = QString::fromStdString(directory) + "\\oneMotorConfigFile_LiftingPlatform.cfg";
m_multiAxisController = new IrisMultiMotorController(configFilePath);
}
catch (std::exception const& e)
{
QMessageBox msgBox;
msgBox.setText(QString::fromLocal8Bit("请连接马达!"));
msgBox.exec();
return;
}
m_multiAxisController->moveToThread(&m_motorThread);
connect(&m_motorThread, SIGNAL(finished()), m_multiAxisController, SLOT(deleteLater()));
connect(m_multiAxisController, SIGNAL(broadcastLocationSignal(std::vector<double>)), this, SLOT(display_x_loc(std::vector<double>)));
connect(this, SIGNAL(moveSignal(int, bool, double, int)), m_multiAxisController, SLOT(move(int, bool, double, int)));
connect(this, SIGNAL(move2LocSignal(int, double, double, int)), m_multiAxisController, SLOT(moveTo(int, double, double, int)));
connect(this, SIGNAL(stopSignal(int)), m_multiAxisController, SLOT(stop(int)));
connect(this, SIGNAL(zeroStartSignal(int)), m_multiAxisController, SLOT(zeroStart(int)));
connect(this, SIGNAL(rangeMeasurement(int, double, int)), m_multiAxisController, SLOT(rangeMeasurement(int, double, int)));
connect(this, SIGNAL(testConnectivitySignal(int, int)), m_multiAxisController, SLOT(testConnectivity(int, int)));
connect(m_multiAxisController, SIGNAL(broadcastConnectivity(std::vector<int>)), this, SLOT(display_motors_connectivity(std::vector<int>)));
m_motorThread.start();
emit testConnectivitySignal(0, 1000);
}
void OneMotorControl_LiftingPlatform::display_x_loc(std::vector<double> loc)
{
double tmp = round(loc[0] * 100) / 100;
this->ui.realTimeLoc_lineEdit->setText(QString::number(tmp));
emit broadcastLocationSignal(loc);
}
void OneMotorControl_LiftingPlatform::display_motors_connectivity(std::vector<int> connectivity)
{
//std::cout << "-----------------------------------"<<connectivity.size()<< std::endl;
if (connectivity[0])
{
m_xMotorConnectionStatus = true;
this->ui.motor_state_label->setStyleSheet(R"(
QLabel
{
background-color: #08FACE;
border-radius: 4px;
}
)");
}
else
{
m_xMotorConnectionStatus = false;
this->ui.motor_state_label->setStyleSheet(R"(
QLabel
{
background-color: red;
border-radius: 4px;
}
)");
}
if (getMotorsConnectionStatus())
{
this->ui.connect_btn->setText(QString::fromLocal8Bit("已连接"));
}
else
{
this->ui.connect_btn->setText(QString::fromLocal8Bit("重新连接"));
}
}
void OneMotorControl_LiftingPlatform::zeroStart()
{
zeroStartSignal(0);
}
void OneMotorControl_LiftingPlatform::onx_rangeMeasurement()
{
double s0 = ui.speed_lineEdit->text().toDouble();
emit rangeMeasurement(0, s0, 1000);
}
void OneMotorControl_LiftingPlatform::onxMove2Loc()
{
double s = ui.speed_lineEdit->text().toDouble();
double l = ui.move2loc_lineEdit->text().toDouble();
emit move2LocSignal(0, l, s, 1000);
}
void OneMotorControl_LiftingPlatform::onxMotorRight()
{
double s = ui.speed_lineEdit->text().toDouble();
emit moveSignal(0, false, s, 1000);
}
void OneMotorControl_LiftingPlatform::onxMotorLeft()
{
double s = ui.speed_lineEdit->text().toDouble();
emit moveSignal(0, true, s, 1000);
}
void OneMotorControl_LiftingPlatform::onxMotorStop()
{
emit stopSignal(0);
}
void OneMotorControl_LiftingPlatform::run()
{
if (m_coordinator)//当高光谱相机停止采集后,马达还未回到原点时,上次任务的m_coordinator还没有被销毁
{
onSequenceComplete(0);
}
}
void OneMotorControl_LiftingPlatform::stop()
{
emit stopStepMotionSignal();
}
void OneMotorControl_LiftingPlatform::onSequenceComplete(int state)
{
emit sequenceComplete();
disconnect(this, SIGNAL(start(OneMotionCapturePathLine)), m_coordinator, SLOT(startStepMotion(OneMotionCapturePathLine)));
disconnect(this, SIGNAL(stopStepMotionSignal()), m_coordinator, SLOT(stopStepMotion()));
disconnect(m_coordinator, SIGNAL(sequenceComplete(int)), this, SLOT(onSequenceComplete(int)));
// Use deleteLater() instead of delete: this slot may have been called directly
// from OneMotionCaptureCoordinator's call stack (direct connection), so deleting
// the object here would cause a crash when execution returns to the destroyed object.
m_coordinator->deleteLater();
m_coordinator = nullptr;
}
bool OneMotorControl_LiftingPlatform::getMotorsConnectionStatus()
{
return m_xMotorConnectionStatus;
}

View File

@ -77,3 +77,61 @@ private:
bool m_xMotorConnectionStatus = false;
};
class OneMotorControl_LiftingPlatform : public QDialog, public MotorWindowBase
{
Q_OBJECT
public:
OneMotorControl_LiftingPlatform(QWidget* parent = nullptr);
~OneMotorControl_LiftingPlatform();
void run();
void stop();
bool getMotorsConnectionStatus();
void connectMotor(bool isNotification);
public Q_SLOTS:
void onConnectMotor();
void display_x_loc(std::vector<double> loc);
void display_motors_connectivity(std::vector<int> connectivity);
void onxMove2Loc();
void zeroStart();
void onx_rangeMeasurement();
void onxMotorRight();
void onxMotorLeft();
void onxMotorStop();
void onSequenceComplete(int state);
signals:
void moveSignal(int, bool, double, int);
void move2LocSignal(int, double, double, int);
void move2LocSignal(const std::vector<double>, const std::vector<double>, int);
void stopSignal(int);
void rangeMeasurement(int, double, int);
void zeroStartSignal(int);
void testConnectivitySignal(int, int);
void start(OneMotionCapturePathLine);
void stopStepMotionSignal();
void sequenceComplete();
void broadcastLocationSignal(std::vector<double>);
private:
Ui::OneMotorControl_UI ui;
QThread m_motorThread;
IrisMultiMotorController* m_multiAxisController = nullptr;
QPointer<OneMotionCaptureCoordinator> m_coordinator;
bool m_xMotorConnectionStatus = false;
};

View File

@ -145,6 +145,7 @@ QJsonObject TimedDataCollectionDataStructuresReaderWriter::subTaskToJson(const S
obj["depthInfoY"] = subTask.depthInfoY;
obj["averageNumberOfTimes"] = subTask.averageNumberOfTimes;
obj["percentageOfEffectiveArea"] = subTask.percentageOfEffectiveArea;
obj["depthType"] = subTask.depthType;
return obj;
}
@ -170,6 +171,7 @@ bool TimedDataCollectionDataStructuresReaderWriter::jsonToSubTask(const QJsonObj
subTask.depthInfoY = json["depthInfoY"].toDouble();
subTask.averageNumberOfTimes = json["averageNumberOfTimes"].toInt();
subTask.percentageOfEffectiveArea = json["percentageOfEffectiveArea"].toDouble();
subTask.depthType = json["depthType"].toInt();
return true;
}

View File

@ -50,6 +50,7 @@ struct SubTask {
int captureIntervalSeconds = 5; // 单反/深度相机用
//任务ObtainingDepthInformation所需的x和y坐标
int depthType = 0;//0表示植被深度,1表示白板/调焦版深度
double depthInfoX = 0.0;
double depthInfoY = 0.0;
int averageNumberOfTimes = 1; //任务ObtainingDepthInformation所需的平均次数

View File

@ -210,8 +210,10 @@ void TwoMotorControl::onBack2Origin2()
emit back2OriginSignal_TimedDataCollection();
}
void TwoMotorControl::run4_ObtainTargetDepthInfo(DepthCameraWindow* window, double depthInfoX, double depthInfoY, int averageNumberOfTimes, double percentageOfEffectiveArea)
void TwoMotorControl::run4_ObtainTargetDepthInfo(DepthCameraWindow* window, int depthType, double depthInfoX, double depthInfoY, int averageNumberOfTimes, double percentageOfEffectiveArea)
{
m_depthType = depthType;
window->m_DepthCameraOperation->setAverageNumberOfTimes(averageNumberOfTimes);
window->m_DepthCameraOperation->setPercentageOfEffectiveArea(percentageOfEffectiveArea);
@ -219,7 +221,8 @@ void TwoMotorControl::run4_ObtainTargetDepthInfo(DepthCameraWindow* window, doub
connect(m_ObtainTargetDepthInfoCoordinator, &TwoMotor1PosCoordinator::ArrivalSignal, window, &DepthCameraWindow::OpenDepthCamera_getDepthValue);
connect(window->m_DepthCameraOperation, &DepthCameraOperation::DepthValueSignal, m_ObtainTargetDepthInfoCoordinator, &TwoMotor1PosCoordinator::back2origin);
connect(window->m_DepthCameraOperation, &DepthCameraOperation::DepthValueSignal, this, &TwoMotorControl::sequenceComplete);//关灯
connect(window->m_DepthCameraOperation, &DepthCameraOperation::DepthValueSignal, this, &TwoMotorControl::sequenceComplete, Qt::UniqueConnection);//关灯
connect(window->m_DepthCameraOperation, &DepthCameraOperation::DepthValueSignal, this, &TwoMotorControl::saveDepthValue, Qt::UniqueConnection);//控制白板平台升降
connect(m_ObtainTargetDepthInfoCoordinator, &TwoMotor1PosCoordinator::back2OriginSignal, this, &TwoMotorControl::onBack2Origin3);
@ -229,6 +232,18 @@ void TwoMotorControl::run4_ObtainTargetDepthInfo(DepthCameraWindow* window, doub
m_ObtainTargetDepthInfoCoordinator->moveToTarget(depthInfoX, depthInfoY, xmotor_move_speed, ymotor_move_speed);
}
void TwoMotorControl::saveDepthValue(double depthValue)
{
if (m_depthType == 0)//0表示植被深度,1表示白板/调焦版深度
{
DepthValueLogger::instance().appendPlantDepthValue(depthValue);
}
else if (m_depthType == 1)
{
DepthValueLogger::instance().appendLiftingPlatformDepthValue(depthValue);
}
}
void TwoMotorControl::onBack2Origin3()
{
m_ObtainTargetDepthInfoCoordinator->deleteLater();

View File

@ -15,6 +15,8 @@
#include "PathLine.h"
#include "DepthValueLogger.h"
#define PI 3.1415926
class TwoMotorControl : public QDialog, public MotorWindowBase
@ -82,8 +84,9 @@ public Q_SLOTS:
void run2(SingleLensReflexCameraWindow* w);
void run3(DepthCameraWindow* window);
void run4_ObtainTargetDepthInfo(DepthCameraWindow* window, double depthInfoX, double depthInfoY, int averageNumberOfTimes, double percentageOfEffectiveArea);
void run4_ObtainTargetDepthInfo(DepthCameraWindow* window, int depthType, double depthInfoX, double depthInfoY, int averageNumberOfTimes, double percentageOfEffectiveArea);
void onBack2Origin2();
void saveDepthValue(double depthValue);
void onBack2Origin3();
void stop_record();
@ -120,4 +123,6 @@ private:
QThread m_motorThread;
IrisMultiMotorController* m_multiAxisController = nullptr;
int m_depthType;
};