Assembly Workbench/ko

조립 작업대 아이콘

소개

introduced in 1.0

조립 작업대(Assembly Workbench)는 FreeCAD의 내장 조립 작업대입니다. 오픈 소스 Ondsel solver를 사용합니다.

도구

조립(Assembly)

이 도구는 개발자를 대상으로 하며 향후 릴리스에는 포함되지 않을 예정입니다. (포럼 스레드 참조)

조인트(Joints)

  • Gears Joint: 두 개의 서로 다른 회전 조인트의 두 파트 회전을 연결하는 기어 조인트를 만듭니다.
  • Belt Joint: 두 개의 서로 다른 회전 조인트의 두 파트 회전을 연결하는 벨트 조인트를 만듭니다.

환경설정(Preferences)

크랭크 슬라이더 예제

이 예제는 임시적인 것으로, 적절한 설명/튜토리얼이 제공되면 삭제될 수 있습니다.

크랭크 슬라이더 조립품

제작할 어셈블리는 베이스(Base), 슬라이더 로드(Rod), 크랭크(Crank), 커넥팅 로드(Connecting Rod)의 네 부분으로 구성됩니다. 각 부분은 네 개의 조인트로 연결됩니다.

조립된 부품: 베이스(Base-황갈색), 슬라이더 로드(Slider Rod-하늘색), 크랭크(Crank-빨간색), 컨넥팅 로드(Connecting Rod-초록색)

부품 준비

이 예에서는 모든 부품과 어셈블리가 하나의 문서에 생성됩니다.

객체의 원통형 형상은 평행하거나 수직이며, 충돌이 없는 한 나머지 형상은 이 예제와 관련이 없습니다. 이를 염두에 두고 직접 객체를 모델링하거나 아래 Python 코드로 생성할 수 있습니다. 이 코드는 네 개의 객체가 포함된 새 문서를 만듭니다(이미지보다 단순함). 다음 줄을 파이썬 콘솔에 복사하여 붙여넣으세요:

import FreeCAD as App
import FreeCADGui as Gui
import Part

doc = App.newDocument()

box1 = Part.makeBox(140, 40, 7, App.Vector(0, -20, 0))
cyl1 = Part.makeCylinder(4, 8, App.Vector(120, 0, 7))
box2 = Part.makeBox(20, 12, 10, App.Vector(5, -6, 7))
cyl2 = Part.makeCylinder(6, 20, App.Vector(25, 0, 17), App.Vector(-1, 0, 0))
cyl3 = Part.makeCylinder(4, 20, App.Vector(25, 0, 17), App.Vector(-1, 0, 0))
shape = box1.fuse([cyl1, box2, cyl2]).removeSplitter().cut(cyl3)
base = doc.addObject("Part::Feature", "Base")
base.Shape = shape

box1 = Part.makeBox(4, 12, 12, App.Vector(-12, -6, 0))
box2 = Part.makeBox(14, 12, 4, App.Vector(-8, -6, 0))
cyl1 = Part.makeCylinder(4, 8, App.Vector(0, 0, 4))
cyl2 = Part.makeCylinder(4, 88, App.Vector(-12, 0, 6),App.Vector(-1, 0, 0))
shape = box1.fuse([box2, cyl1, cyl2]).removeSplitter()
slider_rod = doc.addObject("Part::Feature", "SliderRod")
slider_rod.Shape = shape
slider_rod.Placement.Base = App.Vector(100, -40, 0)

cyl1 = Part.makeCylinder(7.5, 4)
box1 = Part.makeBox(15, 30, 4, App.Vector(-7.5, 0, 0))
cyl2 = Part.makeCylinder(7.5, 4, App.Vector(0, 30, 0))
cyl3 = Part.makeCylinder(4, 6, App.Vector(0, 30, 4))
cyl4 = Part.makeCylinder(4, 4)
shape = cyl1.fuse([box1, cyl2]).removeSplitter().fuse(cyl3).cut(cyl4)
crank = doc.addObject("Part::Feature", "Crank")
crank.Shape = shape
crank.Placement.Base = App.Vector(125, -70, 0)

