레이블이 CAD인 게시물을 표시합니다. 모든 게시물 표시
레이블이 CAD인 게시물을 표시합니다. 모든 게시물 표시

2016년 4월 9일 토요일

STEP AP242 Standard

STEP AP242 Standard

3D 설계데이타 교환을 위한 국제표준 중립 포멧으로 STEP이 제정된지 수십년이 흘렀는데, 아직 CAD 분야에서는 STEP Native Modeler는 개발되지 않았다. 그리고 현재 현업에서는 대부분 AP203 및 AP214를 사용한다.

우선 AP203에 관해서.

IGES를 대체하기 위해 STEP 표준이 논의되면서, 제일 먼저 나온 사양이 AP203이다. AP 명세를 구현하는 실체는, 텍스트로 표현된 Express 스키마 문법이다. 이때는 아직 XML이 나오기 전이므로, 전용의 구문 문법을 개발해서 사용한 것이다. AP203을 주도적으로 제정한 곳은 미국 국방분야의 항공회사들로 보통 알려져 있다. 즉 보잉 같은 회사를 말한다. 초창기 표준이므로 가벼운 대신 담고 있는 정보가 좀 부족하다. 예를 들어, 내부에 구성되어 있는 엔티티들의 색상을 정의하는 구문이 존재하지 않는다. 따라서 초창기 AP203 표준에 따라 구현된 STEP 파일 생성기로 만들어낸 STEP 파일을 CAD에서 읽어들이면, 색상정보가 다 빠져버려 탈색된 빨래 같은 것이 뜨게 된다.
물론 시대가 흐르면서 AP203도 계속 업그레이드 되었다. 따라서 요즘 사용하는 버전의 3D CAD 툴에서 생성한 AP203 STEP 파일들은 색상 정보를 담아낼 수 있다. 그리고, 어셈블리 구조를 모듈화하여 표현한 업그레이드 버전인 AP203-ed2가 거의 최종 버전인 것 같은데… 이 표준으로 생성할 경우에는 여러 CAD 시스템 간에 데이타 교환시 실패 확률이 매우 높아진다. 서로 제대로 구현하지 못했다는 이야기다.
좋은 예로, CREO 2에서 AP203-ed2로 STEP 파일을 생성한 후에, 그 파일을 CATIA V5R20에서 읽어들이면 형상 재구성에 실패한다. 때문에 STEP 버전을 낮춰서, CREO 2에서 구버전의 AP203으로 생성한 후에, 그 파일을 CATIA V5R20에서 읽어들이면 성공적인 결과를 볼 수 있다. 이런 식의 미묘함 때문에, STEP을 이용한 이기종 CAD 간의 데이타 교환시에는 가능한 구버전의 STEP 표준으로 파일을 생성하는 것이 유리하다. 물론 그 대신, STEP 파일 안에 담기는 정보는 더 적어지게 되고, 실질적으로는 IGES 수준의 형상 정보 정도만 교환하는데 만족할 수 밖에 없다.
사실 AP203 표준에는 더 많은 정보를 담아내서 교환하는데 써야 하는게 맞는데, CAD 시스템에서 제대로 지원해 주지를 못한다. 예를 들어, 어떤 부품이 Made 인지 Bought 인지를 구분해 주는 아규먼트가 AP203의 Express 스키마 구문 중에 분명히 있지만(PRODUCT 관련 구문), CREO 이든 CATIA이든 간에 이 항목을 제대로 매치시켜 STEP으로 생성해 주지 못한다. 그냥 없는 항목처럼 무시하는 것이다.
따라서 이렇게 생성된 STEP 파일을 PDM 시스템에 업로드 해서 해석하여 데이타베이스화하고자 해도, 형상 정보 이외의 이런 부가 정보들은 현실적으로 활용되지 못하게 된다. 각 CAD 시스템이 STEP 표준을 이렇게 대충 지원하는 이유는, 일반적으로 자사 전용 포멧을 고객에게 강요함으로써 시장지배력을 올리고 또 고객을 자사 솔루션에 종속시키기 위한 것으로 생각된다.

그리고 AP214에 관해서.

한편, AP214는 독일쪽 자동차 업계에서 주도하여 2000년 경에 확정되었다. 미국에 정면 대항하려는 목적도 있지 않았을까. 당연히 AP203보다 더 발전된 사양으로, 방대한 명세를 포함한 거대한 포멧이 되었다. 그러나 이기종 CAD간 호환성은 확실하게 AP203보다 확연히 떨어진다. AP214로 생성한 파일을 협력업체에 보냈을 때, 높은 확률로 "이 파일 읽어지지가 않아요"라는 전화를 받곤 한다.
내 경우엔, 이전 직장에서는 시제품 제작을 위해 목업 업체에 보낼 데이타는 AP214를 사용했다. 함께 서로 포멧을 조정한 기간을 거친 후에, 안정적으로 데이타 교환을 했기 때무에 문제가 없었다. 문제는 직장을 옮기고 다른 협력업체와 일을 하게 되자 그 새로운 회사에서 사용하는 소프트웨어가 달라졌기 때문에 자료 교환에 문제가 생긴 것이다. 결국 울며 겨자먹기로 AP203으로 포멧을 바꿀 수 밖에 없었다.
아무튼 AP214는 보다 더 많은 정보를 담아낼 수 있어 편리성은 높아졌지만, 이기종 CAD 간에 살짝 미묘하게 호환성이 떨어졌기 때문에 범용적으로 사용하기는 곤란한 점이 있었다.

AP242

마침내 AP203과 AP214를 합쳐서 AP242가 제정되었다. 그리고 그게 다시 업그레이드 되어 AP242-ed2 확정이 다 된 거나 마찬가지다. ed2로 올라가면서, 온갖 정보들을 더 많이 담게 되었다. PDM 관련한 각종 부가정보들, 부품간의 조립 구속조건, 파라메트릭 구속조건, 제조 관련 정보들, 전장 하네스 설계 정보, 히스토리 정보, 프리젠테이션을 위한 정보, 3차원 기구학 애니메이션을 위한 정보 등등. 이제까지의 전례를 봤을 때, 각 CAD 개발사들이 과연 AP242를 얼마나 적극적으로 준수해서 제대로 된 STEP 포멧 생성기를 만들어낼지 약간 의문이긴 하다. CAD 개발사들이 적극적으로 지원하지 않으면 사실상 죽은 표준이 될 공산도 크고…
아무튼 PTC사의 경우에는, 이 자료를 신뢰한다면, CREO 4에 AP242 관련 기능들이 다 구현되어 제공될 것으로 보인다. 경쟁관계에 있는 CATIA 및 UG 쪽도 앞서거니 뒷서거니 하면서 비슷한 시기에 나오지 않을까 싶다.
한가지 가정을 해 보자.
CAD 시스템에서 올바르게 AP242 표준에 맞도록 데이타를 만들어 낸다면?
그러면 오랫동안 사용자들이 원해왔던, 그리고 CAD 개발사들이 그렇게 오랫동안 방해해왔던 이기종 CAD 간의 그나마 쓸만한 자료교환이 가능해 질 것이다.
그렇게 될 경우, 특정 CAD의 Native Format을 지원하는 모듈을 별도 개발할 필요 없이, AP242만 지원하는 오픈소스 모듈만 가지고도 필요한 PDM/PLM, CAD 및 기타 관련 소프트웨어들의 구현이 될 것이고, 이것만으로도 프로페셔널한 수준의 진정한 산업표준 데이타 교환이 가능해져 제조업 효율이 좋아질 것이다.
개인적으로, 특정 CAD 솔루션에 종속된 설계환경은 벗어나야 한다고 본다. 생각해 보자.
CREO를 능숙하게 사용하는 구조설계자가 있을 경우, 이 사람은 CATIA를 사용하는 업체에 취업할 수 없다. 이 얼마나 바보같은 일인가?
CAD 소프트웨어가 본질이 아니고, 공학적 설계능력 자체가 핵심인데 그전에 엉뚱한 허들이 형성되어 있는 것이다.
또, CATIA를 쓰는 업체는 다른 CAD로 전환하거나 다종의 CAD 시스템을 운용하기가 어렵다. 인력 운용면에서도 그렇고 시스템 측면에서도 그렇다. (물론 대규모 자동차 업계 같은 데서는 이기종 CAD간의 조합이 이루어지기는 하지만, 악영향이 최소화되도록 모듈별로 철저하게 구분된 방식이라고 생각된다.)
따라서 CATIA에 종속된 제조업체는, 높은 비용을 고정적으로 강요받게 되고, 자금운용에서 경직적인 부분이 발생한다. 이것은 경영상의 위협요인이 아닐 수 없다.

대한민국의 현실

아마 정년퇴임하셨을 것으로 추측되는 이상헌 교수라는 분이 80~90년대에 걸쳐 초창기 CAD 분야에서 업적을 꽤 쌓았다고 알고 있다. 당시 이 분야에서의 화두 중의 하나였던 비다양체 위상 기하학을 다룰 수 있는 구조체를 구현한 CAD커널이 한창 개발되고 실용화 단계에 접어들 때의 이야기다. 현재는 비다양체 CAD커널은 그냥 아주 당연한 기본 특성이 되어 있다.
이상헌 교수의 경우, 자신의 연구에 펀딩만 좀 받았더라면 독자적인 CAD커널로 제대로 발전시킬 수 있었을 것이다. 왜냐면 같은 시기에 다른 나라들의 수준과 별 차이가 없었기 때문이다. 그러나 그런 장기적인 연구를 지원하는 시스템은 한국에 없었다. 사업가들의 눈에 띄지도 못했다. 이상헌 교수에게는 글로벌 경쟁에 나설 기회가 아예 주어지지 못했던 것이다.
그래서 지금의 한국은 그냥 CAD 및 PLM 분야에서 종속 국가가 되었다.
한국이 제조업 기반으로 흥한 나라라고들 하는데, 현재 상태로는 '제조업 2.0 (?)'이라던가 하는 말은 과대망상 내지는 허언증이다. 기초가 부실하다고 하는 좋은 예가 될 수 있을지 모르겠다.

2016년 4월 5일 화요일

OpenSource PLM에 관한 생각


OpenSource PLM에 관한 생각

PLM 시스템은 전통적으로 3D CAD 개발업체에서 나온 전용 솔루션을 쓰는 것이 당연하다고 생각된 시기가 있었다.
Pro/E를 쓰는 회사에서는 거기에 딸려서 붙어나오는 IntraLink 및 그보다 발전된 WindChill 같은 것을 쓰고,
CATIA는 Enovia 쓰고 뭐 이런 식으로...
간혹 해당 CAD와 붙어서 쓸 수 있는 3rd Party 솔루션도 있으나, 역시 전용인 것은 마찬가지다.

이런 전용 PLM 시스템은 거액의 도입비용 및 높은 실패위험을 감수해야 하므로
중소기업들은 그냥 없는 셈 치고 살았고
대기업들은 실무자의 고통과는 상관없이 시스템에 실무자(인간)들이 갈려들어가는 구조였다.
탁상머리 산업공학의 실패 아닌가 싶다.

최근 수년간의 동향을 보면,
특정 3D CAD에 종속되지 않는 개념의 PLM 솔루션들이 여럿 등장하고 있는데,
그와 함께 몇가지 특징들이 보인다.

