2017년 1월 31일

2D/3D 스캐너 보정방법

SCANLAB 사의 RTC 제품들을 사용해 정밀한 레이저 가공을 위해서는 필히 스캐너의 왜곡을 보정해서 사용해야 합니다. 왜곡이 발생되는 이유는

- X, Y 라는 두개의 반사거울의 각도를 돌리면 서로간의 거리가 각도에 따라 변하고,
- 빛을 초점에 모아 에너지를 집중하는 광학적인 렌즈를 사용하기 때문입니다.

그럼 아래의 예에서 3가지 방법을 살펴보겠습니다.

1. RTC4 기반의 2D 보정

아래와 같이 텍스트 기반의 파일을 만들어 CFMP.exe 의 인자로 지정하면 됩니다.

CFMP 는 위와 같은 형식의 텍스트 파일을 분석해 그 결과로 새로운 보정 파일(ctb)을 생성하게 됩니다.


this line for comments
c:\test\Cor_1to1.ctb   inputfile (*.ctb)
c:\test\result.ctb   outputfile (*.ctb)
580   calibration [bit/mm]
25   number of points [9,25,49,81,121,169,225,289]
25   grid spacing[mm]
-50  50
-25  50
0  50
25  50
50  50
-50  25
-25  25
0  25
25  25
50  25
-50  0
-25  0
0  0
25  0
50  0
-50  -25
-25  -25
0  -25
25  -25
50  -25
-50  -50
-25  -50
0  -50
25  -50
50  -50







2. RTC5 기반의 2D 보정



RTC5는 좀더 많은 개수의 격자(Grid)를 지원합니다.
RTC4의 경우는 17*17 이 최대치이지만 RTC5는 더욱 많은 보정포인트를 지정 할 수 있습니다. 그러나 보정 격자의 개수가 많다고 좋은것은 아닌데, 예를 들어 100mm 의 FOV(Field Of View)를 가지는 경우 11*11 개수의 격자를 이용해 보정할 경우 1.5um 미만의 표준편차를 가진다고 하니 많은 수의 격자를 측정할 필요는 없겠지요?
아래와 같은 텍스트 파일을 생성하여 CorreXion5.exe 와 같은 프로그램에 인자로 실행하게 되면 그 결과로 대상 보정파일(ct5)및 로그 파일(.log)이 생성됩니다. 보정파일이 생성되지 않는 다면 로그 파일을 통해 문제점등을 파악할수 있습니다.


[INPUT]  = c:\test\Cor_1to1; input table file
[OUTPUT] = c:\test\result; output table file
[CALIBRATION] = 10000; bit/mm
[RTC4]  = 0
[FITORDER] = 0
[SMOOTHING] = -1
[AUTO_FIT] = 0
[TOLERANCE] = 0.1
[APPLY_OFFSET] = 0
[Limit(Bits)] = 524288
[Limit(mm)] = 52.4288
[OffsetX] = 0
[OffsetY] = 0
[Deviation] = 0

[GRIDNUMBERS] = 2 2

[GRIDVALUES_X] = -50
[GRIDVALUES_X] = -25
[GRIDVALUES_X] = 0
[GRIDVALUES_X] = 25
[GRIDVALUES_X] = 50

[GRIDVALUES_Y] = 50
[GRIDVALUES_Y] = 25
[GRIDVALUES_Y] = 0
[GRIDVALUES_Y] = -25
[GRIDVALUES_Y] = -50

 Xn Yn X mm Y mm

 -2 2 -50 50
 -1 2 -25 50
 0 2 0 50
 1 2 25 50
 2 2 50 50
 -2 1 -50 25
 -1 1 -25 25
 0 1 0 25
 1 1 25 25
 2 1 50 25
 -2 0 -50 0
 -1 0 -25 0
 0 0 0 0
 1 0 25 0
 2 0 50 0
 -2 -1 -50 -25
 -1 -1 -25 -25
 0 -1 0 -25
 1 -1 25 -25
 2 -1 50 -25
 -2 -2 -50 -50
 -1 -2 -25 -50
 0 -2 0 -50
 1 -2 25 -50
 2 -2 50 -50


로그 파일의 예



Tue Jan 31 13:23:04 2017

Current working path: c:\test
Current data file: 20170131-13-23.dat
[INPUT] = Cor_1to1
[OUTPUT] = result
InFile Calibration: 0 ignored
[RTC4] = 0
[CALIBRATION] = 10000
[GRIDNUMBERS]  = 2  2; grid X, grid Y; points: 5 per line, 5 per column, total 25
Piecewise linear interpolation used for calculation, 
[OUTPUT]: result written successfully
[CALIBRATION] = 10000 ;final
[APPLY_OFFSET] = 0
[Limit(Bits)] = 524288 +/- [Bits]
[Limit(mm)]   = 52.4288 +/- [mm]

[OffsetX] = 0 [Bits]
[OffsetY] = 0 [Bits]
[TOLERANCE] = 0.1
[Deviation] = 0
correXion5.exe terminated







3. RTC5 기반의 3D 보정


앞서 2D 영역 (Z=0)에서의 보정을 보여드렸다면, 3차원 영역을 보정하는 방법도 소개해 드리겠습니다.


그러기 위해서는 3D 스캐너를 장착(VarioScan 등)하고, 사용자가 원하는 공간 (Z - , Z +) 에 대해 측정이 필요합니다. 즉 Z=0 에 대한 보정을 우선 실시하고, Z - , Z + 두 영역에 대해 실측하여 아래 텍스트 파일을 생성해야 합니다.


[INPUT]   = c:\test\D3_1351.ct5; input table filename
[OUTPUT]  = c:\test\result.ct5; output table filename

[GRIDNUMBERS] = 2 2; grid x(col), y(row)

[GRIDVALUES_X] =-50
[GRIDVALUES_X] =-25
[GRIDVALUES_X] =0
[GRIDVALUES_X] =25
[GRIDVALUES_X] =50

[GRIDVALUES_Y] =50
[GRIDVALUES_Y] =25
[GRIDVALUES_Y] =0
[GRIDVALUES_Y] =-25
[GRIDVALUES_Y] =-50

