If you're seeing this message, it means we're having trouble loading external resources on our website.

웹 필터가 올바르게 작동하지 않으면 도메인 *. kastatic.org*.kasandbox.org이 차단되어 있는지 확인하세요.

주요 내용

삼각법과 힘: 진자

이전 수업에서 배운 뉴턴의 운동 법칙을 기억하시나요? 이제 이 법칙을 코드로 만들어 볼 겁니다. 삼각형, 탄젠트와 파동을 배우는 것도 좋지만 이 과정의 핵심은 물체를 물리법칙에 따라 움직이는 것입니다. 삼각함수로 어떻게 우리의 목표를 달성할 수 있을지 한 번 살펴봅시다.
특정한 각을 이루는 진자
진자는 축(피벗) 에 매달린 추입니다. 현실에서 진자는 당연히 삼차원 공간에 있겠지만 진자가 이차원 공간—프로그램 캔버스에 있는 단순한 시나리오를 생각하도록 합니다.
힘 수업에서 어떻게 (위 그림에서 보이는 중력과 같은) 힘이 물체를 가속하는지 배웠습니다. **F = M * A** , **A = F / M** . 하지만 이 경우 진자는 땅으로 떨어지지 않습니다. 진자와 축이 실로 연결되어 있기 때문입니다. 그러므로 각가속도를 정하기 위해 중력뿐만 아니라 정지상태의 진자와 운동 중인 진자 줄의 각에 가해지는 힘을 알 필요가 있습니다.
위 경우 진자 줄의 길이는 변하지 않기 때문에 이 상황에서 유일한 변수는 각도입니다. 각속도와 각가속도를 사용해서 진자 운동을 묘사해 봅시다. 각가속도를 계산하려면 삼각함수와 뉴턴의 제2법칙을 이용해야 합니다.
진자 그림에서 오른쪽 삼각형을 좀 더 상세히 살펴보겠습니다.
진자의 힘 (Fp)은 진자 줄에 대해 직각을 이루며 진자운동을 하는 방향으로 가해집니다. 결국 줄이 없으면 추는 바로 땅으로 떨어질 것입니다. 줄의 장력은 추를 정지상태로 가속하는 힘입니다. 중력 (Fg)은 아래로 향하기 때문에, 이제 이 두 벡터로 직각삼각형을 만들면 뭔가 굉장한 것이 완성됐습니다. 중력은 직각삼각형의 빗변이 되고 벡터를 두 성분으로 분리했습니다. 두 성분 중 하나는 진자의 힘을 나타냅니다. 사인은 높이 나누기 빗변으로 정의되므로, 다음과 같이 쓸 수 있습니다:
sin(θ)=FpFg
그러므로:
Fp=Fg×sin(θ)
지금까지 한 모든 것은 진자의 각가속도를 구하는 방법을 알아내기 위해 한 것입니다. 각가속도를 알면 운동의 법칙을 적용하여 진자의 새로운 각을 찾을 수 있습니다.
각속도 = 각속도 + 각가속도
각도 = 각도 + 각속도
여기서 좋은 소식은 뉴턴의 제2법칙으로 힘과 가속도 사이의 관계 즉, F=M×A 또는 A=F/M를 알고 관계식을 이용하여 각가속도를 알 수 있다는 것입니다. 다음 과정을 보고 함께 생각해 봅시다:
먼저 이렇게 시작합시다:
진자의 힘 = 중력에 의한 힘 * sin(θ)
그다음 우변을 질량으로 나누면 뉴턴 제2법칙에 의해 가속도만 남습니다.
진자의 각가속도 = (중력에 의한 힘 * sin(θ)) / 질량
그럼 중력에 의한 힘을 질량으로 나누면 중력에 의한 가속도를 구할 수 있고 바로 결과를 식에 대입하면 됩니다.
진지의 각가속도 = 중력에 의한 가속도 * sin (θ)
이제 각가속도를 계산하는 방법을 알게 되었습니다.
여기서 한 가지 기억해야 할 것은 우리는 물리학자가 아니라 ProcessingJS 프로그래머라는 겁니다. 지구에서 중력가속도는 제곱 초당 9.8 미터입니다. 그러나 이 값은 여기서 아무런 의미가 없습니다. 우리가 구현하려는 중력가속도는 단지 물체가 자연스럽게 움직이는 선에서 조절하는 무작위 상수일 뿐입니다.
각가속도 = 중력 * sin(θ)
놀랍게도 공식이 매우 단순해졌습니다. 공식을 굳이 왜 유도하는지 궁금할 수도 있습니다. 배워서 나쁠 건 없지만 그냥 "진자의 각가속도는 특정 상숫값 곱하기 각의 사인값입니다." 라고 알려주는 게 쉬워 보일 수도 있겠죠. 하지만 이 수업의 목적은 진자가 어떻게 흔들리는지 또는 중력이 어떻게 작용하는지 배우는 것이 아니라는 점을 기억하세요. 중요한 건 컴퓨터가 그래픽 처리하여 보여주는 화면에서 물체가 움직이는 방법을 창의적으로 생각해내는 것입니다. 진자는 단지 하나의 예로 배우는 것입니다. 진자를 프로그래밍하는 방식을 이해하면 다른 프로그램을 설계할 때 똑같은 기술을 적용할 수 있습니다.
물론 아직 다 살펴본 것은 아닙니다. 단순하고 멋진 공식을 알게 된 건 좋지만, 이 공식을 코드에 적용해야 합니다. 이제 객체 지향 프로그래밍 실력을 발휘해 Pendulum 객체를 만들어 봅시다. 진자를 배우면서 본 모든 성질을 떠올리며 객체가 무엇을 갖춰야 할지 생각해 봅시다.
  • 줄길이(arm length)
  • 각(angle)
  • 각속도(angular velocity)
  • 각가속도(angular acceleration)