(1) 웹서비스 기반 (클라우드화)
(2) 오픈소스 (커스텀 개발 가능하도록)
(3) 패키지 판매가 아닌 유지보수 서비스 판매 모델로의 전환 (서비스업화)

대표적으로 실적이 좋은 솔루션이 바로 ARAS INNOVATOR인 것 같다.
미국 MIT 출신 개발자들이 뚝딱뚝딱 만들어서 사업화 한 거라고 한다.
완성도가 높고 외국 대기업들 중에 채용한 사례가 많다.
계약을 하고, 아마존 클라우드나 MS 애저에 클라우드 서버를 개설하고, 요구조건에 맞춰 몇가지 기능 첨삭을 해서 서비스를 해 준다.
시스템 유지보수는 계약한 벤더가 알아서 해 주니 편하다.
대신 고정 비용이 들어간다.
만일 스스로 개조,유지보수할 자신이 있다면, 오픈소스이기 때문에 직접 설치해서 써도 된다.

문제는 이 녀석은 MS .Net 기반으로 개발된 거라는 점이다.
MS 윈도우 서버에서만 돌아간다.
또 (최근 버전업되어 해소된 것 같지만) 최근까지는 MS IE 브라우저에서만 이상없이 사용할 수 있다는 단점이 있었다.
아울러, 대기업 사용을 상정하다 보니 지나치게 복잡하고 거대하다.
꼭 필요한 기능이 많이 빠져 있어서, 이걸 집어넣으면 건건마다 비용이 발생한다.

간단히 말해 이름만 오픈소스고, 실무에 실제로 쓰려면 그냥 상용 솔루션이라고 봐도 무방하다.


이런 솔루션조차 부담스럽다면 진짜 오픈소스를 찾아야 할 것이다.

제일 눈에 띄는게 2가지였다.

(1) OpenPLM
(2) DocDoku

이중에 OenPLM은 파이선 쟝고(python Django) 웹프레임웍으로 개발된 것이다.
가장 해커스럽다고 할 만 하다.
구성에 필요한 유틸리티들은 전부 오픈소스를 끌어다 만들었다.
그런데도 의외로 상당히 강력하다.
VirtualBox로 패키지화된 것을 받아다가 바로 실행시켜 이것저것 사용해 봤더니 상당히 좋다.

가벼워서 반응이 상당히 빠르고,
3D STEP 어셈블리 파일을 업로드했더니 그걸 자기가 처리해서
곧바로 BOM까지 만들어준다.
전체 어셈블된 3D 모델 및 그 구성부품들 각각을 별도로 3D로 볼 수도 있다.
PDF로 된 도면은 각 파트마다 따로 업로드해 줬더니 웹브라우저에서 바로 열람이 가능하다.

부품번호를 자동으로 매겨주는 로직을 자사 규정에 맞게 고친다던가 하려면
소스코드를 좀 손봐야 하겠지만, 파이썬 쟝고 개발자가 만약 있다면 별로 어렵지 않다고 생각된다.

문제는 이 솔루션은 현재 개발이 버전 2.0에서 중단되었다는 점이다.
따라서 지속적인 향상은 기대하기 어렵다.


한편, DocDoku는 기존의 OpenPLM보다 좀 더 진보된 모습을 보여준다.
더 성숙된 JavaEE 기반으로 된 웹서버로 구성된다.
또 더욱 최신 웹기술이 대거 적용되어 있다.
화면도 깔끔한 디자인이다.
기능도 더 많은 것 같다.
사업화도 나름 잘 되고 있는 것 같으므로 개발이 갑자기 중단되거나 할 것 같지는 않다.

문제는, 서버 구성이 꽤 어려워 보인다.
내가 직접 하기는 좀 부담스럽다.
직업적인 자바 서버 개발자나 관리자라면 가능하지 않을까.

또, OpenPLM처럼 3D STEP 파일을 업로드하는 것만으로 BOM이 자동으로 구성되고 각 부품들이 자동으로 배치되는 기능이 아직 없는 것 같다.
각각의 부품들을 직접 하나하나 올린 다음에 그걸 좌표 및 오리엔테이션을 입력해서 어셈블해야 하는데 이건 정말 비현실적이다.
CATIA 플러그인이 있어서 이 경우에는 자동적인 구성이 되는 것 같기는 한데, CATIA 커넥션은 유료로 별도 제공된다.
또 국내 벤더도 아직 없다.

OpenPLM이든 DocDoku이든 간에 모두 GPL V3 기반이라, 데이타 변환 같은 기능들은 FreeCAD 같은 다른 오픈소스를 갖다 넣어 쓴다.
현명하고 효율적인 선택이다.

내가 개인적으로 보기엔, 중소기업 구조설계자에게 딱 맞는 솔루션은 오히려 OpenPLM 쪽인 것 같다.
복잡성도 낮고, BOM 구성하는데 들어가는 노력과 시간을 크게 단축해 준다.
(물론 보완해 줘야 할 것들이 몇가지 있기는 하지만, 기본 개념 자체는 아주 좋다.)

OpenPLM은 철저하게 ISO10303 STEP 기초를 둔다는 개념이 확실히 처음부터 잡혀 있었기 때문이라고 생각된다.
따라서 모든 3D 데이타는 표준 STEP 파일을 통해 받아들이게 함으로써, 단순성과 범용성을 동시에 만족한다.
아울러 이 과정에서 쓸데없는 잡 정보는 다 걸러버린다.
이런 단순성은, 맨파워가 충분해서 실무자들이 일을 분업하는 것이 가능한 대기업에게는 안 맞겠지만,
소수(1명 또는 1개 팀)의 설계자가 모든 일을 감당해야 하는 중소기업에게는 꼭 필요한 특성이다.


만약 내가 PLM을 직접 개발한다면 어떻게 할까...
아마 다음 특징과 기능들을 반드시 넣을 것 같다.

(1) 제품 전체 어셈블리 모델링 파일은 3D STEP으로 업로드.
(2) 2D 도면은 PDF 및 DWG 2가지를 반드 동시에 업로드. (DWG 파일만 올릴 경우, 웹브라우저에서 직접 보기가 어렵다.  상용 솔루션을 또 사다가 갖다 붙여야 구현 가능한데 그렇게까지 할 필요가 있을까.)
(3) 부품번호=부품명칭으로 동일화하거나 부품번호 자동으로 매겨지도록 로직 구성. (이 경우 부품명칭은 반드시 유일성을 갖도록 설계자가 잘 정해야 한다.  보통 프로젝트 네임을 두문자로 넣으면 중복 우려가 크게 줄어든다.)
(4) STEP 파일을 서버에서 자동 해석해서 BOM 및 PartList 작성, 각 부품별로 STEP 파일을 분해해서 알아서 저장.
(5) 기존에 이미 저장되어 있는 것과 동일한 이름의 것으로 중복이 발견된다면, 기존의 것을 대체하여 리비전을 올릴지 또는 기존 것을 그대로 쓸지 대화창을 통해 사용자가 결정하도록 함.
(6) 사용자가 고른 2D 도면을 서버측에서 Merge해 주는 기능, 웹브라우저에서 곧바로 볼 수 있는 기능. (하나의 파일로 합쳐서 다운로드..  실무자가 프린트하기 편하게.)
(7) 3D 모델은 WebGL로 변환해서 별도 플러그인 없이 웹브라우저상에서 직접 볼 수 있도록.  (가능하면 자동 분해 또는 수동 분해 기능, 단면 짤라보기 기능 같은게 있으면 좋겠는데 없어도 무방.)
(8) 리비전 관리는 새로 파일,정보가 갱신될 때 마다 자동으로 매겨지도록 한다.  리비전 갱신될 때는 반드시 자동으로 ECR,ECO 문서가 자동으로 생성되어 PDF로 저장되도록 한다.  이렇게 하면 ISO9001 인증 준비할 때 필요한 구비서류 작업하는데 들어가는 삽질을 줄일 수 있다.
(9) BOM, PartList에 들어가는 항목(열)을 관리자가 임의로 정의할 수 있도록 한다.  (예를 들어 부품 단가 항목을 새로 추가한다던가.)
(10) BOM, PartList는 Excel 포멧으로 다운로드 받을 수 있도록 한다.  (한국 기업에서는 Excel과 연계 안되면 안된다!)


아마 (4)번 기능이 좀 난이도가 있을 것 같은데, 능력있는 개발자라면 능히 구현 가능하다고 본다.  OpenPLM에서 구현된 바 있으니 이걸 레퍼런스로 갖다 써도 되지 않을까.

그리고 다음 기능은 뺄 것이다.

(1) 프로젝트 관리 기능 (한국 중소기업에 독불장군식 꼰대(?) 경영자들 및 변심이 잦은 고객의 존재 때문에 체계적인 프로젝트 관리가 현실적으로 어렵다는 사실을 인정하자.  정 필요하면 다른 툴을 쓰는게 낫다.)
(2) 소프트웨어 형상관리 기능 (소프트웨어 개발자의 형상관리툴은 따로 있으니.)
(3) 유저그룹 관리 기능 (불필요)
(4) PLM과의 DB 연동 (구현이 그다지 어렵지 않고, 또 필요성도 있긴 한데, 실무적으로 이걸 실제로 가동시키려면 구매팀이나 경영지원팀을 설득시켜야 하는 어려움이 존재한다.  쓸데없는 이런 노력을 할 필요가 있을까.)

아무튼 이런식으로 심플하게 구성하면 쓸만할 것 같다.

PLM에서 흔히 생각하는 3D 모델링 원시데이타를 직접 관리하겠다는 생각 자체를 버리는 것이 일의 규모를 줄여준다.
결과 데이타인 STEP 파일만 가지고 다루도록 하면 소규모 프로젝트로 충분히 쓸만하게 만들 수 있다고 생각이 된다.
그리고, 이런 컨셉으로 개발해서 오픈소스화하고, 이걸 필요한 기업에 살짝살짝 커스텀화해 주면서 저렴한 유지보수비용을 받으면서 고정수입을 얻는 비즈니스 모델도 성립하지 않을까 생각이 된다.
서버는 뭐 아마존 같은 클라우드에 올려서 해 주면, 자체 데이타센터 서버 관리도 필요 없으니까 순수한 소프트웨어 사업이 되지 않나 한다.
보통 엔터프라이즈 솔루션을 개발하는 회사나 개발자들을 보면, 그들의 고객인 '기업 경영자'를 생각하게 되는데, 영업적인 측면에서 실제 돈을 쓰겠다는 결정권자가 경영자이므로 당연하겠지만, 개발자는 그보다 실제로 그걸 사용하는 실무자(유저)를 생각해야 하지 않나 싶다.

2015년 11월 8일 일요일

Git for PDM

Git for PDM

구글링 해 보면 이 주제와 관련하여 하나의 제안이 보인다.
간단히 말해 CAD 데이타도 분산 버전 관리 개념을 도입하면 어떠냐는 것이다.
사실 Git을 써 본 설계자라면 자연스럽게 이런 아이디어를 떠올릴 것은 당연한 귀결이다.
과연 이 아이디어가 현실성이 있을까??

기존 상용 PDM의 특징

  • 거의 모든 제품이 중앙집중식 파일관리 개념을 기반으로 하고 있으며, 유연성이 심각하게 떨어진다.
  • 이유는 간단하다. CAD 소프트웨어 개발회사들이 전부 자사의 CAD 제품 안에 고객들을 가둬놓고 돈을 쪽쪽 빨아먹는다는 개념의 비즈니스 모델을 가지고 있기 때문이다.
  • 따라서 산업 표준 호환성이나 데이타 교환의 용이성 등은 심각하게 등한시되어 왔다.

