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
在本例中,摆锤不会直接掉到地上,因为它跟定点之间由摆线连接。摆锤会向平衡位置摆动,我们要计算的就是摆锤在摆动过程中的加速度。我们称之为角加速度,因为摆锤是沿着弧线加速的。
为了计算单摆的角加速度,我们要先将重力进行分解。一个分量的方向是定点到摆锤连线的延长线,由于拉力始终会抵消这个分量,所以我们不用管它。另一个垂直于连线分量是我们需要关心的,因为它始终把摆锤向“侧向”拉,产生摆动。我们把图放大来看看重力的分解:
Fg 是向下的总重力。Fp 是垂直于连线的分量,指向单摆摆动的反方向。Fa 是连线延长线方向的分量,可以忽略,因为这个分量不影响角加速度。
如何计算 Fp?这就需要用到三角函数了。Fg 是直角三角形的斜边,θ 是其中一个锐角,它的正弦等于对边除以斜边:
sin(θ)=FpFg
于是:
Fp=Fgsin(θ)
很好,我们有了一个计算 Fp 的简单表达式。现在回到最初的问题:单摆的角加速度是多少?如果算出角加速度,我们就能用牛顿定律来计算单摆的实时角度。
角速度 = 角速度 + 角加速度
角度 = 角度 + 角速度
根据牛顿第二定律,力与加速度的关系是F=M×A,或者 A=F/M,用这个公式来计算角加速度,试着跟上推导过程:
最初,我们知道:
重力垂直于连线的分量 = 重力 * sin(θ)
根据牛顿第二定律,我们将等式两边都除以质量,得到角加速度表达式:
角加速度 = (重力 * 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() {
    // Arbitrary constant
    var gravity = 0.4;
    // Calculate acceleration
    this.aAcceleration = -1 * gravity * sin(this.angle);
    // Increment velocity
    this.aVelocity += this.aAcceleration;
    // Increment angle
    this.angle += this.aVelocity;    
};
…同样还需要 display() 方法在窗口中绘制单摆。这引出一个问题:“我们在哪里画摆锤?” 我们知道角度和摆线长度,但怎么算出单摆的定点(称为原点,origin)和摆锤的位置(称为位置,position)的直角坐标 x,y ?这仍然需要用到三角函数,如图。
原点位置和摆线长度是由我们来定的,假设我们构造一个单摆对象如下:
var p = new Pendulum(new PVector(100, 10), 125);
当前角度储存在 angle 属性中,因此相对于原点,单摆的位置是一个极坐标: (r,angle) 。但我们需要的是直角坐标,幸好我们在角度那一章里推导过极坐标转换为直角坐标的公式。在那一章,我们用的是与水平轴的夹角,但这里我们用的是与垂直轴的夹角,因此计算的时候需要反过来,用 sin()cos() 来分别计算 x 分量和 y 分量,而不是cos()sin()。所以最终,我们用该转换公式计算摆锤相对于原点的位置,再计算与原点的位置向量和。
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。更多相关内容可以看看 简单单摆
this.aAcceleration = (-1 * gravity / this.armLength) * sin(this.angle);
最后,现实世界的单摆会受到一定的摩擦力(在定点处)和空气阻力。而在我们的程序里单摆会永远摆动,所以为了更逼真,我们可以使用“阻尼”技巧(damping)。我说 技巧 是因为,相比于在一定的精确程度上模拟阻力(正如在力那一章我们所做的),我们可以更简单的通过在每个周期降低一点角速度来实现类似的结果。下面的代码在动画的每一帧都把速度减小1%(或者说乘以99%):
this.aVelocity *= this.damping;
综合以上所有部分,我们有了如下程序。程序里面添加了一些功能,能让我们把摆锤拖放到不同高度。试试吧!

“自然模拟”系列课程是由 Daniel Shiffman 的 "编程的本质" 衍生而来,基于 知识共享 著名-非商用性 3.0 本地化许可协议

想加入讨论吗?

尚无帖子。
你会英语吗?单击此处查看更多可汗学院英文版的讨论.