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