cyl1 = Part.makeCylinder(6, 4)
box1 = Part.makeBox(50, 12, 4, App.Vector(0, -6, 0))
cyl2 = Part.makeCylinder(6, 4, App.Vector(50, 0, 0))
cyl3 = Part.makeCylinder(4, 4)
cyl4 = Part.makeCylinder(4, 4, App.Vector(50, 0, 0))
shape = cyl1.fuse([box1, cyl2]).removeSplitter().cut(cyl3.fuse(cyl4))
connecting_rod = doc.addObject("Part::Feature", "ConnectingRod")
connecting_rod.Shape = shape
connecting_rod.Placement.Base = App.Vector(25, -70, 0)

mat = base.ViewObject.ShapeAppearance[0]
mat.DiffuseColor = (0.80, 0.60, 0.15, 0.0)
base.ViewObject.ShapeAppearance = (mat,)

mat = slider_rod.ViewObject.ShapeAppearance[0]
mat.DiffuseColor = (0.55, 0.70, 0.70, 0.0)
slider_rod.ViewObject.ShapeAppearance = (mat,)

mat = crank.ViewObject.ShapeAppearance[0]
mat.DiffuseColor = (0.70, 0.30, 0.20, 0.0)
crank.ViewObject.ShapeAppearance = (mat,)

mat = connecting_rod.ViewObject.ShapeAppearance[0]
mat.DiffuseColor = (0.55, 0.70, 0.0, 0.0)
connecting_rod.ViewObject.ShapeAppearance = (mat,)

doc.recompute()
view = Gui.ActiveDocument.ActiveView
view.viewIsometric()
view.fitAll()

어셈블리 추가

새로운 어셈블리(New Assembly) 도구를 사용하여 문서에 어셈블리를 추가합니다.

부품 및 어셈블리의 트리 뷰

부품을 어셈블리로 옮기기

트리 뷰(Tree View)에서 부품을 어셈블리 객체로 드래그 앤 드롭 합니다. 이제 어셈블리 솔버에서 파트를 처리할 수 있습니다.

이제 부품이 어셈블리 컨테이너 안에 있습니다

부품 고정

어셈블리를 원하는 위치에 유지하려면 베이스 부품을 잠그거나, 여기서 말하는 고정(grounded) 상태로 만들어야 합니다. 트리 뷰(Tree View) 또는 3D 뷰(3D View)에서 Base를 선택하고 고정 토글(Toggle Grounded) 도구를 사용합니다. 이렇게 하면 어셈블리 컨테이너의 로컬 좌표계(LCS)에 대한 Base의 위치가 고정됩니다. GroundedJoint 객체가 Joints 컨테이너에 추가됩니다.

GroundedJoint 객체를 찾으려면 Joints 컨테이너를 펼치세요

대안: 링크 추가

위에서 언급한 두 단계 대신 Component 도구를 사용하여 어셈블리 안에 객체를 배치할 수도 있습니다. 첫 번째 객체는 자동으로 고정된 부품이 됩니다. 따라서 Base 객체부터 시작해야 합니다. 이 도구는 링크를 생성하며 원본 객체는 어셈블리 외부에 남아 있습니다. 혼란을 피하기 위해 원본 객체를 보이지 않게 설정하는 것이 좋습니다.

조인트 적용

조인트는 서로 다른 부품의 정확히 두 요소를 연결합니다. 원하는 조인트 도구를 실행하기 전에 선택적으로 요소를 선택할 수 있습니다(두 개가 아닌 수의 요소를 선택하면 빈 선택이 됩니다). 요소는 로컬 XY 평면의 채워진 원과 로컬 X축(빨간색), Y축(초록색), Z축(파란색)을 따라 세 개의 선으로 표현되는 LCS의 위치와 방향을 정의합니다.

  • 베이스와 크랭크 사이의 회전 조인트

선택한 요소 + 회전 조인트(Revolute Joint) → 재배치된 크랭크

크랭크는 마우스 왼쪽 버튼을 사용하여 움직이세요. 피벗을 중심으로 회전만 가능합니다.

  • 베이스(Base)와 슬라이더 로드(Slider Rod) 사이의 슬라이더 조인트

선택한 요소 + 슬라이더 조인트(Slider Joint) → 재배치된 슬라이더 로드(Slider Rod)

슬라이더로드를 마우스 왼쪽 버튼으로 이동하세요. 중심선을 따라서만 이동이 가능합니다.

  • 크랭크와 컨넥팅 로드 사이의 회전 조인트