[Z_VALUE] =20; z1(mm)

 iX iY X[mm] Y[mm]

 -2 -2 -50 -50
 -1 -2 -25 -50
 0 -2 0 -50
 1 -2 25 -50
 2 -2 50 -50
 -2 -1 -50 -25
 -1 -1 -25 -25
 0 -1 0 -25
 1 -1 25 -25
 2 -1 50 -25
 -2 0 -50 0
 -1 0 -25 0
 0 0 0 0
 1 0 25 0
 2 0 50 0
 -2 1 -50 25
 -1 1 -25 25
 0 1 0 25
 1 1 25 25
 2 1 50 25
 -2 2 -50 50
 -1 2 -25 50
 0 2 0 50
 1 2 25 50
 2 2 50 50

[Z_VALUE] =-20; z2(mm)

 iX iY X[mm] Y[mm]

 -2 -2 -50 -50
 -1 -2 -25 -50
 0 -2 0 -50
 1 -2 25 -50
 2 -2 50 -50
 -2 -1 -50 -25
 -1 -1 -25 -25
 0 -1 0 -25
 1 -1 25 -25
 2 -1 50 -25
 -2 0 -50 0
 -1 0 -25 0
 0 0 0 0
 1 0 25 0
 2 0 50 0
 -2 1 -50 25
 -1 1 -25 25
 0 1 0 25
 1 1 25 25
 2 1 50 25
 -2 2 -50 50
 -1 2 -25 50
 0 2 0 50
 1 2 25 50
 2 2 50 50



이후 StretchCorreXion5.exe 와 같은 전용 프로그램을 이용해 해당 텍스트 파일을 인자로 주게 되면,
그 결과를 로그 파일(.log)로 알려주는 방식입니다.



Tue Jan 31 13:28:07 2017

Current data file: 20170131-13-28.dat

[INPUT]  = c:\test\D3_1351.ct5; input table filename
[OUTPUT] = c:\test\result.ct5; output table filename

[GRIDNUMBERS]  = 2  2; grid X (columns), grid Y (rows)

[GRIDVALUES_X] =      -50.000000     -25.000000       0.000000      25.000000
[GRIDVALUES_X] =       50.000000

[GRIDVALUES_Y] =      -50.000000     -25.000000       0.000000      25.000000
[GRIDVALUES_Y] =       50.000000

DataPoints: total points = 25

[Z_VALUE] =      20.000000;  Z1[mm]

    iX     iY     X[mm]          Y[mm]

    -2     -2       -50.000000     -50.000000
    -1     -2       -25.000000     -50.000000
     0     -2         0.000000     -50.000000
     1     -2        25.000000     -50.000000
     2     -2        50.000000     -50.000000
    -2     -1       -50.000000     -25.000000
    -1     -1       -25.000000     -25.000000
     0     -1         0.000000     -25.000000
     1     -1        25.000000     -25.000000
     2     -1        50.000000     -25.000000
    -2      0       -50.000000       0.000000
    -1      0       -25.000000       0.000000
     0      0         0.000000       0.000000
     1      0        25.000000       0.000000
     2      0        50.000000       0.000000
    -2      1       -50.000000      25.000000
    -1      1       -25.000000      25.000000
     0      1         0.000000      25.000000
     1      1        25.000000      25.000000
     2      1        50.000000      25.000000
    -2      2       -50.000000      50.000000
    -1      2       -25.000000      50.000000
     0      2         0.000000      50.000000
     1      2        25.000000      50.000000
     2      2        50.000000      50.000000

[Z_VALUE] =     -20.000000;  Z2[mm]

    iX     iY     X[mm]          Y[mm]

    -2     -2       -50.000000     -50.000000
    -1     -2       -25.000000     -50.000000
     0     -2         0.000000     -50.000000
     1     -2        25.000000     -50.000000
     2     -2        50.000000     -50.000000
    -2     -1       -50.000000     -25.000000
    -1     -1       -25.000000     -25.000000
     0     -1         0.000000     -25.000000
     1     -1        25.000000     -25.000000
     2     -1        50.000000     -25.000000
    -2      0       -50.000000       0.000000
    -1      0       -25.000000       0.000000
     0      0         0.000000       0.000000
     1      0        25.000000       0.000000
     2      0        50.000000       0.000000
    -2      1       -50.000000      25.000000
    -1      1       -25.000000      25.000000
     0      1         0.000000      25.000000
     1      1        25.000000      25.000000
     2      1        50.000000      25.000000
    -2      2       -50.000000      50.000000
    -1      2       -25.000000      50.000000
     0      2         0.000000      50.000000
     1      2        25.000000      50.000000
     2      2        50.000000      50.000000


[Stretch_X] =   0.0038050720
[Stretch_Y] =   0.0031820973

c:\test\result.ct5 written successfully


로그 파일의 마지막 줄에는 3D 보정에서 사실상 가장 중요한 두가지 값이 추출된것을 확인하실수 있습니다.
Stretch X, Stretch Y 로 결국 3D 보정을 하게 되면 이 두 개의 상수를 구하게 되는 것입니다.
해당 수치는 보정파일의 헤더 영역에 기록되게 됩니다.



4. 유틸리티

RTC , 2D , 3D 모드등을 선택하여 사용자가 손쉽게 보정을 실시할수있도록 위의 기능들을 모아 아래 유틸리티를 만들어 보았습니다.


2D 보정의 예


3D 보정의 예 


3D 보정에 의해 Stretch X, Y 가 변경된 모습


다운로드 링크 : 다운로드 (평가판으로 7일간 모든 기능을 지원합니다. 상용버전 문의는 labspiral@gmail.com 으로 해주시기 바랍니다)

2016년 1월 13일

HPGL 을 이용한 가공 데이타 처리방법

