You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于WebVPython的一维双弹球模拟:橡皮筋效应与动量计算求助

一维小球弹跳与碰撞模拟的问题修复方案

问题根源分析

  • 橡皮筋效应与穿透:当前代码使用匀变速运动的解析公式(y = y0 + v0*t - 0.5*g*t²)更新位置,这种方法假设运动过程中加速度恒定,但碰撞是瞬时速度突变的事件,解析公式无法实时响应碰撞后的状态变化,导致物体穿透、反复触发碰撞逻辑,出现“橡皮筋”式的抖动。
  • 碰撞逻辑错误:
    1. 两球碰撞判断仅比较中心y坐标,未考虑小球半径,实际碰撞发生在两球中心距离等于2倍半径时。
    2. 碰撞时的速度处理错误,直接对单个球速度反转并乘以恢复系数,未遵循动量守恒与能量守恒,导致碰撞结果不符合物理规律。

核心修复方案

1. 改用数值积分更新运动状态

放弃解析公式,改用逐帧数值积分(欧拉法)更新速度和位置,每帧基于当前速度和加速度计算新位置,这样能及时响应碰撞带来的速度突变:

# 逐帧更新速度和位置
ball.vy += -g * dt
ball.pos.y += ball.vy * dt
ball2.vy += -g * dt
ball2.pos.y += ball2.vy * dt

2. 正确的碰撞检测与动量计算

一维弹性碰撞(带恢复系数c)的速度更新公式:
对于两个质量为m1、m2,碰撞前速度为v1、v2的物体,碰撞后速度v1'、v2'为:

v1' = ((m1 - c*m2)*v1 + (1 + c)*m2*v2) / (m1 + m2)
v2' = ((1 + c)*m1*v1 + (m2 - c*m1)*v2) / (m1 + m2)

其中c为恢复系数(0≤c≤1,c=1为完全弹性碰撞,c=0为完全非弹性碰撞)。

碰撞检测需判断两球中心距离是否小于等于2*R,同时要确保两球是相向运动(避免碰撞后重复触发)。

3. 修复地面碰撞逻辑

地面碰撞时,先将小球位置修正到刚好接触地面的位置,再更新速度,避免穿透:

# 地面碰撞修复
if ball.pos.y <= floor.pos.y + R:
    ball.pos.y = floor.pos.y + R  # 修正位置,避免穿透
    ball.vy = -sqrt(c) * ball.vy

修改后的完整代码

Web VPython 3.2

# 图表设置
g1 = graph(xtitle="t [s]",ytitle="y [m]", width=500, height=150)
fp = gcurve(color=color.green)
g2 = graph(xtitle="t [s]",ytitle="v [m/s]", width=500, height=150)
fv = gcurve(color=color.red)
g3 = graph(xtitle="t [s]",ytitle="E [J]", width=500, height=150)
fK = gcurve(color=color.blue)
fU = gcurve(color=color.red)
fE = gcurve(color=color.magenta)

g4 = graph(xtitle="t [s]",ytitle="v [m/s]", width=500, height=150)
fyA = gcurve(color=color.green)
fyB = gcurve(color=color.orange)

# 地面与小球参数
R = 0.02
floor = box(pos = vector(0,-0.005-R,0),size=vector(0.1,0.01,0.1))

h = .3
g = 9.8
c = 0.5  # 恢复系数
t = 0
dt = 0.01

# 小球A初始化
mA = 2
ball = sphere(pos=vector(0, h, 0), radius=R, color=color.green, 
              make_trail=True, trail_type="points", interval=10, retain=10)
ball.vy = 0  # 直接给小球添加速度属性

# 小球B初始化
mB = .05
ball2 = sphere(pos=vector(0, h+0.1, 0), radius=R, color=color.orange, 
               make_trail=True, trail_type="points", interval=10, retain=10)
ball2.vy = 0

# 主循环
while t < 2:
    rate(100)  # 提高帧率,减少视觉抖动
    
    # 数值积分更新运动状态
    ball.vy += -g * dt
    ball.pos.y += ball.vy * dt
    ball2.vy += -g * dt
    ball2.pos.y += ball2.vy * dt
    
    # 小球A与地面碰撞
    if ball.pos.y <= floor.pos.y + R:
        ball.pos.y = floor.pos.y + R  # 修正位置,避免穿透
        ball.vy = -sqrt(c) * ball.vy
    
    # 小球B与地面碰撞
    if ball2.pos.y <= floor.pos.y + R:
        ball2.pos.y = floor.pos.y + R
        ball2.vy = -sqrt(c) * ball2.vy
    
    # 两球碰撞检测与处理
    distance = abs(ball.pos.y - ball2.pos.y)
    # 判断是否碰撞,且相向运动(避免碰撞后重复触发)
    if distance <= 2*R and ((ball.vy > ball2.vy and ball.pos.y < ball2.pos.y) or 
                           (ball.vy < ball2.vy and ball.pos.y > ball2.pos.y)):
        # 临时存储碰撞前速度
        v1 = ball.vy
        v2 = ball2.vy
        # 应用弹性碰撞公式计算新速度
        ball.vy = ((mA - c*mB)*v1 + (1 + c)*mB*v2) / (mA + mB)
        ball2.vy = ((1 + c)*mA*v1 + (mB - c*mA)*v2) / (mA + mB)
        # 轻微分离两球,避免连续碰撞
        if ball.pos.y < ball2.pos.y:
            ball.pos.y -= 0.001
            ball2.pos.y += 0.001
        else:
            ball.pos.y += 0.001
            ball2.pos.y -= 0.001
    
    # 绘制图表
    K = 0.5*mA*(ball.vy**2)
    U = mA*g*ball.pos.y
    E = K + U
    fp.plot(t, ball.pos.y)
    fv.plot(t, ball.vy)
    fE.plot(t, E)
    fK.plot(t, K)
    fU.plot(t, U)
    
    fyA.plot(t, ball.vy)
    fyB.plot(t, ball2.vy)
    
    t += dt

print("t = ", t , "seconds")

关键修改说明

  • 给每个小球直接添加vy属性,简化状态管理,避免原代码中vy0A、btA等冗余变量。
  • 改用数值积分逐帧更新速度和位置,确保碰撞后状态能实时生效。
  • 碰撞检测加入半径判断和运动方向判断,避免误触发。
  • 碰撞后轻微分离两球,防止连续触发碰撞逻辑。
  • 地面碰撞时先修正位置,彻底解决穿透问题。

内容的提问来源于stack exchange,提问作者Amir G

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 23:57:08