工件定位基本调试通

This commit is contained in:
yiyi 2026-02-02 23:24:24 +08:00
parent 6f32e3b9af
commit d0574aec23
29 changed files with 1279 additions and 312 deletions

View File

@ -144,147 +144,3 @@
| 读取失败 | 记录日志,下次轮询重试 |
| 写入失败 | 记录日志,返回失败状态 |
| 超时 | 默认1秒超时触发重连 |
## 8. 配置文件
配置文件路径:`config/config.xml`
```xml
<TcpServerConfig
plcServerIp="192.168.0.88"
plcServerPort="502"
robotServerIp=""
robotServerPort="502"/>
```
> **注意**: robotServerIp 留空表示不使用机械臂直连
## 9. 代码接口
### 9.1 PLCModbusClient 类
```cpp
// 寄存器地址默认值(代码协议地址 = D寄存器 + 1
static constexpr int DEFAULT_ADDR_PHOTO_REQUEST = 1001; // D1000: 拍照请求
static constexpr int DEFAULT_ADDR_DATA_COMPLETE = 1003; // D1002: 数据完成
static constexpr int DEFAULT_ADDR_COORD_DATA_START = 2001; // D2000: 坐标数据起始
// 每个点占用的寄存器数量6个float × 2寄存器/float = 12寄存器
static constexpr int REGS_PER_POINT = 12;
// 最大点数
static constexpr int MAX_POINTS = 10;
// 坐标数据结构float类型大端序
struct CoordinateData {
float x = 0.0f; // X坐标 (mm)
float y = 0.0f; // Y坐标 (mm)
float z = 0.0f; // Z坐标 (mm)
float pitch = 0.0f; // Pitch角度 (°)
float roll = 0.0f; // Roll角度 (°)
float yaw = 0.0f; // Yaw角度 (°)
};
// 初始化连接
bool Initialize(const std::string& plcIP, int plcPort = 502, const RegisterConfig& regConfig = RegisterConfig());
// 关闭连接
void Shutdown();
// 启动/停止轮询
void StartPolling(int intervalMs = 100);
void StopPolling();
// 设置拍照触发回调
void SetPhotoTriggerCallback(PhotoTriggerCallback callback);
// 清除拍照请求写0到D1000
bool ClearPhotoRequest();
// 发送单个坐标数据到PLC写入D2000开始
bool SendCoordinateToPLC(const CoordinateData& coord, int pointIndex = 0);
// 发送多个坐标数据到PLC
bool SendCoordinatesToPLC(const std::vector<CoordinateData>& coords);
// 通知数据输出完成写1到D1002
bool NotifyDataComplete();
// 连接状态查询
bool IsPLCConnected() const;
```
### 9.2 信号
```cpp
// 拍照请求信号
void photoRequested(int cameraIndex);
// 连接状态变化
void connectionStateChanged(bool plcConnected);
// 正在重连
void reconnecting(const QString& deviceName, int attemptCount);
// 错误发生
void errorOccurred(const QString& errorMsg);
```
## 10. 使用示例
```cpp
// 创建客户端
PLCModbusClient* client = new PLCModbusClient(this);
// 从配置读取IP并初始化只连接PLC
PLCModbusClient::RegisterConfig regConfig;
regConfig.addrPhotoRequest = 1001; // D1000
regConfig.addrDataComplete = 1003; // D1002
regConfig.addrCoordDataStart = 2001; // D2000
client->Initialize(
config.plcRobotServerConfig.plcServerIp,
config.plcRobotServerConfig.plcServerPort,
regConfig
);
// 设置拍照回调
client->SetPhotoTriggerCallback([this, client](int cameraIndex) {
// 1. 清除拍照请求
client->ClearPhotoRequest();
// 2. 执行拍照和算法
auto results = doDetection(cameraIndex);
// 3. 发送多个坐标点到PLCD2000开始float大端序
std::vector<PLCModbusClient::CoordinateData> coords;
for (const auto& result : results) {
PLCModbusClient::CoordinateData coord;
coord.x = result.x; // float (mm)
coord.y = result.y; // float (mm)
coord.z = result.z; // float (mm)
coord.pitch = result.pitch; // float (°)
coord.roll = result.roll; // float (°)
coord.yaw = result.yaw; // float (°)
coords.push_back(coord);
}
client->SendCoordinatesToPLC(coords);
// 4. 通知完成
client->NotifyDataComplete();
});
// 启动轮询
client->StartPolling(100);
// 程序退出时
client->Shutdown();
```
## 11. 版本历史
| 版本 | 日期 | 说明 |
|------|------|------|
| 1.0 | 2025-01 | 初始版本支持PLC和机械臂 |
| 1.1 | 2025-01 | 移除机械臂直连坐标数据输出到PLC |
| 1.2 | 2025-01 | 坐标数据改为float大端序支持最多10个点 |

View File

@ -31,6 +31,7 @@ public:
LaserDataLoader& dataLoader,
const double clibMatrix[16],
int eulerOrder,
int dirVectorInvert,
WorkpieceHoleDetectionResult& detectionResult);
};

View File

@ -108,6 +108,7 @@ private:
// 连接管理
bool checkConnection();
bool tryConnectPLC();
void disconnectPLC(); // 主动断开连接(用于触发重连)
// 寄存器操作
int readPhotoRequest();

View File

@ -1,9 +1,11 @@
#include "DetectPresenter.h"
#include "workpieceHolePositioning_Export.h"
#include "SG_baseAlgo_Export.h"
#include <fstream>
#include <QPainter>
#include <QPen>
#include <QColor>
#include <opencv2/opencv.hpp>
#include "CoordinateTransform.h"
#include "VrConvert.h"
@ -25,6 +27,7 @@ int DetectPresenter::DetectWorkpieceHole(
LaserDataLoader& dataLoader,
const double clibMatrix[16],
int eulerOrder,
int dirVectorInvert,
WorkpieceHoleDetectionResult& detectionResult)
{
if (laserLines.empty()) {
@ -173,9 +176,16 @@ int DetectPresenter::DetectWorkpieceHole(
LOG_INFO("wd_workpieceHolePositioning: found %zu workpieces, err=%d runtime=%.3fms\n", workpiecePositioning.size(), errCode, oTimeUtils.GetElapsedTimeInMilliSec());
ERR_CODE_RETURN(errCode);
// 直接使用4x4齐次变换矩阵避免欧拉角转换误差
CTHomogeneousMatrix calibHMatrix;
memcpy(calibHMatrix.data, clibMatrix, sizeof(double) * 16);
// 从4x4齐次变换矩阵中提取旋转矩阵R(3x3)和平移向量T(3x1)
// clibMatrix 是行优先存储的4x4矩阵
cv::Mat R = cv::Mat::zeros(3, 3, CV_64F);
cv::Mat T = cv::Mat::zeros(3, 1, CV_64F);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
R.at<double>(i, j) = clibMatrix[i * 4 + j];
}
T.at<double>(i, 0) = clibMatrix[i * 4 + 3];
}
// 构建用于可视化的点数组
std::vector<std::vector<SVzNL3DPoint>> objOps;
@ -192,47 +202,72 @@ int DetectPresenter::DetectWorkpieceHole(
// 六轴转换:位置+姿态(转换到机械臂坐标系)
SVzNL3DPoint centerEye = workpiece.center;
// 从方向向量构建旋转矩阵
// 算法输出的 x_dir, y_dir, z_dir 是工件坐标系的三个轴在相机坐标系下的方向
CTRotationMatrix rotMatrix;
// 第一列X轴方向
rotMatrix.at(0, 0) = workpiece.x_dir.x;
rotMatrix.at(1, 0) = workpiece.x_dir.y;
rotMatrix.at(2, 0) = workpiece.x_dir.z;
// 第二列Y轴方向
rotMatrix.at(0, 1) = workpiece.y_dir.x;
rotMatrix.at(1, 1) = workpiece.y_dir.y;
rotMatrix.at(2, 1) = workpiece.y_dir.z;
// 第三列Z轴方向
rotMatrix.at(0, 2) = workpiece.z_dir.x;
rotMatrix.at(1, 2) = workpiece.z_dir.y;
rotMatrix.at(2, 2) = workpiece.z_dir.z;
// 1. 位置转换:使用 pointRT_2 进行坐标变换
cv::Point3d ptEye(centerEye.x, centerEye.y, centerEye.z);
cv::Point3d ptRobot;
pointRT_2(R, T, ptEye, ptRobot);
// 将旋转矩阵转换为欧拉角(使用配置的旋转顺序)
CTEulerAngles eulerAngles = CCoordinateTransform::rotationMatrixToEuler(rotMatrix, static_cast<CTEulerOrder>(eulerOrder));
// 2. 姿态转换:按照测试代码的方式处理方向向量
// 从算法输出获取方向向量
std::vector<cv::Point3d> dirVectors_eye(3);
dirVectors_eye[0] = cv::Point3d(workpiece.x_dir.x, workpiece.x_dir.y, workpiece.x_dir.z);
dirVectors_eye[1] = cv::Point3d(workpiece.y_dir.x, workpiece.y_dir.y, workpiece.y_dir.z);
dirVectors_eye[2] = cv::Point3d(workpiece.z_dir.x, workpiece.z_dir.y, workpiece.z_dir.z);
// 构建相机坐标系下的完整位姿
CTCameraPose cameraPose;
cameraPose.x = centerEye.x;
cameraPose.y = centerEye.y;
cameraPose.z = centerEye.z;
cameraPose.rx = eulerAngles.roll;
cameraPose.ry = eulerAngles.pitch;
cameraPose.rz = eulerAngles.yaw;
// 根据配置决定方向向量反向
switch (dirVectorInvert) {
case DIR_INVERT_NONE:
// 不反向
break;
case DIR_INVERT_XY:
// X和Y方向反向
dirVectors_eye[0] = cv::Point3d(-dirVectors_eye[0].x, -dirVectors_eye[0].y, -dirVectors_eye[0].z);
dirVectors_eye[1] = cv::Point3d(-dirVectors_eye[1].x, -dirVectors_eye[1].y, -dirVectors_eye[1].z);
break;
case DIR_INVERT_XZ:
// X和Z方向反向
dirVectors_eye[0] = cv::Point3d(-dirVectors_eye[0].x, -dirVectors_eye[0].y, -dirVectors_eye[0].z);
dirVectors_eye[2] = cv::Point3d(-dirVectors_eye[2].x, -dirVectors_eye[2].y, -dirVectors_eye[2].z);
break;
case DIR_INVERT_YZ:
default:
// Y和Z方向反向默认兼容原有行为
dirVectors_eye[1] = cv::Point3d(-dirVectors_eye[1].x, -dirVectors_eye[1].y, -dirVectors_eye[1].z);
dirVectors_eye[2] = cv::Point3d(-dirVectors_eye[2].x, -dirVectors_eye[2].y, -dirVectors_eye[2].z);
break;
}
// 进行六轴Eye-to-Hand转换位置+姿态)
// 使用配置的旋转顺序
CTRobotPose robotPose;
CCoordinateTransform::sixAxisEyeToHandCalcGraspPose(cameraPose, calibHMatrix, static_cast<CTEulerOrder>(eulerOrder), robotPose);
// 使用 pointRotate 对方向向量进行旋转变换
std::vector<cv::Point3d> dirVectors_robot;
for (int j = 0; j < 3; j++) {
cv::Point3d rt_pt;
pointRotate(R, dirVectors_eye[j], rt_pt);
dirVectors_robot.push_back(rt_pt);
}
// 3. 构建旋转矩阵(按照测试代码的方式)
double R_pose[3][3];
R_pose[0][0] = dirVectors_robot[0].x;
R_pose[0][1] = dirVectors_robot[1].x;
R_pose[0][2] = dirVectors_robot[2].x;
R_pose[1][0] = dirVectors_robot[0].y;
R_pose[1][1] = dirVectors_robot[1].y;
R_pose[1][2] = dirVectors_robot[2].y;
R_pose[2][0] = dirVectors_robot[0].z;
R_pose[2][1] = dirVectors_robot[1].z;
R_pose[2][2] = dirVectors_robot[2].z;
// 4. 使用 rotationMatrixToEulerZYX 转换为欧拉角外旋ZYX即RZ-RY-RX
SSG_EulerAngles robotRpy = rotationMatrixToEulerZYX(R_pose);
// 将机器人坐标系下的位姿添加到positions列表
HolePosition centerRobotPos;
centerRobotPos.x = robotPose.x;
centerRobotPos.y = robotPose.y;
centerRobotPos.z = robotPose.z;
centerRobotPos.roll = CCoordinateTransform::radToDeg(robotPose.rx);
centerRobotPos.pitch = CCoordinateTransform::radToDeg(robotPose.ry);
centerRobotPos.yaw = CCoordinateTransform::radToDeg(robotPose.rz);
centerRobotPos.x = ptRobot.x;
centerRobotPos.y = ptRobot.y;
centerRobotPos.z = ptRobot.z;
centerRobotPos.roll = robotRpy.roll; // 已经是角度
centerRobotPos.pitch = robotRpy.pitch; // 已经是角度
centerRobotPos.yaw = robotRpy.yaw; // 已经是角度
detectionResult.positions.push_back(centerRobotPos);
@ -246,16 +281,19 @@ int DetectPresenter::DetectWorkpieceHole(
objOps.push_back(holePoints);
if(debugParam.enableDebug && debugParam.printDetailLog){
LOG_INFO("[Algo Thread] Direction vectors: X=(%.3f,%.3f,%.3f), Y=(%.3f,%.3f,%.3f), Z=(%.3f,%.3f,%.3f)\n",
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);
LOG_INFO("[Algo Thread] Center Eye Coords: X=%.2f, Y=%.2f, Z=%.2f, R=%.4f, P=%.4f, Y=%.4f\n",
cameraPose.x, cameraPose.y, cameraPose.z,
CCoordinateTransform::radToDeg(cameraPose.rx), CCoordinateTransform::radToDeg(cameraPose.ry), CCoordinateTransform::radToDeg(cameraPose.rz));
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,
dirVectors_robot[2].x, dirVectors_robot[2].y, dirVectors_robot[2].z);
LOG_INFO("[Algo Thread] Center Eye Coords: X=%.2f, Y=%.2f, Z=%.2f\n",
centerEye.x, centerEye.y, centerEye.z);
LOG_INFO("[Algo Thread] Center Robot Coords: X=%.2f, Y=%.2f, Z=%.2f, R=%.4f, P=%.4f, Y=%.4f\n",
robotPose.x, robotPose.y, robotPose.z,
CCoordinateTransform::radToDeg(robotPose.rx), CCoordinateTransform::radToDeg(robotPose.ry), CCoordinateTransform::radToDeg(robotPose.rz));
ptRobot.x, ptRobot.y, ptRobot.z,
centerRobotPos.roll, centerRobotPos.pitch, centerRobotPos.yaw);
}
}

