LGES-OC5-PtoP-Welder/Doc/인수인계/260907_LaserFrequency_단위계통_분석.md
NohDoyeon241104 a3238cc2d6 docs: 인수인계 산출물 일괄 커밋 (260903~260916)
그동안 미추적으로 쌓여 있던 인수인계 문서를 정리해 올린다.

260903 Door 인터락 현장검증 가이드 / 파워캘 3000W 오적용 환산

260907 Arc 출사 파라미터 시각화 / LaserFrequency 단위계통 / NRE 위험구간 점검 / 레시피화면 사용설명서

260915 레시피화면 작업매뉴얼

260916 PTOP 프로그램 사용설명서 / 자동운전 전환 사전점검 현장용

작업 중 임시본(_backup)은 제외했다.
2026-09-16 17:10:50 +09:00

192 lines
8.8 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Laser Frequency / Pulse Duration 단위 계통 분석
- 작성 : 2026-09-07
- 계기 : 현장 Arc 출사 이상 → 조치 후 정상. 실제 원인은 **Frequency 값 오입력**으로 확인됨.
그 값이 어떤 경로로 스캐너에 들어가는지 역추적한 결과.
- 범위 : **정적 분석 전용. 소스 미수정. 현장 검증 미실시.**
- 결론 요약 : 화면값 → RTC 전달 경로에 **단위/캐스팅 문제 3건 + 적용 범위 문제 1건 + 크래시 경로 1건**이 있습니다.
---
## 1. 값이 흘러가는 경로
```
Scanner Parameter 화면
Laser Frequency(kHz) double, 범위 0.2 ~ 5 ScannerParam.cs:142
Laser Pulse Duration(ms) double ScannerParam.cs:164
│
▼ SeqUploadWeldingList.InitializeDeviceStatus() :206-212
IDrawObject firstObject = _InitParam.SelectedDrawObjectList[0] as IDrawObject;
int firstObjectScannerNo = firstObject.ScannerParamID - 1;
_ScannerParam = ScannerCanvasUserCtrl.ScannerParams[firstObjectScannerNo];
_Scanner.SetLaserTiming( (uint)_ScannerParam.LaserFrequency,
_ScannerParam.LaserPulseDuration,
_ScannerParam.LaserPulseDuration );
│
▼ Scanner.cs:163
SetLaserTiming(uint periodHz, double laser1PulseWidthUs, double laser2PulseWidthUs)
│
▼ ScannerRTC6.cs:772-784 (ScannerRTC5.cs:770-782 동일)
//uint halfPeriodBit = (32 / periodkHz) * 1000; ← 구버전(주석 처리됨)
uint halfPeriodBit = (uint)((32.0 / (double)periodHz) * 1000000.0);
uint laser1PulseWidthBit = (uint)(laser1PulseWidthUs * 64);
uint laser2PulseWidthBit = (uint)(laser2PulseWidthUs * 64);
RTC6Wrap.n_set_laser_timing(dev, halfPeriodBit, laser1PulseWidthBit, laser2PulseWidthBit, 1);
```
기준 단위 : RTC의 half period·pulse width는 **1/64 µs (15.625 ns)** 단위.
펄스폭 변환이 `× 64` 인 것으로 이 기준이 확인됩니다.
---
## 2. 발견 사항
### ① `(uint)` 캐스팅이 소수점을 버립니다 — `SeqUploadWeldingList.cs:211`
`LaserFrequency` 는 **double** 이고 화면 설명상 범위가 **0.2 ~ 5 kHz** 입니다.
그런데 전달 시 `(uint)` 로 캐스팅합니다.
| 화면 입력 | `(uint)` 후 | 비고 |
|---|---|---|
| 0.2 | **0** | 0 이 되어 아래 ②에서 0 나눗셈 |
| 0.5 | **0** | 〃 |
| 2.5 | 2 | 0.5 손실 |
| 4.9 | 4 | 0.9 손실 |
| 5 | 5 | 정상 |
**화면에서는 소수 입력이 가능한데 전달 과정에서 정수로 잘립니다.**
설명상 범위(0.2~5)의 상당 부분이 0 또는 절삭값이 됩니다.
### ② 0 이 들어가면 0 으로 나눕니다 — `ScannerRTC6.cs:775`
```csharp
uint halfPeriodBit = (uint)((32.0 / (double)periodHz) * 1000000.0);
```
`periodHz == 0` 이면 `32.0 / 0` = `+Infinity` → `(uint)+Infinity` 는 **C# 명세상 정의되지 않은 동작**입니다.
(x64 실측 동작은 0) 예외는 발생하지 않고 **조용히 엉뚱한 값**이 RTC로 넘어갑니다.
### ③ 환산식이 1000배 큽니다 — `ScannerRTC6.cs:775` / `ScannerRTC5.cs:773`
정확한 환산 (f 단위 kHz, 결과 단위 1/64 µs) :
```
반주기[µs] = 1 / (2 · f[kHz] · 1000) · 1e6 = 500 / f
halfPeriodBit = 반주기[µs] × 64 = 32000 / f
```
| 구분 | 식 | f=5 일 때 |
|---|---|---|
| 정확값 | `32000 / f` | 6,400 |
| **현재 코드** | `(32.0 / f) × 1,000,000` | **6,400,000** |
| 바로 위 주석(구버전) | `(32 / f) × 1000` | 6,000 ※정수나눗셈 절삭은 있으나 자릿수는 맞음 |
**현재 코드는 정확값의 1000배**입니다. 주석 처리된 구버전이 자릿수상 맞는 식이었습니다.
결과적으로 **화면에 kHz 로 넣은 값이 실제로는 Hz 수준으로 동작**합니다.
| 화면 입력 | 의도 | 실제 (현재 코드) |
|---|---|---|
| 1 kHz | 1,000 Hz | 1 Hz |
| 2.5 kHz | 2,500 Hz | 2 Hz (①의 절삭까지 겹침) |
| 5 kHz | 5,000 Hz | 5 Hz |
### ④ Pulse Duration 이 ms → µs 변환 없이 전달됩니다 — `SeqUploadWeldingList.cs:211`
- 화면 항목 : `Laser Pulse Duration(ms)` — **ms**
- 받는 파라미터 : `laser1PulseWidthUs` — **µs**
- 활성 코드 : `_ScannerParam.LaserPulseDuration` **그대로 전달** (변환 없음)
- 같은 파일에 주석 처리된 코드(`:628`) : `(uint)(param.LaserPulseDuration * 1000)` ← **ms→µs 변환이 있었음**
| 화면 입력 | 의도 (ms→µs) | 실제 |
|---|---|---|
| 0.1 ms | 100 µs → 6,400 bit | 0.1 → **6 bit ≈ 0.094 µs** |
| 1.0 ms | 1,000 µs → 64,000 bit | 1.0 → **64 bit = 1 µs** |
**1000배 작게** 전달됩니다.
### ⑤ 첫 번째 객체의 값만 적용됩니다 — `SeqUploadWeldingList.cs:206-212`
`SetLaserTiming` 은 **시퀀스당 1회**, `SelectedDrawObjectList[0]` 의 Parameter ID 값으로만 호출됩니다.
리스트가 아니라 COMMAND 방식이라 리스트 실행 전에 한 번 세팅되고 끝입니다.
→ **Parameter ID 2, 3 … 의 `Laser Frequency` · `Laser Pulse Duration` 은 무시됩니다.**
  (`Laser Power`, `Mark Speed`, `Delay` 등은 객체마다 리스트에 들어가므로 정상 적용됩니다)
