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

8.8 KiB
Raw Blame History

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) — 이름이 계층마다 다름