View File

@ -240,6 +240,10 @@ void PLCModbusClient::pollThreadFunc()
notifyPhotoRequested(1);
}
m_lastPhotoRequestState = currentState;
} else {
// 读取失败,可能是连接断开,主动断开连接以触发重连
LOG_WARNING("Failed to read photo request, disconnecting to trigger reconnect\n");
disconnectPLC();
}
std::this_thread::sleep_for(std::chrono::milliseconds(m_pollIntervalMs));
@ -277,6 +281,16 @@ bool PLCModbusClient::tryConnectPLC()
return true;
}
void PLCModbusClient::disconnectPLC()
{
std::lock_guard<std::mutex> locker(m_mutex);
if (m_plcClient) {
m_plcClient->Disconnect();
LOG_INFO("PLC disconnected for reconnection\n");
}
}
int PLCModbusClient::readPhotoRequest()
{
std::lock_guard<std::mutex> locker(m_mutex);

View File

@ -380,11 +380,15 @@ int WorkpieceHolePresenter::ProcessAlgoDetection(std::vector<std::pair<EVzResult
int eulerOrder = configResult.handEyeCalibMatrix.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);
WorkpieceHoleDetectionResult detectionResult;
int nRet = m_pDetectPresenter->DetectWorkpieceHole(m_currentCameraIndex, detectionDataCache,
algorithmParams, debugParam, m_dataLoader,
currentClibMatrix.clibMatrix, eulerOrder, detectionResult);
currentClibMatrix.clibMatrix, eulerOrder, dirVectorInvert, detectionResult);
// 根据项目类型选择处理方式
if (GetStatusCallback<IYWorkpieceHoleStatus>()) {
QString err = QString("错误:%1").arg(nRet);
@ -729,8 +733,9 @@ int WorkpieceHolePresenter::InitPLCModbus()
LOG_INFO("PLC connection state changed: PLC=%s\n",
plcConnected ? "connected" : "disconnected");
// 通知 UI 更新连接状态
// 通知 UI 更新连接状态PLC连接状态关联到机械臂状态指示器
if (auto pStatus = GetStatusCallback<IYWorkpieceHoleStatus>()) {
pStatus->OnRobotConnectionChanged(plcConnected);
std::string statusMsg = std::string("PLC:") + (plcConnected ? "已连接" : "断开");
pStatus->OnStatusUpdate(statusMsg);
}
@ -743,6 +748,15 @@ int WorkpieceHolePresenter::InitPLCModbus()
}
});
// 设置重连回调,通知用户正在尝试重连
m_pPLCModbusClient->SetReconnectingCallback([this](const std::string& device, int attempt) {
LOG_INFO("Attempting to reconnect %s (attempt %d)\n", device.c_str(), attempt);
if (auto pStatus = GetStatusCallback<IYWorkpieceHoleStatus>()) {
std::string statusMsg = device + "正在重连(第" + std::to_string(attempt) + "次)...";
pStatus->OnStatusUpdate(statusMsg);
}
});
// 构建寄存器地址配置
PLCModbusClient::RegisterConfig regConfig;
regConfig.addrPhotoRequest = configResult.plcRobotServerConfig.registerConfig.addrPhotoRequest;
@ -825,6 +839,11 @@ void WorkpieceHolePresenter::SendCoordinateDataToPLC(const WorkpieceHoleDetectio
return;
}
// 获取姿态输出顺序配置
ConfigResult configResult = m_pConfigManager->GetConfigResult();
int poseOutputOrder = configResult.plcRobotServerConfig.poseOutputOrder;
LOG_INFO("Using pose output order: %d\n", poseOutputOrder);
// 构建坐标数据列表(每个工件的中心点+姿态最多10个
std::vector<PLCModbusClient::CoordinateData> coords;
int pointCount = std::min(static_cast<int>(result.positions.size()), PLCModbusClient::MAX_POINTS);
@ -837,13 +856,50 @@ void WorkpieceHolePresenter::SendCoordinateDataToPLC(const WorkpieceHoleDetectio
coord.x = static_cast<float>(pos.x);
coord.y = static_cast<float>(pos.y);
coord.z = static_cast<float>(pos.z);
coord.roll = static_cast<float>(pos.roll);
coord.pitch = static_cast<float>(pos.pitch);
coord.yaw = static_cast<float>(pos.yaw);
// 根据姿态输出顺序配置调整输出
switch (poseOutputOrder) {
case POSE_ORDER_RPY: // Roll, Pitch, Yaw默认
coord.roll = static_cast<float>(pos.roll);
coord.pitch = static_cast<float>(pos.pitch);
coord.yaw = static_cast<float>(pos.yaw);
break;
case POSE_ORDER_RYP: // Roll, Yaw, Pitch
coord.roll = static_cast<float>(pos.roll);
coord.pitch = static_cast<float>(pos.yaw);
coord.yaw = static_cast<float>(pos.pitch);
break;
case POSE_ORDER_PRY: // Pitch, Roll, Yaw
coord.roll = static_cast<float>(pos.pitch);
coord.pitch = static_cast<float>(pos.roll);
coord.yaw = static_cast<float>(pos.yaw);
break;
case POSE_ORDER_PYR: // Pitch, Yaw, Roll
coord.roll = static_cast<float>(pos.pitch);
coord.pitch = static_cast<float>(pos.yaw);
coord.yaw = static_cast<float>(pos.roll);
break;
case POSE_ORDER_YRP: // Yaw, Roll, Pitch
coord.roll = static_cast<float>(pos.yaw);
coord.pitch = static_cast<float>(pos.roll);
coord.yaw = static_cast<float>(pos.pitch);
break;
case POSE_ORDER_YPR: // Yaw, Pitch, Roll
coord.roll = static_cast<float>(pos.yaw);
coord.pitch = static_cast<float>(pos.pitch);
coord.yaw = static_cast<float>(pos.roll);
break;
default: // 默认 RPY
coord.roll = static_cast<float>(pos.roll);
coord.pitch = static_cast<float>(pos.pitch);
coord.yaw = static_cast<float>(pos.yaw);
break;
}
coords.push_back(coord);
LOG_INFO("Workpiece[%d]: X=%.2f, Y=%.2f, Z=%.2f, P=%.2f, R=%.2f, Y=%.2f\n", i, coord.x, coord.y, coord.z, coord.roll, coord.pitch, coord.yaw);
LOG_INFO("Workpiece[%d]: X=%.2f, Y=%.2f, Z=%.2f, R1=%.2f, R2=%.2f, R3=%.2f (order=%d)\n",
i, coord.x, coord.y, coord.z, coord.roll, coord.pitch, coord.yaw, poseOutputOrder);
}
LOG_INFO("Sending %d workpiece positions to PLC\n", pointCount);

