스캐너 필드 보정하기

스캐너라는 것은 위와 같이 두개의 모터를 서로 떨어뜨려 배치해 놓고 반사 거울을 각각 달아 2차원 영역에 대한 위치를 제어하는 것입니다. 그러나 위 그림에서도 볼 수 있듯이 반사 거울의 이동(각도 변화)에 따라 두 반사 거울 사이의 거리가 조금씩 변하는데 이로 인해 실제 레이저의 위치는 정사각형이 아닌 왜곡(distortion)되는 문제가 있습니다. 별로 믿음이 않 가지요?

또한 레이저는 아시겠지만 유도된 빛이기 때문에 볼록렌즈와 같이 집광(focusing)이 가능한 광학 장치를 사용해 에너지 밀도를 높혀 가공이 가능한 빛으로 만들 수 있습니다. 이 또한 렌즈를 통과하기 때문에 굴절(스넬의 법칙)등이 발생하고 결국 내가 가공하고자 하는 위치가 왜곡 될 수 밖에 없습니다.

정리하면, 2개의 반사거울을 사용하는 스캐너의 구조상 배게모양(pillow-shaped)의 왜곡과 렌즈를 사용하여 생기는 술통 모양(barrel-shaped)의 왜곡이 겹쳐 결국 위 그림에서 오른쪽과 같이 괴이한 왜곡이 발생하게 됩니다.

이런 왜곡을 해결(혹은 보상)해 주기 위해 결국 매 micro-vector 시간(통상 10usec) 마다 위치 보정을 해주게 됩니다. 물론 사용자가 2차원 영역에 대한 측정을 마치고 이 역 변환 테이블 파일을 생성 시켜야 한답니다. 이를 통상 스캐너 필드 보정 파일이라고 하며, RTC 의 경우 ctb 혹은 ct5 파일이 이에 해당합니다.

  1. 2D 필드 보정하기
static float kfactor = 10000.0f;
static void Main(string[] args)
{
    SpiralLab.Core.Initialize();
    ConsoleKeyInfo key;
    do
    {
        Console.WriteLine("Testcase for spirallab.sirius.");
        Console.WriteLine("");
        Console.WriteLine("'C' : create new field correction for 2D");
        Console.WriteLine("'Q' : quit");
        Console.WriteLine("");
        Console.Write("select your target : ");
        key = Console.ReadKey(false);
        if (key.Key == ConsoleKey.Q)
            break;
        Console.WriteLine("");
        switch (key.Key)
        {
            case ConsoleKey.C:
                string result = CreateFieldCorrection();
                Console.WriteLine(result);
                break;
        }
    } while (true);
}
      
private static string CreateFieldCorrection()
{
    //현재 스캐너 보정 파일
    var srcFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "correction", "cor_1to1.ct5");
    //신규로 생성할 스캐너 보정 파일
    var targetFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "correction", $"newfile.ct5");

    // 2차원 스캐너 보정용 IRtcCorrection 객체 생성
    // 3x3 (9개) 위치에 대한 보정 테이블 입력 용
    var correction = new RtcCorrection2D(kfactor, 3, 3, 20, srcFile, targetFile);

    #region inputs relative error deviation : 상대적인 오차위치 값을 넣는 방법 (머신 비전 오차값을 넣는 것과 유사)
    correction.AddRelative(new Vector2(-20, 20), new Vector2(0.01f, 0.01f));
    correction.AddRelative(new Vector2(0, 20), new Vector2(0.01f, 0.01f));
    correction.AddRelative(new Vector2(20, 20), new Vector2(0.01f, 0.01f));
    correction.AddRelative(new Vector2(-20, 0), new Vector2(0.01f, 0.01f));
    correction.AddRelative(new Vector2(0, 0), new Vector2(0.01f, 0.01f));
    correction.AddRelative(new Vector2(20, 0), new Vector2(0.01f, 0.01f));
    correction.AddRelative(new Vector2(-20, -20), new Vector2(0.01f, 0.01f));
    correction.AddRelative(new Vector2(0, -20), new Vector2(0.01f, 0.01f));
    correction.AddRelative(new Vector2(20, -20), new Vector2(0.01f, 0.01f));
    #endregion

    //correction.AddAbsolute(0, 0, new Vector2(-20, 20), new Vector2(-20.01f, 20.01f));
    //correction.AddAbsolute(0, 1, new Vector2(0, 20), new Vector2(0.01f, 20.01f));
    //correction.AddAbsolute(0, 2, new Vector2(20, 20), new Vector2(20.01f, 20.01f));
    //correction.AddAbsolute(1, 0, new Vector2(-20, 0), new Vector2(-20.01f, 0.01f));
    //correction.AddAbsolute(1, 1, new Vector2(0, 0), new Vector2(0.01f, 0.01f));
    //correction.AddAbsolute(1, 2, new Vector2(20, 0), new Vector2(20.01f, 0.01f));
    //correction.AddAbsolute(2, 0, new Vector2(-20, -20), new Vector2(-20.01f, -20.01f));
    //correction.AddAbsolute(2, 1, new Vector2(0, -20), new Vector2(0.01f, -20.01f));
    //correction.AddAbsolute(2, 2, new Vector2(20, -20), new Vector2(20.01f, -20.01f));
    #endregion

    //신규 보정 파일 생성 실시
    bool success = correction.Convert();
    // 보정 파일을 테이블 1번으로 로딩
    //success &= rtc.CtlLoadCorrectionFile(CorrectionTableIndex.Table1, targetFile);
    // 테이블1 번을 1번 스캐너(Primary Head)에 지정
    //success &= rtc.CtlSelectCorrection(CorrectionTableIndex.Table1);
    if (success)
        return correction.ResultMessage;
    else
        return "fail to convert new correction file !";
}