기존 상용 PDM의 문제점

  • 사용자는 모두 온라인으로 연결해서 CAD데이타를 당겨서 써야 한다.
  • 이런 중앙집중식 모델은 네트워크 속도가 받쳐주지 못하면 느려서 도저히 작업 진행이 안 될 지경이다. 특히 그냥 소스코드 따위의 텍스트들도 아니고, 수십~수백 메가바이트 또는 심한 경우 기가바이트 단위의 데이타를 온라인으로 당겨와서 써야 하는데 제대로 속도가 나올리 없다.
  • 다중작업시 권한관리 문제 때문에 역시 설계자의 자유도가 떨어진다.
  • 브랜치를 내는 것이 쉽지 않다. 이는 달리 말해, 하나의 소스 제품이 있을 경우 이에 대해 고객 요구에 따라 커스터마이제이션된 파생 모델을 쉽게 만들기 힘들다는 이야기다.
  • 다양한 파생 모델을 만들어내야 하는 것은 주로 다품종 소량생산 제품을 만들어내야 하는 고부가가치 장비 쪽 업계에서 직면하는 문제다. 이에 대해 기존의 CAD 회사들은 제대로 된 솔루션을 전혀 제시해주지 못하고 있다.
  • 기존 CAD 회사들의 주요 관심사는, 대규모 다국적 기업들 달리말해 큰 돈을 주는 대기업에 주로 촛점이 맞춰져 있었다.
  • 중소기업을 위한 솔루션이라고 구색을 맞춰서 나온 것들이 있기는 하지만, 역시 실제 중소기업 현실에 맞춘 마음에 드는 솔루션을 본 적이 없다.
  • 중소기업의 경우 1명~수명 정도의 소수의 설계 인원만으로 설계작업은 물론 도면 생성 관리 배포 파생제품 설계 생산이관 자료준비 등등 모든 단계를 모두 다 수행해야 한다. 이는 심각한 업무 로드를 강요하게 되는데, 실무자의 업무량을 진짜로 줄여주는 일에 관심을 가진 공급자는 단 한 회사도 없었다고 본다.
  • 특히 중소기업(스타트업,벤쳐등)의 경우, 관리를 위한 관리 보다는 좀 더 유연하게 신제품을 개발하는게 중요하고 또 기존 제품들 즉 레거시 데이타의 구애를 덜 받기 때문에, 중앙집중식 데이타 관리 보다는 분산형이 훨씬 더 필요성이 높다.

diff 문제

  • Git은 기본적으로 텍스트 포멧을 다루는데 최적화되어 있다. 그런데, 대부분의 CAD 데이타는 바이너리 파일이다. 따라서 사용하기에 불리해진다.
  • diff 기능은 고도로 발달되어 있으나 역시 텍스트 이외에는 전혀 쓸모가 없다.
  • 다만, STEP 파일을 보면 기본적으로 텍스트 포멧을 바이너리화 한 것으로 볼 수 있기 때문에 중간에 적절한 필터만 거치게 해 줄 경우 관리가 완전히 불가능한 것만은 아닐 것이다.
  • 또한 CATIA의 Catpart, CREO의 prt 파일 등의 경우 역시 해당 포멧의 구조 중에서 헤더 정도에 해당하는 내용만 파싱해 낼 수 있다면 어느정도 관리가 가능하지 않을까 생각이 들기도 한다.
  • 그럼에도 불구하고, 텍스트 포멧을 다룰때 보다는 훨씬 많은 용량의 저장공간과 관리를 위한 컴퓨팅 파워가 필요할 것이다.
  • 그러나 저장공간의 가격은 매우 저렴해져 있고, 컴퓨팅 파워는 사실 PC 정도 사양만으로도 충분하지 않을까 싶다.

사용자 인터페이스 문제

  • 소프트웨어 개발자가 아닌 기계설계자들을 위한 심플하고 깔끔한 GUI 클라이언트 소프트웨어가 제공될 필요가 있다. 또는 완전히 웹 기반으로 가도 좋지 않나 한다.
  • 인증, 커밋, 브랜치, 머지 등등의 개념을 몰라도 자동적으로 수행되도록 세심하게 개발되어야 할 것이다.

BOM 생성 문제

  • 가장 현실적인 방법은, CAD 종류에 구애받지 않도록 모든 정보를 포기하고 오로지 파일네임만으로 모든 것을 구성하는 것이다.
  • 이것이 성립되려면 파일네임이 유일성을 가지도록 설계단계에서 유의해야 할 필요가 있다. 이는 설계자 교육을 통해 습관하는 것이 충분히 가능하다고 본다.

잠정 결론

  • 아무튼, Git 기반으로 소스코드 관리가 아닌 pdf,dwg,stp 같은 바이너리 파일(도면 포함)을 분산 버전 관리할 수 있도록 솔루션을 구성하고 이를 사업화하는 방법도 꽤 재미있는 아이디어 아닐까 싶다.
  • 한국에서 이게 나올 확률은 0%에 수렴할 것 같아 조금 슬프다.

2015년 6월 3일 수요일

FGPG_V11 ::: Introduce of Fine Involute Gear Tooth Profile Generator


FGPG_V11


새로운 인볼류트 기어 설계용 프로그램을 작성해 보았다.
기존의 GPG_V08을 대체한다는 의미로 FGPG 라고 이름을 붙였다.
Fine Gear Profile Generator 의 줄임말이다.
아직 미완성본이지만, 일단 필요한 기능들은 대부분 어설프게나마 다 구현되었으므로 기록 차원에서 올려본다.

특징은 대략 다음과 같다.

1. Julia Language로 작성되었다.
2. 아직 독립 실행파일(Standalone Executable)은 빌드하지 않았다.
3. Julia를 이용하여 Python MatPlotLib의 플랏팅 능력을 활용한다.
4. Gmsh 및 Elmer를 이용하여 자동적으로 FEM 해석을 실시해 주는 Batch 파일을 만들어준다.  윈도우용 뿐만 아니라, 리눅스용 Shell Script도 만들어지므로 운영체제에 상관없이 사용 가능하다.
5. 입력하는 파라미터들은 별도의 파일로 작성해 두면, 그것을 읽어서 계산해 낸다.
6. GUI는 없다.
7. AutoCAD 또는 DraftSight에서 기어 형상을 자동으로 그려줄 수 있도록 Script 파일이 자동으로 생성된다.  여기서 dxf 파일로 저장한 후, 3D CAD 에서 모델링 할 수 있다.


우선 프로그램의 소스코드는 다음과 같다.
파일 이름은 FGPG_V11.jl 이다.  다른 이름을 붙여도 상관없다.



#########################################################################
# FGPG (Fine_Gear_Profile_Generator) V11
# 20150602
# by Dongho Kim  from Korea
# dymaxion.kim@gmail.com
# http://dymaxionkim.blogspot.kr/
#########################################################################
## Ref.
# http://tro.kr/29
# http://en.wikipedia.org/wiki/Coordinate_rotations_and_reflections
# http://dip28p.web.fc2.com/
#########################################################################
## History
# 20141114 V0.44 : SageMath Version
# 20150519 V0.1 : Trying to Conversion for Julia, Trying Interact Macro
# 20150519 V0.2 : Studying about Lack
# 20150527 V01 : Plotting Whole Gear at first
# 20150527 V02 : Trying Interact Macro
# 20150527 V03 : Debug V01
# 20150527 V04 : Adding Annotations, Saving Profile Data File, ...
# 20150527 V05 : Adding More Error Checks, Making geo File
# 20150528 V06 : Debug Making geo File
# 20150528 V07 : Align vertically Gear, Add Shaft
# 20150528 V08 : Modifying Sequences, Debug Making geo File, Make new Figure, Make svg Pictures, Save scr File, Modify Annotations
# 20150530 V09 : Modifying Shaft, Internal-External Condition, geo File
# 20150531 V10 : Adding version Variable
# 20150602 V11 : Making case.sif, case.bat, case.sh files
#########################################################################
## Todo
# Write Theory Doc
# Write User Manual
# Write Some Actual Examples
# Tip Relief to Modifying Tooth Profile
# Making Spec Table
# Making case.sif
# Profile Repairing in Undercut Condition
# Technical Error Check & Anouncing
# Change Variable Names for easy code reading
# Re-code in Functional Criteria (?)
# Wndows Batch File
# Bash Script
# Change Result File Name based in Time
# Build to Standalone Executable

# On JUNO envirenment
cd(dirname(@__FILE__))

using PyPlot
PyPlot.svg(true)

############################
# Parameters
version = "v11"
m = 1  # Module
z = 20  # Teeth Number
alpha_0_deg = 20  # Pressure Angle [Deg]
x = 0.2  # Offset Factor
b = 0  # Backlash Factor
a = 1.0  # Addendum of Tooth
d = 1.2  # Dedendum of Tooth
c = 0.3  # Radius Factor of Edge Round of Hob
e = 0.2  # Radius Factor of Edge Round of Tooth
# Center of Gear
x_0 = 0
y_0 = 0
# Segmentation Points Numbers for each curves
seg_circle = 360
seg_involute = 50
seg_edge_r = 4
seg_root_r = 20
seg_center = 2
seg_outter = 4
seg_root = 4


############################
# Input Parameters from input4fgpg.csv file
input4fgpg = readcsv("input4fgpg.csv")
input4fgpg[:,3] = float64(input4fgpg[:,3])
m = input4fgpg[1,3]
z = int64(input4fgpg[2,3])
alpha_0_deg = input4fgpg[3,3]
alpha_0 = alpha_0_deg*2*pi/360
x = input4fgpg[4,3]
b = input4fgpg[5,3]
a = input4fgpg[6,3]
d = input4fgpg[7,3]
c = input4fgpg[8,3]
e = input4fgpg[9,3]
x_0 = input4fgpg[10,3]
y_0 = input4fgpg[11,3]
seg_circle = int64(input4fgpg[12,3])
seg_involute = int64(input4fgpg[13,3])
seg_edge_r = int64(input4fgpg[14,3])
seg_root_r = int64(input4fgpg[15,3])
seg_center = int64(input4fgpg[16,3])
seg_outter = int64(input4fgpg[17,3])
seg_root = int64(input4fgpg[18,3])


############################
# Involute Curve
# alpha_m = Center Line's Slope [Rad]
alpha_m = pi/z
# alpha_is = Start Angle for Involute Curve
alpha_is = alpha_0 + pi/(2*z) + b/(z*cos(alpha_0)) - (1+2*x/z)*sin(alpha_0)/cos(alpha_0)
# theta_is = Minimum Range of Parameter to Draw Involute Curve
theta_is = sin(alpha_0)/cos(alpha_0) + 2*(c*(1-sin(alpha_0))+x-d)/(z*cos(alpha_0)*sin(alpha_0))
# theta_ie = Maximum Range of Parameter to Draw Involute Curve
theta_ie = 2*e/(z*cos(alpha_0)) + sqrt( ((z+2*(x+a-e))/(z*cos(alpha_0)))^2 - 1 )
# Condition of theta_ie
if alpha_malpha_m && alpha_m>alpha_is+theta_ie-atan(theta_ie)
    e = (z/2)*cos(alpha_0)*( theta_ie - sqrt( (1/cos(alpha_is+theta_ie-alpha_m))^2-1 ) )