View File

@ -3,7 +3,7 @@
// 版本字符串
#define WORKPIECEHOLE_VERSION_STRING "1.0.1"
#define WORKPIECEHOLE_BUILD_STRING "2"
#define WORKPIECEHOLE_BUILD_STRING "5"
#define WORKPIECEHOLE_FULL_VERSION_STRING "V" WORKPIECEHOLE_VERSION_STRING "_" WORKPIECEHOLE_BUILD_STRING
// 构建日期

View File

@ -1,4 +1,10 @@
# 1.0.1
## build_4
1. 增加了协议姿态输出顺序
## build_3 2026-02-01
1. 更新了算法,输出的姿态
## build_2 2026-02-01
1. 协议测试修正

View File

@ -4,7 +4,7 @@ QT += core gui network serialport
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
CONFIG += c++11
# 项目名称
TARGET = WorkpieceHoleApp
@ -44,6 +44,10 @@ INCLUDEPATH += $$PWD/../../../DataUtils/CoordinateTransform/Inc
# eigen 依赖
INCLUDEPATH += $$PWD/../../../SDK/eigen-3.3.9
#opencv
INCLUDEPATH += $$PWD/../../../SDK/OpenCV320/include
# ModbusTCP 客户端库(只包含导出接口目录,不包含内部 modbus 头文件)
INCLUDEPATH += $$PWD/../../../Module/ModbusTCPClient/Inc

View File

@ -38,6 +38,12 @@ DialogAlgoarg::DialogAlgoarg(ConfigManager* configManager, QWidget *parent)
// 初始化旋转顺序下拉框
InitEulerOrderComboBox();
// 初始化姿态输出顺序下拉框
InitPoseOutputOrderComboBox();
// 初始化方向向量反向下拉框
InitDirVectorInvertComboBox();
// 从配置文件加载数据到界面
LoadConfigToUI();
@ -387,19 +393,56 @@ void DialogAlgoarg::InitEulerOrderComboBox()
ui->comboBox_eulerOrder->setCurrentIndex(0);
}
void DialogAlgoarg::InitPoseOutputOrderComboBox()
{
// 添加姿态输出顺序选项
ui->comboBox_poseOutputOrder->addItem("RPY (Roll, Pitch, Yaw)", POSE_ORDER_RPY); // 默认
ui->comboBox_poseOutputOrder->addItem("RYP (Roll, Yaw, Pitch)", POSE_ORDER_RYP);
ui->comboBox_poseOutputOrder->addItem("PRY (Pitch, Roll, Yaw)", POSE_ORDER_PRY);
ui->comboBox_poseOutputOrder->addItem("PYR (Pitch, Yaw, Roll)", POSE_ORDER_PYR);
ui->comboBox_poseOutputOrder->addItem("YRP (Yaw, Roll, Pitch)", POSE_ORDER_YRP);
ui->comboBox_poseOutputOrder->addItem("YPR (Yaw, Pitch, Roll)", POSE_ORDER_YPR);
// 默认选择 RPY
ui->comboBox_poseOutputOrder->setCurrentIndex(0);
}
void DialogAlgoarg::InitDirVectorInvertComboBox()
{
// 添加方向向量反向选项
ui->comboBox_dirVectorInvert->addItem("不反向", DIR_INVERT_NONE);
ui->comboBox_dirVectorInvert->addItem("XY反向", DIR_INVERT_XY);
ui->comboBox_dirVectorInvert->addItem("XZ反向", DIR_INVERT_XZ);
ui->comboBox_dirVectorInvert->addItem("YZ反向默认", DIR_INVERT_YZ);
// 默认选择 YZ反向兼容原有行为
ui->comboBox_dirVectorInvert->setCurrentIndex(3);
}
void DialogAlgoarg::LoadPlcRobotServerConfigToUI(const VrPlcRobotServerConfig& config)
{
if (!ui) return;
// 加载PLC服务端配置
ui->lineEdit_plcServerIp->setText(QString::fromStdString(config.plcServerIp));
ui->lineEdit_plcServerPort->setText(QString::number(config.plcServerPort));
ui->lineEdit_robotServerIp->setText(QString::fromStdString(config.robotServerIp));
ui->lineEdit_robotServerPort->setText(QString::number(config.robotServerPort));
// 加载PLC寄存器地址配置
ui->lineEdit_addrPhotoRequest->setText(QString::number(config.registerConfig.addrPhotoRequest));
ui->lineEdit_addrDataComplete->setText(QString::number(config.registerConfig.addrDataComplete));
ui->lineEdit_addrCoordDataStart->setText(QString::number(config.registerConfig.addrCoordDataStart));
// 加载姿态输出顺序
int poseOrderIndex = ui->comboBox_poseOutputOrder->findData(config.poseOutputOrder);
if (poseOrderIndex >= 0) {
ui->comboBox_poseOutputOrder->setCurrentIndex(poseOrderIndex);
}
// 加载方向向量反向配置
int dirInvertIndex = ui->comboBox_dirVectorInvert->findData(config.dirVectorInvert);
if (dirInvertIndex >= 0) {
ui->comboBox_dirVectorInvert->setCurrentIndex(dirInvertIndex);
}
}
bool DialogAlgoarg::SavePlcRobotServerConfigFromUI(VrPlcRobotServerConfig& config)
@ -420,19 +463,6 @@ bool DialogAlgoarg::SavePlcRobotServerConfigFromUI(VrPlcRobotServerConfig& confi
return false;
}
// 获取机械臂服务端IP
QString robotIp = ui->lineEdit_robotServerIp->text().trimmed();
if (robotIp.isEmpty()) {
return false;
}
config.robotServerIp = robotIp.toStdString();
// 获取机械臂服务端端口
config.robotServerPort = ui->lineEdit_robotServerPort->text().toInt(&ok);
if (!ok || config.robotServerPort <= 0 || config.robotServerPort > 65535) {
return false;
}
// 获取PLC寄存器地址配置
config.registerConfig.addrPhotoRequest = ui->lineEdit_addrPhotoRequest->text().toInt(&ok);
if (!ok || config.registerConfig.addrPhotoRequest < 0) {
@ -449,5 +479,11 @@ bool DialogAlgoarg::SavePlcRobotServerConfigFromUI(VrPlcRobotServerConfig& confi
return false;
}
// 获取姿态输出顺序
config.poseOutputOrder = ui->comboBox_poseOutputOrder->currentData().toInt();
// 获取方向向量反向配置
config.dirVectorInvert = ui->comboBox_dirVectorInvert->currentData().toInt();
return true;
}

View File

@ -40,9 +40,11 @@ private:
bool SaveFilterParamFromUI(VrOutlierFilterParam& param);
bool SaveGrowParamFromUI(VrTreeGrowParam& param);
// 网络配置PLC和机械臂服务端)
// 网络配置PLC服务端)
void LoadPlcRobotServerConfigToUI(const VrPlcRobotServerConfig& config);
bool SavePlcRobotServerConfigFromUI(VrPlcRobotServerConfig& config);
void InitPoseOutputOrderComboBox();
void InitDirVectorInvertComboBox();
// 手眼标定矩阵
void LoadCalibMatrixToUI();

View File