선택한 요소 + 회전 조인트(Revolute Joint) → 재배치된 Connecting Rod

크랭크는 마우스 왼쪽 버튼을 사용하여 움직이세요. 피벗을 중심으로 회전만 가능합니다.

한 줄에 여러 조인트가 있는 경우 솔버(solver)가 합리적인 해를 찾을 수 있도록 도움을 줘야 합니다.
필요한 경우 부품을 클릭하고 드래그하여 계산하기 쉬운 위치로 이동하세요.

  • 컨넥팅 로드와 슬라이더 로드 사이의 원통형 조인트(Cylindrical Joint)

선택한 요소 + 원통형 조인트(Cylindrical Joint) → 완성된 어셈블리

완성된 조립품에서 마우스 포인터를 사용하여 사용된 조인트에 따라 부품을 드래그합니다.

참고

슬라이더 로드의 핀은 중복으로 방향이 지정되어 있습니다. 핀의 중심선은 베이스에서 크랭크와 컨넥팅 로드를 거치는 운동학적 체인을 통해 베이스의 핀과 평행합니다. 즉, 로컬 Z축은 X축이나 Y축을 중심으로 회전할 수 없습니다. 슬라이더 조인트는 Z축의 두 로컬 축 주위 회전도 방지하므로 두 개의 중복 구속된 자유도가 발생합니다. 슬라이더 조인트 대신 원통형 조인트(Cylindrical Joint)를 사용하면 하나의 회전만 잠겨 단일 중복 구속 자유도만 발생합니다.

크랭크를 돌리세요

베이스와 크랭크 사이의 각도로 어셈블리의 배치를 제어하려면 두 부품 사이의 회전 조인트(Revolute Joint)를 고정 조인트(Fixed Joint)로 변경해야 합니다. 이를 위해 트리 뷰(Tree View)에서 회전 객체를 더블 클릭합니다. 대화 상자에서 Revolute를 Fixed로 변경하고 원하는 대로 Rotation 값을 변경합니다(마우스 휠 동작에 따라 움직임이 따라옵니다).

조인트 유형을 변경하면 조인트의 Label은 변경되지만 Name은 변경되지 않습니다. 이 경우 Label이 "Fixed"로 변경됩니다.

어셈블리를 애니메이션화하려면 Python 코드로 고정 조인트(Fixed Joint)의 Rotation (Offset1.Angle) 값을 변경할 수 있습니다. 다음 줄을 파이썬 콘솔(Python Console)에 복사하여 붙여넣으세요:

import math
import FreeCAD as App
import FreeCADGui as Gui

actuator = App.ActiveDocument.getObjectsByLabel("Fixed")[0]

for angle in range(0, 361, 10):
    # A full rotation of the Crank in steps of 10°
    actuator.Offset1.Rotation.Angle = math.radians(angle)
    App.ActiveDocument.recompute()
    Gui.updateGui()

범위의 끝값은 이 각도를 유효한 결과로 포함하기 위해 360보다 커야 합니다.

범용 조인트의 예

이 예제는 임시적인 것으로, 적절한 설명/튜토리얼이 제공되면 삭제될 수 있습니다.

유니버설 조인트 어셈블리

이 예제에서는 유니버설 조인트(universal joint)를 만듭니다.

어셈블리는 세 개의 솔리드 부품으로 구성됩니다: 동일한 두 개의 Fork와 하나의 Cross. 또한 각진 축을 나타내는 두 개의 비솔리드 요소인 Axle1과 Axle2도 필요합니다. 축과 솔리드 부품은 여러 조인트로 연결됩니다.

부품 준비

이 예제에서는 모든 부품과 어셈블리가 하나의 문서에서 생성됩니다.

아래 Python 코드는 네 개의 객체(Fork 1개만 포함)가 있는 새 문서를 만듭니다. 다음 줄을 파이썬 콘솔(Python Console)에 복사하여 붙여넣으세요:

import math
import FreeCAD as App
import FreeCADGui as Gui
import Part

doc = App.newDocument()

axle1 = doc.addObject("Part::Line", "Axle1")
axle1.X2 = -80
axle1.Y2 = 0
axle1.Z2 = 0

axle2 = doc.addObject("Part::Line", "Axle2")
axle2.X2 = 80
axle2.Y2 = 0
axle2.Z2 = 0
axle2.Placement.Rotation.Angle = math.radians(20)