또한 윈폼(WinForms)을 직접 이용하는 방법이 훨씬 효과적입니다. 이 경우에는 동일하게 측정된 데이타를 준비한 이후 시리우스 라이브러리에서 제공하는 Correction2DForm 폼을 생성합니다.

private static void CreateFieldCorrectionWithWinForms()
{
    //현재 스캐너 보정 파일
    var srcFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "correction", "cor_1to1.ct5");
    //신규로 생성할 스캐너 보정 파일
    var targetFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "correction", $"newfile.ct5");

    // 2차원 스캐너 보정용 IRtcCorrection 객체 생성
    // 3x3 (9개) 위치에 대한 보정 테이블 입력 용
    var correction = new RtcCorrection2D(kfactor, 3, 3, 20, srcFile, targetFile);

    #region inputs relative error deviation : 상대적인 오차위치 값을 넣는 방법 (머신 비전 오차값을 넣는 것과 유사)
    correction.AddRelative(0, 0, new Vector2(-20, 20), new Vector2(0.01f, 0.01f));
    correction.AddRelative(0, 1, new Vector2(0, 20), new Vector2(0.002f, 0.001f));
    correction.AddRelative(0, 2, new Vector2(20, 20), new Vector2(-0.0051f, 0.01f));
    correction.AddRelative(1, 0, new Vector2(-20, 0), new Vector2(0.01f, 0.01f));
    correction.AddRelative(1, 1, new Vector2(0, 0), new Vector2(0.0f, 0.0f));
    correction.AddRelative(1, 2, new Vector2(20, 0), new Vector2(-0.01f, 0.01f));
    correction.AddRelative(2, 0, new Vector2(-20, -20), new Vector2(0.01f, 0.002f));
    correction.AddRelative(2, 1, new Vector2(0, -20), new Vector2(0.005f, -0.003f));
    correction.AddRelative(2, 2, new Vector2(20, -20), new Vector2(0.002f, -0.008f));
    #endregion

    var form = new Correction2DForm(correction);
    form.ShowDialog();
}
데이타를 전달한 후 2D 스캐너 보정 윈폼이 실행된 모습
보정 성공에 대한 로그 메시지

2. 3D 필드 보정하기

스캐너를 이용해 3차원의 공간을 가공하고 싶다면, 당연히 일이 더 복잡해 집니다. 제일 먼저 해야 할 일은 Z= 0 위치의 2D 필드 보정을 해야 한다는 것입니다. 일에는 순서가 있습니다. !

그 이후 Z 위치를 + 혹은 – 위치의 거의 끝까지 이동시킨후 변화된 위치를 측정해야 합니다. 이 측정 값을 가지고 보정 파일을 만들게 되는데 이때 계산되는 값은 Stretch X, Y 으로, 일종의 선형적 비율 (기울기) 값이 구해지게 되며, 이 값을 보정파일(ct5)의 헤더 영역에 쓰여지게 됩니다.

static float kfactor = 0.0f;
static void Main2(string[] args)
{
    SpiralLab.Core.Initialize();
    ConsoleKeyInfo key;
    do
    {
        Console.WriteLine("Testcase for spirallab.sirius");
        Console.WriteLine("");
        Console.WriteLine("'C' : create new field correction for 3D");
        Console.WriteLine("'Q' : quit");
        Console.WriteLine("");
        Console.Write("select your target : ");
        key = Console.ReadKey(false);
        if (key.Key == ConsoleKey.Q)
            break;
        Console.WriteLine("");
        switch (key.Key)
        {
            case ConsoleKey.C:
                string result = CreateFieldCorrection();
                Console.WriteLine("");
                Console.WriteLine(result);
                break;
        }
    } while (true);
}
      
