用Python模拟碰撞实验:5分钟搞懂动量守恒定律(附完整代码)
2026/4/22 20:35:21 网站建设 项目流程

用Python模拟碰撞实验:5分钟搞懂动量守恒定律(附完整代码)

记得第一次在物理课上听到"动量守恒"这个词时,脑子里全是问号。直到后来用代码模拟了两个小球的碰撞,那些抽象的公式突然变得生动起来。今天我们就用Python,从零开始构建一个弹性碰撞模拟器,让动量守恒定律看得见、摸得着。

1. 环境准备与基础设置

在开始编码前,我们需要准备好Python环境和必要的库。推荐使用Anaconda创建虚拟环境,避免与其他项目产生依赖冲突:

conda create -n physics_sim python=3.8 conda activate physics_sim pip install pygame numpy matplotlib

为什么选择Pygame?相比纯Matplotlib方案,Pygame能提供更流畅的实时动画效果,特别适合需要交互的场景。我们主要用到以下核心组件:

  • Pygame:处理图形渲染和事件循环
  • NumPy:向量运算和物理计算
  • Matplotlib(可选):后期数据分析

基础物理参数设置建议:

参数类型变量名示例推荐初始值单位
质量mass11.0kg
初速度velocity1[3.0, 0.0]m/s
位置position1[100, 200]px
半径radius30px

提示:使用国际单位制(SI)能保持物理量纲一致性,但显示时可以按比例缩放

2. 构建小球物理模型

在二维空间中,每个小球需要完整的物理属性描述。我们创建一个Ball类来封装这些特性:

class Ball: def __init__(self, mass, radius, position, velocity, color): self.mass = mass self.radius = radius self.position = np.array(position, dtype=float) self.velocity = np.array(velocity, dtype=float) self.color = color self.trail = [] # 用于记录运动轨迹 def update(self, dt): """根据当前速度更新位置""" self.position += self.velocity * dt self.trail.append(self.position.copy()) if len(self.trail) > 50: # 限制轨迹长度 self.trail.pop(0)

弹性碰撞的核心算法实现要点:

  1. 碰撞检测:当两球中心距离 ≤ 半径之和时触发
  2. 动量计算:使用向量形式的动量守恒公式
  3. 能量守恒:弹性碰撞动能不变

关键计算公式:

v1' = v1 - (2*m2)/(m1+m2) * dot(v1-v2, x1-x2) / |x1-x2|² * (x1-x2) v2' = v2 - (2*m1)/(m1+m2) * dot(v2-v1, x2-x1) / |x2-x1|² * (x2-x1)

3. 完整碰撞模拟实现

下面是将所有组件整合后的主程序框架:

def main(): pygame.init() screen = pygame.display.set_mode((800, 600)) clock = pygame.time.Clock() # 创建两个不同质量的小球 ball1 = Ball(mass=2.0, radius=30, position=[200, 300], velocity=[2.5, 0], color=(255, 0, 0)) ball2 = Ball(mass=1.0, radius=25, position=[500, 300], velocity=[-1.0, 0], color=(0, 0, 255)) running = True while running: dt = 0.1 # 时间步长 # 处理事件 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False # 更新物理状态 ball1.update(dt) ball2.update(dt) # 碰撞检测与处理 if check_collision(ball1, ball2): handle_collision(ball1, ball2) # 边界反弹 handle_boundary(ball1, screen.get_size()) handle_boundary(ball2, screen.get_size()) # 渲染 screen.fill((240, 240, 240)) draw_ball(screen, ball1) draw_ball(screen, ball2) pygame.display.flip() clock.tick(60)

碰撞处理函数的具体实现:

def handle_collision(ball1, ball2): """处理两球的弹性碰撞""" pos1, pos2 = ball1.position, ball2.position vel1, vel2 = ball1.velocity, ball2.velocity m1, m2 = ball1.mass, ball2.mass # 计算法线向量 normal = pos1 - pos2 dist_sq = np.sum(normal**2) normal = normal / np.sqrt(dist_sq) # 计算相对速度在法线方向的投影 v_rel = np.dot(vel1 - vel2, normal) # 只有当小球相互靠近时才处理碰撞 if v_rel > 0: return # 计算冲量 j = -(1 + 1.0) * v_rel / (1/m1 + 1/m2) # 1.0是弹性系数 # 更新速度 ball1.velocity = vel1 + (j/m1) * normal ball2.velocity = vel2 - (j/m2) * normal