sph1 = Part.makeSphere(50, App.Vector(0, 0, 0), App.Vector(-1, 0, 0), 0, 90, 360)
box1 = Part.makeBox(50, 40, 80, App.Vector(-50, -20, -40))
cyl1 = Part.makeCylinder(20, 80, App.Vector(0, 0, -40))
cyl2 = Part.makeCylinder(20, 80, App.Vector(0, 0, 0), App.Vector(-1, 0, 0))
cyl3 = Part.makeCylinder(30, 60, App.Vector(0, -30, 0), App.Vector(0, 1, 0))
box2 = Part.makeBox(30, 60, 60, App.Vector(0, -30, -30))
cyl4 = Part.makeCylinder(15, 80, App.Vector(0, 0, -40))
cyl5 = Part.makeCylinder(15, 80, App.Vector(0, 0, 0), App.Vector(-1, 0, 0))
shape = sph1.common(box1).fuse([cyl1, cyl2]).cut(cyl3.fuse([box2, cyl4, cyl5]))
fork = doc.addObject("Part::Feature", "Fork")
fork.Shape = shape.removeSplitter()
fork.Placement.Base = App.Vector(0, 100, 0)

cyl1 = Part.makeCylinder(15, 80, App.Vector(0, 0, -40))
cyl2 = Part.makeCylinder(15, 80, App.Vector(0, -40, 0), App.Vector(0, 1, 0))
shape = cyl1.fuse([cyl2])
cross = doc.addObject("Part::Feature", "Cross")
cross.Shape = shape.removeSplitter()
cross.Placement.Base = App.Vector(70, 100, 0)

mat = fork.ViewObject.ShapeAppearance[0]
mat.DiffuseColor = (0.80, 0.60, 0.15, 0.0)
fork.ViewObject.ShapeAppearance = (mat,)

mat = cross.ViewObject.ShapeAppearance[0]
mat.DiffuseColor = (0.55, 0.70, 0.70, 0.0)
cross.ViewObject.ShapeAppearance = (mat,)

doc.recompute()
view = Gui.ActiveDocument.ActiveView
view.viewIsometric()
view.fitAll()

축의 각도 변경

축 사이의 각도는 20도로 설정되어 있습니다. 이 값을 변경하려면 Axle2를 선택하고 Placement.Angle 속성을 변경하세요. 이 속성은 Axle2를 어셈블리로 이동하기 전에 변경해야 합니다.

경고: 각도가 너무 크면 부품이 충돌할 수 있습니다.

어셈블리 추가

New Assembly 도구를 사용하여 문서에 어셈블리를 추가합니다.

축을 어셈블리로 이동

트리 뷰(Tree View)에서 축을 어셈블리 객체로 드래그 앤 드롭합니다.

축 고정

트리 뷰(Tree View)에서 두 축을 선택하고 Toggle Grounded 도구를 사용합니다.

축을 어셈블리로 이동

나머지 객체에는 Component 도구를 사용합니다:

  1. 도구를 실행합니다.
  2. 열리는 대화 상자에서 Cross 객체를 한 번, Fork 객체를 두 번 클릭합니다.
  3. OK 버튼을 누릅니다.
  4. 어셈블리 외부의 객체를 보이지 않게 설정합니다.
  5. 어셈블리 내부의 객체가 너무 많이 겹치면 새 위치로 드래그할 수 있습니다.

조인트 적용

  • Axle1과 Fork001 사이의 회전 조인트(Revolute Joint)

선택한 요소 + Revolute Joint + +40mm 또는 -40mm 오프셋 → 재배치된 Fork001

도구를 먼저 실행한 후 요소를 선택하면 오프셋을 입력하지 않아도 되도록 Axle1의 올바른 끝점 근처를 클릭할 수 있습니다.

  • Fork001과 Cross001 사이의 원통형 조인트(Cylindrical Joint)

선택한 요소 + Cylindrical Joint → 재배치된 Cross001

  • Axle2와 Fork002 사이의 원통형 조인트(Cylindrical Joint)

선택한 요소 + Cylindrical Joint → 재배치된 Fork002

필요한 경우 작업 패널의 버튼을 사용하여 조인트 방향을 반전합니다.

  • Cross001과 Fork002 사이의 원통형 조인트(Cylindrical Joint)

