Haskell로 4바 연동기 시뮬레이터 만들기
출처: Dev.to
대부분의 개발자는 Haskell을 함수형 프로그래밍, 타입 안전성, 컴파일러, 파서, 그리고 아름다운 수학적 추상화의 언어로 알고 있습니다.
하지만 Haskell을 이용해 인터랙티브한 공학 시뮬레이터를 만들 수 있을까요?
그것이 바로 제가 진행한 프로젝트의 동기였습니다:
Four‑Bar Mechanism Haskell Simulator
레포지토리: https://github.com/mohammadijoo/Four-Bar-Mechanism-Haskell
이 프로젝트는 Haskell로 작성된 브라우저 기반 데스크톱 스타일 GUI 애플리케이션입니다. 평면 사바링크(四棒) 메커니즘을 시각화·분류·애니메이션으로 보여주며, 사바링크는 기계공학·운동학·기계 설계에서 가장 고전적인 메커니즘 중 하나입니다.
GUI는 Threepenny‑GUI 로 구현돼 로컬 브라우저 창에서 동작하고, 수학 모델과 메커니즘 로직은 Haskell에 그대로 남아 있습니다.
저에게 흥미로운 부분은 단순히 움직이는 링크를 그리는 것이 아니라, 메커니즘 설계 이론, 계산 기하학, 함수형 프로그래밍을 하나의 작은 교육용 시뮬레이터에 결합한 것이었습니다.
사바링크(四棒)란?
사바링크는 네 개의 강체 링크가 네 개의 회전 조인트(리볼루트 조인트)로 연결된 폐쇄 루프 기계 시스템입니다.
이 프로젝트에서 네 개의 링크는 다음과 같습니다:
| 기호 | 이름 | 설명 |
|---|---|---|
g | 고정 링크 | 피벗 A와 B 사이의 고정 거리 |
a | 입력 링크 | A에서 움직이는 피벗 C 로 회전하는 링크 |
b | 출력 링크 | 고정 피벗 B에서 움직이는 피벗 D 로 연결되는 링크 |
f | 플로팅(연결) 링크 | 움직이는 피벗 C와 D 를 연결하는 링크 |
고정 피벗은 다음 위치에 놓입니다:
A = (0, 0), B = (g, 0)
입력 링크는 각도 α 로 회전합니다. 따라서 점 C는 바로 다음과 같이 계산됩니다:
C = (g·cosα, g·sinα)
점 D는 좀 더 흥미롭습니다. 두 개의 거리 제약을 만족해야 합니다:
|D − C| = f
|D − B| = b
시뮬레이터는 원 교차법을 이용해 점 D의 위치를 구합니다.
- 반지름
f로 C를 중심으로 한 원 - 반지름
b로 B를 중심으로 한 원
두 원이 교차하는 지점이 메커니즘을 닫을 수 있는 위치이며, 이것이 시뮬레이터의 기본 기하학적 핵심입니다.
사바링크는 다양한 실제 기계 시스템에 등장합니다. 몇 가지 예시:
| 적용 분야 | 사바링크의 역할 |
|---|---|
| 엔진 | 회전·진동 운동 변환 |
| 서스펜션 시스템 | 제한된 운동 가이드 |
| 로봇 그리퍼 | 개폐 경로 제어 |
| 펌프 | 회전 운동을 왕복 운동으로 변환 |
| 보행 메커니즘 | 반복적인 다리 움직임 생성 |
| 와이퍼 | 모터 회전을 휩쓸기 운동으로 변환 |
| 공작기계 | 반복 가능한 제한 운동 제공 |
사바링크는 시각적으로 이해하기 쉬울 정도로 단순하지만, 실제 메커니즘 설계 개념을 가르치기에 충분히 풍부합니다. 그래서 기계공학·기계 설계 과목에서 자주 등장합니다.
평면 메커니즘은 Kutzbach‑Grübler 자유도 방정식으로 분석할 수 있습니다.
[ M = 3(n-1) - 2j_1 - j_2 ]
| 기호 | 의미 |
|---|---|
M | 자유도(운동 가능성) |
n | 링크 수 |
j_1 | 회전 조인트 등 하위 쌍(lower pair) 수 |
j_2 | 고차 쌍(higher pair) 수 |
이상적인 평면 사바링크에 대해선 n = 4, j_1 = 4, j_2 = 0 이므로
[ M = 3(4-1) - 2·4 - 0 = 9 - 8 = 1 ]
즉, 사바링크는 정확히 1 자유도를 가집니다. 입력 각도가 정해지면 루프 폐쇄 제약에 의해 나머지 구성은 자동으로 결정됩니다(단, 선택된 조립 분기(branch) 제외). 이것은 “하나의 입력 움직임이 전체 메커니즘을 구동한다”는 멋진 결과입니다.
시뮬레이터는 링크 길이와 입력 각도로부터 메커니즘 위치를 계산합니다.
- 먼저
B와C사이 거리d = |B - C|를 구합니다. - 메커니즘이 닫히려면 두 원이 교차해야 하므로 조립 조건은
[ |f - b| \le d \le f + b ]
이 조건을 만족하지 않으면 실존하는 점 D 가 없으며, 해당 입력 각도에서는 메커니즘을 조립할 수 없습니다.
조건을 만족하면 보통 두 개의 D 후보가 나오며, 이는 메커니즘의 두 조립 분기를 의미합니다. GUI에서는 Flip branch 버튼이나 F 키로 두 분기 사이를 전환할 수 있습니다.
프로젝트에서 가장 마음에 드는 부분: 애니메이션이 미리 그려진 시각 효과가 아니라, 시뮬레이터가 실제로 링크 기하학을 풀어낸다는 점입니다.
그라쇼프(Grashof) 조건
사바링크 설계에서 가장 중요한 아이디어 중 하나는 그라쇼프 조건입니다. 네 링크 길이를 짧은 순서대로 정렬하면
[ s \le p \le q \le l ]
(s = 최단 링크, l = 최장 링크)
그라쇼프 지수는
[ G = s + l - p - q ]
| 조건 | 의미 |
|---|---|
G ≤ 0 | 그라쇼프 메커니즘 (적어도 하나의 링크가 완전 회전 가능) |
G > 0 | 비그라쇼프 메커니즘 |
그라쇼프 메커니즘이면 입력 링크가 연속 회전할 수 있는지, 아니면 앞뒤로 흔들리는지(rocking) 판단할 수 있어 설계 초기 단계에서 중요한 질문에 답을 줍니다.
시뮬레이터는 또한 메커니즘이 물리적으로 조립 가능한지 검사합니다. 유효성 지수는
[ V = l - s - p - q ]
유효한 사바링크는 V ≤ 0 을 만족해야 합니다. V > 0 이면 최장 링크가 나머지 세 링크의 합보다 길어 루프를 닫을 수 없습니다. 따라서 시뮬레이터는 운동 유형뿐 아니라 링크 길이가 유효한 메커니즘을 구성하는지도 알려줍니다.
시뮬레이터는 다음과 같은 세 가지 초과값을 계산합니다:
[ \begin{aligned} T_1 &= g + f - b - a \ T_2 &= b + g - f - a \ T_3 &= f + b - g - a \end{aligned} ]
이 값들의 부호를 통해 상세한 링크 상태를 분류하고, GUI 에서는 간단한 운동 유형을 다음과 같이 표시합니다:
| 분류 | 의미 |
|---|---|
crank‑crank | 두 관련 링크 모두 완전 회전 가능 |
crank‑rocker | 입력은 완전 회전, 출력은 흔들림 |
rocker‑crank | 입력은 흔들림, 출력은 완전 회전 |
rocker‑rocker | 두 링크 모두 흔들림 |
이렇게 프로젝트는 단순한 움직이는 도식이 아니라 작은 메커니즘 분류 도구가 됩니다.
왜 Haskell인가?
기계 시뮬레이터를 구현할 때 대부분은 Python, MATLAB, C++, JavaScript, Julia 등을 선택합니다. 하지만 바로 그 점이 이 프로젝트를 흥미롭게 만들었습니다. Haskell 은 순수 수학 로직과 부수 효과가 있는 애플리케이션 코드를 명확히 분리하는 데 뛰어납니다.
프로젝트 구조는 다음과 같습니다:
- 메커니즘 모델 – 순수 Haskell 모듈
src/FourBar.hsLinkage,Pose,excesses,grashofIndex,validityIndex,classifyMotion,