4. 可视化与数据分析

单纯的动画演示还不够,我们需要定量验证动量守恒。添加以下监控代码:

def calculate_momentum(balls): """计算系统总动量""" total_p = np.zeros(2) for ball in balls: total_p += ball.mass * ball.velocity return total_p def calculate_energy(balls): """计算系统总动能""" total_ke = 0.0 for ball in balls: total_ke += 0.5 * ball.mass * np.sum(ball.velocity**2) return total_ke

在模拟过程中记录这些数据,然后用Matplotlib绘制:

plt.figure(figsize=(12, 4)) plt.subplot(1, 2, 1) plt.plot(times, px_values, label='Px') plt.plot(times, py_values, label='Py') plt.title("Total Momentum Over Time") plt.legend() plt.subplot(1, 2, 2) plt.plot(times, ke_values) plt.title("Total Kinetic Energy Over Time") plt.tight_layout() plt.show()

典型输出结果应显示:

  • 动量曲线基本保持水平(守恒)
  • 动能曲线在碰撞瞬间有微小波动(理想弹性碰撞应完全守恒)

5. 交互式实验设计

为了让学习体验更丰富,我们可以添加交互控制:

def create_slider(surface, x, y, width, height, min_val, max_val, initial_val): """创建简易滑块控件""" rect = pygame.Rect(x, y, width, height) handle_x = x + (initial_val - min_val)/(max_val - min_val) * width handle_rect = pygame.Rect(handle_x-5, y-5, 10, height+10) return {'rect': rect, 'handle': handle_rect, 'min': min_val, 'max': max_val, 'value': initial_val} def draw_slider(surface, slider, color): """绘制滑块""" pygame.draw.rect(surface, (200, 200, 200), slider['rect']) pygame.draw.rect(surface, color, slider['handle'])

添加控制参数:

  • 质量调节滑块(0.1kg ~ 5kg)
  • 初速度控制(-5m/s ~ 5m/s)
  • 弹性系数调节(0~1,1为完全弹性)

6. 常见问题与调试技巧

在实现过程中可能会遇到这些典型问题:

  1. 小球粘在一起

    • 原因:碰撞后未及时分离导致连续检测
    • 修复:添加最小分离距离判断
  2. 能量逐渐损失

    • 检查时间步长dt是否过大
    • 确保碰撞计算没有数值误差累积
  3. 动量不守恒

    • 确认边界处理是否正确
    • 检查向量运算是否有维度错误

性能优化建议:

  • 对多球场景使用空间分区算法(如四叉树)
  • 将NumPy运算向量化
  • 限制轨迹点的存储数量
# 示例:改进后的碰撞检测 def check_collision(ball1, ball2): min_dist = ball1.radius + ball2.radius actual_dist = np.linalg.norm(ball1.position - ball2.position) return actual_dist <= min_dist and actual_dist > 0.9*min_dist # 添加缓冲阈值

7. 教学应用扩展

这个模拟器可以轻松扩展为教学工具:

  1. 非弹性碰撞: 修改弹性系数(0 ≤ e ≤ 1)

    # 在handle_collision中 e = 0.7 # 弹性系数 j = -(1 + e) * v_rel / (1/m1 + 1/m2)
  2. 多球系统

    balls = [Ball(...) for _ in range(5)] # 创建多个球
  3. 不同形状物体: 虽然原理相同,但需要更复杂的碰撞检测算法

教学场景建议:

  • 对比不同质量比下的速度变化
  • 观察斜碰时的动量分量守恒
  • 演示完全非弹性碰撞的特殊情况
# 示例:速度矢量的可视化 def draw_velocity(surface, ball): end_pos = ball.position + 20*ball.velocity pygame.draw.line(surface, (0, 255, 0), ball.position, end_pos, 2)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询