선택한 요소 + Cylindrical Joint → 재배치된 Cross001과 Fork002

유니버셜 조인트 구동

유니버설 조인트는 마우스 왼쪽 버튼으로 Fork001을 이동하여 구동할 수 있습니다.

특정 회전 각도에서 상태를 확인하려면 다음을 수행합니다:

  1. Axle1과 Fork001 사이의 원통형 조인트(Cylindrical Joint)를 고정 조인트(Fixed Joint)로 변경합니다.
  2. 고정 조인트(Fixed Joint)의 Offset1.Angle 속성을 선택하고 마우스 휠을 굴립니다.
  3. 유니버설 조인트를 임의의 각도로 위치시킬 수 있습니다.

예시 바이스

This example is temporary and may be removed once proper descriptions/tutorials are available.

바이스 조립

In this example a vise is created.

The assembly consists of three solid parts: a fixed and a movable jaw and a screw with a lever. One additional non solid element, a crank, is also needed. The crank and the solid parts are connected with several joints.

A Screw Joint couples the translation of a part with a Slider Joint to the rotation of a part with a Revolute Joint. The screw part shall make both a translation and a rotation movement hence it must be a part with a Cylindrical Joint. In this assembly, the screw part will be coupled to the movable jaw with a Distance Joint, to the non solid crank with a Parallel Joint, and to the fixed jaw with a Cylindrical Joint.

부품 준비

In this example all parts and the assembly are created in one document.

The Python code below will create a new document with four objects. Just copy-paste the following lines in the Python Console:

import math
import FreeCAD as App
import FreeCADGui as Gui
import Part

doc = App.newDocument()

box1 = Part.makeBox(95, 40, 75, App.Vector(0, -20, -22))
cyl1 = Part.makeCylinder(35, 80, App.Vector(0, -40, 53), App.Vector(0, 1, 0), 90)
box2 = Part.makeBox(20, 80, 30, App.Vector(-20, -40, 58))
cyl2 = Part.makeCylinder(15, 80, App.Vector(-15, -40, 58), App.Vector(0, 1, 0), 90)
box3 = Part.makeBox(5, 80, 15, App.Vector(-20, -40, 58))
box4 = Part.makeBox(35, 24, 24, App.Vector(0, -12, -12))
box5 = Part.makeBox(60, 34, 69, App.Vector(35, -17, -19))
cyl3 = Part.makeCylinder(20, 55, App.Vector(-20, -40, 53), App.Vector(1, 0, 0))
cyl4 = Part.makeCylinder(20, 55, App.Vector(-20, 40, 53), App.Vector(1, 0, 0))
cyl5 = Part.makeCylinder(5, 35, App.Vector(0, 0, 38), App.Vector(1, 0, 0))
box6 = Part.makeBox(7, 88, 15, App.Vector(-22, -44, 75))
box7 = Part.makeBox(95, 90, 10, App.Vector(0, -45, -32))
shape = box1.fuse([cyl1, box2, box6, box7]).cut(cyl2.fuse([box3, cyl3, cyl4, cyl5, box4, box5]))
fixedJaw = doc.addObject("Part::Feature", "FixedJaw")
fixedJaw.Shape = shape.removeSplitter()
fixedJaw.Placement.Rotation.Axis = App.Vector(0, 0, 1)
fixedJaw.Placement.Rotation.Angle = math.radians(180)

box1 = Part.makeBox(35, 40, 75, App.Vector(0, -20, -22))
cyl1 = Part.makeCylinder(35, 80, App.Vector(0, -40, 53), App.Vector(0, 1, 0), 90)
box2 = Part.makeBox(20, 80, 30, App.Vector(-20, -40, 58))
cyl2 = Part.makeCylinder(15, 80, App.Vector(-15, -40, 58), App.Vector(0, 1, 0), 90)
box3 = Part.makeBox(160, 24, 24, App.Vector(-160, -12, -12))
box4 = Part.makeBox(5, 80, 15, App.Vector(-20, -40, 58))
box5 = Part.makeBox(160, 18, 18, App.Vector(-160, -9, -9))
cyl3 = Part.makeCylinder(20, 55, App.Vector(-20, -40, 53), App.Vector(1, 0, 0))
cyl4 = Part.makeCylinder(20, 55, App.Vector(-20, 40, 53), App.Vector(1, 0, 0))
cyl5 = Part.makeCylinder(5, 35, App.Vector(0, 0, 38), App.Vector(1, 0, 0))
box6 = Part.makeBox(7, 88, 15, App.Vector(-22, -44, 75))
shape = box1.fuse([cyl1, box2, box3, box6]).cut(cyl2.fuse([box4, cyl3, cyl4, box5, cyl5]))
movableJaw = doc.addObject("Part::Feature", "MovableJaw")
movableJaw.Shape = shape.removeSplitter()
movableJaw.Placement.Base = App.Vector(150, 100, 0)