end
# x_e, y_e = Location of Tooth's End Point
x_e = x_0 + m*((z/2)+x+a)*cos(alpha_e)
y_e = y_0 + m*((z/2)+x+a)*sin(alpha_e)
# x_e0, y_e0 = Location of Edge Round Center
x_e0 = m*(z/2+x+a-e)*cos(alpha_e) + x_0
y_e0 = m*(z/2+x+a-e)*sin(alpha_e) + y_0
# Parameter Range of Edge Round
theta3_min = atan((Y1[length(Y1)]-y_e0)/(X1[length(X1)]-x_e0))
theta3_max = atan((y_e-y_e0)/(x_e-x_e0))
THETA3 = linspace(theta3_min,theta3_max,seg_edge_r)
X3 = m*e*cos(THETA3)+x_e0
Y3 = m*e*sin(THETA3)+y_e0
#plot(X3,Y3,color="green",linestyle="-")


############################
# Root Round Curve of Tooth
# Condition Check
# alpha_ts = Start Angle of Root Round Curve
# THETA_s = Substitution Variable to plot Root Round Curve
alpha_ts = (2*(c*(1-sin(alpha_0))-d)*sin(alpha_0)+b)/(z*cos(alpha_0)) - 2*c*cos(alpha_0)/z + pi/(2*z)
theta_te = 2*c*cos(alpha_0)/z - 2*(d-x-c*(1-sin(alpha_0)))*cos(alpha_0)/(z*sin(alpha_0))
THETA_t = linspace(0,theta_te,seg_root_r)
if c!=0 && (d-x-c)==0
    # mc를 반지름으로 하는 원호를 그려서 대체하게 됨
    THETA_s = (pi/2)*ones(length(THETA_t))
    elseif c==0 && (d-x-c)==0
    # 루트커브 작도 생략하고, (x_is,y_is) 점을 대칭으로 이뿌리호를 그려서 대체하게 됨
    elseif (d-x-c)!=0
    THETA_s = atan((m*z*THETA_t/2)/(m*d-m*x-m*c))
end
X_t = x_0 + m*( (z/2+x-d+c)*cos(THETA_t+alpha_ts) + (z/2)*THETA_t.*sin(THETA_t+alpha_ts) - c*cos(THETA_s+THETA_t+alpha_ts) )
Y_t = y_0 + m*( (z/2+x-d+c)*sin(THETA_t+alpha_ts) - (z/2)*THETA_t.*cos(THETA_t+alpha_ts) - c*sin(THETA_s+THETA_t+alpha_ts) )
#plot(X_t,Y_t,color="green",linestyle="-")


############################
# Outter Arc
#alpha_em = 2*alpha_m-alpha_e
#x_em = x_0 + m*(z/2+x+a)*cos(alpha_em)
#y_em = y_0 + m*(z/2+x+a)*cos(alpha_em)
THETA6 = linspace(alpha_e,alpha_m,seg_outter)
X6 = m*(z/2+a+x)*cos(THETA6)+x_0
Y6 = m*(z/2+a+x)*sin(THETA6)+y_0
#plot(X6,Y6,color="red",linestyle="-")


############################
# Root Arc
#x_rm = x_0 + m*(z/2+x-d)*cos(alpha_ts)
#y_rm = y_0 - m*(z/2+x-d)*sin(alpha_ts)
THETA7 = linspace(0,alpha_ts,seg_root)
X7 = m*(z/2-d+x)*cos(THETA7)+x_0
Y7 = m*(z/2-d+x)*sin(THETA7)+y_0
#plot(X7,Y7,color="red",linestyle="-")


############################
# Combine Curves
# Not using Root Round Curve (X_t,Y_t)
if c==0 && (d-x-c)==0
    Xc = X7[1:length(X7)-1]
    Yc = Y7[1:length(Y7)-1]
else
    Xc = [ X7[1:length(X7)-1]; X_t[1:length(X_t)-1] ]
    Yc = [ Y7[1:length(Y7)-1]; Y_t[1:length(Y_t)-1] ]
end
# Not using Edge Round Curve (X3,Y3)
if e==0 || alpha_m==alpha_is+theta_ie-atan(theta_ie)
    Xc = [ Xc; X1[1:length(X1)-1] ]
    Yc = [ Yc; Y1[1:length(Y1)-1] ]
else
    Xc = [ Xc; X1[1:length(X1)-1]; X3[1:length(X3)-1] ]
    Yc = [ Yc; Y1[1:length(Y1)-1]; Y3[1:length(Y3)-1] ]
end
# Not using Outter Arc (X6,Y6)
if alpha_e == alpha_m || alpha_m==alpha_is+theta_ie-atan(theta_ie)
    Xc = Xc
    Yc = Yc
else
    Xc = [ Xc; X6[1:length(X6)-1] ]
    Yc = [ Yc; Y6[1:length(Y6)-1] ]
end
#Xc = [ X7[1:length(X7)-1]; X_t[1:length(X_t)-1]; X1[1:length(X1)-1]; X3[1:length(X3)-1]; X6[1:length(X6)-1]]
#Yc = [ Y7[1:length(Y7)-1]; Y_t[1:length(Y_t)-1]; Y1[1:length(Y1)-1]; Y3[1:length(Y3)-1]; Y6[1:length(Y6)-1]]
#plot(Xc,Yc,color="black",linestyle="-",linewidth=2)



############################
# Make Whole One Tooth
Xc2 = Xc[2:length(Xc)]
Yc2 = Yc[2:length(Yc)]
# Reflect Transform
Xcc = cos(2*alpha_m)*(Xc2-x_0) + sin(2*alpha_m)*(Yc2-y_0)
Ycc = sin(2*alpha_m)*(Xc2-x_0) - cos(2*alpha_m)*(Yc2-y_0)
# Location Transform to (x_0,y_0)
Xcc = Xcc + x_0
Ycc = Ycc + y_0
# Invert
Xcc = Xcc[length(Xcc):-1:1]
Ycc = Ycc[length(Ycc):-1:1]
# Combine
Xcc = [Xc; Xcc]
Ycc = [Yc; Ycc]
#plot(Xcc,Ycc,color="black",linestyle="-",linewidth=2)


############################
# Align to Top
align_angle = pi/2-pi/z
X_align = Xcc
Y_align = Ycc
# Rotate
X_align = cos(align_angle)*(Xcc-x_0) - sin(align_angle)*(Ycc-y_0)
Y_align = sin(align_angle)*(Xcc-x_0) + cos(align_angle)*(Ycc-y_0)
# Location Transform to (x_0,y_0)
X_align = X_align + x_0
Y_align = Y_align + y_0
#plot(X_align,Y_align,color="black",linestyle="-",linewidth=2)


############################
# Make Whole Gear
p_angle = 2*pi/z
Xccc = X_align
Yccc = Y_align
Xtemp = X_align[2:length(X_align)]
Ytemp = Y_align[2:length(Y_align)]
for i = 1:z-1
    # Rotate
    Xtemp = cos(p_angle*i)*(X_align[2:length(X_align)]-x_0) - sin(p_angle*i)*(Y_align[2:length(Y_align)]-y_0)
    Ytemp = sin(p_angle*i)*(X_align[2:length(X_align)]-x_0) + cos(p_angle*i)*(Y_align[2:length(Y_align)]-y_0)
    # Location Transform to (x_0,y_0)
    Xtemp = Xtemp + x_0
    Ytemp = Ytemp + y_0
    Xccc = [Xccc; Xtemp]
    Yccc = [Yccc; Ytemp]
end



############################
# Plot Figure
f=figure(figsize=(10,10))
grid("on")
title("FGPG (Fine_Gear_Profile_Generator) $version \n by Dymaxion.Kim")
axis("equal")


############################
# Base, Pitch, Offset, Outter, Root Circle
THETA0 = linspace(0.0,2*pi,seg_circle)
# Base Circle
base_dia = m*z*cos(alpha_0)
X_base = base_dia/2*sin(THETA0) + x_0
Y_base = base_dia/2*cos(THETA0) + y_0
plot(X_base, Y_base, color="cyan", linestyle="--")
# Pitch Circle
pitch_dia = m*z
X_pitch = pitch_dia/2*sin(THETA0) + x_0
Y_pitch = pitch_dia/2*cos(THETA0) + y_0
plot(X_pitch, Y_pitch, color="magenta", linestyle="--")
# Offset Circle
offset_dia = 2*m*(z/2+x)
X_offset = (offset_dia/2)*sin(THETA0) + x_0
Y_offset = (offset_dia/2)*cos(THETA0) + y_0
plot(X_offset, Y_offset, color="red", linestyle="--")
# Outter Circle
outter_dia = 2*m*(z/2+x+a)
X_out = (outter_dia/2)*sin(THETA0) + x_0
Y_out = (outter_dia/2)*cos(THETA0) + y_0
plot(X_out, Y_out, color="brown", linestyle="--")
# Root Circle
root_dia = 2*m*(z/2+x-d)
X_root = (root_dia/2)*sin(THETA0) + x_0
Y_root = (root_dia/2)*cos(THETA0) + y_0
plot(X_root, Y_root, color="grey", linestyle="--")


############################
# Center Line of Tooth
X_m = linspace(x_0, (m*(z/2+a+x))*cos(alpha_m)+x_0, seg_center)
Y_m = (X_m-x_0)*tan(alpha_m) + y_0
plot(X_m,Y_m,color="orange",linestyle="--")


############################
# Shaft
THETA8 = linspace(0,2*pi,2*z)
if ad  # Internal Gear
    d_shaft = m*(z+x+a)+m*(4*(a+d))
    elseif a==d  # No correct
    #d_shaft = m*(z+x-d)-m*(2*(a+d))
    d_shaft = 0
end
X_shaft = (d_shaft/2)*sin(THETA8) + x_0
Y_shaft = (d_shaft/2)*cos(THETA8) + y_0
plot(X_shaft, Y_shaft, color="black", linestyle="-", linewidth=2)


############################
# Plot Whole Gear
plot(Xccc,Yccc,color="black",linestyle="-",linewidth=2)
char_scale = 0.03*m*z
annotate(["Module = $m mm"],xy=(x_0,y_0+5*char_scale),ha="center",va="center",color="black",fontsize="x-small")
annotate(["Teeth Number = $z ea"],xy=(x_0,y_0+4*char_scale),ha="center",va="center",color="black",fontsize="x-small")
annotate(["Pressure Angle = $alpha_0_deg DEG"],xy=(x_0,y_0+3*char_scale),ha="center",va="center",color="black",fontsize="x-small")
annotate(["Offset Factor = $x"],xy=(x_0,y_0+2*char_scale),ha="center",va="center",color="black",fontsize="x-small")
annotate(["Backlash Factor = $b"],xy=(x_0,y_0+1*char_scale),ha="center",va="center",color="black",fontsize="x-small")
annotate(["Addendum Factor = $a"],xy=(x_0,y_0+0*char_scale),ha="center",va="center",color="black",fontsize="x-small")
annotate(["Dedendum Factor = $d"],xy=(x_0,y_0-1*char_scale),ha="center",va="center",color="black",fontsize="x-small")
annotate(["Radius Factor of Edge Round of Hob = $c"],xy=(x_0,y_0-2*char_scale),ha="center",va="center",color="black",fontsize="x-small")
annotate(["Radius Factor of Edge Round of Tooth = $e"],xy=(x_0,y_0-3*char_scale),ha="center",va="center",color="black",fontsize="x-small")
annotate(["Center Position [mm] = ($x_0,$y_0)"],xy=(x_0,y_0-4*char_scale),ha="center",va="center",color="black",fontsize="x-small")
legend(["Base Circle Dia = $base_dia", "Pitch Circle Dia = $pitch_dia", "Offset Circle Dia = $offset_dia",
    "Outter Circle Dia = $outter_dia", "Root Circle Dia = $root_dia", "Center Line"], loc="lower left", fontsize="x-small")