또 진자가 어디에 걸려 있는지를 정해야 하므로 다음과 같은 생성자를 만듭니다.
var Pendulum  = function(origin, armLength) {
    this.origin = origin;
    this.armLength = armLength;

    this.angle = PI/4;
    this.aVelocity = 0.0;
    this.aAcceleration = 0.0;
};
또한 공식에 따라 진자의 각을 갱신하기 위해 update() 메소드를 작성해야 합니다…
Pendulum.prototype.update = function() {
    // 임의의 상수
    var gravity = 0.4;
    // 가속도 계산
    this.aAcceleration = -1 * gravity * sin(this.angle);
    // 속도 증가량
    this.aVelocity += this.aAcceleration;
    // 각 증가량
    this.angle += this.aVelocity;    
};
…뿐만 아니라 화면에 진자를 출력하기 위해 display() 메소드도 만들어야 합니다. 그러면 이런 생각이 들겠죠. “진자를 어디에 그려야 할까요?” 각과 줄 길이는 알고 있지만, 진자의 축이 있는 원점과(origin) 추의 위치(location)에 대한 x,y(데카르트) 좌표를 어디로 두어야 할까요? 답은 너무 익숙한 삼각법입니다. 왼쪽의 그림을 살펴봅시다.
원점과 실의 길이는 임의로 만든 것입니다. 다음과 같이 진자를 생성합니다:
var p = new Pendulum(new PVector(100, 10), 125);
현재의 각도는 angle 프로퍼티에 저장합니다. 따라서 원점을 기준으로 진자의 위치는 극좌표 (r,각도) 입니다. 이 좌표를 데카르트 좌표로 바꿔야 합니다. 다행히 각에 관한 수업에서 극좌표를 데카르트 좌표로 변환하는 공식을 배웠습니다. 이 수업에서 각은 수평축에 대하지만 여기에서는 수직축에 대한 각을 사용합니다. 그래서 x 위치에 대한 cos()과 y 위치에 대한 sin() 대신에 x 위치에 대한 sin()과 y 위치에 대한 cos()을 이용합니다. 이제 변환 공식을 이용하여 원점에서 상대적인 위치를 계산한 후 그 결과에 원점 위치를 더합니다.
this.position = new PVector(
   this.armLength * sin(this.angle),
   this.armLength * cos(this.angle));
this.position.add(this.origin);
stroke(0, 0, 0);
fill(175, 175, 175);
line(this.origin.x, this.origin.y, this.position.x, this.position.y);
ellipse(this.position.x, this.position.y, 16, 16);
모든 코드를 합치기 전에, 언급하지 않은 것이 하나 있습니다. 잠시 진자에 달린 줄에 관해 생각해 봅시다. 줄은 금속 막대일까요? 끈일까요? 고무줄일까요? 줄은 축에 어떻게 연결되어 있을까요? 얼마나 길까요? 질량은 얼마일까요? 바람이 불까요? 시뮬레이션에 영향을 줄 수 있는 요소가 수없이 많습니다. 물론 여기는 진자의 줄이 절대로 구부러지지 않는 이상적인 막대이고 추의 질량이 매우 작은 점에 모여있는 완벽한 세상입니다.
앞의 모든 질문을 고려하지 않는다고 하더라도 각가속도 계산에 변수 한 개를 더 추가해야 합니다. 단순히 하기 위해 진자의 가속도를 구할 때 진자의 실의 길이를 1로 가정합니다. 실제로 진자의 줄 길이는 가속도에 매우 큰 영향을 미칩니다. 줄이 길수록 가속도는 느려집니다. 진자의 운동을 좀 더 현실적으로 만들기 위해 줄 길이로 나눕니다. 이 경우 armLength입니다. 더 심화된 설명은 The Simple Pendulum 웹 사이트를 참조하세요.
this.aAcceleration = (-1 * gravity / this.armLength) * sin(this.angle);
현실에서 진자는 어느 정도의 축 마찰과 공기 저항을 받게 됩니다. 지금 있는 코드를 실행하면 진자는 영원히 흔들립니다. 진자에 “제동을 하는” 트릭을 이용하면 좀 더 현실적이게 보일 수 있습니다. 이걸 트릭 이라고 하는 이유는 저항력을 현실처럼 모델링 하지 않고 왕복을 거듭하며 각속도를 감소시키는 방법을 써서 속임수 같아 보이기 때문입니다. 아래 나와 있는 코드는 매 프레임마다 속도를 1%만큼(또는 99%을 곱하는 만큼) 줄입니다.
this.aVelocity *= this.damping;
이제 모든 코드를 합치면 아래 예제처럼 됩니다. 기능을 따로 추가해서 추를 드래그하고 서로 다른 높이에서 떨어트릴 수 있습니다. 직접 한 번 해보세요!

본 "내추럴 시뮬레이션" 과정은 다니엘 쉬프만(Daniel Shiffman)이 저술한 "The Nature of Code"의 내용을 차용한 것이며, 본 내용물의 저작권은 Creative Commons Attribution-NonCommercial 3.0 Unported License를 적용합니다.