cyl1 = Part.makeCylinder(5, 190, App.Vector(0, 0, 0), App.Vector(1, 0, 0))
cyl2 = Part.makeCylinder(10, 20, App.Vector(190, 0, 0), App.Vector(1, 0, 0))
cyl3 = Part.makeCylinder(4, 100, App.Vector(200, 0, -50), App.Vector(0, 0, 1))
shape = cyl1.fuse([cyl2, cyl3])
leverScrew = doc.addObject("Part::Feature", "LeverScrew")
leverScrew.Shape = shape.removeSplitter()
leverScrew.Placement.Base = App.Vector(150, -100, 0)

wire1 = Part.makePolygon([App.Vector(0, 0, 100), App.Vector(0, 0, 0), App.Vector(100, 0, 0)])
crank = doc.addObject("Part::Feature", "Crank")
crank.Shape = wire1
crank.Placement.Base = App.Vector(0, -100, 0)

mat = fixedJaw.ViewObject.ShapeAppearance[0]
mat.DiffuseColor = (0.80, 0.60, 0.15, 0.0)
fixedJaw.ViewObject.ShapeAppearance = (mat,)

mat = movableJaw.ViewObject.ShapeAppearance[0]
mat.DiffuseColor = (0.55, 0.70, 0.70, 0.0)
movableJaw.ViewObject.ShapeAppearance = (mat,)

mat = leverScrew.ViewObject.ShapeAppearance[0]
mat.DiffuseColor = (0.70, 0.30, 0.20, 0.0)
leverScrew.ViewObject.ShapeAppearance = (mat,)

doc.recompute()
view = Gui.ActiveDocument.ActiveView
view.viewIsometric()
view.fitAll()

어셈블리 추가

With the New Assembly tool add an assembly to the document.

Move the parts into the assembly container

In the Tree View drag and drop the parts on the Assembly object. They can now be handled by the Assembly's solver.

Ground a part

To keep the assembly at the desired position, the FixedJaw part should be locked, or grounded as it is called here. Select the FixedJaw in the Tree View or in the 3D View and use the Toggle Grounded tool. A GroundedJoint object is added to the Joints container.

Apply joints

  • A Revolute joint between FixedJaw and Crank

Selected elements + Revolute Joint → rearranged Crank

  • A Slider joint between FixedJaw and MovableJaw

Selected elements + Slider Joint → rearranged MovableJaw

Set the Min length to -77 mm and the Max length to -7 mm. This limits the opening of the vise to 70 mm.

The next three joints are necessary to force the LeverScrew to: translate like the MovableJaw, rotate like the Crank, and rotate around the main axis.

  • A Distance joint between LeverScrew and MovableJaw

Selected elements + Distance Joint → rearranged LeverScrew

두 면을 선택하세요. 거리 값을 20mm로 설정하세요.

  • A Parallel joint between LeverScrew and Crank

Selected elements + Parallel Joint → rearranged LeverScrew

  • A Cylindrical joint between LeverScrew and FixedJaw

Selected elements + Cylindrical Joint → rearranged LeverScrew

  • A Screw joint between MovableJaw and Crank

Selected elements (LeverScrew invisible) + Screw Joint → complete vise mechanism (LeverScrew visible)

If necessary make the LeverScrew invisible during selection.

피치 반지름을 5mm로 설정합니다.

바이스를 돌리세요

The vise can be driven by moving Crank or MovableJaw with the left mouse.

Example shock absorber

This example is temporary and may be removed once proper descriptions/tutorials are available.

Assembly of a shock absorber

In this example a shock absorber is created.

The assembly consists of three solid parts: a piston, a cylinder and a spring. Three additional non solid elements, two axles and a rod are also needed. All parts are connected with several joints.

