그동안 미추적으로 쌓여 있던 인수인계 문서를 정리해 올린다. 260903 Door 인터락 현장검증 가이드 / 파워캘 3000W 오적용 환산 260907 Arc 출사 파라미터 시각화 / LaserFrequency 단위계통 / NRE 위험구간 점검 / 레시피화면 사용설명서 260915 레시피화면 작업매뉴얼 260916 PTOP 프로그램 사용설명서 / 자동운전 전환 사전점검 현장용 작업 중 임시본(_backup)은 제외했다.
192 lines
8.8 KiB
Markdown
192 lines
8.8 KiB
Markdown
# 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`) — 이름이 계층마다 다름 |
|