현장에서 "ID 마다 Frequency 를 다르게 넣었는데 차이가 없다" 면 이것이 원인입니다.
### ⑥ 크래시 경로 — `SeqUploadWeldingList.cs:207-209`
```csharp
int firstObjectScannerNo = firstObject.ScannerParamID - 1;
_ScannerParam = ScannerCanvasUserCtrl.ScannerParams[firstObjectScannerNo];
```
`ScannerParamID` 가 0 이면 인덱스 **-1** → `IndexOutOfRangeException`.
`SelectedDrawObjectList` 가 비어 있으면 `[0]` 에서 예외.
둘 다 방어 코드가 없습니다. (2026-09-07 추가한 예외처리는 `FuncLoadDrawObjectScannerParamSelected` 쪽이라 이 경로는 여전히 무방비)
---
## 3. 정황 근거
`SeqUploadWeldingList.cs:624-626` 주석 :
> TrumpLaser의 경우 SetLaserTiming 함수로 Gate 신호를 살려줘야 함
> 우선 무조건 셋팅하는 것으로 쓰고 …
> **LaserFrequency = 10, LaserPulseDuration = 0.1로 셋팅하면 출사 되는 것 확인_241025**
물리적으로 계산해서 정한 값이 아니라 **"이 값을 넣으니 나오더라"로 찾은 경험값**이라는 기록입니다.
위 ①~④의 단위 오류가 있는 상태에서 경험적으로 맞춘 것이므로 서로 정합합니다.
이 값으로 현재 코드가 만드는 실제 신호 :
- halfPeriodBit = 32,000,000 / 10 = 3,200,000 bit = 50,000 µs → **10 Hz**
- pulseWidthBit = 0.1 × 64 = 6 bit → **약 0.094 µs**
즉 **10 Hz 주기에 0.1 µs 폭**의 신호입니다. 듀티로 보면 거의 0 에 가깝습니다.
---
## 4. 그래서 실제로 문제인가 — 판단 보류
**단위 오류 자체는 코드상 명확합니다.** 다만 실제 용접 품질에 영향을 주는지는 별개입니다.
현재 본 레이저는 **아날로그 파워(DA) + 게이트(DO Laser_REMOTE)** 로 제어됩니다
(`SeqAutoRunPTOP.cs:1305`, `SetScannerProcessCondition` 의 `WriteDA`).
이 구조에서 RTC 의 LASER1/LASER2 펄스 신호는 **"살아만 있으면 되는" 역할**일 수 있습니다.
위 주석도 "Gate 신호를 살려줘야 함" 이라고 적고 있습니다.
→ **발진기가 이 펄스 신호를 실제로 어떻게 쓰는지 확인해야 결론이 납니다.**
  발진기 매뉴얼의 변조/게이트 입력 사양 확인 필요.