The hinge of the piston rotates around Axle2, while the hinge of the cylinder moves on an arc of circle centered on Axle1. The non solid Rod is used for this movement. The length of the Rod is the radius of the arc.

Prepare parts

The Python code below will create a new document with 6 objects. Create a new macro and copy-paste the code below in the Python editor (not in the Python Console). Then run the macro.

The code below cannot be run from the Python Console because the spring must be a Part::FeaturePython object defined by of a class with the callback functions execute() and onChanged(). Only then can its height be changed via a property.

import math
import FreeCAD as App
import FreeCADGui as Gui
import Part

doc = App.newDocument()

class Spring():
    def __init__(self, spring):
        spring.addProperty("App::PropertyLength", "Height", "Spring", "Height of the helix").Height = 200.0
        spring.Proxy = self
        spring.ViewObject.Proxy = 0
        
    def execute(self, spring):
        helix = Part.makeHelix(spring.Height/8.5, spring.Height, 35)
        startPnt = helix.Edges[0].Curve.value(0)
        section = Part.Wire([Part.Circle(startPnt, App.Vector(0, 1, 0), 5).toShape()])
        hel1 = helix.makePipeShell([section], True, True)
        box1 = Part.makeBox(80, 80, 10, App.Vector(-40, -40, -10))
        box2 = Part.makeBox(80, 80, 10, App.Vector(-40, -40, spring.Height))
        shape = hel1.cut(box1).cut(box2)
        spring.Shape = shape
        
    def onChanged(self, spring, prop):
        if prop == "Height":
            self.execute(spring) 
            
spring = doc.addObject("Part::FeaturePython", "Spring")
Spring(spring)
spring.Placement.Base = App.Vector(0, 100, 0)

axle1 = doc.addObject("Part::Line", "Axle1")
axle1.X2 = 0
axle1.Y2 = 80
axle1.Z2 = 0

axle2 = doc.addObject("Part::Line", "Axle2")
axle2.X2 = 0
axle2.Y2 = 80
axle2.Z2 = 0
axle2.Placement.Base = App.Vector(120, 0, -250)

rod = doc.addObject("Part::Line", "Rod")
rod.X2 = 100
rod.Y2 = 0
rod.Z2 = 0
rod.Placement.Base = App.Vector(0, -50, 0)

cyl1 = Part.makeCylinder(40, 10,App.Vector(0, 0, -5))
tor1 = Part.makeTorus(40, 5)
cyl2 = Part.makeCylinder(45, 5)
box1 = Part.makeBox(30, 10, 30,App.Vector(-15, -5, -35))
cyl3 = Part.makeCylinder(15, 10, App.Vector(0, -5, -35), App.Vector(0, 1, 0))
cyl4 = Part.makeCylinder(40, 5)
cyl5 = Part.makeCylinder(5, 10,App.Vector(0, -5, -35), App.Vector(0, 1, 0))
cyl6 = Part.makeCylinder(5, 130)
cyl7 = Part.makeCylinder(20, 5,App.Vector(0, 0, 130))
shape = cyl1.fuse([tor1,cyl2, box1, cyl3]).cut(cyl4.fuse([cyl5])).fuse([cyl6, cyl7])
piston = doc.addObject("Part::Feature", "Piston")
piston.Shape = shape.removeSplitter()
piston.Placement.Base = App.Vector(200, 100, -200)

cyl1 = Part.makeCylinder(40, 10,App.Vector(0, 0, -5))
tor1 = Part.makeTorus(40, 5)
cyl2 = Part.makeCylinder(45, 5)
box1 = Part.makeBox(30, 10, 30,App.Vector(-15, -5, -35))
cyl3 = Part.makeCylinder(15, 10,App.Vector(0, -5, -35), App.Vector(0, 1, 0))
cyl4 = Part.makeCylinder(40, 5)
cyl5 = Part.makeCylinder(5, 10,App.Vector(0, -5, -35), App.Vector(0, 1, 0))
cyl6 = Part.makeCylinder(25, 130)
tor2 = Part.makeTorus(20, 5,App.Vector(0, 0, 130))
cyl7 = Part.makeCylinder(20, 135)
cyl8 = Part.makeCylinder(20, 130)
cyl9 = Part.makeCylinder(5, 135)
shape = cyl1.fuse([tor1, cyl2, box1, cyl3]).cut(cyl4.fuse([cyl5])).fuse([cyl6, tor2, cyl7]).cut(cyl8.fuse([cyl9]))
cylinder = doc.addObject("Part::Feature", "Cylinder")
cylinder.Shape = shape.removeSplitter()
cylinder.Placement.Rotation.Axis = App.Vector(0, 1, 0)
cylinder.Placement.Rotation.Angle = math.pi
cylinder.Placement.Base = App.Vector(100, 100, 0)