savefig("case01.svg")





############################
# Figuer 2
f2=figure(figsize=(10,10))
grid("on")
title("FGPG (Fine_Gear_Profile_Generator) $version \n by Dymaxion.Kim")
axis("equal")


############################
# Base, Pitch, Offset, Outter, Root Circle 2
THETA9 = linspace(atan(X_align[length(X_align)]/Y_align[length(Y_align)]),
                  atan(X_align[1]/Y_align[1]), int(seg_circle/10))
# Base Circle
X_base9 = base_dia/2*sin(THETA9) + x_0
Y_base9 = base_dia/2*cos(THETA9) + y_0
plot(X_base9, Y_base9, color="cyan", linestyle="--")
# Pitch Circle
X_pitch9 = pitch_dia/2*sin(THETA9) + x_0
Y_pitch9 = pitch_dia/2*cos(THETA9) + y_0
plot(X_pitch9, Y_pitch9, color="magenta", linestyle="--")
# Offset Circle
X_offset9 = (offset_dia/2)*sin(THETA9) + x_0
Y_offset9 = (offset_dia/2)*cos(THETA9) + y_0
plot(X_offset9, Y_offset9, color="red", linestyle="--")
# Outter Circle
X_out9 = (outter_dia/2)*sin(THETA9) + x_0
Y_out9 = (outter_dia/2)*cos(THETA9) + y_0
plot(X_out9, Y_out9, color="brown", linestyle="--")
# Root Circle
X_root9 = (root_dia/2)*sin(THETA9) + x_0
Y_root9 = (root_dia/2)*cos(THETA9) + y_0
plot(X_root9, Y_root9, color="grey", linestyle="--")


############################
# Plot only One Tooth
plot(X_align,Y_align,color="black",linestyle="-",linewidth=1)
plot(X_align,Y_align,"b.")
base_radius = base_dia/2
pitch_radius = pitch_dia/2
offset_radius = offset_dia/2
outter_radius = outter_dia/2
root_radius = root_dia/2
xmm = x*m
emm = e*m
bmm = b*m
cmm = c*m
annotate(["Base Radius = $base_radius mm"],xy=(0,base_dia/2),ha="center",va="bottom",color="cyan",fontsize="x-small")
annotate(["Pitch Radius = $pitch_radius mm"],xy=(0,pitch_dia/2),ha="left",va="bottom",color="magenta",fontsize="x-small")
annotate(["Offset Radius = $offset_radius mm"],xy=(0,offset_dia/2),ha="right",va="bottom",color="red",fontsize="x-small")
annotate(["Outter Radius = $outter_radius mm"],xy=(0,outter_dia/2),ha="right",va="bottom",color="brown",fontsize="x-small")
annotate(["Root Radius = $root_radius mm"],xy=(0,root_dia/2),ha="center",va="bottom",color="grey",fontsize="x-small")
annotate(["Offset = $xmm mm"],xy=(0,offset_dia/2),ha="right",va="top",color="red",fontsize="x-small")
annotate(["Radius of Hob Edge = $cmm mm"],
        xy=(X_align[seg_root+int(seg_root_r/2)],Y_align[seg_root+int(seg_root_r/2)]),
        ha="right",va="top",color="blue",fontsize="x-small")
annotate(["Radius of Edge = $emm mm"],
        xy=(X_align[seg_root+seg_root_r+seg_involute],Y_align[seg_root+seg_root_r+seg_involute]),
        ha="left",va="bottom",color="blue",fontsize="x-small")
annotate(["Backlash = $bmm mm"],
        xy=(X_align[seg_root+seg_root_r+int(seg_involute/2)],Y_align[seg_root+seg_root_r+int(seg_involute/2)]),
        ha="left",va="bottom",color="blue",fontsize="x-small")
amm = a*m
dmm = d*m
hmm = amm+dmm
tmm = 2*X_align[seg_root+seg_root_r]
#temm = 2*X_align[seg_root+seg_root_r+seg_involute+int(seg_edge_r/2)]
temm = 2*X_align[seg_root+seg_root_r+seg_involute]
annotate(["Addendum = $amm mm"],xy=(X_out9[1],Y_out9[1]),ha="left",va="top",color="black",fontsize="x-small")
annotate(["Dedendum = $dmm mm"],xy=(X_root9[1],Y_root9[1]),ha="left",va="top",color="black",fontsize="x-small")
annotate(["Tooth Height = $hmm mm"],xy=(X_offset9[1],Y_offset9[1]),ha="left",va="top",color="black",fontsize="x-small")
annotate(["Approx Tooth Thickness = $tmm mm"],xy=(0,base_dia/2),ha="center",va="top",color="black",fontsize="x-small")
annotate(["Approx Tooth End Thickness = $temm mm"],xy=(0,outter_dia/2),ha="center",va="top",color="black",fontsize="x-small")

savefig("case02.svg")





############################
# Save case.csv
writecsv("case.csv",[Xccc Yccc])


############################
# Save case.geo
fileout = open("case.geo", "w")
println(fileout, "cl__1 = 1;")
num_Point_gear = length(Xccc)
num_Line_gear = num_Point_gear-1
num_Point_shaft = num_Point_gear + length(X_shaft) -1
num_Line_shaft = num_Point_shaft-1
for i=1:num_Point_gear
    println(fileout, "Point(",i,") = {",Xccc[i],", ",Yccc[i],", ",0,", ","cl__1};")
end
for i=num_Point_gear+1:num_Point_shaft
    println(fileout, "Point(",i,") = {",X_shaft[i-num_Point_gear],", ",Y_shaft[i-num_Point_gear],", ",0,", ","cl__1};")
end
for j=1:num_Line_gear
    println(fileout, "Line(",j,") = {",j,", ",j+1,"};")
end
println(fileout, "Line(",num_Line_gear+1,") = {",num_Line_gear+1,", ",1,"};")
for j=num_Line_gear+2:num_Line_shaft
    println(fileout, "Line(",j,") = {",j,", ",j+1,"};")
end
println(fileout, "Line(",num_Line_shaft+1,") = {",num_Line_shaft+1,", ",num_Line_gear+2,"};")
# Combine
array_LineLoop = [1:num_Line_shaft+1]
array_LineLoop[num_Line_gear+2:num_Line_shaft+1] = -array_LineLoop[num_Line_gear+2:num_Line_shaft+1]
array_LineLoop = string(array_LineLoop)
array_LineLoop = array_LineLoop[2:length(array_LineLoop)-1]
println(fileout, "Line Loop(",num_Line_shaft+3,") = {",array_LineLoop,"};")
println(fileout, "Plane Surface(",num_Line_shaft+3,") = {",num_Line_shaft+3,"};")
#Physical Line(13) = {8, 9};
#Physical Surface(14) = {11};
array_PhysicalLine = [num_Line_gear+2:num_Line_shaft+1]
array_PhysicalLine = string(array_PhysicalLine)
array_PhysicalLine = array_PhysicalLine[2:length(array_PhysicalLine)-1]
#println(fileout, "Physical Line(",num_Line_shaft+4,") = {",array_PhysicalLine,"};")
# Position for Normal Force
#position_NormalForce = (length(Xcc)-1) - (seg_root+seg_root_r-2) - (seg_involute-3)
#println(fileout, "Physical Line(",num_Line_shaft+5,") = {",position_NormalForce,"};")
# Fixed
#println(fileout, "Physical Surface(",num_Line_shaft+6,") = {",num_Line_shaft+3,"};")
println(fileout, "Mesh.ElementOrder = 2;")
println(fileout, "Mesh.RecombineAll = 1;")
close(fileout)


############################
# Save case.scr
fileout = open("case.scr", "w")
println(fileout, "spline")
for i=1:num_Point_gear
    println(fileout, Xccc[i],",",Yccc[i],",0")
end
println(fileout, Xccc[1],",",Yccc[1],",0")
println(fileout, "  ")
println(fileout, "circle")
println(fileout, x_0,",",y_0)
println(fileout, d_shaft/2)
close(fileout)


############################
# Save case.sif

fileout = open("case.sif", "w")
println(fileout, "Header")
println(fileout, "  CHECK KEYWORDS Warn")
println(fileout, "  Mesh DB \".\" \".\"")
println(fileout, "  Include Path \"\"")
println(fileout, "  Results Directory \"\"")
println(fileout, "End")
println(fileout, "")
println(fileout, "Simulation")
println(fileout, "  Max Output Level = 5")
println(fileout, "  Coordinate System = Cartesian")
println(fileout, "  Coordinate Mapping(3) = 1 2 3")
println(fileout, "  Simulation Type = Steady state")
println(fileout, "  Steady State Max Iterations = 1")
println(fileout, "  Output Intervals = 1")
println(fileout, "  Timestepping Method = BDF")
println(fileout, "  BDF Order = 1")
println(fileout, "  Solver Input File = case.sif")
println(fileout, "  Post File = case.ep")
println(fileout, "  Coordinate Scaling = Real 0.001")
println(fileout, "End")
println(fileout, "")
println(fileout, "Constants")
println(fileout, "  Gravity(4) = 0 -1 0 9.82")
println(fileout, "  Stefan Boltzmann = 5.67e-08")
println(fileout, "  Permittivity of Vacuum = 8.8542e-12")
println(fileout, "  Boltzmann Constant = 1.3807e-23")
println(fileout, "  Unit Charge = 1.602e-19")
println(fileout, "End")
println(fileout, "")
println(fileout, "Body 1")
println(fileout, "  Target Bodies(1) = 1")
println(fileout, "  Name = \"Body 1\"")
println(fileout, "  Equation = 1")
println(fileout, "  Material = 1")
println(fileout, "End")
println(fileout, "")
println(fileout, "Solver 1")
println(fileout, "  Equation = Result Output")
println(fileout, "  Single Precision = True")
println(fileout, "  Procedure = \"ResultOutputSolve\" \"ResultOutputSolver\"")
println(fileout, "  Save Geometry Ids = True")
println(fileout, "  Output File Name = case_output")
println(fileout, "  Output Format = Gmsh")
println(fileout, "  Exec Solver = After Timestep")
println(fileout, "End")
println(fileout, "")
println(fileout, "Solver 2")
println(fileout, "  Equation = Linear elasticity")
println(fileout, "  Procedure = \"StressSolve\" \"StressSolver\"")
println(fileout, "  Variable = -dofs 2 Displacement")
println(fileout, "  Exec Solver = Always")
println(fileout, "  Stabilize = True")
println(fileout, "  Bubbles = False")
println(fileout, "  Lumped Mass Matrix = False")
println(fileout, "  Optimize Bandwidth = True")
println(fileout, "  Steady State Convergence Tolerance = 1.0e-5")
println(fileout, "  Nonlinear System Convergence Tolerance = 1.0e-7")
println(fileout, "  Nonlinear System Max Iterations = 20")
println(fileout, "  Nonlinear System Newton After Iterations = 3")
println(fileout, "  Nonlinear System Newton After Tolerance = 1.0e-3")
println(fileout, "  Nonlinear System Relaxation Factor = 1")
println(fileout, "  Linear System Solver = Iterative")
println(fileout, "  Linear System Iterative Method = BiCGStab")
println(fileout, "  Linear System Max Iterations = 500")
println(fileout, "  Linear System Convergence Tolerance = 1.0e-10")
println(fileout, "  BiCGstabl polynomial degree = 2")
println(fileout, "  Linear System Preconditioning = Diagonal")
println(fileout, "  Linear System ILUT Tolerance = 1.0e-3")
println(fileout, "  Linear System Abort Not Converged = False")
println(fileout, "  Linear System Residual Output = 1")
println(fileout, "  Linear System Precondition Recompute = 1")
println(fileout, "End")
println(fileout, "")
println(fileout, "Equation 1")
println(fileout, "  Name = \"Equation 1\"")
println(fileout, "  Calculate Stresses = True")
println(fileout, "  Active Solvers(2) = 1 2")
println(fileout, "End")
println(fileout, "")
println(fileout, "Material 1")
println(fileout, "  Name = \"Steel (carbon - generic)\"")
println(fileout, "  Heat Conductivity = 44.8")
println(fileout, "  Youngs modulus = 200.0e9")
println(fileout, "  Mesh Poisson ratio = 0.285")
println(fileout, "  Heat Capacity = 1265.0")
println(fileout, "  Density = 7850.0")
println(fileout, "  Poisson ratio = 0.285")
println(fileout, "  Sound speed = 5100.0")
println(fileout, "  Heat expansion Coefficient = 13.8e-6")
println(fileout, "End")
println(fileout, "")
println(fileout, "Boundary Condition 1")
println(fileout, "  Target Boundaries(47) = 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 ")
println(fileout, "  Name = \"Fixed\"")
println(fileout, "  Displacement 2 = 0")
println(fileout, "  Displacement 1 = 0")
println(fileout, "End")
println(fileout, "")
println(fileout, "Boundary Condition 2")
println(fileout, "  Target Boundaries(1) = 52 ")
println(fileout, "  Name = \"NormalForce\"")
println(fileout, "  Normal Force = -100")
println(fileout, "End")
println(fileout, "")
close(fileout)