private static string CreateFieldCorrection()
{
    //현재 스캐너 보정 파일
    var srcFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "correction", "cor_1to1.ct5");
    //신규로 생성할 스캐너 보정 파일
    var targetFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "correction", $"newfile.ct5");

    // 3차원 스캐너 보정용 IRtcCorrection 객체 생성
    // 우선 Z= 0 (2D) 영역에 대한 정밀 보정을 진행한후 3D 보정이 진행되어야 한다 !
    var correction = new RtcCorrection3D(kfactor, 3, 3, 5, -5, srcFile, targetFile);

    #region inputs relative error deviation : 상대적인 오차위치 값을 넣는 방법 (머신 비전 오차값을 넣는 것과 유사)
    // Z= 5mm 위치에 포커스를 하여 레이저를 출사하고 그 오차를 입력
    correction.AddRelative(0, 0, new Vector3(-20, 20, 5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(0, 1, new Vector3(0, 20, 5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(0, 2, new Vector3(20, 20, 5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(1, 0, new Vector3(-20, 0, 5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(1, 1, new Vector3(0, 0, 5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(1, 2, new Vector3(20, 0, 5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(2, 0, new Vector3(-20, -20, 5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(2, 1, new Vector3(0, -20, 5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(2, 2, new Vector3(20, -20, 5), new Vector3(0.01f, 0.01f, 0));

    // Z= -5mm 위치에 포커스를 하여 레이저를 출사하고 그 오차를 입력
    correction.AddRelative(0, 0, new Vector3(-20, 20, -5), new Vector3(0.01f, -0.02f, 0));
    correction.AddRelative(0, 1, new Vector3(0, 20, -5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(0, 2, new Vector3(20, 20, -5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(1, 0, new Vector3(-20, 0, -5), new Vector3(-0.01f, 0.01f, 0));
    correction.AddRelative(1, 1, new Vector3(0, 0, -5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(1, 2, new Vector3(20, 0, -5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(2, 0, new Vector3(-20, -20, -5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(2, 1, new Vector3(0, -20, -5), new Vector3(0.01f, -0.01f, 0));
    correction.AddRelative(2, 2, new Vector3(20, -20, -5), new Vector3(0.01f, 0.01f, 0));
    #endregion

    //신규 보정 파일 생성 실시
    bool success = correction.Convert();
    // 보정 파일을 테이블 1번으로 로딩
    //success &= rtc.CtlLoadCorrectionFile(CorrectionTableIndex.Table1, targetFile);
    // 테이블1 번을 1번 스캐너(Primary Head)에 지정
    //success &= rtc.CtlSelectCorrection(CorrectionTableIndex.Table1);
    if (success)
        return correction.ResultMessage;
    else
        return "fail to convert new correction file !";
}

변환 이후 .log 파일을 통해 상세한 정보가 기록되므로 참고 바랍니다

또한 윈폼(WinForms)을 직접 이용하는 방법이 훨씬 효과적입니다. 이 경우에는 동일하게 측정된 데이타를 준비한 이후 시리우스 라이브러리에서 제공하는 Correction3DForm 폼을 생성합니다.

private static void CreateFieldCorrectionWithWinForms()
{
    //현재 스캐너 보정 파일
    var srcFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "correction", "cor_1to1.ct5");
    //신규로 생성할 스캐너 보정 파일
    var targetFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "correction", $"newfile.ct5");

    // 3차원 스캐너 보정용 IRtcCorrection 객체 생성
    // 우선 Z= 0 (2D) 영역에 대한 정밀 보정을 진행한후 3D 보정이 진행되어야 한다 !
    var correction = new RtcCorrection3D(kfactor, 3, 3, 5, -5, srcFile, targetFile);

    #region inputs relative error deviation : 상대적인 오차위치 값을 넣는 방법 (머신 비전 오차값을 넣는 것과 유사)
    // Z= 5mm 위치에 포커스를 하여 레이저를 출사하고 그 오차를 입력
    correction.AddRelative(0, 0, new Vector3(-20, 20, 5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(0, 1, new Vector3(0, 20, 5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(0, 2, new Vector3(20, 20, 5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(1, 0, new Vector3(-20, 0, 5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(1, 1, new Vector3(0, 0, 5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(1, 2, new Vector3(20, 0, 5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(2, 0, new Vector3(-20, -20, 5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(2, 1, new Vector3(0, -20, 5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(2, 2, new Vector3(20, -20, 5), new Vector3(0.01f, 0.01f, 0));

    // Z= -5mm 위치에 포커스를 하여 레이저를 출사하고 그 오차를 입력
    correction.AddRelative(0, 0, new Vector3(-20, 20, -5), new Vector3(0.01f, -0.02f, 0));
    correction.AddRelative(0, 1, new Vector3(0, 20, -5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(0, 2, new Vector3(20, 20, -5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(1, 0, new Vector3(-20, 0, -5), new Vector3(-0.01f, 0.01f, 0));
    correction.AddRelative(1, 1, new Vector3(0, 0, -5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(1, 2, new Vector3(20, 0, -5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(2, 0, new Vector3(-20, -20, -5), new Vector3(0.01f, 0.01f, 0));
    correction.AddRelative(2, 1, new Vector3(0, -20, -5), new Vector3(0.01f, -0.01f, 0));
    correction.AddRelative(2, 2, new Vector3(20, -20, -5), new Vector3(0.01f, 0.01f, 0));
    #endregion

    var form = new Correction3DForm(correction);
    form.ShowDialog();
}
Z = -5mm 위치의 데이타를 전달한 후 3D 스캐너 보정 윈폼이 실행된 모습
Z = +5mm 위치의 데이타를 전달한 후 3D 스캐너 보정 윈폼이 실행된 모습

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

Scroll to Top