mat = piston.ViewObject.ShapeAppearance[0]
mat.DiffuseColor = (0.80, 0.60, 0.15, 0.0)
piston.ViewObject.ShapeAppearance = (mat,)

mat = cylinder.ViewObject.ShapeAppearance[0]
mat.DiffuseColor = (0.55, 0.70, 0.70, 0.0)
cylinder.ViewObject.ShapeAppearance = (mat,)

doc.recompute()
view = Gui.ActiveDocument.ActiveView
view.viewIsometric()
view.fitAll()

어셈블리 추가

With the New Assembly tool add an assembly to the document.

부품을 조립 용기로 옮기세요

In the Tree View drag and drop the parts on the Assembly object. They can now be handled by the Assembly's solver.

두 축을 접지합니다

To keep the assembly at the desired position, the two axles should be locked, or grounded as it is called here. Select the two axles in the Tree View or in the 3D View and use the Toggle Grounded tool. Two GroundedJoint objects are added to the Joints container.

조인트 적용

  • A Revolute joint between Axle2 and Piston

Revolute Joint + Selected elements → rearranged Piston

  • 피스톤과 실린더 사이의 슬라이더 조인트

Slider Joint + Selected elements → rearranged and moved Cylinder

면을 선택하기 전에 좌표계의 위치를 확인하세요. 각 면의 중앙에 있어야 합니다.

실린더를 드래그하여 피스톤과 실린더 사이에 뚜렷한 차이를 만드세요. 스프링 지지면이 보여야 합니다.

  • 피스톤과 실린더 사이의 거리 조인트

Distance Joint + Selected faces → rearranged Cylinder Distance set to 200 mm

거리 값을 200mm로 설정합니다.

다음 두 개의 관절은 실린더의 힌지를 원호를 따라 움직이게 하는 데 필요합니다.

  • Axle1과 Rod 사이의 원통형 조인트

Cylindrical Joint + Selected elements → rearranged Rod

끝점을 선택하여 좌표계의 Z축(파란색)이 막대에 수직인지 확인하세요.

  • 로드와 실린더 사이의 회전 조인트

Revolute Joint + Selected elements → rearranged Cylinder

다시 한번 좌표계의 Z축(파란색)이 막대에 수직인지 확인하세요.

이 조인트에 문제가 발생할 수 있습니다. 문제가 발생하면 다음을 시도해 보세요.

  1. 조인트를 삭제합니다.
  2. 정면 뷰로 전환합니다.
  3. 피스톤을 드래그하여 어셈블리를 회전하고, 실린더의 힌지 구멍 중심이 로드에 오도록 로드를 회전합니다.
  4. 조인트를 다시 생성합니다.

다음 두 개의 조인트는 스프링을 지지면에 고정하는 데 필요합니다.

  • 스프링과 피스톤 사이의 평행 조인트

Parallel Joint + Selected faces → rearranged Spring

피스톤 지지면의 중심과 스프링 바닥면의 중심을 선택합니다. 거리 값은 0으로 유지합니다.

  • 스프링과 피스톤 사이의 고정 조인트

Fixed Joint + Selected elements → rearranged Spring

피스톤에서 실린더 솔기의 아래쪽 정점을 선택하고 스프링에서 모서리 정점을 선택합니다.

  • 표현식을 사용하여 Distance 조인트의 거리 속성을 스프링의 Height 속성에 연결합니다.
  1. 트리 뷰에서 스프링을 선택합니다.
  2. 높이 속성 필드에서 파란색 아이콘 을 선택합니다.
  3. 표현식 편집기에 다음을 입력합니다: <<Distance>>.Distance

쇼바를 구동하세요

이렇게 하려면 트리 뷰에서 Distance 객체를 두 번 클릭하고 Distance 속성을 변경합니다. 문서를 다시 계산합니다. 스프링의 길이가 변경됩니다.