############################
# Save case.bat
fileout = open("case.bat", "w")
println(fileout, "gmsh -2 case.geo")
println(fileout, "ElmerGrid 14 2 case.msh")
println(fileout, "move .\\case\\*.* .\\")
println(fileout, "rmdir case")
println(fileout, "ElmerSolver case.sif")
println(fileout, "gmsh case_output.msh")
close(fileout)


############################
# Save case.sh
fileout = open("case.sh", "w")
println(fileout, "#!/bin/sh")
println(fileout, "gmsh -2 ./case.geo")
println(fileout, "ElmerGrid 14 2 case.msh")
println(fileout, "mv ./case/* ./")
println(fileout, "rmdir case")
println(fileout, "ElmerSolver ./case.sif")
println(fileout, "gmsh ./case_output.msh")
close(fileout)
if OS_NAME == "Linux"
    run(`chmod 751 case.sh`)
end


이것을 실행하려면 우선 Julia가 실행 가능한 환경이어야 한다.
자신의 PC에 Julia가 깔려있지 않고, 또 깔고 싶지 않다면 http://juliabox.org 사이트에서 클라우드 서비스를 제공해 주므로 거기서 소스코드를 복사해서 붙여넣은 후 실행해도 된다.
실행 후 생성된 파일들을 다운로드 받을 수 있기 때문에 전혀 문제가 안된다.

(사실 이것도 불편하기 때문에, 추후에 윈도우용 및 리눅스용 독립 실행파일을 생성해서 제공해 주려고 한다.  아직 Julia v0.3.9 에서는 독립 실행 파일 생성이 불가능하고, 조만간 나올 Julia v0.4 부터는 독립실행파일을 생성해 줄 수 있는 패키지가 안정화되어 나올 예정이기 때문에 시간이 좀 필요하다.)

그리고, 이 프로그램은 기어 파라미터를 설정해 준 별도의 파일을 통해 입력받도록 해 놨기 때문에 입력용 파일을 따로 작성해 둬야 한다.  파일 이름은 input4fgpg.csv 로 해야 한다.
이 파일은 콤마로 필드가 구분된 텍스트파일이다.  엑셀 같은 스프레드 시트에서 읽으면 다루기 쉽다.

내용은 아래와 같다.



Module,m,1
Teeth Number,z,24
Pressure Angle [Deg],alpha_0_deg,20
Offset Factor,x,0.2
Backlash Factor,b,0.025
Addendum Factor,a,1.2
Dedendum Factor,d,1.0
Radius Factor of Edge Round of Hob,c,0.3
Radius Factor of Edge Round of Tooth,e,0.2
Center of Gear,x_0,0
Center of Gear,y_0,0
Segmentation Numbers,seg_circle,360
Segmentation Numbers,seg_involute,20
Segmentation Numbers,seg_edge_r,4
Segmentation Numbers,seg_root_r,10
Segmentation Numbers,seg_center,2
Segmentation Numbers,seg_outter,6
Segmentation Numbers,seg_root,6



기어 사양을 변경하고 싶다면, 위 내용 중에 다른 것은 건드리지 말고 매 행의 맨 마지막 필드의 숫자만 적절히 바꿔주면 된다.
 기어 사양에 관한 자세한 설명은 나중에 버전업 하고 나서 쓸만하게 되면 자세한 도큐먼트를 작성하려고 하므로 일단 생략한다.


아무튼 PC에 Julia 환경이 구축된 경우의 작업 예시를 순서대로 설명해 보겠다.
본 예제에서는 윈도우7 환경이다.




일단 작업하려고 하는 디렉토리를 만들고, 소스코드 및 입력파일을 위와 같이 넣는다.
Juliabox.org 에서 작업할 경우도 역시 마찬가지다.  작업하려는 디렉토리를 만들고 똑같이 넣어두면 된다.



 FGPG_V11.jl 소스코드를 실행하기 위해서는 크게 3가지 방법이 있다.
그냥 cmd 즉 소위 말하는 도스창에서



julia FGPG_V11.jl 



이렇게 커맨드를 직접 때려주는 방법이 있고, 또 다른 방법은 위 그림과 같이 JUNO IDE를 실행시켜서 여기서 실행해 주는 방법이 있다.
마지막으로는 Juliabox.org의 웹브라우저 상에서의 개발환경에서 실행해 줄 수 있다.  Juliabox.org는 기본적으로 IPython Notebook 기반이기 때문에, IJulia Notebook을 자신의 PC에 설치해서 똑같이 해도 된다.

본 예제에서는 위 그림 처럼 JUNO 상에서 실행한다.
 실행 전에 input4fgpg.csv 파일도 열어서 내용 확인하고 적절히 수정해 준 후에, 다시 재 실행하면서 기어 형상 변화를 확인해 갈 수 있을 것이다.


JUNO 에서 소스를 실행하려면, 아래 그림처럼 메뉴 중에서 "Eval All"을 선택해 주면 된다.


참고로, 본 소스코드는 외부 패키지는  PyPlot만 사용한다.
가급적 외부 의존성을 줄여서 간단하게 만들기 위해서이다.
Julia는 JIT 방식이기 때문에, 최초 실행시에는 로딩 및 내부 컴파일 시간이 좀 걸린다.
하지만 두 번째 실행 부터는 상당히 빠르게 수행된다.

실행이 완료되면, 우선 아래와 같은 2개의 그림이 별도 창으로 뜬다.



이것을 보면서 기어의 형상 및 결과로 나오는 주요 치수들을 확인할 수 있다.
기어 사양이 적절치 않다면, input4fgpg.csv 안의 파라미터 값들을 조절해 주면서 반복 실행해 주면 된다.

아울러, 실행이 완료되면 작업 디렉토리 안에 여러 가지 파일들이 만들어져 있음을 볼 수 있다.


우선 case.csv 파일은, 생성된 기어 형상의 원시(Raw) 데이타이다.
이것을 엑셀에서 읽어서 그래프로 그대로 그려줄 수 있다.
또는 Matlab이나 Octave, Scilab 등에서 읽어서 이용할 수도 있다.


case01.svg 및 case02.svg 파일들은 앞서 나타났던 기어 형상 그림들이 파일로 저장된 것이다.
벡터 그림 파일이기 때문에, Inkscape 또는 Adobe Illustrator 같은 것으로 읽어서 활용할 수 있다.
아래의 그림은 Inkscape에서 읽어본 것이다.


case.scr 파일은 AutoCAD Script 파일이다.
AutoCAD 대신 DraftSight에서도 실행 가능하기 때문에, DraftSight에서 실행한 예를 아래 그림과 같이 볼 수 있다.


DraftSight를 실행해서 빈 화면이 나오며느, 화면 아래쪽에 "도면요소 스냅"이 보통 기본적으로 눌러진 상태로 되어 있다.  이것을 마우스로 한 번 눌러줘서 해제 상태로 바꾼다.


"도면요소 스냅"을 해제하는 이유는, 눌러진 상태에서 case.scr 파일을 실행시키면 제대로 그려지지 않고 이상하게 나오기 때문이다.  AutoCAD 에서도 똑같다.



아무튼, 이렇게 실행하면 약간의 수초~십수초 정도의 시간을 소요한 후에 기어 형상이 그려져 있음을 확인할 수 있다.
기어의 치형은 하나의 Spline으로 그려지도록 해 놨다.
또 Addendum 및 Dedendum 값을 비교해서 Internal & External Gear를 자동 판별해서 그려지도록 해 놨다.


이것을 case.dxf로 저장한다.  버전은 R2000~2002 버전으로 적당히 옛날 버전으로 해 두는 습관이 좋겠다.  왜냐면 구버전일수록 여러 소프트웨어를 가리지 않고 잘 열리기 때문이다.

한가지 팁은, R12 버전의 *.dxf 파일로 저장시키면 Spline이 많은 짧은 직선으로 구성된 Polyline으로 바꿔져서 저장된다.  R12 버전에서는 Spline 사양이 없기 때문이다.
이런 특징을 이용해서, Spline 궤적을 생성할 수 없는 공작기계로 가공할 경우에 (구형 와이어 방전 가공기나 밀링머신들은 Spline 궤적 보간 기능이 없고, 오로지 원호 보간 기능만 지원된다.  최신 기계들은 대부분 Spline 보간 기능이 있다.) 대응 가능할 것이다.



한편, case.bat 및 case.sh 파일은 각각 윈도우 및 리눅스용 해석 스크립트이다.
이 파일들을 실행시키면 PC에 이미 Gmsh 및 Elmer가 설치되어 있고 또 path가 잡혀 있는 경우에 한해서 이상없이 FEM 해석이 실시될 것이다.


Batch 파일의 스크립트 내용은 별것 없다.  위 그림과 같다.
순서대로 명령을 실행하는 것이다.
리눅스용 case.sh 파일 역시 같은 동작을 하도록 리눅스용 Bash 쉘에 맞춰서 내용이 들어있다.

gmsh -2 case.geo 명령은, case.geo 형상 파일을 이용해서 gmsh 프로그램으로 2차원 매쉬를 생성하라는 것이다.
매쉬 생성에는 수십초 이상이 소요될 것이다.  커맨트창이 떠서 진행 상황이 확인될 것이다.


