feat: 카메라 각도(D70510) 기반 Laser 발진 offset 분기
각도에 따라 발진 위치가 달라져, Front/Down angle 평균을 임계값으로 Front/Down offset을 자동 선택해 적용(자동·수동 발진 공통). - LaserInterlock: Horizontal/Vertical → Front/Down angle 로 변경 - SystemParameter: CellCenterOffset X/Y → Front/Down offset X/Y (4개) - LaserAngleOffsetSelector 헬퍼 신규 + 선택값 LOG - PLC 미연결 시 Front offset fallback 주의: 설정 프로퍼티명 변경으로 기존 저장값 자동 이관 안 됨(재입력 필요) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2684685014
commit
2871db6e8f
@ -106,6 +106,7 @@
|
|||||||
<Compile Include="EquipmentSetup.cs" />
|
<Compile Include="EquipmentSetup.cs" />
|
||||||
<Compile Include="Models\EquipmentSpecificModel_PTOP.cs" />
|
<Compile Include="Models\EquipmentSpecificModel_PTOP.cs" />
|
||||||
<Compile Include="Recipes\RMSHandlerForPTOP.cs" />
|
<Compile Include="Recipes\RMSHandlerForPTOP.cs" />
|
||||||
|
<Compile Include="Sequences\LaserAngleOffsetSelector.cs" />
|
||||||
<Compile Include="Sequences\Manual\SeqComponentTeachingRun.cs" />
|
<Compile Include="Sequences\Manual\SeqComponentTeachingRun.cs" />
|
||||||
<Compile Include="Sequences\Monitoring\SeqAlarmMonitor.cs" />
|
<Compile Include="Sequences\Monitoring\SeqAlarmMonitor.cs" />
|
||||||
<Compile Include="Sequences\Monitoring\SeqStatusMonitor.cs" />
|
<Compile Include="Sequences\Monitoring\SeqStatusMonitor.cs" />
|
||||||
|
|||||||
@ -0,0 +1,46 @@
|
|||||||
|
using Aurora.Api;
|
||||||
|
using ILSOS_PrismaticRD_PTOP.Device;
|
||||||
|
using ILSOS_PrismaticRD_PTOP.Settings;
|
||||||
|
using static ILSOS_PrismaticRD_PTOP.Device.PLCControllerPTOP;
|
||||||
|
using SystemParameter = WeldingCommon.Settings.SystemParameter;
|
||||||
|
|
||||||
|
namespace ILSOS_PrismaticRD_PTOP.Sequences
|
||||||
|
{
|
||||||
|
// 카메라 헤드 각도(D70510)에 따라 Front/Down 발진 offset 을 선택한다.
|
||||||
|
// thetaDeg = D70510 / PlcScale 가 (Front angle + Down angle)/2 보다 크면 Down, 아니면 Front.
|
||||||
|
// PLC 미연결/미활성으로 각도를 못 읽으면 Front offset 적용(fallback).
|
||||||
|
internal static class LaserAngleOffsetSelector
|
||||||
|
{
|
||||||
|
public static (double x, double y) Select(
|
||||||
|
PLCControllerPTOP plc, SystemParameter sp, LaserInterlockSetting setting, Logger log, string tag)
|
||||||
|
{
|
||||||
|
double frontAngle = setting != null ? setting.HeadThetaFront : 0;
|
||||||
|
double downAngle = setting != null ? setting.HeadThetaDown : 90;
|
||||||
|
double avg = (frontAngle + downAngle) / 2.0;
|
||||||
|
double scale = (setting != null && setting.HeadThetaPlcScale != 0) ? setting.HeadThetaPlcScale : 1;
|
||||||
|
|
||||||
|
bool useDown = false;
|
||||||
|
double thetaDeg = double.NaN;
|
||||||
|
if (plc != null && plc.IsActivated)
|
||||||
|
{
|
||||||
|
short raw = plc.ReadWord(eWordInput.HEAD_THETA_POSITION).data; // D70510
|
||||||
|
thetaDeg = raw / scale;
|
||||||
|
useDown = thetaDeg > avg; // 평균보다 높으면 Down
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.W($"[{tag}] 각도 offset: PLC 미연결/미활성 → Front offset 적용(fallback)");
|
||||||
|
}
|
||||||
|
|
||||||
|
double selX = useDown ? sp.DownOffsetX : sp.FrontOffsetX;
|
||||||
|
double selY = useDown ? sp.DownOffsetY : sp.FrontOffsetY;
|
||||||
|
|
||||||
|
log.I($"[{tag}] 각도 offset 선택: D70510={thetaDeg:F1}deg, avg={avg:F1} " +
|
||||||
|
$"(FrontAngle={frontAngle}, DownAngle={downAngle}) → {(useDown ? "Down" : "Front")} 적용 | " +
|
||||||
|
$"FrontOffset=({sp.FrontOffsetX},{sp.FrontOffsetY}) DownOffset=({sp.DownOffsetX},{sp.DownOffsetY}) " +
|
||||||
|
$"→ 적용=({selX},{selY})");
|
||||||
|
|
||||||
|
return (selX, selY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,6 +17,7 @@ using GUI;
|
|||||||
using Hermes;
|
using Hermes;
|
||||||
using ILSOS_PrismaticRD_PTOP.Device;
|
using ILSOS_PrismaticRD_PTOP.Device;
|
||||||
using ILSOS_PrismaticRD_PTOP.Device.IO;
|
using ILSOS_PrismaticRD_PTOP.Device.IO;
|
||||||
|
using ILSOS_PrismaticRD_PTOP.Settings;
|
||||||
using Matrox.MatroxImagingLibrary;
|
using Matrox.MatroxImagingLibrary;
|
||||||
using OpenCvSharp.Dnn;
|
using OpenCvSharp.Dnn;
|
||||||
using System;
|
using System;
|
||||||
@ -502,21 +503,23 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences
|
|||||||
{
|
{
|
||||||
Status.SetWorkingPosition(eWorkingPosition.UPLOADING_WELD_LIST);
|
Status.SetWorkingPosition(eWorkingPosition.UPLOADING_WELD_LIST);
|
||||||
|
|
||||||
|
// 각도(D70510) 기반 Front/Down offset 선택 (기존 단일 CellCenterOffset 대체)
|
||||||
|
var thetaOffsetSetting = context.GetSetting<LaserInterlockSetting>(SettingIds.LaserInterlock);
|
||||||
|
var (angleOffX, angleOffY) = LaserAngleOffsetSelector.Select(_plc, _SystemParameter, thetaOffsetSetting, Log, "ComponentTeachingRun");
|
||||||
|
|
||||||
// TeachingRun에는 용접 시퀀스만 정의해서 Auto Run에서도 사용하므로 Offset 넣어줘야 함
|
// TeachingRun에는 용접 시퀀스만 정의해서 Auto Run에서도 사용하므로 Offset 넣어줘야 함
|
||||||
SeqUploadWeldingList_InitParam seqParams = new SeqUploadWeldingList_InitParam() {
|
SeqUploadWeldingList_InitParam seqParams = new SeqUploadWeldingList_InitParam() {
|
||||||
NeedSetLaserTiming = _InitParam.NeedSetLaserTiming,
|
NeedSetLaserTiming = _InitParam.NeedSetLaserTiming,
|
||||||
IncludeListStartEnd = true,
|
IncludeListStartEnd = true,
|
||||||
CellMeasureCenterOffsetX = _SystemParameter.CellPosMeasureCenterOffsetX + _InitParam.CellCenterOffsetX,
|
CellMeasureCenterOffsetX = angleOffX + _InitParam.CellCenterOffsetX,
|
||||||
CellMeasureCenterOffsetY = _SystemParameter.CellPosMeasureCenterOffsetY + _InitParam.CellCenterOffsetY,
|
CellMeasureCenterOffsetY = angleOffY + _InitParam.CellCenterOffsetY,
|
||||||
SelectedDrawObjectList = _DivideObjectsQueue.Peek(),
|
SelectedDrawObjectList = _DivideObjectsQueue.Peek(),
|
||||||
WeldingScannerListNo = _WeldingScannerListNo,
|
WeldingScannerListNo = _WeldingScannerListNo,
|
||||||
};
|
};
|
||||||
|
|
||||||
var _PageNo = _SequenceSetting.LastPageNo;
|
var _PageNo = _SequenceSetting.LastPageNo;
|
||||||
|
|
||||||
Log.W($"page {_PageNo}({Status.GetProcessingWeldingData().CellId}) : Cell Offset Value Check ; X : {_SystemParameter.CellPosMeasureCenterOffsetX} + {_InitParam.CellCenterOffsetX} ; Y : {_SystemParameter.CellPosMeasureCenterOffsetY} + {_InitParam.CellCenterOffsetY}");
|
Log.D(string.Format($"[ComponentTeachingRun] Set UploadWelindList Seq Param (page {_PageNo}, Cell {Status.GetProcessingWeldingData().CellId})"));
|
||||||
|
|
||||||
Log.D(string.Format($"[ComponentTeachingRun] Set UploadWelindList Seq Param"));
|
|
||||||
|
|
||||||
return SequenceAction.Ok(seqParams);
|
return SequenceAction.Ok(seqParams);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,14 +636,17 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
// TeachingRun 에서는 CellMeasureCenterOffset 사용하지 않음. 0 으로 넘기자
|
// 각도(D70510) 기반 Front/Down offset 선택 (기존 단일 CellCenterOffset 대체)
|
||||||
|
var thetaOffsetSetting = Application.Current.AuroraEnv().SettingEntry[SettingIds.LaserInterlock] as LaserInterlockSetting;
|
||||||
|
var (angleOffX, angleOffY) = LaserAngleOffsetSelector.Select(_PLC, _SystemParameter, thetaOffsetSetting, Log, "ExecuteWelding");
|
||||||
|
|
||||||
SeqUploadWeldingList_InitParam seqParams = new SeqUploadWeldingList_InitParam()
|
SeqUploadWeldingList_InitParam seqParams = new SeqUploadWeldingList_InitParam()
|
||||||
{
|
{
|
||||||
NeedSetLaserTiming = _InitParam.NeedSetLaserTiming,
|
NeedSetLaserTiming = _InitParam.NeedSetLaserTiming,
|
||||||
IncludeListStartEnd = true,
|
IncludeListStartEnd = true,
|
||||||
CellMeasureCenterOffsetX = _SystemParameter.CellPosMeasureCenterOffsetX + _InitParam.CellCenterOffsetX,
|
CellMeasureCenterOffsetX = angleOffX + _InitParam.CellCenterOffsetX,
|
||||||
CellMeasureCenterOffsetY = _SystemParameter.CellPosMeasureCenterOffsetY + _InitParam.CellCenterOffsetY,
|
CellMeasureCenterOffsetY = angleOffY + _InitParam.CellCenterOffsetY,
|
||||||
SelectedDrawObjectList = _SelectedObjectList, // 이게 중요 포인트
|
SelectedDrawObjectList = _SelectedObjectList, // 이게 중요 포인트
|
||||||
WeldingScannerListNo = _WeldingScannerListNo,
|
WeldingScannerListNo = _WeldingScannerListNo,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -761,12 +764,12 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences
|
|||||||
short thetaRaw = _PLC.ReadWord(eWordInput.HEAD_THETA_POSITION).data;
|
short thetaRaw = _PLC.ReadWord(eWordInput.HEAD_THETA_POSITION).data;
|
||||||
double scale = thetaSetting.HeadThetaPlcScale != 0 ? thetaSetting.HeadThetaPlcScale : 1; // 0 가드
|
double scale = thetaSetting.HeadThetaPlcScale != 0 ? thetaSetting.HeadThetaPlcScale : 1; // 0 가드
|
||||||
double thetaDeg = thetaRaw / scale;
|
double thetaDeg = thetaRaw / scale;
|
||||||
bool okH = Math.Abs(thetaDeg - thetaSetting.HeadThetaHorizontal) <= thetaSetting.HeadThetaTolerance;
|
bool okFront = Math.Abs(thetaDeg - thetaSetting.HeadThetaFront) <= thetaSetting.HeadThetaTolerance;
|
||||||
bool okV = Math.Abs(thetaDeg - thetaSetting.HeadThetaVertical) <= thetaSetting.HeadThetaTolerance;
|
bool okDown = Math.Abs(thetaDeg - thetaSetting.HeadThetaDown) <= thetaSetting.HeadThetaTolerance;
|
||||||
Log.I($"[ExecuteWelding] HeadTheta raw={thetaRaw}, deg={thetaDeg:F1} (H={thetaSetting.HeadThetaHorizontal}/V={thetaSetting.HeadThetaVertical}/±{thetaSetting.HeadThetaTolerance})");
|
Log.I($"[ExecuteWelding] HeadTheta raw={thetaRaw}, deg={thetaDeg:F1} (Front={thetaSetting.HeadThetaFront}/Down={thetaSetting.HeadThetaDown}/±{thetaSetting.HeadThetaTolerance})");
|
||||||
if (!okH && !okV)
|
if (!okFront && !okDown)
|
||||||
{
|
{
|
||||||
Log.W($"[ExecuteWelding] HeadTheta 인터락 → 수평/수직 ±{thetaSetting.HeadThetaTolerance}° 벗어남(deg={thetaDeg:F1}) → 발진 차단");
|
Log.W($"[ExecuteWelding] HeadTheta 인터락 → Front/Down ±{thetaSetting.HeadThetaTolerance}° 벗어남(deg={thetaDeg:F1}) → 발진 차단");
|
||||||
blocked = true;
|
blocked = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,14 +16,14 @@ namespace ILSOS_PrismaticRD_PTOP.Settings
|
|||||||
[Browsable(true)]
|
[Browsable(true)]
|
||||||
[ReadOnly(false)]
|
[ReadOnly(false)]
|
||||||
[Category("Head Theta Interlock")]
|
[Category("Head Theta Interlock")]
|
||||||
[DisplayName("Horizontal Angle (deg)")]
|
[DisplayName("Front Angle (deg)")]
|
||||||
public double HeadThetaHorizontal { get; set; } = 0;
|
public double HeadThetaFront { get; set; } = 0;
|
||||||
|
|
||||||
[Browsable(true)]
|
[Browsable(true)]
|
||||||
[ReadOnly(false)]
|
[ReadOnly(false)]
|
||||||
[Category("Head Theta Interlock")]
|
[Category("Head Theta Interlock")]
|
||||||
[DisplayName("Vertical Angle (deg)")]
|
[DisplayName("Down Angle (deg)")]
|
||||||
public double HeadThetaVertical { get; set; } = 90;
|
public double HeadThetaDown { get; set; } = 90;
|
||||||
|
|
||||||
[Browsable(true)]
|
[Browsable(true)]
|
||||||
[ReadOnly(false)]
|
[ReadOnly(false)]
|
||||||
|
|||||||
@ -387,13 +387,23 @@ namespace WeldingCommon.Settings
|
|||||||
[Browsable(true)]
|
[Browsable(true)]
|
||||||
[ReadOnly(false)]
|
[ReadOnly(false)]
|
||||||
[Category("CellPosMeasure")]
|
[Category("CellPosMeasure")]
|
||||||
[DisplayName("CellCenterOffsetX")]
|
[DisplayName("Front Offset X")]
|
||||||
public double CellPosMeasureCenterOffsetX { get; set; } = 0;
|
public double FrontOffsetX { get; set; } = 0;
|
||||||
[Browsable(true)]
|
[Browsable(true)]
|
||||||
[ReadOnly(false)]
|
[ReadOnly(false)]
|
||||||
[Category("CellPosMeasure")]
|
[Category("CellPosMeasure")]
|
||||||
[DisplayName("CellCenterOffsetY")]
|
[DisplayName("Front Offset Y")]
|
||||||
public double CellPosMeasureCenterOffsetY { get; set; } = 0;
|
public double FrontOffsetY { get; set; } = 0;
|
||||||
|
[Browsable(true)]
|
||||||
|
[ReadOnly(false)]
|
||||||
|
[Category("CellPosMeasure")]
|
||||||
|
[DisplayName("Down Offset X")]
|
||||||
|
public double DownOffsetX { get; set; } = 0;
|
||||||
|
[Browsable(true)]
|
||||||
|
[ReadOnly(false)]
|
||||||
|
[Category("CellPosMeasure")]
|
||||||
|
[DisplayName("Down Offset Y")]
|
||||||
|
public double DownOffsetY { get; set; } = 0;
|
||||||
|
|
||||||
[Category("Transfer")]
|
[Category("Transfer")]
|
||||||
[ReadOnly(false)]
|
[ReadOnly(false)]
|
||||||
|
|||||||
18
docs/작업일지/2026_06_24.md
Normal file
18
docs/작업일지/2026_06_24.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# 작업일지 — 2026-06-24
|
||||||
|
|
||||||
|
## 각도(D70510) 기반 Laser 발진 Offset 분기 구현
|
||||||
|
|
||||||
|
- **배경**: D70510(카메라 헤드 각도, Head_Theta_Position)에 따라 Laser 발진 위치가 달라지는데, 기존엔 단일 Cell center offset 하나만 적용해 각도 변화 시 위치가 어긋남. → 각도에 따라 Front/Down 두 벌의 offset을 자동 선택 적용하도록 변경.
|
||||||
|
- **선택 로직**: `thetaDeg = D70510 / HeadThetaPlcScale` vs `avg = (Front angle + Down angle)/2`.
|
||||||
|
- `thetaDeg > avg` → **Down offset**, 이하 → **Front offset** 적용.
|
||||||
|
- PLC 미연결/미활성으로 각도 못 읽으면 **Front offset** 적용(fallback) + 경고 로그.
|
||||||
|
- **적용 범위**: 자동(`SeqAutoRunPTOP.SetUploadWeldingList_SeqParams`)·수동(`SeqComponentTeachingRun.SetUploadWeldingList_SeqParams`) 발진 모두. 발진 offset = `선택 Front/Down offset + 비전 offset(_InitParam.CellCenterOffset)` — 기존 SystemParameter 단일 offset을 **대체**.
|
||||||
|
- **변경 파일**:
|
||||||
|
- `Settings/LaserInterlockSetting.cs`: `HeadThetaHorizontal/Vertical` → `HeadThetaFront/Down` (DisplayName "Front/Down Angle (deg)")
|
||||||
|
- `WeldingCommon/Settings/SystemParameter.cs`: `CellPosMeasureCenterOffsetX/Y`(2개) → `FrontOffsetX/Y` + `DownOffsetX/Y`(4개)
|
||||||
|
- `Sequences/LaserAngleOffsetSelector.cs`(신규): 각도 비교 → offset 선택 + 로그 헬퍼
|
||||||
|
- `Sequences/SeqAutoRunPTOP.cs` / `Sequences/Manual/SeqComponentTeachingRun.cs`: 헬퍼 호출로 offset 대체. (자동 인터락 참조·로그 라벨도 Horizontal/Vertical → Front/Down)
|
||||||
|
- `ILSOS_PrismaticRD_PTOP.csproj`: 신규 헬퍼 Compile Include 등록
|
||||||
|
- **LOG**: 발진 시 Front/Down angle, Front/Down offset(x,y), 선택 결과를 한 줄로 기록.
|
||||||
|
- **검증**: Debug|x64 컴파일 통과. sub agent 점검 완료(Critical 없음, 옛 이름 잔존 참조 0건, 공유 모듈 부작용 없음).
|
||||||
|
- **⚠ 배포 전 주의**: 설정 클래스 프로퍼티명 rename으로 **기존 저장 설정값이 자동 이관되지 않음**(Front/Down offset·angle 기본값으로 리셋). 현장 적용 전 기존 보정값을 신규 Front/Down 필드에 **수동 재입력** 필요.
|
||||||
Loading…
x
Reference in New Issue
Block a user