@ -62,7 +62,7 @@ QGroupBox { color: rgb(221, 225, 233); border: 1px solid rgb(100, 100, 100); bor
QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; }</string>
</property>
<property name="currentIndex">
<number>0</number>
<number>5</number>
</property>
<widget class="QWidget" name="tab_workpiece">
<attribute name="title">
@ -809,7 +809,7 @@ QComboBox QAbstractItemView {
<x>10</x>
<y>20</y>
<width>671</width>
<height>251</height>
<height>291</height>
</rect>
</property>
<property name="font">
@ -826,7 +826,7 @@ QComboBox QAbstractItemView {
<x>20</x>
<y>40</y>
<width>631</width>
<height>200</height>
<height>246</height>
</rect>
</property>
<layout class="QGridLayout">
@ -950,106 +950,120 @@ QComboBox QAbstractItemView {
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QGroupBox" name="groupBox_robot">
<property name="geometry">
<rect>
<x>10</x>
<y>290</y>
<width>671</width>
<height>201</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="title">
<string>机械臂服务端配置</string>
</property>
<widget class="QWidget" name="layoutWidget_robot">
<property name="geometry">
<rect>
<x>20</x>
<y>40</y>
<width>631</width>
<height>148</height>
</rect>
</property>
<layout class="QVBoxLayout">
<item>
<widget class="QLabel" name="label_robotServerIp">
<item row="5" column="0">
<widget class="QLabel" name="label_poseOutputOrder">
<property name="font">
<font>
<pointsize>16</pointsize>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string>机械臂服务端IP地址</string>
<string>姿态输出顺序</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_robotServerIp">
<item row="5" column="1">
<widget class="QComboBox" name="comboBox_poseOutputOrder">
<property name="font">
<font>
<pointsize>16</pointsize>
<pointsize>14</pointsize>
</font>
</property>
<property name="placeholderText">
<string>例如: 192.168.1.20</string>
<property name="styleSheet">
<string notr="true">QComboBox {
background-color: rgb(50, 52, 56);
color: rgb(221, 225, 233);
border: 1px solid rgb(80, 82, 86);
border-radius: 4px;
padding: 5px;
}
QComboBox::drop-down {
border: none;
}
QComboBox QAbstractItemView {
background-color: rgb(50, 52, 56);
color: rgb(221, 225, 233);
selection-background-color: rgb(60, 120, 180);
}</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_robotServerPort">
<item row="6" column="0">
<widget class="QLabel" name="label_dirVectorInvert">
<property name="font">
<font>
<pointsize>16</pointsize>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string>机械臂服务端端口</string>
<string>方向向量反向</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_robotServerPort">
<item row="6" column="1">
<widget class="QComboBox" name="comboBox_dirVectorInvert">
<property name="font">
<font>
<pointsize>16</pointsize>
<pointsize>14</pointsize>
</font>
</property>
<property name="placeholderText">
<string>例如: 502</string>
<property name="styleSheet">
<string notr="true">QComboBox {
background-color: rgb(50, 52, 56);
color: rgb(221, 225, 233);
border: 1px solid rgb(80, 82, 86);
border-radius: 4px;
padding: 5px;
}
QComboBox::drop-down {
border: none;
}
QComboBox QAbstractItemView {
background-color: rgb(50, 52, 56);
color: rgb(221, 225, 233);
selection-background-color: rgb(60, 120, 180);
}</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
</widget>
</widget>
<widget class="QPushButton" name="btn_camer_ok">
<widget class="QWidget" name="">
<property name="geometry">
<rect>
<x>190</x>
<x>2</x>
<y>670</y>
<width>100</width>
<height>45</height>
<width>751</width>
<height>44</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>178</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btn_camer_ok">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
background-color: rgb(60, 120, 180);
color: rgb(221, 225, 233);
border: none;
@ -1062,28 +1076,35 @@ QPushButton:hover {
QPushButton:pressed {
background-color: rgb(40, 100, 160);
}</string>
</property>
<property name="text">
<string>保存</string>
</property>
</widget>
<widget class="QPushButton" name="btn_camer_cancel">
<property name="geometry">
<rect>
<x>410</x>
<y>670</y>
<width>100</width>
<height>45</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
</property>
<property name="text">
<string> 保存 </string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>50</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btn_camer_cancel">
<property name="font">
<font>
<pointsize>16</pointsize>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
background-color: rgb(120, 120, 120);
color: rgb(221, 225, 233);
border: none;
@ -1096,10 +1117,26 @@ QPushButton:hover {
QPushButton:pressed {
background-color: rgb(100, 100, 100);
}</string>
</property>
<property name="text">
<string>取消</string>
</property>
</property>
<property name="text">
<string> 取消 </string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>178</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
<resources/>

View File

@ -107,6 +107,35 @@ struct VrPlcRegisterConfig
VrPlcRegisterConfig() = default;
};
/**
* @brief 姿
*
* 姿
*/
enum VrPoseOutputOrder
{
POSE_ORDER_RPY = 0, // Roll, Pitch, Yaw默认
POSE_ORDER_RYP = 1, // Roll, Yaw, Pitch
POSE_ORDER_PRY = 2, // Pitch, Roll, Yaw
POSE_ORDER_PYR = 3, // Pitch, Yaw, Roll
POSE_ORDER_YRP = 4, // Yaw, Roll, Pitch
POSE_ORDER_YPR = 5 // Yaw, Pitch, Roll
};
/**
* @brief
*
*
*
*/
enum VrDirVectorInvert
{
DIR_INVERT_NONE = 0, // 不反向
DIR_INVERT_XY = 1, // X和Y方向反向
DIR_INVERT_XZ = 2, // X和Z方向反向
DIR_INVERT_YZ = 3 // Y和Z方向反向默认兼容原有行为
};
/**
* @brief PLC和机械臂服务端配置
*
@ -121,6 +150,8 @@ struct VrPlcRobotServerConfig
std::string robotServerIp = "192.168.0.90"; // 机械臂服务端IP地址配天机械臂
int robotServerPort = 502; // 机械臂服务端端口Modbus TCP默认502
VrPlcRegisterConfig registerConfig; // PLC 寄存器地址配置
int poseOutputOrder = POSE_ORDER_RPY; // 姿态输出顺序默认RPY
int dirVectorInvert = DIR_INVERT_YZ; // 方向向量反向配置默认YZ反向兼容原有行为
// 显式赋值构造函数
VrPlcRobotServerConfig& operator=(const VrPlcRobotServerConfig& other) {
@ -130,6 +161,8 @@ struct VrPlcRobotServerConfig
robotServerIp = other.robotServerIp;
robotServerPort = other.robotServerPort;
registerConfig = other.registerConfig;
poseOutputOrder = other.poseOutputOrder;
dirVectorInvert = other.dirVectorInvert;
}
return *this;
}
@ -140,7 +173,9 @@ struct VrPlcRobotServerConfig
, plcServerPort(other.plcServerPort)
, robotServerIp(other.robotServerIp)
, robotServerPort(other.robotServerPort)
, registerConfig(other.registerConfig) {
, registerConfig(other.registerConfig)
, poseOutputOrder(other.poseOutputOrder)
, dirVectorInvert(other.dirVectorInvert) {
}
// 默认构造函数

View File

@ -274,6 +274,18 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
configResult.plcRobotServerConfig.registerConfig.addrDataComplete = tcpServerConfigElement->IntAttribute("addrDataComplete");
if (tcpServerConfigElement->Attribute("addrCoordDataStart"))
configResult.plcRobotServerConfig.registerConfig.addrCoordDataStart = tcpServerConfigElement->IntAttribute("addrCoordDataStart");
// 解析姿态输出顺序配置
if (tcpServerConfigElement->Attribute("poseOutputOrder"))
configResult.plcRobotServerConfig.poseOutputOrder = tcpServerConfigElement->IntAttribute("poseOutputOrder");
else
configResult.plcRobotServerConfig.poseOutputOrder = POSE_ORDER_RPY; // 默认RPY
// 解析方向向量反向配置
if (tcpServerConfigElement->Attribute("dirVectorInvert"))
configResult.plcRobotServerConfig.dirVectorInvert = tcpServerConfigElement->IntAttribute("dirVectorInvert");
else
configResult.plcRobotServerConfig.dirVectorInvert = DIR_INVERT_YZ; // 默认YZ反向兼容原有行为
}
return LOAD_CONFIG_SUCCESS;
@ -431,6 +443,10 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
tcpServerConfigElement->SetAttribute("addrPhotoRequest", configResult.plcRobotServerConfig.registerConfig.addrPhotoRequest);
tcpServerConfigElement->SetAttribute("addrDataComplete", configResult.plcRobotServerConfig.registerConfig.addrDataComplete);
tcpServerConfigElement->SetAttribute("addrCoordDataStart", configResult.plcRobotServerConfig.registerConfig.addrCoordDataStart);
// 保存姿态输出顺序配置
tcpServerConfigElement->SetAttribute("poseOutputOrder", configResult.plcRobotServerConfig.poseOutputOrder);
// 保存方向向量反向配置
tcpServerConfigElement->SetAttribute("dirVectorInvert", configResult.plcRobotServerConfig.dirVectorInvert);
root->InsertEndChild(tcpServerConfigElement);
// 保存到文件

View File

@ -24,5 +24,5 @@
<DebugParam enableDebug="false" savePointCloud="false" saveDebugImage="false" printDetailLog="false" debugOutputPath="./debug"/>
<SerialConfig portName="COM1" baudRate="115200" dataBits="8" stopBits="1" parity="0" flowControl="0" enabled="false"/>
<HandEyeCalibMatrix m0="1" m1="0" m2="0" m3="0" m4="0" m5="1" m6="0" m7="0" m8="0" m9="0" m10="1" m11="0" m12="0" m13="0" m14="0" m15="1"/>
<TcpServerConfig plcServerIp="192.168.0.88" plcServerPort="502" robotServerIp="192.168.0.90" robotServerPort="502" addrPhotoRequest="1001" addrDataComplete="1003" addrCoordDataStart="2002"/>
<TcpServerConfig plcServerIp="192.168.0.88" plcServerPort="502" robotServerIp="192.168.0.90" robotServerPort="502" addrPhotoRequest="1001" addrDataComplete="1003" addrCoordDataStart="2002" poseOutputOrder="0" dirVectorInvert="3"/>
</WorkpieceHoleConfig>

View File

@ -0,0 +1,755 @@
#pragma once
#include "SG_algo_Export.h"
#include <vector>
#include <opencv2/opencv.hpp>
//滤除离群点:z跳变门限方法大于门限视为不连续根据连续段点数量判断噪声
SG_APISHARED_EXPORT void sg_lineDataRemoveOutlier(
SVzNL3DPosition* lineData,
int dataSize,
SSG_outlierFilterParam filterParam,
std::vector<SVzNL3DPosition>& filerData,
std::vector<int>& noisePts);
//滤除离群点z跳变门限方法,改变原始数据
SG_APISHARED_EXPORT void sg_lineDataRemoveOutlier_changeOriginData(
SVzNL3DPosition* lineData,
int dataSize,
SSG_outlierFilterParam filterParam);
//滤除离群点z跳变门限方法, vecotr对象
SG_APISHARED_EXPORT void wd_vectorDataRemoveOutlier_overwrite(
std::vector<SVzNL3DPosition>& a_line,
SSG_outlierFilterParam filterParam);
//滤除离群点:点距离门限方法(大于门限视为不连续,根据连续段点数量判断噪声)
SG_APISHARED_EXPORT void sg_lineDataRemoveOutlier_ptDistMethod(
SVzNL3DPosition* lineData,
int dataSize,
SSG_outlierFilterParam filterParam,
std::vector<SVzNL3DPosition>& filerData,
std::vector<int>& noisePts);
//平滑
SG_APISHARED_EXPORT void sg_lineDataSmoothing(
std::vector<SVzNL3DPosition>& input,
int smoothWin,
std::vector<SVzNL3DPosition>& output);
//分段平滑,这样不会影响分段端点
SG_APISHARED_EXPORT void sg_lineSegSmoothing(
std::vector<SVzNL3DPosition>& input,
double seg_y_deltaTh, //分段的Y间隔。大于此间隔为新的分段
double seg_z_deltaTh,//分段的Z间隔。大于此间隔为新的分段
int smoothWin,
std::vector<SVzNL3DPosition>& output);
//VZ_APISHARED_EXPORT void sg_getLineMeanVar();
SG_APISHARED_EXPORT void sg_getLineLVFeature(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const SSG_slopeParam slopeParam,
const SSG_VFeatureParam valleyPara,
SSG_lineFeature* line_features);
//获取扫描线拐点特征
SG_APISHARED_EXPORT void sg_getLineCornerFeature(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const SSG_cornerParam cornerPara, //scale通常取bagH的1/4
SSG_lineFeature* line_features);
// 对激光线上由Mask(nPointIdx转义定义指定的点提取激光线上的拐点特征
SG_APISHARED_EXPORT void sg_maskData_getLineCornerFeature(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
int maskID,
const SSG_cornerParam cornerPara, //scale通常取bagH的1/4
std::vector<SSG_basicFeature1D>& features);
//提取Gap特征。Gap特征由两个负的Corner构成的区域
SG_APISHARED_EXPORT void sg_getLineGapFeature(
std::vector<SVzNL3DPosition>& lineData, //扫描线
int lineIdx, //当前扫描线序号
const SSG_cornerParam cornerPara,
const SVzNLRangeD gapParam,
std::vector<SSG_basicFeatureGap>& line_gaps);
SG_APISHARED_EXPORT void sg_getLineCornerFeature_BQ(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
double refSteppingZ,
const SSG_cornerParam cornerPara,
std::vector<SSG_basicFeature1D>& line_features);
/// <summary>
/// 提取激光线上的拐点特征,地面端点附近的拐点视为合格拐点
/// 根据Seg进行corner提取
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// 1逐点计算前向角和后向角
/// 2逐点计算拐角顺时针为负逆时针为正
/// 3搜索正拐角的极大值。
/// 4判断拐角是否为跳变
/// </summary>
SG_APISHARED_EXPORT void sg_getLineContourCornerFeature_groundMask_BQ(
std::vector<SVzNL3DPosition>& lineData,
std::vector<int> groundMask,
int lineIdx,
double contourWin, //ground边缘范围
const SSG_cornerParam cornerPara, //scale通常取bagH的1/4
std::vector<SSG_basicFeature1D>& line_features);
SG_APISHARED_EXPORT void wd_getLineDataIntervals(
std::vector<SVzNL3DPosition>& lineData,
const SSG_lineSegParam lineSegPara,
std::vector<SSG_RUN>& segs);
// 最小点集数量(小于此数无法拟合直线)
const int MIN_POINT_COUNT = 3;
//使用端点直线,检查点到直线的距离,大于门限的分割
SG_APISHARED_EXPORT void split(
SSG_RUN a_run,
std::vector< SVzNL3DPosition>& lineData,
const double maxError,
std::vector< SSG_RUN>& lineSegs);
/// <summary>
/// 提取激光线上的特征跳变、低于z阈值、V及L型用于粒径检测PSM)
/// seg端点z距离大于门限
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// 1逐点计算前向角和后向角
/// 2逐点计算拐角顺时针为负逆时针为正
/// 3搜索正拐角的极大值。
/// 4判断拐角是否为跳变
/// </summary>
SG_APISHARED_EXPORT void wd_getLineCornerFeature_PSM(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const double groundZ,
const SSG_cornerParam cornerPara,
SSG_lineFeature* line_features);
/// 提取激光线上的圆环的上半段弧。宽度由圆环的宽度确定
/// seg端点z距离大于门限
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// 1逐点计算前向角和后向角
/// 2逐点计算拐角顺时针为负逆时针为正
/// 3搜索正拐角的极大值。
/// 4判断拐角是否为跳变
/// </summary>
SG_APISHARED_EXPORT void wd_getRingArcFeature(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
const SSG_cornerParam cornerPara,
SVzNLRangeD ringArcWidth, //定子的环宽度
std::vector<SWD_segFeature>& line_ringArcs //环
);
/// <summary>
/// 提取激光线上的拐点特征。是在PSM LVTypeFeature, BQ等拐点算法的基础上的版本。
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// 1逐点计算前向角和后向角
/// 2逐点计算拐角顺时针为负逆时针为正
/// 3搜索正拐角的极大值。
/// 4判断拐角是否为跳变
/// </summary>
SG_APISHARED_EXPORT void wd_getLineCorerFeature(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
const SSG_cornerParam cornerPara,
std::vector<SSG_basicFeature1D>& line_cornerFeatures //拐点
);
/// 提取激光线上的拐点特征。是在PSM LVTypeFeature, BQ等拐点算法的基础上的版本。
/// 使用平均点距进行加速
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// 1逐点计算前向角和后向角
/// 2逐点计算拐角顺时针为负逆时针为正
/// 3搜索正拐角的极大值。
/// 4判断拐角是否为跳变
/// </summary>
SG_APISHARED_EXPORT void wd_getLineCorerFeature_accelerate(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
const SSG_cornerParam cornerPara,
const double pointInterval,
std::vector<SSG_RUN_EX>& segs,
std::vector<SSG_basicFeature1D>& line_cornerFeatures //拐点特征
);
//提取凸起段
SG_APISHARED_EXPORT void wd_getLineRaisedFeature(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
SSG_raisedFeatureParam raisedFeaturePara, //凸起参数
std::vector<SWD_segFeature>& line_raisedFeatures //凸起
);
//直线特征提取对split-and-merge法作了简化以起点终点直线代替拟合直线
SG_APISHARED_EXPORT void wd_surfaceLineSegment(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
SVzNLRangeD lineLenRange,
const SSG_lineSegParam lineSegPara,
std::vector<SSG_featureSemiCircle>& lineSegs,
std::vector<SSG_featureSemiCircle>& invlaidLineSegs);
#if 0
//对一个面的边缘特征进行提取。
//此算法首先获取扫描线上各个段,然后对段的端点进行处理,得到边缘特征
void wd_getSurfaceEdgeFeatures(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
const SSG_lineSegParam lineSegPara,
std::vector<SWDIndexing3DPoint>& edgeFeatures);
#endif
/// <summary>
/// 提取激光线上的Jumping特征
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// 1逐点计算前向角和后向角
/// 2逐点计算拐角顺时针为负逆时针为正
/// 3搜索正拐角的极大值。
/// 4判断拐角是否为跳变
/// </summary>
SG_APISHARED_EXPORT void sg_getLineJumpFeature_cornerMethod(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const SSG_cornerParam cornerPara, //scale通常取bagH的1/4
std::vector< SSG_basicFeature1D>& jumpFeatures);
SG_APISHARED_EXPORT void wd_getLineGloveArcs(
std::vector<SVzNL3DPosition>& lineData,
int lineIdx,
const SSG_gloveArcParam arcPara,
std::vector<SSG_basicFeature1D>& gloveArcs);
/// 提取激光线上的圆柱形特征
SG_APISHARED_EXPORT void sg_getLineUpperSemiCircleFeature(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const double sieveDiameter,
const SSG_slopeParam slopePara,
std::vector< SSG_featureSemiCircle>& line_features,
cv::Mat& holeMask);
/// <summary>
/// 提取激光线上的极值点(极大值点和极小值点)
///
/// </summary>
SG_APISHARED_EXPORT void sg_getLineLocalPeaks(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const double scaleWin,
std::vector< SSG_basicFeature1D>& localMax,
std::vector< SSG_basicFeature1D>& localMin);
SG_APISHARED_EXPORT void sg_getFlatLineLocalPeaks_vector(
std::vector<SVzNL3DPosition>& lineData,
int lineIdx,
const double scaleWin,
const double minPkHeighth,
const double holeR,
std::vector< SSG_basicFeature1D>& localMax);
SG_APISHARED_EXPORT void sg_getLineDownJumps(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const double jumpTh,
std::vector< SSG_basicFeature1D>& downJumps);
//对feature进行生长
SG_APISHARED_EXPORT void sg_getFeatureGrowingTrees(
std::vector<SSG_lineFeature>& lineFeatures,
std::vector<SSG_featureTree>& trees,
SSG_treeGrowParam growParam);
//对feature进行生长:不比较type只判断位置是否相邻
SG_APISHARED_EXPORT void wd_getFeatureGrowingTrees_noTypeMatch(
std::vector<SSG_lineFeature>& lineFeatures,
std::vector<SSG_featureTree>& feature_trees,
std::vector<SSG_featureTree>& ending_trees,
SSG_treeGrowParam growParam);
//对segment feature进行生长
SG_APISHARED_EXPORT void wd_getSegFeatureGrowingTrees(
std::vector<std::vector<SWD_segFeature>>& all_lineFeatures,
std::vector<SWD_segFeatureTree>& feature_trees,
SSG_treeGrowParam growParam);
SG_APISHARED_EXPORT void sg_lineFeaturesGrowing(
int lineIdx,
bool isLastLine,
std::vector<SSG_basicFeature1D>& features,
std::vector<SSG_featureTree>& trees,
SSG_treeGrowParam growParam);
//corner特征生长
SG_APISHARED_EXPORT void sg_cornerFeaturesGrowing(
std::vector<std::vector<SSG_basicFeature1D>>& cornerFeatures,
std::vector<SSG_featureTree>& trees,
SSG_treeGrowParam growParam);
//gap特征生长
SG_APISHARED_EXPORT void sg_lineGapsGrowing(
int lineIdx,
bool isLastLine,
std::vector<SSG_basicFeatureGap>& features,
std::vector<SSG_gapFeatureTree>& trees,
SSG_treeGrowParam growParam);
//SG_APISHARED_EXPORT void sg_getTreeROI(SSG_featureTree* a_tree);
//对ending进行生长
SG_APISHARED_EXPORT void sg_getEndingGrowingTrees(
std::vector<SSG_2DValueI>& lineEndings,
SVzNL3DLaserLine* laser3DPoints,
bool isVScan,
int featureType,
std::vector<SSG_featureTree>& trees,
SSG_treeGrowParam growParam);
//对ending进行生长垂直扫描时只进行水平生长水平扫描时只进行垂直生长
SG_APISHARED_EXPORT void sg_getEndingGrowingTrees_angleCheck(
std::vector<SSG_2DValueI>& lineEndings,
SVzNL3DLaserLine* laser3DPoints,
bool isVScan,
int featureType,
std::vector<SSG_featureTree>& trees,
SSG_treeGrowParam growParam,
double angleCheckScale);
//对扫描线上的
SG_APISHARED_EXPORT void sg_LVFeatureGrowing(
std::vector<SSG_lineFeature>& lineFeatures,
std::vector<SSG_featureTree>& trees,
SSG_bagParam bagParam,
SSG_treeGrowParam growParam,
std::vector<SSG_2DValueI>& edgePts_0,
std::vector<SSG_2DValueI>& edgePts_1);
SG_APISHARED_EXPORT void sg_peakFeatureGrowing(
std::vector<std::vector< SSG_basicFeature1D>>& lineFeatures,
std::vector<SSG_featureTree>& trees,
SSG_treeGrowParam growParam);
//semiCircle特征生长
SG_APISHARED_EXPORT void sg_getFeatureGrowingTrees_semiCircle(
std::vector< SSG_featureSemiCircle>& lineFeatures,
const int lineIdx,
const int lineSize,
std::vector<SSG_semiCircleFeatureTree>& trees,
std::vector<SSG_semiCircleFeatureTree>& stopTrees,
std::vector<SSG_semiCircleFeatureTree>& invalidTrees, //被移除的树,这些树可能将目标分成多个树,从而被移除。
SSG_treeGrowParam growParam);
SSG_meanVar _computeMeanVar(double* data, int size);
///搜索局部最高点z最小点
///搜索方法:每次步进搜索窗口长度的一半。对局部最高点进行标记,防止被重复记录。
SG_APISHARED_EXPORT void sg_getLocalPeaks(
SVzNL3DLaserLine* scanLines,
int lineNum,
std::vector<SSG_2DValueI>& peaks,
SSG_localPkParam searchWin);
/// <summary>
/// 区域生长法:以局部最高点作为生长种子进行生长
/// 生长方法与一般的区域生长不同:以种子点为圆心作圆周扫描,记录扫描到的边界
/// </summary>
//SG_APISHARED_EXPORT void sg_peakPolarScan(cv::Mat& edgeMask, SVzNL2DPoint a_peak, SSG_polarScanParam polarScanParam, std::vector< SSG_2DValueI>& rgnContour);
//从Peak点进行水平垂直扫描得到区域边界
SG_APISHARED_EXPORT void sg_peakXYScan(
SVzNL3DLaserLine* laser3DPoints,
int lineNum,
cv::Mat& featureEdgeMask,
SSG_2DValueI a_peak,
SSG_treeGrowParam growParam,
SSG_bagParam bagParam,
bool rgnPtAsEdge,
std::vector< SSG_lineConotours>& topContour,
std::vector< SSG_lineConotours>& bottomContour,
std::vector< SSG_lineConotours>& leftContour,
std::vector< SSG_lineConotours>& rightContour,
int* maxEdgeId_top,
int* maxEdgeId_btm,
int* maxEdgeId_left,
int* maxEdgeId_right);
//取出与给定edgeId相同的边界点
SG_APISHARED_EXPORT void sg_getContourPts(
std::vector< SSG_lineConotours>& contour_all,
int vldEdgeId,
std::vector< SSG_2DValueI>& contourFilter,
int* lowLevelFlag);
//从边界点对中取出与给定edgeId相同的边界点
SG_APISHARED_EXPORT void sg_getPairingContourPts(
std::vector<SSG_conotourPair>& contourPairs,
std::vector<SSG_intPair>& idPairs,
std::vector< SSG_conotourPair>& contourFilter,
SVzNLRangeD range,
bool isTBDir,
int* lowLevelFlag_0,
int* lowLevelFlag_1);
//取出最短距离的边界点
SG_APISHARED_EXPORT void sg_contourPostProc(
std::vector< SSG_contourPtInfo>& contour,
int maxEdgeIdx,
double sameConturDistTh,
std::vector< SSG_2DValueI>& contourFilter,
int sideID,
int* blockFlag);
//距离变换
//input, output均为float型
SG_APISHARED_EXPORT void sg_distanceTrans(const cv::Mat input, cv::Mat& output, int distType);
/// <summary>
/// 以5x5方式寻找localPeaks
/// </summary>
/// <param name="input"></param>
/// <param name="peaks"></param>
SG_APISHARED_EXPORT void sg_getLocalPeaks_distTransform(
cv::Mat& input,
std::vector<SSG_2DValueI>& peaks,
SSG_localPkParam searchWin);
/// <summary>
/// 使用模板法提取直角特征
/// 水平向下直角特征拐点左侧deltaZ在一个很小的范围内拐点右侧deltaY在一个很小的范围内
/// </summary>
SG_APISHARED_EXPORT void sg_getLineRigthAngleFeature(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const SSG_lineRightAngleParam templatePara_HF,
const SSG_lineRightAngleParam templatePara_FH,
const SSG_lineRightAngleParam templatePara_HR,
const SSG_lineRightAngleParam templatePara_RH,
SSG_lineFeature* line_features);
//计算扫描ROI
SG_APISHARED_EXPORT SVzNL3DRangeD sg_getScanDataROI(
SVzNL3DLaserLine* laser3DPoints,
int lineNum);
//计算扫描ROI: vecotr格式
SG_APISHARED_EXPORT SVzNL3DRangeD sg_getScanDataROI_vector(
std::vector< std::vector<SVzNL3DPosition>>& scanLines
);
//计算点云ROI: vecotr格式
SG_APISHARED_EXPORT SVzNL3DRangeD wd_getPointCloudROI(
std::vector<SVzNL3DPoint>& scanData);
//计算点云的ROI和scale: vecotr格式
SG_APISHARED_EXPORT SWD_pointCloudPara wd_getPointCloudPara(
std::vector< std::vector<SVzNL3DPosition>>& scanLines);
//XY平面直线拟合
SG_APISHARED_EXPORT void lineFitting(
std::vector< SVzNL3DPoint>& inliers,
double* _k,
double* _b);
//拟合成通用直线方程,包括垂直
SG_APISHARED_EXPORT void lineFitting_abc(
std::vector< SVzNL3DPoint>& inliers,
double* _a,
double* _b,
double* _c);
/**
* @brief 线
* @param points 2
* @param center 线P0
* @param direction 线v
* @return true
*/
SG_APISHARED_EXPORT bool fitLine3DLeastSquares(
const std::vector<SVzNL3DPoint>& points,
SVzNL3DPoint& center,
SVzNL3DPoint& direction);
//圆最小二乘拟合
SG_APISHARED_EXPORT double fitCircleByLeastSquare(
const std::vector<SVzNL3DPoint>& pointArray,
SVzNL3DPoint& center,
double& radius);
//抛物线最小二乘拟合 y=ax^2 + bx + c
SG_APISHARED_EXPORT bool leastSquareParabolaFitEigen(
const std::vector<cv::Point2d>& points,
double& a, double& b, double& c,
double& mse, double& max_err);
//计算Z均值
SG_APISHARED_EXPORT double computeMeanZ(std::vector< SVzNL3DPoint>& pts);
//计算角度差值在0-180度范围
SG_APISHARED_EXPORT double computeAngleDiff(double theta1, double theta2);
//计算直线交点
SG_APISHARED_EXPORT SVzNL3DPoint computeLineCrossPt_abs(
double a1, double b1, double c1,
double a2, double b2, double c2);
//计算过2点的直线方程
SG_APISHARED_EXPORT void compute2ptLine(
SVzNL3DPoint pt1,
SVzNL3DPoint pt2,
double* _a, double* _b, double* _c);
//计算过2点的直线方程,不使用结构体
SG_APISHARED_EXPORT void compute2ptLine_2(
double x1, double y1,
double x2, double y2,
double* _a, double* _b, double* _c);
//旋转45度后的直线方程
SG_APISHARED_EXPORT void rotateLine45Deg(
double _a, double _b, double _c,
double x0, double y0,
double* r_a, double* r_b, double* r_c);
//计算直线角度
SG_APISHARED_EXPORT double getLineAngle(const double _a, const double _b, const double _c);
//计算两点的2D距离
SG_APISHARED_EXPORT double compute2DLen(SVzNL3DPoint pt1, SVzNL3DPoint pt2);
//计算XY平面面的三角形顶角(p0的张角)
SG_APISHARED_EXPORT double computeXOYVertexAngle(SVzNL3DPoint p0, SVzNL3DPoint p1, SVzNL3DPoint p2);
//计算点到直线距离
SG_APISHARED_EXPORT double computePtDistToLine(double x0, double y0, double a, double b, double c);
//计算垂足点直线方程y = kx + b
SG_APISHARED_EXPORT SVzNL2DPointD sx_getFootPoint(
double x0,
double y0,
double k,
double b);
//计算垂足点直线方程ax+by+c = 0
SG_APISHARED_EXPORT SVzNL2DPointD sx_getFootPoint_abc(
double x0,
double y0,
double A,
double B,
double C);
//Bresenham算法
SG_APISHARED_EXPORT void drawLine(
int x0,
int y0,
int x1,
int y1,
std::vector<SVzNL2DPoint>& pts);
/// <summary>
/// 两步法标注
/// </summary>
/// <param name="bwImg"> 目标点为“1” 空白点为“0”</param>
/// <param name="labImg"> 标注结果。每个点为rgnID, ID从2开始 </param>
/// <param name="labelRgns"></param>
SG_APISHARED_EXPORT void SG_TwoPassLabel(
const cv::Mat& bwImg,
cv::Mat& labImg,
std::vector<SSG_Region>& labelRgns,
int connectivity = 4);
//计算面参数: z = Ax + By + C
//res: [0]=A, [1]= B, [2]=-1.0, [3]=C,
SG_APISHARED_EXPORT void vzCaculateLaserPlane(
std::vector<cv::Point3f> Points3ds,
std::vector<double>& res);
//计算一个平面调平参数。
//数据输入中可以有一个地平面和参考调平平面,以最高的平面进行调平
//旋转矩阵为调平参数,即将平面法向调整为垂直向量的参数
SG_APISHARED_EXPORT SSG_planeCalibPara sg_getPlaneCalibPara(
SVzNL3DLaserLine* laser3DPoints,
int lineNum);
SG_APISHARED_EXPORT SSG_planeCalibPara sg_HCameraVScan_getGroundCalibPara(
std::vector< std::vector<SVzNL3DPosition>>& scanLines);
//将一个平面调整为水平平面(z为固定值
SG_APISHARED_EXPORT SSG_planeCalibPara adjustPlaneToXYPlane(
double plane_A, double plane_B, double plane_C //平面法向量
);
//计算一个平面调平参数。
//数据输入中可以有一个地平面和参考调平平面,以最高的平面进行调平
//旋转矩阵为调平参数,即将平面法向调整为垂直向量的参数
SG_APISHARED_EXPORT SSG_planeCalibPara sg_getPlaneCalibPara2(
std::vector< std::vector<SVzNL3DPosition>>& scanLines);
//针对孔板计算一个平面调平参数:提取不经过孔的扫描线进行平面拟合
SG_APISHARED_EXPORT SSG_planeCalibPara sg_getHolePlaneCalibPara(
std::vector< std::vector<SVzNL3DPosition>>& scanLines);
//计算一个平面调平参数。
//以数据输入中ROI以内的点进行平面拟合计算调平参数
//旋转矩阵为调平参数,即将平面法向调整为垂直向量的参数
SG_APISHARED_EXPORT SSG_planeCalibPara sg_getPlaneCalibPara_ROIs(
SVzNL3DLaserLine* laser3DPoints,
int lineNum,
std::vector<SVzNL3DRangeD>& ROIs);
//计算一个平面调平参数。
//以数据输入中ROI以内的点进行平面拟合计算调平参数
//旋转矩阵为调平参数,即将平面法向调整为垂直向量的参数
SG_APISHARED_EXPORT SSG_planeCalibPara sg_getPlaneCalibPara2_ROI(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
SVzNL3DRangeD roi);
//根据两个向量计算旋转矩阵
SG_APISHARED_EXPORT SSG_planeCalibPara wd_computeRTMatrix(
SVzNL3DPoint& vector1,
SVzNL3DPoint& vector2);
// 从旋转矩阵计算欧拉角Z-Y-X顺序
SG_APISHARED_EXPORT SSG_EulerAngles rotationMatrixToEulerZYX(const double R[3][3]);
// 从欧拉角计算旋转矩阵Z-Y-X顺序
SG_APISHARED_EXPORT void eulerToRotationMatrixZYX(const SSG_EulerAngles& angles, double R[3][3]);
//相机姿态调平,并去除地面
///camPoseR为3x3矩阵
SG_APISHARED_EXPORT void lineDataRT(
SVzNL3DLaserLine* a_line,
const double* camPoseR,
double groundH);
SG_APISHARED_EXPORT void lineDataRT_vector(
std::vector< SVzNL3DPosition>& a_line,
const double* camPoseR,
double groundH);
SG_APISHARED_EXPORT void HCamera_lineDataRT_vector(
std::vector< SVzNL3DPosition>& a_line,
const double* camPoseR,
double groundH);
SG_APISHARED_EXPORT void lineDataRT_RGBD(
SVzNLXYZRGBDLaserLine* a_line,
const double* camPoseR,
double groundH);
//基于相邻点的聚类
SG_APISHARED_EXPORT void sg_pointClustering(
std::vector< SVzNL3DPosition>& pts,
double clusterDist,
std::vector<std::vector< SVzNL3DPosition>>& objClusters //result
);
//基于栅格上点的窗口内的相邻点的聚类聚类条件由3D点的邻域决定
//使用vector构成2维结构体数组
SG_APISHARED_EXPORT void wd_gridPointClustering(
std::vector<std::vector<SSG_featureClusteringInfo>>& featureMask,
std::vector<std::vector<SVzNL3DPoint>>& feature3DInfo,
int clusterCheckWin, //搜索窗口
SSG_treeGrowParam growParam,//聚类条件
int clusterID, //当前Cluster的ID
std::vector< SVzNL2DPoint>& a_cluster, //result
SVzNL3DRangeD& clusterRoi //roi3D
);
//对扫描数据的聚类
SG_APISHARED_EXPORT void wd_gridPointClustering_2(
std::vector<std::vector<SVzNL3DPosition>>& gridData,
std::vector<std::vector<SSG_intPair>>& pointMaskInfo, //防止聚类时重复选中
int clusterCheckWin, //搜索窗口
SSG_treeGrowParam growParam,//聚类条件
int clusterID, //当前Cluster的ID
std::vector< SVzNL2DPoint>& a_cluster, //result
SVzNL3DRangeD& clusterRoi //roi3D
);
//使用聚类方法完成8连通连通域分析
SG_APISHARED_EXPORT void wd_gridPointClustering_labelling(
std::vector<std::vector<SSG_featureClusteringInfo>>& featureMask,
std::vector<std::vector<SVzNL3DPoint>>& feature3DInfo,
int clusterID, //当前Cluster的ID
std::vector< SVzNL2DPoint>& a_cluster, //result
SVzNLRect& clusterRoi //roi2D
);
//对栅格化数据进行XY平面上的投影量化并对量化产生的空白点进行插值
SG_APISHARED_EXPORT void pointClout2DProjection(
std::vector< std::vector<SVzNL3DPosition>>& gridScanData,
SVzNLRangeD x_range,
SVzNLRangeD y_range,
double scale,
double cuttingGrndZ,
int edgeSkip,
double inerPolateDistTh, //插值阈值。大于此阈值的不进行量化插值
cv::Mat& projectionData,//投影量化数据初始化为一个极大值1e+6
cv::Mat& backIndexing //标记坐标索引用于回找3D坐标
);
//对栅格化数据进行XY平面上的投影量化Z值保留并对量化产生的空白点进行插值
SG_APISHARED_EXPORT void pointClout2DQuantization(
std::vector< std::vector<SVzNL3DPosition>>& gridScanData,
SVzNLRangeD x_range,
SVzNLRangeD y_range,
double scale,
double cuttingGrndZ,
int edgeSkip,
double inerPolateDistTh, //插值阈值。大于此阈值的不进行量化插值
std::vector<std::vector<double>> quantiData, //量化数据初始化为一个极大值1e+6
std::vector<std::vector<SVzNL2DPoint>> backIndexing //标记坐标索引用于回找3D坐标
);
//分水岭算法
SG_APISHARED_EXPORT void watershed(SWD_waterShedImage& img);
// 根据输入的种子点进行分水岭算法
SG_APISHARED_EXPORT void wd_seedWatershed(
SWD_waterShedImage& img,
std::vector<SSG_2DValueI>& watershedSeeds, //种子点
int maxLevel, //最大水位
int startMakerID //起始Marker的ID
);
//点云滤波预处理
SG_APISHARED_EXPORT void wd_noiseFilter(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const SSG_outlierFilterParam filterParam,
int* errCode);
// Eigen库实现计算从pts1向pts2的旋转平移矩阵
//需要
SG_APISHARED_EXPORT void caculateRT(
const std::vector<cv::Point3d>& pts1,
const std::vector<cv::Point3d>& pts2,
cv::Mat& R, cv::Mat& T,
cv::Point3d& C1, cv::Point3d& C2);
//计算点旋转平移后的位置
SG_APISHARED_EXPORT void pointRT(const cv::Mat& R, const cv::Mat& T,
const cv::Point3d& originPt, const cv::Point3d& rtOriginPT, //RT(旋转平移)前后的质心
const cv::Point3d& pt, cv::Point3d& rtPt); //RT前后的点
//计算点旋转平移后的位置
SG_APISHARED_EXPORT void pointRT_2(const cv::Mat& R, const cv::Mat& T,
const cv::Point3d& pt, cv::Point3d& rtPt); //RT前后的点
SG_APISHARED_EXPORT void pointRotate(const cv::Mat& R,
const cv::Point3d& pt, cv::Point3d& rtPt); //Rotate前后的点
//从欧拉角计算旋转矩阵
SG_APISHARED_EXPORT void WD_EulerRpyToRotation(double rpy[3], double matrix3d[9]);
//从欧拉角计算方向矢量
SG_APISHARED_EXPORT void WD_EulerRpyToDirVectors(double rpy[3], std::vector<cv::Point3d>& dirVectors);

View File

@ -12,6 +12,7 @@
#include <opencv2/opencv.hpp>
#include <Windows.h>
#include <limits>
#include "SG_baseAlgo_Export.h"
typedef struct
{
@ -153,6 +154,12 @@ void _outputWorkpieceInfo(char* fileName, std::vector< WD_workpieceInfo>& workpi
}
sprintf_s(dataStr, 50, " center: (%g, %g, %g)", workpiecePositioning[i].center.x, workpiecePositioning[i].center.y, workpiecePositioning[i].center.z);
sw << dataStr << std::endl;
sprintf_s(dataStr, 50, " x_dir: (%g, %g, %g)", workpiecePositioning[i].x_dir.x, workpiecePositioning[i].x_dir.y, workpiecePositioning[i].x_dir.z);
sw << dataStr << std::endl;
sprintf_s(dataStr, 50, " y_dir: (%g, %g, %g)", workpiecePositioning[i].y_dir.x, workpiecePositioning[i].y_dir.y, workpiecePositioning[i].y_dir.z);
sw << dataStr << std::endl;
sprintf_s(dataStr, 50, " z_dir: (%g, %g, %g)", workpiecePositioning[i].z_dir.x, workpiecePositioning[i].z_dir.y, workpiecePositioning[i].z_dir.z);
sw << dataStr << std::endl;
}
sw.close();
}
@ -368,20 +375,27 @@ void _outputRGBDResult_RGBD(
size = 2;
for (int i = 0; i < objNumber; i++)
{
SVzNL3DPoint dirPt;
dirPt = { workpiecePositioning[i].center.x + workpiecePositioning[i].y_dir.x * 10,
workpiecePositioning[i].center.y + workpiecePositioning[i].y_dir.y * 10,
workpiecePositioning[i].center.z + workpiecePositioning[i].y_dir.z * 10 };
sw << "Poly_" << lineIdx << "_2" << std::endl;
sw << "{" << workpiecePositioning[i].center.x << "," << workpiecePositioning[i].center.y << "," << workpiecePositioning[i].center.z << "}-";
sw << "{0,0}-{0,0}-";
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
sw << "{" << workpiecePositioning[i].y_dir.x << "," << workpiecePositioning[i].y_dir.y << "," << workpiecePositioning[i].y_dir.z << "}-";
sw << "{" << dirPt.x << "," << dirPt.y << "," << dirPt.z << "}-";
sw << "{0,0}-{0,0}-";
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
lineIdx++;
dirPt = { workpiecePositioning[i].center.x + workpiecePositioning[i].z_dir.x * 10,
workpiecePositioning[i].center.y + workpiecePositioning[i].z_dir.y * 10,
workpiecePositioning[i].center.z + workpiecePositioning[i].z_dir.z * 10 };
sw << "Poly_" << lineIdx << "_2" << std::endl;
sw << "{" << workpiecePositioning[i].center.x << "," << workpiecePositioning[i].center.y << "," << workpiecePositioning[i].center.z << "}-";
sw << "{0,0}-{0,0}-";
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
sw << "{" << workpiecePositioning[i].z_dir.x << "," << workpiecePositioning[i].z_dir.y << "," << workpiecePositioning[i].z_dir.z << "}-";
sw << "{" << dirPt.x << "," << dirPt.y << "," << dirPt.z << "}-";
sw << "{0,0}-{0,0}-";
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
lineIdx++;
@ -398,15 +412,17 @@ void _outputRGBDResult_RGBD(
lineIdx++;
}
}
SVzNL3DPoint dirPt = { workpiecePositioning[0].center.x + workpiecePositioning[0].y_dir.x * 10,
workpiecePositioning[0].center.y + workpiecePositioning[0].y_dir.y * 10,
workpiecePositioning[0].center.z + workpiecePositioning[0].y_dir.z * 10 };
sw << "Poly_" << lineIdx << "_2" << std::endl;
sw << "{" << workpiecePositioning[0].center.x << "," << workpiecePositioning[0].center.y << "," << workpiecePositioning[0].center.z << "}-";
sw << "{0,0}-{0,0}-";
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
sw << "{" << workpiecePositioning[0].y_dir.x << "," << workpiecePositioning[0].y_dir.y << "," << workpiecePositioning[0].y_dir.z << "}-";
sw << "{" << dirPt.x << "," << dirPt.y << "," << dirPt.z << "}-";
sw << "{0,0}-{0,0}-";
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
sw.close();
}
@ -423,7 +439,8 @@ SVzNL3DPoint _pointRT(SVzNL3DPoint& origin, const double* R, const double* T)
}
#define TEST_COMPUTE_CALIB_PARA 0
#define TEST_COMPUTE_HOLE 1
#define TEST_COMPUTE_HOLE 0
#define TEST_COMPUTE_RT 1
#define TEST_GROUP 1
int main()
{
@ -433,15 +450,107 @@ int main()
};
SVzNLRange fileIdx[TEST_GROUP] = {
{1,4},
{6,6},
};
const char* ver = wd_workpieceHolePositioningVersion();
printf("ver:%s\n", ver);
#if TEST_COMPUTE_RT
std::vector<cv::Point3d> pts_eye;
pts_eye.resize(6);
std::vector<cv::Point3d> pts_robot;
pts_robot.resize(6);
pts_eye[0] = { 187.22, -99.43, 1797.70 }; //, R = 0.6413, P = 0.0302, Y = -91.1494
pts_eye[1] = { 186.50, -140.52, 1797.69 }; // R = 0.6413, P = 0.0302, Y = -88.8130
pts_eye[2] = { 256.73, -93.55, 1797.83 }; //, R = 0.6413, P = 0.0302, Y = -89.3432
pts_eye[3] = { 236.69, -48.82, 1797.77 }; // R = 0.6413, P = 0.0302, Y = -89.5285
pts_eye[4] = { 173.62, 13.92, 1797.30 };// R = 0.6413, P = 0.0302, Y = -89.6450
pts_eye[5] = { 232.11, -153.38, 1797.88 };// R = 0.6413, P = 0.0302, Y = -82.0278
//A:绕z, B绕y C:绕x
pts_robot[0] = { 55.830, -984.472, 97.687 }; // A:0.018 B : -0.638 C : -0.292
pts_robot[1] = { 95.469, -985.345, 97.782 }; // A:-1.032 B : -0.354 C : 1.001
pts_robot[2] = { 47.908, -1053.709, 97.802 }; // A:-1.032 B : -0.356 C : 0.998
pts_robot[3] = { 3.646, -1033.152, 96.494 }; // A:-1.028 B : -0.367 C : 0.442
pts_robot[4] = { -58.843, -969.793, 95.297 }; // A:-1.029 B : -0.367 C : 0.442
pts_robot[5] = { 108.031, -1029.803, 98.333 }; // A:-9.208 B : -0.367 C : 0.442
//使用前6组数据
std::vector<cv::Point3d> test_pts_eye;
std::vector<cv::Point3d> test_pts_robot;
for (int i = 0; i < 5; i++)
{
test_pts_eye.push_back(pts_eye[i]);
test_pts_robot.push_back(pts_robot[i]);
}
//对空间两组对应点计算旋转平移矩阵
// Eigen库实现
cv::Mat R, T;
cv::Point3d C_eye, C_robot;
caculateRT(
test_pts_eye,
test_pts_robot,
R, T,
C_eye, C_robot);
std::cout << "方向向量转换结果:" << std::endl;
std::cout << std::fixed << std::setprecision(6); // 固定小数位数为6
std::cout << R << std::endl;
std::cout << T << std::endl;
//验算6轴姿态
std::vector<cv::Point3d> verify_pts_eye;
verify_pts_eye.insert(verify_pts_eye.end(), pts_eye.begin(), pts_eye.end());
cv::Point3d a_center = { 232.997, -173.533, 1795.9 };
verify_pts_eye.push_back(a_center);
std::vector<std::vector< cv::Point3d>> pose_eye;
pose_eye.resize(7);
for (int i = 0; i < 7; i++)
pose_eye[i].resize(3);
pose_eye[0][0] = { -0.020, -1.000, -0.011 }; pose_eye[0][1] = { 1.000, -0.020, -0.001 }; pose_eye[0][2] = { 0.001, -0.011, 1.000 };
pose_eye[1][0] = { 0.021,-1.000,-0.011 }; pose_eye[1][1] = { 1.000,0.021,-0.000 }; pose_eye[1][2] = { 0.001,-0.011,1.000 };
pose_eye[2][0] = { 0.011,-1.000,-0.011 }; pose_eye[2][1] = { 1.000,0.011,-0.000 }; pose_eye[2][2] = { 0.001,-0.011,1.000 };
pose_eye[3][0] = { 0.008,-1.000,-0.011 }; pose_eye[3][1] = { 1.000,0.008,-0.000 }; pose_eye[3][2] = { 0.001,-0.011,1.000 };
pose_eye[4][0] = { 0.006,-1.000,-0.011 }; pose_eye[4][1] = { 1.000,0.006,-0.000 }; pose_eye[4][2] = { 0.001,-0.011,1.000 };
pose_eye[5][0] = { 0.139,-0.990,-0.011 }; pose_eye[5][1] = { 0.990,0.139,0.001 }; pose_eye[5][2] = { 0.001,-0.011,1.000 };
pose_eye[6][0] = { 0.136746, -0.990563, -0.00926168 }; pose_eye[6][1] = { 0.990606, 0.136747, 0.000517805 }; pose_eye[6][2] = { 0.000753588, -0.00924548, 0.999957 };
for (int i = 0; i < 7; i++)
{
cv::Point3d rtPt;
pointRT_2(R, T, verify_pts_eye[i], rtPt); //RT前后的点
std::vector<cv::Point3d> dirVectors_eye = pose_eye[i];
//dirVectors_eye[0] = { -dirVectors_eye[0].x, -dirVectors_eye[0].y, -dirVectors_eye[0].z };
dirVectors_eye[1] = { -dirVectors_eye[1].x, -dirVectors_eye[1].y, -dirVectors_eye[1].z };
dirVectors_eye[2] = { -dirVectors_eye[2].x, -dirVectors_eye[2].y, -dirVectors_eye[2].z };
std::vector<cv::Point3d> dirVectors_robot;
for (int j = 0; j < 3; j++)
{
cv::Point3d rt_pt;
pointRotate(R, dirVectors_eye[j], rt_pt);
dirVectors_robot.push_back(rt_pt);
}
//生成旋转矩阵
double R_pose[3][3];
R_pose[0][0] = dirVectors_robot[0].x;
R_pose[0][1] = dirVectors_robot[1].x;
R_pose[0][2] = dirVectors_robot[2].x;
R_pose[1][0] = dirVectors_robot[0].y;
R_pose[1][1] = dirVectors_robot[1].y;
R_pose[1][2] = dirVectors_robot[2].y;
R_pose[2][0] = dirVectors_robot[0].z;
R_pose[2][1] = dirVectors_robot[1].z;
R_pose[2][2] = dirVectors_robot[2].z;
SSG_EulerAngles test_rpy = rotationMatrixToEulerZYX(R_pose);
std::cout << i << ":" << std::endl;
std::cout << rtPt.x << "," << rtPt.y << "," << rtPt.z << std::endl;
std::cout << test_rpy.roll << "," << test_rpy.pitch << "," << test_rpy.yaw << std::endl;
}
#endif
#if TEST_COMPUTE_CALIB_PARA
char _calib_datafile[256];
sprintf_s(_calib_datafile, "%sLaserData_ground.txt", dataPath[0]);
sprintf_s(_calib_datafile, "%sLaserData_ground2.txt", dataPath[0]);
int lineNum = 0;
float lineV = 0.0f;
int dataCalib = 0;
@ -471,7 +580,7 @@ int main()
sprintf_s(_out_file, "%sscanData_ground_calib_verify.txt", dataPath[0]);
int headNullLines = 0;
_outputScanDataFile_vector(_out_file, scanData, false, &headNullLines);
#if 0
for (int fidx = fileIdx[0].nMin; fidx <= fileIdx[0].nMax; fidx++)
{
//fidx =4;
@ -491,6 +600,7 @@ int main()
int headNullLines = 0;
_outputScanDataFile_vector(_scan_file, scanLines, false, &headNullLines);
}
#endif
printf("%s: calib done!\n", _calib_datafile);
}
#endif