gmsh 작업이 끝나면, case.msh 파일이 생성된다.

참고로, case.msh로 만들어지는 매쉬의 특성은, Delaunay 삼각형 자동 생성 알고리즘으로 2nd Order 조건으로 먼저 생성된 후에, Recombine 옵션을 줘 놨기 때문에 삼각형들을 합쳐서 사각형으로 바꿔준다.  따라서 결과물로 나오는 매쉬는 2nd Ordered Quad Mesh가 된다.

매쉬의 조밀함은 input4fgpg.csv 내용 중에서 Segmentation 값들을 올려주면 더 조밀하게 할 수 있다.  대신 해석시간은 기하급수적으로 증가될 것이다.

아무튼 이어서  곧바로 ElmerGrid 명령이 실행된다.  ElmerGrid는 case.msh 파일을 Elmer 전용의 매쉬 포멧으로 바꿔준다.  Elmer용 매쉬 파일은 단일 파일이 아니고, 속성별로 4개의 파일로 저장된다.


따라서 매쉬 생성 결과는, 위 그림과 같이 나타난다.
이어서 곧바로 ElmerSolver가 작동해서 해석을 시작한다.
각종 해석 조건을 설정한 ElmerSolver용 입력파일인 case.sif 파일은 이미 FGPG_V11.jl이 만들어 뒀다.
재질(Carbon Steel), 경계조건의 위치, 힘의 강도(100N)은 상수로 고정시켜 놨기 때문에 소스를 보고 수정해야 바꿀 수 있다.  일단은 잘 되는지 보기 위해 편의상 상수로 뒀다.


아무튼 이에 따라 ElmerSolver가 수행된다.  커맨드창에 나타나는 숫자들은 수렴 오차이다.  수렴오차를 계속 줄여가다가 설정된 오차값 안에 들어오면 해석이 완료되도록 되어 있다.


한참 기다리면 최종적으로 완료된다.  본 예제에서는 해석시간이 406.28초 걸렸다.


이제 어떤 파일이 생겼나 확인해 보면, case_output.msh 파일이 생겨나 있는 것을 확인할 수 있다.  물론 ElmerSolver 결과 파일인 case.ep 역시 함께 생성되어 있다.
이들 파일이 생성된 후에 자동으로 Post Processor로서 gmsh가 실행되어 결과 파일을 띄워준다.


gmsh는 처음 다뤄보는 사람이면 GUI 체계가 좀 생소해서 막막할 수 있는데, 기능들 자체는 매우 단순하다.  이리저리 보면서 건드려 보면 쉽게 습득 가능할 것이다.
일단 본 화면에서는 기본적으로 결과데이타 8가지가 한꺼번에 겹쳐서 보이므로 번잡스럽다.
6번이 VonMises 응력 데이타이므로, 이것을 제외하고 전부 Uncheck 한다.


일단 메뉴에서 Option을 선택해서 화면 상태를 조정해 줄 수 있도록 옵션을 건드려 본다.


Mesh의 Visability 중에서 Surface Edges, Volume Edges를 Uncheck 하면 좀 깔끔해진다.



그리고, Filled iso-values를 선택해서 등고선식으로 보이도록 해 본다.




등고선 단계는 50단계 정도로 해 본다.


그러면 좀 볼 만 하다.  다만 몇 개의 거슬리는 포인트들이 보인다.


거슬리는 포인트는 위 그림처럼 Points를 Uncheck 하면 안보이게 된다.



결과 확인.  그림은 캡춰하거나 다른이름으로 저장할 때 그림포멧으로 지정하면 된다.


이제 7번 Displacement를 본다.



상태 확인하고...


등고선식으로 보이도록 해 보고...


Displacement Factor를 크게 높여서 변형되는 형상을 과장해서 확인해 본다.
이런 식으로 결과 확인이 가능하다.


한 편, 앞서 만들어둔 기어 형상 2D CAD 파일인  case.dxf 파일을 3D CAD에서 읽어들여 모델링해 본다....

일단 FreeCAD에서 잘 되는지 확인.


FreeCAD를 최초로 띄우면 위 그림처럼 나온다.



빈 파일에 case.dxf를 Import 시킨다.


최초에 FreeCAD에는 dxf Reader가 내장되어 있지는 않으나, 우와 같은 창이 뜨면 Yes 해 주면 자동으로 스크립트를 설치해서 읽어들인다.  dxf Reader 부분의 저작권 조건이 달라서 FreeCAD 설치본에 포함하지 못했다고 한다.  (FreeCAD v0.15 기준)


읽어들이는데 성공하면 위 그림과 같다.
간단하게는 이것을 Extrude 시켜서 모델링 하면 된다.


일단은 외곽 라인 부분을 Extrude 해 주는데 일단 Solid가 생성되도록 체크해 준다.


또 기어 치형 부분도 Solid로 Extrude 해 준다.


마지막으로, 생성된 2개의 Extrude Feature를 Boolean Cut 해 준다.
그러면 이상없이 만들어진 모습을 볼 수 있다.
이 파일을 *.step 포멧으로 저장 가능하다.

한편, 프로페셔널하게 쓰는 상용 3D CAD에서도 잘 되는지 확인해 본다...


잘 된다.  모델링 과정 설명은 생략한다.  대충 해 본 거라서..
재밌게 응용해 보기 위해 각도를 살짝 틀어서 Scew를 줘 봤다.  헬리컬기어 느낌이 난다.

이런식으로 응용하면, 상당히 엄밀한 Bevel Gear 및 Bevel Spiral Gear 모델링도 가능할 것이다.




추후에 본 프로그램의 버전업 계획은 아래와 같다.


1. 언더컷 자동 감지 및 겹치는 라인 자동 제거
2. 이끝단 살 소멸 자동 감지 및 겹치는 라인 자동 제거
3. 치형 수정(End Tip Relief) 기능 추가 (Linear Type)
4. 해석 속도 증가를 위해 기어 전체를 해석하지 않고, 3개 정도의 Teeth만 살려서 해석할 수 있도록 수정
5. Theory 및 User Manual 작성


요즘 시간이 점점 쪼들리고 있는데, 신속하게 가능할지는 잘 모르겠다.

이상 끝!


2015년 5월 11일 월요일

Open Source CAE Toolchain 14 - FEM with FreeCAD 0.15 including Netgen and Calculix


FreeCAD를 처음 테스트해 본게 V0.1 정도 즈음부터였던 것 같다.
그때는 완성도가 너무 낮아서 "재미있는 장난감이군" 정도의 느낌이었는데, 몇 년 지난 지금의 V0.15 에서는 굉장히 완성도가 높이 올라와 있어서 대단히 놀랍다.
물론 여전히 상용 CAD의 완성도에는 크게 못 미치지만, 3D 프린터를 기반으로 하는 교육 커리큘럼 같은데서 사용하기에는 충분한 완성도에 이미 도달했지 않나 싶다.

일단 옛날보다 안정성이 굉장히 좋아졌다.
갑자기 다운되어서 데이타가 날아가버린다던가 하는 증상의 빈도수가 크게 줄었다.

개발 속도도 갈수록 가속도가 붙는 것 같다.
특히 Python 스크립트로 확장성을 개방해 놓은 오픈소스 특유의 특징 덕분에, 사용자 커뮤니티가 마치 눈덩이처럼 점점 더 불어나면서 동시에 각종 모듈이나 매크로 기능들이 빠르게 붙어나간다.

가만히 보면 이미 구색은 다 갖춘 것 같다.  심지어 OpenPLM 하고도 연동될 지경이니....

아무튼 이렇게 붙은 확장 모듈들 중에서, FreeCAD에 기본으로 포함된 주요 모듈의 하나로 FEM 모듈이 있다.  이걸 어떻게 사용하나 싶어서 이리저리 혼자 해 봤는데 도무지 해법이 보이지 않았고, 아직 제대로 된 Tutorial 문서도 없는 상태라 어떻게 사용하는지 지침도 없었다.

그러다가 오늘 사용법을 알아냈기에 간단히 기록해 본다.


현재 FEM 모듈은 Netgen 매쉬 생성기와 Calculix 솔버를 사용하도록 준비되어 있다.
FreeCAD를 우분투 계열 리눅스에 설치할 때 PPA 저장소를 설정해서 apt-get 으로 설치하도록 되어 있는데, 이때 FreeCAD만 설치하지 말고 ccx 및 Netgen을 함께 설치하면 환경 구성은 끝난다.

FEM 모듈이 중요하다고 판단했는지, FreeCAD 저장소에 ccx(Calculix)와 Netgen을 함께 제공해줘서 환경구성하는데 삽질할 필요가 없도록 배려해 놨다.

다만 윈도우 버전의 FreeCAD에서는 Calculix 솔버 사용이 불가능하다.  Calculix 자체가 리눅스 기반이라 그런 것 같다.  뭐 억지로 윈도우 버전으로 된 걸 구해다가 해킹해서 이어붙이면 안될것도 없겠지만 그런 방식은 생명연장을 위해 지양하자.

아무튼 이걸 테스트한 컴퓨터는, 오래된 Core2Duo CPU 및 2GB RAM을 가진 노트북이다.
설치된 리눅스 배포판은 Chromixium이다.
현재의 Chromixium 1.0은 우분투 14.04 32bit 위에 크롬북 느낌 나게 이것저것 유틸리티들을 조합해서 만들어 놓은 것이다.  잡스러운 어플리케이션들이 다 빠져있어서, 1GB 용량의 USB 메모리 스틱에 심어넣고 컴퓨터에 설치하는 것이 가능하다.
이런 저성능의 컴퓨터에서 칼큘릭스를 돌려본다는 느낌도 나쁘지 않은 것 같다.

이제 이렇게 환경 구성이 되었으니 시작해 본다.


1. 3D 모델링


FreeCAD의 3D 모델링 방법은 일반적인 Parametric 3D CAD 툴들과 큰 차이는 없다.
Parametric 툴의 특징은, 2D 스케치를 일단 닥치고 먼저 그린다는 것이다.
이때 그리는 2D 스케치 단계에서, 선이나 점 따위 엔티티들의 치수 및 구속조건들을 지정해 주는 것이 Parametric 툴의 기본 개념이다.
FreeCAD에서는 2D 스케치를 그리는 툴이 Part Design 모듈에 들어 있으므로 그것을 이용하면 된다.
2D 스케치에서 각종 구속조건들을 줄 때, CAD 시스템 내부적으로는 주어지는 구속조건들의 기하학적 정합성을 계산해서 모순관계가 없느냐 따위를 실시간으로 판단하게 되는데, 이 일을 해 주는 알고리즘을 보통 "스케치 솔버"라고들 부른다.

현재 상용 CAD 툴들 중에서 만족스러운 성능을 가진 스케치 솔버는 "없다".
현존 스케치 솔버들은 굉장히 멍청해서, 스케치가 조금만 복잡해져도 버벅대거나 스케치가 폭발(?) 해 버린다.
특히 PTC CREO 및 SolidWorks 같은 제품들의 스케치 솔버는 악명이 높다.
UG NX나 CATIA도 별 차이 없이 멍청하다.

개발된지 최소 20~30년 정도 된 제품들인데 이토록 스케치 솔버의 발전이 더딘 것이 놀라울 정도다.

