본문 바로가기
  • You find inspiration to create your own path !
업무 자동화/FreeCAD

Python] 유튜브 동영상을 이해 하고 코드를 만들수 있을까?

by ToolBOX01 2026. 5. 22.
반응형

 

위 동영상을 보고AI는 자동으로 모델을 생성 할수 있을까요? (제미나이). 

AI 답변 : 유유브 동영상을 분석한 내용 입니다.

FreeCAD에서 가장 기본적인 3D 블록 모델링 및 깎아내기(Pocket) 공정을 설명하는 튜토리얼입니다.
영상에 등장하는 연습도면 001의 핵심 치수와 형상은 다음과 같습니다:
  • 전체 크기: 가로 70mm × 세로 40mm × 높이 40mm의 사각기둥 생성
  • 깎아낼 영역: 상단 좌측 부분에서 가로 40mm × 깊이 20mm 만큼 파내기 (Pocket 연산)

 

파이썬 코드

import FreeCAD as App
import FreeCADGui as Gui
import Part
import Sketcher
from PySide6.QtWidgets import QMessageBox

def create_practice_model():
    # ==============================================================================
    # 1. 새 문서 생성 및 PartDesign 워크벤치 활성화
    # ==============================================================================
    doc_name = "Practice_001"
    doc = App.newDocument(doc_name)
    Gui.activateWorkbench("PartDesignWorkbench")
    
    # Body(본체) 생성
    body = doc.addObject("PartDesign::Body", "Body")
    doc.recompute()

    # ==============================================================================
    # 2. 첫 번째 스케치: 베이스 사각형 (70 x 40) 생성 및 XY 평면 매핑
    # ==============================================================================
    sketch1 = body.newObject("Sketcher::SketchObject", "BaseSketch")
    
    # XY 평면 찾기 및 매핑
    xy_plane = None
    for f in body.Origin.OriginFeatures:
        if "Plane" in f.TypeId and "XY" in f.Name.upper():
            xy_plane = f
            break
    if not xy_plane:
        xy_plane = body.Origin.OriginFeatures[3] # 기본 인덱스 백업
        
    sketch1.AttachmentSupport = [(xy_plane, '')]
    sketch1.MapMode = 'FlatFace'
    doc.recompute()

    # 꼭짓점 좌표 설정 (원점 대칭 기준으로 설정)
    # 가로 70 (X: -35 ~ 35), 세로 40 (Y: -20 ~ 20)
    p1 = App.Vector(-35.0, -20.0, 0.0)
    p2 = App.Vector( 35.0, -20.0, 0.0)
    p3 = App.Vector( 35.0,  20.0, 0.0)
    p4 = App.Vector(-35.0,  20.0, 0.0)

    # 선분 추가 (순서대로 사각형 구성)
    l0 = sketch1.addGeometry(Part.LineSegment(p1, p2), False) # 하단 (0)
    l1 = sketch1.addGeometry(Part.LineSegment(p2, p3), False) # 우측 (1)
    l2 = sketch1.addGeometry(Part.LineSegment(p3, p4), False) # 상단 (2)
    l3 = sketch1.addGeometry(Part.LineSegment(p4, p1), False) # 좌측 (3)

    # 일치 구속조건 (꼭짓점 연결)
    sketch1.addConstraint(Sketcher.Constraint('Coincident', l0, 2, l1, 1))
    sketch1.addConstraint(Sketcher.Constraint('Coincident', l1, 2, l2, 1))
    sketch1.addConstraint(Sketcher.Constraint('Coincident', l2, 2, l3, 1))
    sketch1.addConstraint(Sketcher.Constraint('Coincident', l3, 2, l0, 1))

    # 수평/수직 구속조건
    sketch1.addConstraint(Sketcher.Constraint('Horizontal', l0))
    sketch1.addConstraint(Sketcher.Constraint('Horizontal', l2))
    sketch1.addConstraint(Sketcher.Constraint('Vertical', l1))
    sketch1.addConstraint(Sketcher.Constraint('Vertical', l3))

    # 치수 구속조건 및 원점 정렬 (Fully Constrained 완성)
    sketch1.addConstraint(Sketcher.Constraint('DistanceX', l0, 1, l0, 2, 70.0)) # 가로 70mm
    sketch1.addConstraint(Sketcher.Constraint('DistanceY', l1, 1, l1, 2, 40.0)) # 세로 40mm
    sketch1.addConstraint(Sketcher.Constraint('DistanceX', -1, 1, l0, 1, -35.0)) # 원점 기준 좌측 절반 배치
    sketch1.addConstraint(Sketcher.Constraint('DistanceY', -1, 1, l0, 1, -20.0)) # 원점 기준 하단 절반 배치
    doc.recompute()

    # ==============================================================================
    # 3. 돌출 (Pad) 연산 — 높이 40mm
    # ==============================================================================
    pad = body.newObject("PartDesign::Pad", "BasePad")
    pad.Profile = (sketch1, [])
    pad.Length = 40.0
    doc.recompute()

    # ==============================================================================
    # 4. 두 번째 스케치: 상단 깎아낼 영역 스케치 (가로 40 x 세로 20)
    # ==============================================================================
    # 돌출된 블록의 상단면(정면 혹은 윗면)을 잡거나 동일 평면에 매핑하여 포켓을 뚫습니다.
    # 영상과 동일하게 설계하기 위해 사각기둥의 정면 혹은 기준평면을 사용해 상단을 깎아냅니다.
    sketch2 = body.newObject("Sketcher::SketchObject", "CutSketch")
    
    # 깎아낼 방향 정렬을 위해 XZ 평면 선택
    xz_plane = None
    for f in body.Origin.OriginFeatures:
        if "Plane" in f.TypeId and "XZ" in f.Name.upper():
            xz_plane = f
            break
    if not xz_plane:
        xz_plane = body.Origin.OriginFeatures[2]
        
    sketch2.AttachmentSupport = [(xz_plane, '')]
    sketch2.MapMode = 'FlatFace'
    doc.recompute()

    # 포켓팅용 사각형 좌표 정의 (좌상단 영역을 파내기 위함)
    # 전체 가로 70 중 좌측 40mm 영역, 높이 40 중 상단 20mm 영역
    cut_p1 = App.Vector(-35.0, 20.0, 0.0)
    cut_p2 = App.Vector(  5.0, 20.0, 0.0)
    cut_p3 = App.Vector(  5.0, 40.0, 0.0)
    cut_p4 = App.Vector(-35.0, 40.0, 0.0)

    cl0 = sketch2.addGeometry(Part.LineSegment(cut_p1, cut_p2), False)
    cl1 = sketch2.addGeometry(Part.LineSegment(cut_p2, cut_p3), False)
    cl2 = sketch2.addGeometry(Part.LineSegment(cut_p3, cut_p4), False)
    cl3 = sketch2.addGeometry(Part.LineSegment(cut_p4, cut_p1), False)

    sketch2.addConstraint(Sketcher.Constraint('Coincident', cl0, 2, cl1, 1))
    sketch2.addConstraint(Sketcher.Constraint('Coincident', cl1, 2, cl2, 1))
    sketch2.addConstraint(Sketcher.Constraint('Coincident', cl2, 2, cl3, 1))
    sketch2.addConstraint(Sketcher.Constraint('Coincident', cl3, 2, cl0, 1))
    
    sketch2.addConstraint(Sketcher.Constraint('Horizontal', cl0))
    sketch2.addConstraint(Sketcher.Constraint('Vertical', cl1))

    # 치수 부여: 가로 40mm, 세로 20mm
    sketch2.addConstraint(Sketcher.Constraint('DistanceX', cl0, 1, cl0, 2, 40.0))
    sketch2.addConstraint(Sketcher.Constraint('DistanceY', cl1, 1, cl1, 2, 20.0))
    # 위치 고정: 블록의 왼쪽 끝(-35)과 일치시키고 상단 끝(40)에 맞춤
    sketch2.addConstraint(Sketcher.Constraint('DistanceX', -1, 1, cl0, 1, -35.0))
    sketch2.addConstraint(Sketcher.Constraint('DistanceY', -1, 1, cl0, 1,  20.0))
    doc.recompute()

    # ==============================================================================
    # 5. 파내기 (Pocket) 연산 — 전체 관통 혹은 두께(40mm)만큼 관통
    # ==============================================================================
    pocket = body.newObject("PartDesign::Pocket", "CutPocket")
    pocket.Profile = (sketch2, [])
    pocket.Length = 40.0         # 베이스 블록 세로 폭이 40mm이므로 전체가 깔끔하게 잘립니다.
    pocket.UpToFace = None
    pocket.Reversed = True       # 방향 반전 적용하여 내부로 파고들도록 설정
    doc.recompute()

    # ==============================================================================
    # 6. 화면 뷰 최적화 및 안내창 출력
    # ==============================================================================
    view = Gui.activeDocument().activeView()
    if view:
        view.viewIsometric()     # 입체 뷰(ISO 뷰) 전환
        view.fitAll()            # 화면에 맞게 줌 조정
        
    main_window = Gui.getMainWindow()
    msg_box = QMessageBox(main_window)
    msg_box.setIcon(QMessageBox.Information)
    msg_box.setWindowTitle("FreeCAD 자동 모델링 완료")
    msg_box.setText("연습도면 001 모델 생성이 완료되었습니다!")
    msg_box.setInformativeText("가로 70, 세로 40, 높이 40 베이스에\n좌상단 가로 40, 세로 20의 Pocket 가공이 완료되었습니다.")
    msg_box.exec_()

# 스크립트 실행
create_practice_model()

 

테스트 결과: 다음과 같은 오류가 있습니다

1. CUT 스케치가 완벽한 구속조건을 만족하지 못했습니다. 일부선의 수평, 수직 조건이 빠져 있습니다.
2. CUT의 방향이 양쪽 방향 및 깊이 옵션이 틀렸습니다. 

 

시간이 걸리겠지만 기계는 지속적인 학습을 할것 같습니다. 도면을 이해 하지는 못했지만, 동영상을 보고 따라하기는 가능성이 있습니다. 

by korealionkk@gmail.com


 

반응형