---
## 5. 확인·조치 후보 (미실행)
| # | 항목 | 내용 |
|---|---|---|
| 1 | 발진기 사양 확인 | RTC LASER1/LASER2 펄스를 실제 변조에 쓰는지, 게이트로만 쓰는지 |
| 2 | 현재 현장값 확인 | 각 레시피 Parameter ID 의 `Laser Frequency` · `Laser Pulse Duration` 실측 |
| 3 | ①⑤ 우선 | 소수점 절삭과 "첫 객체만 적용"은 사양 확인 없이도 **현장이 오해하기 쉬운 지점** |
| 4 | ③④ 수정 시 | 환산식을 고치면 **현재 경험값이 전부 어긋납니다.** 반드시 재티칭 전제로 진행 |
| 5 | ⑥ 방어 | `ScannerParamID <= 0` / 빈 리스트 방어 추가 |
**주의 — ③④ 를 고치면 현재 잘 나오는 조건이 깨집니다.** 경험값이 오류를 상쇄하고 있을 수 있으므로,
발진기 사양 확인 → 시험 조건 확보 → 재티칭 계획이 선 뒤에 손대야 합니다.
---
## 참조 위치
| 위치 | 내용 |
|---|---|
| `ScannerParam.cs:142` | `LaserFrequency` double, kHz, 0.2~5 |
| `ScannerParam.cs:164` | `LaserPulseDuration` double, ms |
| `SeqUploadWeldingList.cs:206-212` | 첫 객체 param 으로 `SetLaserTiming` 1회 호출 + `(uint)` 캐스팅 |
| `SeqUploadWeldingList.cs:624-629` | 경험값 주석 + ms→µs 변환이 있던 주석 코드 |
| `Scanner.cs:163-169` | 래퍼 (`periodHz`, `laser1PulseWidthUs`) |
| `ScannerRTC6.cs:772-784` | 환산식 — 1000배 문제 지점 |
| `ScannerRTC5.cs:770-782` | RTC5 동일 |
| `ScannerObj.cs:264` | 추상 선언 (`halfPeriod`, `laser1PulseWidth`) — 이름이 계층마다 다름 |