HP-GL은 HP사의 Graphics Language 란 의미로 프린터를 제어하기 위한 언어입니다. 최초에는 플로터(plotter)를 제어하기 위해 사용되었고 이후 PCL(Printer Command Language)로 갈아탄(?)것으로 보입니다. 때문에 HPGL 에 대한 사양이 10여년째 변화가 없어 점차 사라지는 기술로 보여지기도 합니다.

어쨋든, 초기 HPGL은 텍스트 기반으로 알파벳 두 글자의 명령어와 인자로 구성되고 세미콜론(;)으로 구분하는 단순한 포맷입니다. (참고 : https://en.wikipedia.org/wiki/HPGL) 이후 버전2로 개선되고 바이너리 포맷도 지원하게 된것으로 보입니다.

An example HP-GL file
CommandMeaning
IN;initialize, start a plotting job
IP;set the initial point (origin), in this case the default 0,0
SC0,40,0,40;allows scaling in millimeters since 1 mm = 40 plotter units. Each user-unit is 1 millimeter, in both X and Y directions
SP1;select pen 1
PU0,0;lift Pen Up and move to starting point for next action
PD100,0,100,100,0,100,0,0;put Pen Down and move to the following locations (draw a box around the page)
PU50,50;Pen Up and move to X,Y coordinates 50,50 (in this case mm, after the SC command)
CI25;draw a circle with radius 25 (mm)
SS;select the standard font
DT*,1;set the text delimiter to the asterisk, and do not print them (the 1, meaning "true")
PU20,80;lift the pen and move to 20,80
LBHello World*;draw a label
LTlinetype,lengthset line type and its repetition length
CSxxset character set (e.g. 33 is German)
DIx,yset direction of text given as the catheti
SIww,hh

캐드상의 복잡한 가공 형상이 있고 이 데이타를 X,Y 등의 단순 데이타를 추출하여 여러 기계장치에서 사용하기에는 단순한 HPGL 포맷만한 것도 없습니다. 


이를 위해서는 우선 캐드상에서 PLOT" 명령어를 사용하여 좌표정보를 외부 plt 파일로 생성해야 합니다. 대략 다음과 같은 내용의 PLT 텍스트 파일을 만들수 있습니다.

IN;SC;PU;RO0;IP;IW;VS15;VS15;VS15;VS15;VS15;VS15;SP1;PU;PA0,0;SP1;LT;PA3560,5155;PDPA3569,5155;PUPA3655,5246;PDPA4110,5701;PUPA4197,5792;PDPA4206,5792;PUPA3423,4654;PDPA3432,4654;PUPA3518,4745;PDPA3973,5200;PUPA4060,5291;PDPA4069,5291;PUPA4155,5382;PDPA4610,5837;PUPA4697,5928;PDPA4706,5928;PUPA4792,6019;PDPA4794,6021;PUPA3382,4245;PDPA3837,4700;PUPA3924,4791;PDPA3933,4791;PUPA4019,4882;PDPA4474,5337;PUPA4561,5428;PDPA4570,5428;PUPA4656,5519;PDPA5110,5974;PUPA3348,3847;PDPA3700,4199;PUPA3787,4290;PDPA3796,4290;PUPA3882,4381;PDPA4337,4836;PUPA4424,4927;PDPA4433,4927;PUPA4519,5018;PDPA4974,5473;PUPA5061,5564;PDPA5070,5564;PUPA5156,5655;PDPA5516,6015;PUPA3430,3566;PDPA3564,3699;PUPA3651,3790;PDPA3660,3790;PUPA3746,3881;PDPA4201,4336;PUPA4288,4427;PDPA4297,4427;PUPA4383,4518;PDPA4837,4973;PUPA4924,5064;PDPA4933,5064;PUPA5019,5155;PDPA5474,5610;PUPA5561,5701;PDPA5570,5701;PUPA5656,5792;PDPA5797,5932;PUPA3609,3381;PDPA4064,3836;PUPA4151,3927;PDPA4160,3927;PUPA4246,4018;PDPA4701,4472;PUPA4788,4563;PDPA4797,4563;PUPA4883,4654;PDPA5338,5109;PUPA5425,5200;PDPA5434,5200;PUPA5520,5291;PDPA5975,5746;PUPA3693,3100;PDPA3928,3335;PUPA4015,3426;PDPA4024,3426;PUPA4110,3517;PDPA4565,3972;PUPA4652,4063;PDPA4661,4063;PUPA4747,4154;PDPA5201,4609;PUPA5288,4700;PDPA5297,4700;PUPA5383,4791;PDPA5838,5246;PUPA5925,5337;PDPA5934,5337;PUPA6020,5428;PDPA6262,5670;PUPA3878,2926;PDPA3887,2926;PUPA3973,3017;PDPA4428,3472;PUPA4515,3563;PDPA4524,3563;PUPA4610,3654;PDPA5065,4109;PUPA5152,4199;PDPA5161,4199;PUPA5247,4290;PDPA5702,4745;PUPA5789,4836;PDPA5798,4836;PUPA5884,4927;PDPA6339,5382;PUPA6426,5473;PDPA6435,5473;PUPA4062,2742;PDPA4292,2971;PUPA4379,3062;PDPA4388,3062;PUPA4474,3153;PDPA4928,3608;PUPA5015,3699;PDPA5024,3699;PUPA5110,3790;PDPA5565,4245;PUPA5652,4336;PDPA5661,4336;PUPA5747,4427;PDPA6202,4882;PUPA6289,4973;PDPA6298,4973;PUPA6384,5064;PDPA6620,5300;PUPA4337,2653;PDPA4792,3108;PUPA4879,3199;PDPA4888,3199;PUPA4974,3290;PDPA5429,3745;PUPA5516,3836;PDPA5525,3836;PUPA5611,3927;PDPA6066,4381;PUPA6153,4472;PDPA6162,4472;PUPA6248,4563;PDPA6703,5018;PUPA4540,2492;PDPA4656,2607;PUPA4743,2698;PDPA4752,2698;PUPA4837,2789;PDPA5292,3244;PUPA5379,3335;PDPA5388,3335;PUPA5474,3426;PDPA5929,3881;PUPA6016,3972;PDPA6025,3972;PUPA6111,4063;PDPA6566,4518;PUPA6653,4609;PDPA6662,4609;PUPA6748,4700;PDPA6870,4822;PUPA4830,2418;PDPA5156,2744;PUPA5243,2835;PDPA5252,2835;PUPA5338,2926;PDPA5793,3381;PUPA5880,3472;PDPA5889,3472;PUPA5975,3563;PDPA6430,4018;PUPA6517,4109;PDPA6526,4109;PUPA6612,4199;PDPA6945,4533;PUPA5201,2425;PDPA5656,2880;PUPA5743,2971;PDPA5752,2971;PUPA5838,3062;PDPA6293,3517;PUPA6380,3608;PDPA6389,3608;PUPA6475,3699;PDPA6930,4154;PUPA5607,2471;PDPA5616,2471;PUPA5702,2562;PDPA6157,3017;PUPA6244,3108;PDPA6253,3108;PUPA6339,3199;PDPA6793,3654;PUPA6880,3745;PDPA6889,3745;PUPA6368,2864;PDPA6498,2994;PUPA6971,4223;PDPA6969,4307,6963,4392,6954,4476,6940,4559,6922,4642,6901,4724,6876,4805,6847,4884,6815,4962,6779,5039,6740,5114,6697,5187,6651,5257,6601,5326,6549,5392,6493,5456,6435,5517,6374,5576,6310,5631,6243,5684,6175,5733,6104,5780,6031,5822,5956,5862,5880,5898,5801,5930,5722,5959,5641,5984,5559,6005,5476,6023,5393,6036,5309,6046,5224,6052,5140,6054,5055,6052,4971,6046,4887,6036,4803,6023,4721,6005,4639,5984,4558,5959,4478,5930,4400,5898,4324,5862,4249,5822,4176,5780,4105,5733,4036,5684,3970,5631,3906,5576,3845,5517,3787,5456,3731,5392,3679,5326,3629,5257,3583,5187,3540,5114,3501,5039,3465,4962,3432,4884,3404,4805,3379,4724,3357,4642,3340,4559,3326,4476,3316,4392,3311,4307,3309,4223,3311,4138,3316,4054,3326,3970,3340,3886,3357,3803,3379,3721,3404,3641,3432,3561,3465,3483,3501,3406,3540,3331,3583,3259,3629,3188,3679,3119,3731,3053,3787,2989,3845,2928,3906,2869,3970,2814,4036,2761,4105,2712,4176,2666,4249,2623,4324,2583,4400,2547,4478,2515,4558,2486,4639,2461,4721,2440,4803,2423,4887,2409,4971,2399,5055,2393,5140,2391,5224,2393,5309,2399,5393,2409,5476,2423,5559,2440,5641,2461,5722,2486,5801,2515,5880,2547,5956,2583,6031,2623,6104,2666,6175,2712,6243,2761,6310,2814,6374,2869,6435,2928,6493,2989,6549,3053,6601,3119,6651,3188,6697,3259,6740,3331,6779,3406,6815,3483,6847,3561,6876,3641,6901,3721,6922,3803,6940,3886,6954,3970,6963,4054,6969,4138,6971,4223;PU;PA0,0;SP;

이 파일을 파싱하여 사용한다면 레이저나 CNC 장비등의 가공기계에서 다방면으로 활용가능합니다. 파서를 직접 구현하기 귀찮으신분들은 오픈소스를 사용하면 쉽게 제작이이 될겁니다. (오픈소스 링크 : http://www.opensource.apple.com/source/cups/cups-97.1/filter/ )
여기에 나오는 오픈소스는 CUPS 란 프로젝트의 일부이며, CUPS 에 대해서는 위키를 참고 (https://ko.wikipedia.org/wiki/CUPS)해 보시기 바랍니다.
CUPS(영어: Common Unix Printing System, 공식 명칭으로 CUPS가 더 자주 쓰임)는 컴퓨터를 인쇄 서버로 기능하도록 해주는 유닉스 계열 운영 체제를 위한 모듈 방식의 프린팅 시스템이다. CUPS는 유닉스 계열의 운영 체제에서 프린터 형식과 형태마다 독자적으로 써야 했던 장치 드라이버의 작성을 용이하게 해 주었는데, 과거 유닉스 계열 운영 체제가 지원하던 특수 라인 프린터와 포스트 스크립트 프린터 뿐만 아니라, 매킨토시, 윈도용으로 시판되는 프린터의 대부분을 유닉스 계열 운영 체제에서 사용할 수 있게 하였다.
CUPS를 사용하는 컴퓨터는 클라이언트 컴퓨터에서 인쇄 작업을 수신하는 서버가 되어, 해당 작업을 처리하고 적절한 프린터로 자료를 보낸다. 또한 그 때에는 HTTP의 Basic 인증 및 Digest 인증, 로컬 인증, 128비트 TLS/SSL 암호화 등을 이용할 수도 있다.
CUPS는 유닉스 인쇄 스풀러와 스케줄러 필터 시스템 및 백엔드 시스템으로 구성된다. 이 중 필터 시스템은 인쇄 데이터를 프린터가 인식할 수 있는 형식으로 변환하고, 백엔드 시스템이 이 데이터를 프린터로 보내는 역할을 수행한다. CUPS는 인쇄 작업과 대기열을 취급하는 기반으로 IPP (Internet Printing Protocol)을 이용하고 있다. 또한, CUPS는 유닉스에서 기존에 지원하던 System V 형식과 BSD 형식의 커멘드라인 인터페이스도 지원하고 SMB 프로토콜도 부분적으로 지원한다. CUPS가 제공하는 장치 드라이버는 어도비의 PPD (PostScript Printer Description) 형식의 텍스트 파일을 이용하여 설정이 가능하다. CUPS를 설정하는 CUPS 스스로는 웹(HTTP)을 이용한 임베디드 인터페이스를 지원하고 있다.

필요한 파일들은 hpgl- main, attr, char, config, input, polygon, prolog, tops, vector 들로 이 파일들을 추려내어서 컴파일해 보시고 쉽게 몇몇 참조 에러를 해결하시면 되겠습니다.
이를 사용하여 plt 파일을 분석해 보면 대략 다음과 같이 파싱되는것을 보실수 있습니다.


파싱되는 순서를 파악해 보면 맨 처음 페이지에 대한 변환행렬을 설정하고, IN (초기화)되고, SC(펜 선택), PU(펜 Up), RO(Rotate), IP(초기위치 설정) 등 HPGL 포맷을 분석하는 모습을 볼 수 있습니다. 해당 오픈소스에서 지원하는 HPGL 명령어들 목록은 다음과 같습니다. 
 { "BP", BP_begin_plot },
{ "DF", DF_default_values },
{ "IN", IN_initialize },
{ "IP", IP_input_absolute },
{ "IR", IR_input_relative },
{ "IW", IW_input_window },
{ "PG", PG_advance_page },
{ "RO", RO_rotate },
{ "RP", RP_replot },
{ "SC", SC_scale },
{ "AA", AA_arc_absolute },
{ "AR", AR_arc_relative },
{ "AT", AT_arc_absolute3 },
{ "CI", CI_circle },
{ "PA", PA_plot_absolute },
{ "PD", PD_pen_down },
{ "PE", PE_polyline_encoded },
{ "PR", PR_plot_relative },
{ "PS", PS_plot_size },
{ "PU", PU_pen_up },
{ "RT", RT_arc_relative3 },
{ "EA", EA_edge_rect_absolute },
{ "EP", EP_edge_polygon },
{ "ER", ER_edge_rect_relative },
{ "EW", EW_edge_wedge },
{ "FP", FP_fill_polygon },
{ "PM", PM_polygon_mode },
{ "RA", RA_fill_rect_absolute },
{ "RR", RR_fill_rect_relative },
{ "WG", WG_fill_wedge },
{ "AD", AD_define_alternate },
{ "CF", CF_character_fill },
{ "CP", CP_character_plot },
{ "DI", DI_absolute_direction },
{ "DR", DR_relative_direction },
{ "DT", DT_define_label_term },
{ "DV", DV_define_variable_path },
{ "ES", ES_extra_space },
{ "LB", LB_label },
{ "LO", LO_label_origin },
{ "SA", SA_select_alternate },
{ "SD", SD_define_standard },
{ "SI", SI_absolute_size },
{ "SL", SL_character_slant },
{ "SR", SR_relative_size },
{ "SS", SS_select_standard },
{ "TD", TD_transparent_data },
{ "AC", AC_anchor_corner },
{ "FT", FT_fill_type },
{ "LA", LA_line_attributes },
{ "LT", LT_line_type },
{ "NP", NP_number_pens },
{ "PC", PC_pen_color },
{ "CR", CR_color_range },
{ "PW", PW_pen_width },
{ "RF", RF_raster_fill },
{ "SM", SM_symbol_mode },
{ "SP", SP_select_pen },
{ "UL", UL_user_line_type },
{ "WU", WU_width_units }
HP-GL/2 버전과 텍스트 포맷(바이너리 미지원)만 지원하는것으로 보입니다.
실제 가공장치에서는 Pen Up/Down 되는 좌표를 가져와 구동시키는 방식으로 처리해 주시면 되겠습니다. 위 오픈소스에서는 함수포인터와 퀵 소팅을 사용하는 우아한(?) 방법을 사용하고 있네요.

*이미지및 로고등에 대한 PLOT 추가 예정

2015년 12월 22일

스캐너를 이용한 레이저 가공 기법 (laser processing method by controlling galvanometers)

밤 문화를 좋아하신다면 현란한 레이져 쑈(laser show)를 생각할수도 있고, 철판을 자르거나, 반도체 칩 위에 순식간에 글자를 새기는 것을 보신적이 있나요?





이 원리는 레이저라는 매우 순도높은(?) 빛을 발생시키고, 이 빛을 반사시키는 정교한 기계장치및 제어 S/W에 의해 동작한답니다. 일명 스캐너(scanner) 혹은 갈바노메터(galvanometer or galvo) 라고 부르는 장치인데, 반사용 거울을 달고 이걸 아주 미세하고 빠르게 움직여 멀리 떨어진 곳에 그림을 그리기도 하고, 구멍을 뚫기도 하고, 용접이나  철판을 자르기도 합니다.


이번 시간에는 이 스캐너라는 H/W를 이용하는 방법을 소개시켜드리겠습니다. 준비물은 독일 스캔랩(www.scanlab.de) 혹은 XY2-100 통신방식과 호환되는 스캐너 (그림 참고)를 준비해 주시고, 이를 제어해주는 컨트롤러(여기에서는 RTC를 사용함) 도 필요하답니다. 물론 레이저 발생장치가 있으면 좋겠지만 만약 없다면 레이저 포인터라도 준비해야 최소한의 준비가 됩니다.

정교한 제어 S/W를 개발하기 위해서는 다양한 역학(dynamics)을 수식으로 계산하는등의 복잡한 사전준비가 필요하지만 이런것은 H/W가 알아서 처리해준다는 믿음을 갖고 쉬운것 부터 해보는게 좋습니다.

컴파일 가능한 개발 환경 설정은 www.scanlab.de 에서 배포하는 예제 프로그램을 참고하여 준비하여 주시고,

  1. RTC4open() 함수를 호출 : rtc4dll 이 명시적으로 load 되고 함수들의 진입주소가 설정됨)
  2. load_program_file("rtc4d2.hex") 호출 : 펌웨어가 로드됨
  3. load_correction_file("cor_1to1.ctb", 1, 1,1, 0,0,0) 호출 : 무 보정파일(1:1 보정파일) 을 로드함
  4. select_cor_table(1, 0) 호출 : 보정파일을 선택함
  5. set_start_list(1) : 명령을 넣을 버퍼 준비
  6. mark_abs(), jump_abs() 함수 : x, y 가공 좌표를 입력 (move to / line to 와 유사)
  7. set_end_of_list() : 버퍼 닫기
  8. execute_list(1) : 명령 버퍼를 모두 실행(flush)
  9. get_status() : 호출하여 명령이 실행완료될때까지 지속적인 polling 실시
  10. RTC4close() : 호출하여 자원받납 후 프로그램 종료
위의 절차가 가장 간단한 사용법이 되겠습니다. 실제 mark/jump 에 입력하는 좌표를 외부 캐드등의 벡터 데이타를 가져와 입력해주면 가공이 수행되게 됩니다. 3D 프린터등에도 사용이 가능하답니다.

자, 이제 간단한 사용법을 알았으니 좀더 볼까요? 위의 초기화 과정에서 load_correction_file 의 역할을 설명해 보도록 하겠습니다.




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


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

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

이런 왜곡을 해결(혹은 보상)해 주기 위해 결국 위치보정을 해주는기능을 제공하는데, 이 파일이 바로 load_correction_file 에 의해 설정됩니다. 물론 사용자가 2차원영역에 대한 측정을 마치고 이 파일을 생성시켜야 한답니다.

* 관심있는 분들이 있으면 덧글 달아주시기 바랍니다. 좀더 세부적인 설명을 해드리겠습니다.






2015년 5월 31일

콘손 출력 문자열의 리다이렉션

새로운 모듈을 개발하다보면, 오래된(legacy) 시스템이나 모듈을 재사용해야 할 경우가 많은데, 오래된 모듈에서 나오는 최종 결과가 문자열과 같이 고정된 형태만 제공한다면, 이를 전달받아 내 마음대로 처리해야 하고자 할때, 문자열을 리다이렉션하여 처리가 가능하다.



요약하면,
1. 프로세스간 통신용 파이프(pipe)를 만들고, 자식 프로세스의 표준출력 핸들을 연결해 놓자.
2. 오랜된 모듈(.exe 등)을 새로운 자식 프로세스로 만든다. 이때 표준 출력등의 핸들을 사용할수있도록 설정해 놓자.
3. 자식 프로세스에서 발생되는 표준출력/에러는 부모프로세스의 파이프로 전달된다.

// ------------------------------------------------

int _tmain(int argc, _TCHAR* argv[])

{

 ::SECURITY_ATTRIBUTES sa = { 0, };

 sa.nLength = sizeof(SECURITY_ATTRIBUTES);

 sa.bInheritHandle = true;

 sa.lpSecurityDescriptor = NULL;

 HANDLE ReadPipeHandle, WritePipeHandle;

 if (!::CreatePipe(&ReadPipeHandle, &WritePipeHandle, &sa, 0))

  return FALSE;



 ::STARTUPINFO si = { 0, };

 si.cb = sizeof(STARTUPINFO);

 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;

 si.wShowWindow = SW_HIDE;

 si.hStdOutput = WritePipeHandle;

 si.hStdError = WritePipeHandle;



 ::PROCESS_INFORMATION pi = { 0, };



 TCHAR szCmd[MAX_PATH] = { 0, };

 _stprintf(szCmd, _T("C:\\Windows\\System32\\cmd.exe"));

 TCHAR szArgs[MAX_PATH * 2] = { 0, };

 _stprintf(szArgs, _T("C:\\Windows\\System32\\cmd.exe"));



 if (!::CreateProcess(szCmd, szArgs, NULL, NULL, true, 0, NULL, NULL, &si, &pi))

  return FALSE;



 BOOL bSuccess = FALSE;

 CHAR cData[4096] = { 0, };

 for (;;)

 {

  DWORD dwBytesRead, dwTotalBytes, dwBytesLeft;

  if (!PeekNamedPipe(ReadPipeHandle, cData, sizeof(cData), &dwBytesRead, &dwTotalBytes, &dwBytesLeft))

   return FALSE;

  if (dwBytesRead)

  {

   if (!::ReadFile(ReadPipeHandle, cData, sizeof(cData) - 1, &dwBytesRead, NULL))

    return FALSE;



   cData[dwBytesRead] = '\0';

  }

  else

  {

   if (WAIT_OBJECT_0 == WaitForSingleObject(pi.hProcess, 0))

   {

    bSuccess = TRUE;

    break;

   }

  }

 }



 ::CloseHandle(pi.hThread);

 ::CloseHandle(pi.hProcess);

 ::CloseHandle(ReadPipeHandle);

 ::CloseHandle(WritePipeHandle);



 return 0;

}



// ---------------------------

ps. 보통 레거시 모듈은 유니코드가 아닌 ANSI 기반인 경우가 많다. ANSI/UNICODE 여부를 알아야 한다면, ::IsTextUnicode 등의 API 가 도움이 될것이다.


2014년 10월 11일

학교에서도 SW 교육을 한다

한국도 내년 (2015년) 부터 중학교 신입생부터 SW 교육을 의무적으로 이수하고, 초등학교는 2017년, 고등학교는 2018년 부터 시작이라고 합니다. 미래에는 IT 노동자들이 더욱 필요하나 이대로라면 노동력 부족이 예상되기 때문에 북미 등지에서는 이미 추진이 되고 있었지요. 북미에서는 http://code.org 라는 비영리 단체등이 있고, 국내쪽에는 http://opentutorials.org/ 등이 보이네요.

빌 게이츠 - Hour of Code - Bill Gates explains If statements


마크 주커버그 - Hour of Code - Mark Zuckerburg teaches Repeat Loops
 

조만간 컴퓨터 선생님들은 스크래치(Scratch)란 언어를 열심히 배우게 되겠죠

2014년 7월 23일

SDL2 기반의 OPENGL 렌더링 데모 2부

앞선 글에서는 간단한 도형만을 보여주였지만 이번 예제에서는 외부에서 모델 파일을 가져와 좀더 복잡한 형상을 렌더링 해 보자.

요새 각광받은 3D프린터 들을 보면 수많은 모델들이 STL (STereoLithography) 이란 파일 포맷으로 제공되고 있다. 이번 데모에서는 이 파일 포맷을 렌더링 해보자. STL 파일 포맷에 대한 정보는  http://en.wikipedia.org/wiki/STL_(file_format) , http://www.eng.nus.edu.sg/LCEL/RP/u21/wwwroot/stl_library.htm#What 에서 참고 바라며, 이번 예제에서는 바이너리 기반의 STL 파일포맷을 대상으로 한다.

이 데모의 실제 동작 화면을 캡춰해 http://www.youtube.com/watch?v=LTGfNCY0wIA 에 올려놓았다.

자 그렇다면 실제 구현에 들어가자. 마찬가지로 App 를 상속받는다.

--------------------------ex3.h
#ifndef EX3_H
#define EX3_H
 
#include "app.h"
#include <vector>
 
class Example3 : public App
{
public:
 bool OnInit(int argc, TCHAR* argv[]);
 void OnEvent(SDL_Event* Event);
 void OnLoop();
 void OnRender();
 void OnCleanUp();
 
private:
 bool LoadSTL(TCHAR* lpszFilename);
 
#pragma pack(push, 4)
 typedef struct
 {
  float x;
  float y;
  float z;
 }VERTEX; 
 typedef struct
 {
  VERTEX normal;
  VERTEX v[3];
 }FACE;
#pragma pack(pop)
 
 std::vector<face> _faces;
 VERTEX _center;
 GLuint _listid;
 float _scale;
 
public:
 Example3();
 virtual ~Example3();
};
 
#endif
-----------------------------------------ex.cpp

#include "ex3.h"
#include "log.h"

// STL 예제 파일들은 인터넷에서 검색하여 구하도록 한다
TCHAR* gszSamples[] = {
 _T("samples\\Chest_Back.stl"),
 _T("samples\\knot.stl"),
 _T("samples\\porsche.stl"),
 _T("samples\\pump.stl"),
 _T("samples\\tire_v.stl")
};

Example3::Example3()
{
 _scale = 1.0f;
}

Example3::~Example3()
{
}

bool Example3::LoadSTL(TCHAR* lpszFilename)
{
 FILE* fp = _tfopen( lpszFilename, _T("rb"));  //lpszFilename
 if (!fp)
 {
  Log("fail to open the stl file : %s", lpszFilename);
  return false;
 }
 _faces.clear();
 glDeleteLists(_listid, 1);

 // STL 이진 파일 포맷에 맞게 얻어온다
 BYTE header[80];
 fread(header, sizeof(header), 1, fp);
 UINT32 numOfFacets=0;
 fread(&numOfFacets, sizeof(numOfFacets), 1, fp);
 Log("face count = %d", numOfFacets);
 
 FACE f;
 UINT16 attr;
 for (int i=0; i< numOfFacets; i++)
 {
  fread(&f.normal, sizeof(f.normal), 1, fp);
  fread(&f.v, sizeof(f.v), 1, fp); 
  fread(&attr, 2, 1, fp);
  _faces.push_back(f);
 }
 fclose(fp);
 float xmin, xmax;
 float ymin, ymax;
 float zmin, zmax;
 xmin = 9999; xmax = -9999;
 ymin = 9999; ymax = -9999;
 zmin = 9999; zmax = -9999;

 // 크기를 알아내에 화면 중심으로 이동시키기 위해서
 for (int i=0; i< _faces.size(); i++)
 {
  if (_faces[i].v[0].x  < xmin)
   xmin = _faces[i].v[0].x;
  if (_faces[i].v[0].x  > xmax)
   xmax = _faces[i].v[0].x;
  if (_faces[i].v[0].y  < ymin)
   ymin = _faces[i].v[0].y;
  if (_faces[i].v[0].y  > ymax)
   ymax = _faces[i].v[0].y;
  if (_faces[i].v[0].z  < zmin)
   zmin = _faces[i].v[0].z;
  if (_faces[i].v[0].z  > zmax)
   zmax = _faces[i].v[0].z;
 }
 _center.x = (xmin+xmax) / 2.0f;
 _center.y = (ymin+ymax) / 2.0f;
 _center.z = (zmin+zmax) / 2.0f;
 
 // 렌더링 속도 향상을 위해 리스트 버퍼 사용
 _listid = glGenLists( 1 );
 glNewList(_listid, GL_COMPILE );
 glBegin( GL_TRIANGLES );
 glColor3f( 0.3, 0.8, 0.1);
 for (int i=0; i< _faces.size(); i++)
 {
  glNormal3fv( (float*)&_faces[i].normal);
  glVertex3fv( (float*)&_faces[i].v[0]);
  glVertex3fv( (float*)&_faces[i].v[1]);
  glVertex3fv( (float*)&_faces[i].v[2]);
 }
 glEnd();
 glEndList();
 return true;
}

bool Example3::OnInit(int argc, TCHAR* argv[])
{

 // 조명 효과 사용
 float ambLight[] = {0.7f, 0.7f, 0.7f, 1.0f};
 float specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
 float specref[] = {1.0f, 1.0f, 1.0f, 1.0f};
 float lightPos[] = {500, 500, 300, 1.0f};
 glEnable(GL_LIGHTING);
 glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambLight);
 glLightfv(GL_LIGHT0, GL_DIFFUSE, ambLight);
 glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
 glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
 glEnable(GL_COLOR_MATERIAL);
 glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
 glMaterialfv(GL_FRONT, GL_SPECULAR, specref);
 glMateriali(GL_FRONT, GL_SHININESS, 128);
 glEnable(GL_LIGHT0);
 return true;
}

void Example3::OnEvent(SDL_Event* e)
{
  // q : 줌 인
  // z : 줌 아웃
  // p : 렌더링 모드 변경
  switch( e->type )
  {
  case SDL_KEYDOWN:
    if (e->key.keysym.sym == 'q')
    {
     _scale *= 1.2;
    }
    else if (e->key.keysym.sym == 'z')
    {
     _scale *= 0.8;
    }
    break;
        case SDL_KEYUP:
   if (e->key.keysym.sym == 'p')
   {
    static int mode = 0;
    glPolygonMode( GL_FRONT_AND_BACK, GL_POINT+ mode );
    mode++;
    mode= mode % 3;
   }
   else if (e->key.keysym.sym >= '1' && e->key.keysym.sym <= '5')
   {
    int n = e->key.keysym.sym - '1';
    this->LoadSTL( gszSamples[n] );
   }
   
    break;
  default:
      break;
  }
}

void Example3::OnLoop()
{}

void Example3::OnRender()
{
 glClearColor( 0.1f, 0.1f, 0.1f, 1.0f );
 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
 glLoadIdentity();
 gluLookAt(300, 300, 150, 0,0,0, 0,0,1);

 static float angle = 0;
 glRotatef( angle +=0.5, 0,0,1);

 glPushMatrix();
  glScalef(_scale, _scale, _scale);
  glTranslatef(-_center.x, -_center.y, -_center.z);
  glCallList(_listid);
 glPopMatrix();

 glBegin(GL_LINES);
  glColor3f(1, 0, 0);
  glVertex3f( 0, 0, 0 );
  glVertex3f( 500, 0, 0 );
  glColor3f(0, 1, 0);
  glVertex3f( 0, 0, 0 );
  glVertex3f( 0, 500, 0 );
  glColor3f(0, 0, 1);
  glVertex3f( 0, 0, 0 );
  glVertex3f( 0, 0, 500 );
 glEnd();
}

void Example3::OnCleanUp()
{
}

---------------------------------------------------

자 그럼 메인 함수를 보자

#include "app.h"
#include "ex3.h"
int _tmain(int argc, TCHAR* args[])
{ 
 App* app = new Example3;
 int n = app->Execute(argc, args);
 return n;  
}


각종 예제 모델 파일들(STL)은 http://www.eng.nus.edu.sg/LCEL/RP/u21/wwwroot/stl_library.htm#Download 에서 다운로드 받은 것들이다. 또한 키보드 1~5번 키를 누를때 마다 모델파일을 새로 로드하도록 구현되었다.

SDL2 기반의 OPENGL 렌더링 데모 1부

앞선글에서 SDL2 및 이를 쉽게 OPENGL 기반으로 응용할수 있는 개발 환경을 소개한 바 있다. App 라는 클래스를 상속받아 렌더링 부분등의 이벤트만 구현해주면 쉽게 접근이 가능하다.




위 이미지와 같이 3차원 공간에 몇개의 도형을 렌더링 하는 코드를 작성해 보도록 하자.
우선 할것은 App 를 상속받는 것이다.

---------------------------------------ex1.h

#ifndef EX1_H
#define EX1_H

#include "app.h"

class Example1 : public App
{
public:
 bool OnInit(int argc, TCHAR* argv[]);
 void OnEvent(SDL_Event* Event);
 void OnLoop();
 void OnRender();
 void OnCleanUp();

private:

public:
 Example1();
 virtual ~Example1();
};

#endif


다음으로 할것은 실제 이벤트 핸들러등을 구현하는 것이다.

--------------------------------------- ex1.cpp

#include "ex1.h"
#include "log.h"


Example1::Example1()
{}

Example1::~Example1()
{

}

bool Example1::OnInit(int argc, TCHAR* argv[])
{
 return true;
}

void Example1::OnEvent(SDL_Event* Event)
{}

void Example1::OnLoop()
{}

void Example1::OnRender()
{
    glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glLoadIdentity();
 gluLookAt(5,5,10, 0,0,0,0,1,0);

 static float angle =0;
 glRotatef(angle += 1, 0,1,0);

    glBegin( GL_TRIANGLES );           
   glColor3f(1, 1, 1);
      glVertex3f(  0.0f,  1.0f, 0.0f );
      glVertex3f( -1.0f, -1.0f, 0.0f );
      glVertex3f(  1.0f, -1.0f, 0.0f );
    glEnd( );                         
    
 glPushMatrix();
    glTranslatef( 3.0f, 0.0f, 0.0f );

    glBegin( GL_QUADS );               
   glColor3f(1, 1, 0);
      glVertex3f( -1.0f,  1.0f, 0.0f );
      glVertex3f(  1.0f,  1.0f, 0.0f );
      glVertex3f(  1.0f, -1.0f, 0.0f );
      glVertex3f( -1.0f, -1.0f, 0.0f );
    glEnd( );                         
    glPopMatrix();

 glBegin(GL_LINES);
   glColor4f(1, 0, 0, 1);
      glVertex3f( 0.0f, 0.0f, 0.0f );
      glVertex3f( 5.0f, 0.0f, 0.0f );

   glColor4f(0, 1, 0, 1);
      glVertex3f( 0.0f, 0.0f, 0.0f );
      glVertex3f( 0.0f, 5.0f, 0.0f );

   glColor4f(0, 0, 1, 1);
      glVertex3f( 0.0f, 0.0f, 0.0f );
      glVertex3f( 0.0f, 0.0f, 5.0f );
 glEnd();

}

void Example1::OnCleanUp()
{

}



자 모든 구현이 완료되었다. 오직 화면을 렌더링하는 이벤트(OnRender)만 구현하였으며 다른 이벤트는 모두 미 구현상태이다. 특히 OpenGL 함수들로만 구성되어 있어 SDL 등의 익숙치 않는 함수들은 전혀 포함되어 있지 않아 쉽게 응용이 가능하게 되었다.

시작점 즉 메인함수의 모습은 아래와 같다.

#include <windows.h>
#include "app.h"
#include "ex1.h"

int _tmain(int argc, TCHAR* args[])
{ 
 App* app = new Example1;
 int n = app->Execute(argc, args);
 return n;  
}

다음 글에서는 좀더 복잡한 것들을 해보자

시리우스 라이브러리 홈페이지 오픈

현재 시리우스(Sirius) 라이브러리라는 제품을 개발하고 이를 소개하는 홈페이지를 오픈 하였습니다. 관심있는 분들의 많은 방문 요청드립니다. 앞으로 업데이트 소식및 변경사항은 스파이럴랩 홈페이지를 통해 진행할 예정입니다. 스파이럴랩 홈페이지 :  h...