FreeCAD의 스케치 솔버도 조금 사용해 보니 그다지 만족스럽지는 않다.
다만 상용 제품들의 것들과 비교해서 그다지 뒤떨어져 보이지도 않는다.
디테일이나 사용성에 대한 편리성 같은 부분에서 약간의 차이만 있을 뿐인 것 같다.

아무튼 그렇게 스케치 해서 갈고리 모양을 만들고, Extrude 시켜서 입체화 시킨 후에, 모서리에 Fillet을 주고 간단히 마무리했다.

다 그리고 보니, 화면 좌측 Feature Tree의 구성 방식이 PTC CREO에 익숙한 나로서는 좀 이해하기 어려운 체계다.  그냥 그러려니 한다.



 2. 전처리 과정

이제 해석을 하기 위해서, FEM 모듈로 들어간다.


해석하려는 형상을 확실히 해 주기 위해서, 위 그림과 같이 원하는 솔리드가 연두색으로 빛나도록(?) 선택된 상태가 되도록 해 준다.  그렇게 해 주려면, 앞서 모델링한 모델의 트리 구조에서 제일 마지막에 매겨준 Fillet Feature를 위 그림 처럼 마우스로 선택해 주면 된다.
왜 이런 식으로 선택이 되도록 구성되어 있는지는 모르겠으나, 그다지 직관적으로 생각되지는 않는다.  아직 FreeCAD의 체계에 대해 이해가 부족해서 그렇겠지만, 확실한건 이런 사소한 직관성으로 인해서 Professional Tool과의 차이가 갈라지는 것 같다.

아무튼 이렇게 원하는 솔리드 전체가 선택된 상태에서, "A"자 모양의 아이콘을 눌러주거나 또는 메뉴에서  FEM > New Mechanical Analysis 을 선택한다.
그러면 선택된 솔리드에 대한 해석 케이스가 새로 생성된다.


일단은 화면에서 선택된 솔리드가 위 그림처럼 사라진다(?).
 동시에 위 그림의 좌측 Task 영역에서 매쉬 툴이 나타난다.

솔리드가 사라진 이유는, 해당 솔리드의 Visability가 False로 자동 변경되기 때문이다.
매쉬 툴은 당연히 Netgen이다.
컴퓨터의 낮은 사양을 고려하여 매쉬가 약간 성글게 형성되도록  Fineness를 Moderate로 놔 둔다.

참고로, 솔리드를 선택하지 않은 상태에서 New Mechanical Analysis를 해 주면, 매쉬 생성툴이 안 나타난다.  때문에 아무 일도 할 수 없다.  별다른 경고문구도 안 나타나기 때문에 사용자는 자기가 잘못 한 것인줄도 모른다.  때문에 이 과정에서 반드시 솔리드 선택 상태여야 한다는 점을 알고 있는 사용자만이 다음 과정으로 넘어갈 수 있게 된다.  ....  게임인가?

OK버튼을 누르고 약간 기다리면 매쉬가 딱 나타난다.


Netgen이기 때문에 큰 무리없이 왠만하면 잘 나온다.
FEM 모듈에서 별도 아이콘으로 매쉬 생성 아이콘이 있긴 있는데, 이 아이콘은 Netgen이 아니고 SMESH라는 다른 매쉬 생성 툴이다.  SMESH는 Salome에 들어있던 것인데, 그걸 떼어다가 FreeCAD에 내장시키려고 하고 있는 것이다.
다만 아직 개발이 덜 되어서, SMESH로는 에러메시지만 나오고 매쉬 생성은 되지 않는 상태다.
성능좋은 Netgen이 있는데도 불구하고, SMESH를 굳이 집어넣으려는 이유는 당연히 더 간지나는(?) Hexahedron 매쉬를 만들고 싶은 개발자의 욕심 때문일 것이다.



아무튼  이제 노란 공 모양의 아이콘을 누르면, Material 선택 툴이 위 그림처럼 나타난다.
여기서는 Steel 재질로 골랐다.
제공되는 Material 목록을 보면 전부 각국의 표준기호로 표기되어 있어서 뭐가 뭔지 보통 사람은 알기 어렵다만...  잘 살펴보면 중간중간 ABS 라던가 Wood 같은 친숙한 재료 명칭도 보인다.



이제 바운더리 컨디션을 잡아줄 차례다.
FreeCAD FEM 모듈에서는, 매쉬의 면에 선택하는게 아니고, 솔리드 면에다가 바운더리를 선택하도록 되어 있다.   따라서 위 그림처럼 일단 솔리드를 보이도록 해 주고...



또 매쉬를 안 보이도록 해 준다.  그래서 솔리드만 보이도록 해 놓는다.
그래야만 원하는 면을 선택할 수 있다.
역시 이걸 미리 모르는 사용자는 헤맬 것 같은 대목이다.







어떤 경우에는 버그 때문에 마우스로 원하는 면을 아무리 눌러줘도 선택이 안되는 경우가 있다.
그때는 화면상에서 마우스 오른쪽 버튼을 눌러서 나오는 메뉴 중에서 Navigation Styles를 선택해서, 마우스 조작 방식을 CAD naigation 같은 걸로 바꿔준다.
그러면 선택 가능하게 될 가능성이 높아진다.

아무튼 위 그림은 Fixed 면의 경계조건을 선택해 준 것이다.




위 그림은 Forced면의 경계조건을 선택해 준 것이다.
좌측 툴에서 Reverse direction 라디오버튼을 체크해 줘서 힘이 눌러지는 방향이 되도록 해 준다.
그 바로 위의 Direction 에서는 방향을 결정해줄 면을 선택해서 Shear Force로 만들어주는 것도 가능하다.  이번 경우에는 면에 대한 Normal force로 할 것이므로 건드리지 않았다.
그리고 그 바로 위에 Area load를 1000으로 해 준다.
당연히 N 단위다.

그리고 OK하면 끝.


3. FEM 해석
 

이제 솔버를 시작하기 위해 위 그림에서 표시한 아이콘을 누르면, 솔버 툴이 나타난다.
맨 처음 버튼인 Write Caculix Input File을 누른다.
그러면 혼자 꿍얼꿍얼 하면서 잠깐 작업을 한다.




무슨일을 했는지 확인하기 위해 그 밑의 버튼을 누르면, 탐색기가 뜨면서 만들어진 *.inp 파일이 나온다.  칼큘릭스 솔버 인풋 파일이다.
어떤 버전의 리눅스에서는 탐색기가 아니고 에디터가 바로 떠서 해당 파일의 내용을 보여줄 수도 있는데 각자 환경에 따라 동작이 다를 것이다.


 


일단 칼큘릭스 인풋 파일 내용을 뜯어고칠 만한 능력과 의지가 없으므로 그냥 구경만 하고 그대로 둔다.




그리고 마지막 세 번째 버튼을 누르면 솔버가 구동된다.
위 그림처럼 녹색 글씨가 나오면 이상없이 잘 끝난 것이다.
작업 시간은 이번 경우에는 8.6초 걸렸다는 것을 알 수 있다.



4. 후처리(가시화)


해석이 끝났으므로 결과를 보기 위해, Task 탭에서 Model 탭으로 넘어오면 위 그림과 같은 변화를 볼 수 있다.
Displacement 및 MisesStress 항목이 생겼고, 또 후처리기 아이콘이 활성화 되어 있다.
활성화된 후처리기 아이콘을 눌러본다.
 

그러면 위 그림과 같은 툴이 뜬다.
폰미세스 응력 보는 걸로 선택해 준다.
그러면 Max,Ave,Min값이 나오므로 정량적인 판단을 할 수 있는 근거가 된다.

그런데 화면에 아무 변화가 없네??



위 그림 처럼, 아까 안 보이게 해 놨던 매쉬를 보이도록 변경해 준다.
그러면 매쉬의 색깔이 이렇게 변화되어 있는 것을 볼 수 있다.

원래 솔리드와 겹쳐져 있으므로, 변형된 형상 확인이 가능하다.



솔리드 비주얼이 허접하므로, Transparency를 50으로 줘서 반투명하게 해 본다.
좀 보기가 나은 것 같은 기분이 든다.



와이어프레임 선이 거슬리므로, 위 그림에 표시된 아이콘을 눌러서 안 보이는 상태로 변경해 준다.
그럼 보기가 좀 더 나은 것 같다.





이상의 기능이 현재까지 구현되어 있는 FreeCAD FEM 모듈 기능의 거의 전부인 듯 하다.
때문에 "FreeCAD에서 Caculix 돌리는 게 된다는 게 증명됐다"는 의미 정도다.

실제로 쓸만하게 이 FEM 모듈을 개발자가 끈기있게 발전시켜 나가기를 바란다.
보통 개인 오픈소스 개발자가 이런 걸 몇 년 하다가 결국 관두곤 하는 경우가 많기 때문에, 이것이 어느정도까지 발전할지는 예상하기 어렵다.

하지만 FreeCAD 전체적으로 봤을 때, 이런 다양한 기능들이 여러 사람들에 의해 시도되어 빠르게 발전된다는 점은 확실하다.


개인적인 소감은....

FreeCAD 한글화가 꼭 필요하지 않나 싶다.

요즘 기술교육 하는 학교 같은데서 3D 프린터 같은거 들여놓고 한학기 정도 되는 분량의 커리큘럼으로 수업을 만드는 움직임이 많이 보이던데, 3D 프린팅을 하기 전에 먼저 CAD로 모델링을 하는게 순서다.

그런데 상용 3D CAD는 교육용으로 쓰기에는 너무 오버스펙이고 가격도 너무 높아서 비현실적이다.  대안으로 무료로 사용가능한 적당한 툴이 필요한데, 현재까지 이 조건을 잘 만족시키는 툴은 거의 없다.
구글에서 제공하는 스케치업이 있긴 한데....
스케치업은 기본적으로 폴리곤 모델링 기반이기 때문에 한계가 있다.

FreeCAD는 제대로 된 Open Cascade CAD 커널을 사용한 진짜 CAD이기 때문에 실제 현장에서 사용하는 Parametric CAD 시스템의 특성을 그대로 다 가지고 있다.
때문에 FreeCAD를 교육용으로 활용하면 굉장히 좋지 않을까 한다.

(보통 3D CAD 유저들은 하나의 툴만 손에 익히면 다른 툴로 전환하는 것은 매우 쉽다.
모델링 개념이 비슷비슷 하기 때문이다.  서로 경쟁하면서 좋은 특성들을 주고받으면서 발전했기 때문에 그렇다.)


한국에서 이런 좋은 오픈소스 솔루션을 교육용으로 적극 활용 안하는 이유는 간단하다.
학교 선생님들이 각종 업무에 시달리면서 관료주의에 찌들어가기 때문에, 초년의 의욕을 나이가 들어가면서 점차 상실해서 결국 아무것도 안하게 되기 때문이다.
새로운 기술, 새로운 시도, 더 발전적인 연구 같은 것을 이야기하면 아마 교사 사회에서 왕따 비슷하게 당하는 분위기일지도 모르겠다. (교사 사회를 잘 몰라서 그냥 억측일 뿐임)

아무튼 FreeCAD 국내 유저가 많아졌으면 좋겠고, 유저그룹도 자연적으로 발생하였으면 좋겠다 하는 바램이 생긴다.

대략 5~10년 정도 계속 업데이트가 이루어지면 아마 굉장한 완성도를 가진 괴물 툴이 되어 있을지도 모르겠다.