当前位置: 首页 > news >正文

python中学物理实验模拟:摩檫力

python中学物理实验模拟:摩檫力

摩擦力是当两个物体相互接触时,在接触面上阻碍相对运动的力。

摩擦力的作用

阻碍作用:阻碍相对运动

动力作用:有时可以提供动力(如行走、汽车起步)

影响摩擦力的因素

正压力:摩擦力与正压力成正比

接触面材料:决定摩擦系数

接触面粗糙程度:影响摩擦系数

注意:摩擦力与接触面积大小无关

摩擦力的分类

1. 静摩擦力

  • 定义:物体之间有相对运动趋势但未发生相对运动时的摩擦力
  • 特点
    • 大小可变,范围:0 ≤ f ≤ fmax  (fmax为最大静摩擦力)
    • 方向与相对运动趋势方向相反
    • 最大静摩擦力:fmax = μs × N (μs为静摩擦因数,由接触面材料和粗糙程度决定,略大于滑动摩擦系数μ;N为接触面间的正压力,垂直于接触面的力)
    • 说明:中学阶段常默认 fmax ≈ μ × N(用动摩擦因数近似代替静摩擦因数),简化计算。

2. 滑动摩擦力

  • 定义:物体之间发生相对滑动时的摩擦力
  • 计算公式:f = μ × N
    • μ:滑动摩擦系数(无单位)
    • N:正压力(垂直于接触面的力)。
  • 特点
    • 大小恒定
    • 方向与相对运动方向相反

3. 滚动摩擦力

  • 特点:比滑动摩擦力小得多
  • 应用:轮子、球类运动等

易错点

  • 误将静摩擦力直接用μ × N计算(静摩擦力需根据受力平衡求解);
  • 混淆正压力与重力(如斜面上的物体,正压力为重力的分力)。
  • 影响因素:仅与μ和N有关,与物体的运动速度、接触面积无关。

下面这个摩擦力实验模拟程序,用于在水平地面上推动一个物体的情况。

运行截图

源码如下:

import tkinter as tk
from tkinter import ttk, messagebox
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.animation import FuncAnimation
from matplotlib.colors import TABLEAU_COLORS
from matplotlib.patches import Rectangle, FancyBboxPatch
import matplotlib.patches as patchesclass FrictionSimulationApp:def __init__(self, root):self.root = rootself.root.title("摩擦力物理实验模拟器")self.root.geometry("1200x900")# 设置中文支持plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'DejaVu Sans']plt.rcParams['axes.unicode_minus'] = False# 物理常数self.g = 9.81self.dt = 0.05self.max_time = 10.0# 初始化GUI组件self.setup_gui()self.setup_plot()# 动画控制self.animation = Noneself.is_paused = Falseself.finished_trajectories = []self.current_trajectory = Noneself.color_cycle = list(TABLEAU_COLORS.keys())self.current_color_idx = 0self.last_point = None# 动画状态变量self.animation_time = 0self.animation_velocity = 0self.animation_position = 0self.animation_acceleration = 0self.animation_line = Noneself.animation_color = None# 直观运行图相关变量self.visual_box = Noneself.visual_arrow = Noneself.visual_friction_arrow = Noneself.initial_box_x = 0.15  # 物体初始位置# 初始化显示self.update_physics_info()self.setup_visual_plot()def setup_gui(self):"""设置图形用户界面"""# 主框架main_frame = ttk.Frame(self.root)main_frame.pack(fill="both", expand=True, padx=8, pady=8)# 左侧控制面板 - 固定宽度left_frame = ttk.Frame(main_frame, width=400)left_frame.pack(side="left", fill="y", padx=(0, 8))left_frame.pack_propagate(False)# 参数设置面板self.setup_parameter_panel(left_frame)# 物理信息显示面板self.setup_physics_panel(left_frame)# 控制按钮面板self.setup_control_panel(left_frame)# 右侧绘图区域right_frame = ttk.Frame(main_frame)right_frame.pack(side="right", fill="both", expand=True)self.setup_plot_area(right_frame)def setup_parameter_panel(self, parent):"""设置参数控制面板"""param_frame = ttk.LabelFrame(parent, text="参数设置", padding=10)param_frame.pack(fill="x", pady=(0, 8))# 质量控制ttk.Label(param_frame, text="物体质量 (kg):").grid(row=0, column=0, sticky="w", pady=5)self.mass_var = tk.DoubleVar(value=10.0)mass_scale = ttk.Scale(param_frame, from_=1.0, to=50.0, variable=self.mass_var, orient=tk.HORIZONTAL, length=200)mass_scale.grid(row=0, column=1, padx=5, pady=5)self.mass_label = ttk.Label(param_frame, text="10.0", width=6)self.mass_label.grid(row=0, column=2, pady=5)mass_scale.configure(command=self.update_mass_label)# 摩擦系数控制ttk.Label(param_frame, text="摩擦系数 μ:").grid(row=1, column=0, sticky="w", pady=5)self.mu_var = tk.DoubleVar(value=0.3)mu_scale = ttk.Scale(param_frame, from_=0.1, to=1.0, variable=self.mu_var, orient=tk.HORIZONTAL, length=200)mu_scale.grid(row=1, column=1, padx=5, pady=5)self.mu_label = ttk.Label(param_frame, text="0.30", width=6)self.mu_label.grid(row=1, column=2, pady=5)mu_scale.configure(command=self.update_mu_label)# 施加力控制ttk.Label(param_frame, text="施加力 (N):").grid(row=2, column=0, sticky="w", pady=5)self.force_var = tk.DoubleVar(value=40.0)force_scale = ttk.Scale(param_frame, from_=0.0, to=200.0, variable=self.force_var, orient=tk.HORIZONTAL, length=200)force_scale.grid(row=2, column=1, padx=5, pady=5)self.force_label = ttk.Label(param_frame, text="40.0", width=6)self.force_label.grid(row=2, column=2, pady=5)force_scale.configure(command=self.update_force_label)def setup_physics_panel(self, parent):"""设置物理信息显示面板"""info_frame = ttk.LabelFrame(parent, text="物理分析", padding=10)info_frame.pack(fill="both", expand=True, pady=(0, 8))# 创建文本显示区域text_container = ttk.Frame(info_frame)text_container.pack(fill="both", expand=True)self.info_text = tk.Text(text_container, width=42, height=15, font=("Consolas", 9), wrap=tk.WORD)scrollbar = ttk.Scrollbar(text_container, orient=tk.VERTICAL, command=self.info_text.yview)self.info_text.configure(yscrollcommand=scrollbar.set)self.info_text.pack(side="left", fill="both", expand=True)scrollbar.pack(side="right", fill="y")def setup_control_panel(self, parent):"""设置控制按钮面板"""control_frame = ttk.LabelFrame(parent, text="控制面板", padding=10)control_frame.pack(fill="x")self.btn_start = ttk.Button(control_frame, text="开始模拟", command=self.start_simulation, width=15)self.btn_start.pack(pady=3, fill="x")self.btn_pause = ttk.Button(control_frame, text="暂停", command=self.toggle_pause, width=15, state='disabled')self.btn_pause.pack(pady=3, fill="x")self.btn_clear = ttk.Button(control_frame, text="清除轨迹", command=self.clear_trajectories, width=15)self.btn_clear.pack(pady=3, fill="x")def setup_plot_area(self, parent):"""设置绘图区域"""# 使用grid布局来更好地控制比例parent.grid_rowconfigure(0, weight=3)  # 轨迹图占3/4parent.grid_rowconfigure(1, weight=1)  # 直观图占1/4parent.grid_columnconfigure(0, weight=1)# 运动轨迹图(上方,占主要空间)trajectory_frame = ttk.LabelFrame(parent, text="运动轨迹图", padding=5)trajectory_frame.grid(row=0, column=0, sticky="nsew", pady=(0, 5))# 直观运行图(下方,固定合适的高度)visual_frame = ttk.LabelFrame(parent, text="直观运行图", padding=5)visual_frame.grid(row=1, column=0, sticky="nsew", pady=(5, 0))# 设置轨迹图框架内部布局trajectory_frame.grid_rowconfigure(0, weight=1)trajectory_frame.grid_columnconfigure(0, weight=1)# 设置直观图框架内部布局visual_frame.grid_rowconfigure(0, weight=1)visual_frame.grid_columnconfigure(0, weight=1)# 创建轨迹图self.figure = plt.Figure(figsize=(7, 5), tight_layout=True)self.figure.subplots_adjust(left=0.08, right=0.95, top=0.90, bottom=0.15)self.ax = self.figure.add_subplot(111)self.canvas = FigureCanvasTkAgg(self.figure, master=trajectory_frame)canvas_widget = self.canvas.get_tk_widget()canvas_widget.grid(row=0, column=0, sticky="nsew", padx=3, pady=3)# 创建直观运行图 - 不添加工具栏,保持简洁self.visual_figure = plt.Figure(figsize=(7, 3), tight_layout=True)self.visual_figure.subplots_adjust(left=0.05, right=0.95, top=0.85, bottom=0.20)self.visual_ax = self.visual_figure.add_subplot(111)self.visual_canvas = FigureCanvasTkAgg(self.visual_figure, master=visual_frame)visual_canvas_widget = self.visual_canvas.get_tk_widget()visual_canvas_widget.grid(row=0, column=0, sticky="nsew", padx=3, pady=3)def setup_plot(self):"""初始化轨迹图区域"""self.ax.clear()self.ax.set_xlabel("时间 (s)", fontsize=12)self.ax.set_ylabel("位移 (m)", fontsize=12)self.ax.set_title("摩擦力作用下的物体运动", fontsize=14, fontweight='bold', pad=15)self.ax.grid(True, alpha=0.3)self.ax.grid(True, which='minor', alpha=0.2)self.ax.minorticks_on()self.figure.tight_layout()def setup_visual_plot(self):"""初始化直观运行图"""self.visual_ax.clear()# 设置坐标轴范围self.visual_ax.set_xlim(0, 1)self.visual_ax.set_ylim(0, 1)# 绘制地面 - 增加厚度ground = Rectangle((0, 0.15), 1, 0.2, facecolor='#8B4513', alpha=0.9, edgecolor='black')self.visual_ax.add_patch(ground)# 绘制地面纹理(表示摩擦)for i in range(0, 120, 2):x = i / 120self.visual_ax.plot([x, x + 0.008], [0.15, 0.25], 'k-', alpha=0.6, linewidth=1)# 绘制物体 - 进一步增大尺寸box_width = 0.15  # 增大宽度box_height = 0.15  # 增大高度self.visual_box = FancyBboxPatch((self.initial_box_x, 0.35), box_width, box_height,boxstyle="round,pad=0.02",facecolor='#1E3A8A',edgecolor='black',linewidth=3)self.visual_ax.add_patch(self.visual_box)# 在物体上添加质量标识 - 调整字体大小mass = self.mass_var.get()self.visual_ax.text(self.initial_box_x + box_width/2, 0.425, f'{mass:.0f}kg', ha='center', va='center', fontsize=10, color='white', weight='bold')# 获取物理参数force = self.force_var.get()mu = self.mu_var.get()motion_params = self.calculate_motion_parameters(mass, mu, force)# 绘制施加力箭头 - 调整位置避免重叠max_arrow_length = 0.2arrow_length = min(force / 150 * max_arrow_length, max_arrow_length)force_arrow_start_x = self.initial_box_x - 0.08self.visual_arrow = patches.FancyArrowPatch((force_arrow_start_x, 0.425),(force_arrow_start_x + arrow_length, 0.425),arrowstyle='->', mutation_scale=25, color='red', linewidth=2)self.visual_ax.add_patch(self.visual_arrow)# 添加施加力标签 - 放在红色箭头的左侧self.visual_ax.text(force_arrow_start_x - 0.05, 0.425, f'施加力\n{force:.1f}N', ha='right', va='center', fontsize=11, color='red', weight='bold')# 绘制摩擦力箭头(如果有运动)- 位置调整if motion_params['state'] == 'kinetic':friction_force = motion_params['friction']friction_arrow_length = min(friction_force / 150 * max_arrow_length, max_arrow_length)friction_arrow_end_x = self.initial_box_x + box_width + 0.08self.visual_friction_arrow = patches.FancyArrowPatch((friction_arrow_end_x, 0.425),(friction_arrow_end_x - friction_arrow_length, 0.425),arrowstyle='->', mutation_scale=20, color='orange', linewidth=2)self.visual_ax.add_patch(self.visual_friction_arrow)# 添加摩擦力标签 - 放在黄色箭头的右侧self.visual_ax.text(friction_arrow_end_x + 0.05, 0.425, f'摩擦力\n{friction_force:.1f}N', ha='left', va='center', fontsize=11, color='orange', weight='bold')# 在地面内部添加摩擦系数标识 - 改进位置self.visual_ax.text(0.85, 0.25, f'μ={mu:.2f}', ha='center', va='center', fontsize=10, color='white', weight='bold',bbox=dict(boxstyle="round,pad=0.3", facecolor='#654321', alpha=0.8))# 显示状态信息 - 简化内容,避免重复if motion_params['state'] == 'static':status_text = f"物体静止 | 净力: 0N"status_color = 'lightgreen'else:status_text = f"物体运动 | 加速度: {motion_params['acceleration']:.2f}m/s²"status_color = 'lightblue'self.visual_ax.text(0.5, 0.08, status_text, ha='center', va='center', fontsize=11, weight='bold', bbox=dict(boxstyle="round,pad=0.4", facecolor=status_color, alpha=0.8))# 设置图形属性self.visual_ax.set_aspect('equal')self.visual_ax.axis('off')# 根据运动状态设置标题if motion_params['state'] == 'static':title = '静摩擦状态 - 物体保持静止'title_color = 'green'else:title = '动摩擦状态 - 物体正在运动'title_color = 'blue'self.visual_ax.set_title(title, fontsize=13, fontweight='bold', color=title_color, pad=15)self.visual_figure.tight_layout()self.visual_canvas.draw()def update_visual_plot(self, position=None):"""更新直观运行图"""self.visual_ax.clear()# 设置坐标轴范围self.visual_ax.set_xlim(0, 1)self.visual_ax.set_ylim(0, 1)# 绘制地面ground = Rectangle((0, 0.15), 1, 0.2, facecolor='#8B4513', alpha=0.9, edgecolor='black')self.visual_ax.add_patch(ground)# 绘制地面纹理for i in range(0, 120, 2):x = i / 120self.visual_ax.plot([x, x + 0.008], [0.15, 0.25], 'k-', alpha=0.6, linewidth=1)# 计算物体位置box_width = 0.15  # 增大宽度box_height = 0.15  # 增大高度if position is not None:# 将实际位移映射到屏幕坐标max_displacement = 0.5 * self.animation_acceleration * self.max_time**2if max_displacement > 0:relative_position = min(position / max_displacement, 1.0) * 0.55  # 调整移动范围else:relative_position = 0box_x = self.initial_box_x + relative_positionelse:box_x = self.initial_box_x# 确保物体不超出边界box_x = min(box_x, 1 - box_width - 0.1)# 绘制物体self.visual_box = FancyBboxPatch((box_x, 0.35), box_width, box_height,boxstyle="round,pad=0.02",facecolor='#1E3A8A',edgecolor='black',linewidth=3)self.visual_ax.add_patch(self.visual_box)# 在物体上添加质量标识mass = self.mass_var.get()self.visual_ax.text(box_x + box_width/2, 0.425, f'{mass:.0f}kg', ha='center', va='center', fontsize=13, color='white', weight='bold')# 获取物理参数force = self.force_var.get()mu = self.mu_var.get()motion_params = self.calculate_motion_parameters(mass, mu, force)# 绘制施加力箭头max_arrow_length = 0.2arrow_length = min(force / 150 * max_arrow_length, max_arrow_length)force_arrow_start_x = box_x - 0.08self.visual_arrow = patches.FancyArrowPatch((force_arrow_start_x, 0.425),(force_arrow_start_x + arrow_length, 0.425),arrowstyle='->', mutation_scale=25, color='red', linewidth=2)self.visual_ax.add_patch(self.visual_arrow)# 添加施加力标签 - 放在红色箭头的左侧self.visual_ax.text(force_arrow_start_x - 0.05, 0.425, f'施加力 {force:.1f}N', ha='right', va='center', fontsize=11, color='red', weight='bold')# 绘制摩擦力箭头(如果有运动)if motion_params['state'] == 'kinetic':friction_force = motion_params['friction']friction_arrow_length = min(friction_force / 150 * max_arrow_length, max_arrow_length)friction_arrow_end_x = box_x + box_width + 0.08self.visual_friction_arrow = patches.FancyArrowPatch((friction_arrow_end_x, 0.425),(friction_arrow_end_x - friction_arrow_length, 0.425),arrowstyle='->', mutation_scale=20, color='orange', linewidth=2)self.visual_ax.add_patch(self.visual_friction_arrow)# 添加摩擦力标签 - 放在黄色箭头的右侧self.visual_ax.text(friction_arrow_end_x + 0.05, 0.425, f'摩擦力 {friction_force:.1f}N', ha='left', va='center', fontsize=11, color='orange', weight='bold')# 在地面内部添加摩擦系数标识self.visual_ax.text(0.85, 0.25, f'μ={mu:.2f}', ha='center', va='center', fontsize=12, color='white', weight='bold',bbox=dict(boxstyle="round,pad=0.3", facecolor='#654321', alpha=0.8))# 显示当前状态信息 - 简化并避免重复if position is not None:status_text = f"位移: {position:.2f}m | 速度: {self.animation_velocity:.2f}m/s"status_color = 'lightblue'else:if motion_params['state'] == 'static':status_text = f"物体静止 | 净力: 0N"status_color = 'lightgreen'else:status_text = f"物体运动 | 加速度: {motion_params['acceleration']:.2f}m/s²"status_color = 'lightblue'self.visual_ax.text(0.5, 0.08, status_text, ha='center', va='center', fontsize=10, weight='bold', bbox=dict(boxstyle="round,pad=0.3", facecolor=status_color, alpha=0.8))# 设置图形属性self.visual_ax.set_aspect('equal')self.visual_ax.axis('off')# 根据运动状态设置标题if motion_params['state'] == 'static':title = '静摩擦状态 - 物体保持静止'title_color = 'green'else:title = '动摩擦状态 - 物体正在运动'title_color = 'blue'self.visual_ax.set_title(title, fontsize=13, fontweight='bold', color=title_color, pad=15)self.visual_figure.tight_layout()self.visual_canvas.draw()def update_mass_label(self, value):"""更新质量标签"""self.mass_label.config(text=f"{float(value):.1f}")self.update_physics_info()self.setup_visual_plot()def update_mu_label(self, value):"""更新摩擦系数标签"""self.mu_label.config(text=f"{float(value):.2f}")self.update_physics_info()self.setup_visual_plot()def update_force_label(self, value):"""更新力标签"""self.force_label.config(text=f"{float(value):.1f}")self.update_physics_info()self.setup_visual_plot()def calculate_motion_parameters(self, mass, mu, force):"""计算运动参数"""normal_force = mass * self.gmax_static_friction = mu * normal_forceif force <= max_static_friction:return {'state': 'static','normal_force': normal_force,'friction': force,'max_static_friction': max_static_friction,'acceleration': 0,'net_force': 0}else:kinetic_friction = mu * normal_forcenet_force = force - kinetic_frictionacceleration = net_force / massreturn {'state': 'kinetic','normal_force': normal_force,'friction': kinetic_friction,'max_static_friction': max_static_friction,'acceleration': acceleration,'net_force': net_force}def update_physics_info(self):"""更新物理信息显示"""try:mass = self.mass_var.get()mu = self.mu_var.get()force = self.force_var.get()params = self.calculate_motion_parameters(mass, mu, force)# 清空文本self.info_text.delete(1.0, tk.END)# 显示物理分析 - 去掉所有emojiinfo = f"""{'='*38}
物理量计算
{'='*38}
质量: m = {mass:.1f} kg
重力: W = mg = {mass:.1f} × 9.81 = {mass*self.g:.1f} N
正压力: N = W = {params['normal_force']:.1f} N
摩擦系数: μ = {mu:.2f}
施加力: F = {force:.1f} N{'='*38}
摩擦力分析
{'='*38}
最大静摩擦力:
f_max = μN = {mu:.2f} × {params['normal_force']:.1f} = {params['max_static_friction']:.1f} N当前摩擦力: f = {params['friction']:.1f} N{'='*38}
运动状态分析
{'='*38}"""if params['state'] == 'static':info += f"""
状态: 静止状态
原因: F ({force:.1f}N) ≤ f_max ({params['max_static_friction']:.1f}N)
静摩擦力: f = F = {force:.1f} N
净力: F_net = 0 N
加速度: a = 0 m/s²物理原理:
当施加力不超过最大静摩擦力时,物体保持
静止。静摩擦力会自动调节大小以平衡外力。"""else:info += f"""
状态: 运动状态
原因: F ({force:.1f}N) > f_max ({params['max_static_friction']:.1f}N)
动摩擦力: f = μN = {params['friction']:.1f} N
净力: F_net = F - f = {force:.1f} - {params['friction']:.1f} = {params['net_force']:.1f} N
加速度: a = F_net/m = {params['net_force']:.1f}/{mass:.1f} = {params['acceleration']:.2f} m/s²物理原理:
当施加力超过最大静摩擦力时,物体开始运
动。运动过程中受到大小恒定的动摩擦力。"""# 材料特性说明if mu > 0.7:material_info = "高摩擦材料 (如橡胶-混凝土)"elif mu > 0.4:material_info = "中等摩擦材料 (如木材-木材)"else:material_info = "低摩擦材料 (如冰-金属)"info += f"\n\n{'='*38}\n材料特性: {material_info}\n{'='*38}"self.info_text.insert(tk.END, info)except Exception as e:self.info_text.delete(1.0, tk.END)self.info_text.insert(tk.END, f"计算错误: {str(e)}")def validate_parameters(self):"""验证输入参数"""try:mass = self.mass_var.get()mu = self.mu_var.get()force = self.force_var.get()if mass <= 0:raise ValueError("质量必须大于0")if mu < 0:raise ValueError("摩擦系数不能为负")if force < 0:raise ValueError("力不能为负")return mass, mu, forceexcept Exception as e:messagebox.showerror("参数错误", str(e))return Nonedef start_simulation(self):"""开始模拟"""params = self.validate_parameters()if params is None:returnmass, mu, force = paramsif self.animation is not None:self.animation.event_source.stop()self.animation = Noneself.is_paused = Falseself.btn_pause.config(text="暂停", state='normal')self.btn_start.config(state='disabled')if self.current_trajectory is not None:color, m, u, f, times, positions = self.current_trajectoryself.ax.plot(times, positions, '-', lw=1.5, color=color, label=f"F={f:.1f}N, μ={u:.2f}, m={m:.1f}kg")self.finished_trajectories.append(self.current_trajectory)self.current_trajectory = Noneif self.last_point is not None:self.last_point.remove()self.last_point = Nonemotion_params = self.calculate_motion_parameters(mass, mu, force)if motion_params['state'] == 'static':self.ax.set_title(f"静摩擦状态: F={force:.1f}N ≤ f_max={motion_params['max_static_friction']:.1f}N", fontsize=14, fontweight='bold', pad=15)self.canvas.draw()self.update_visual_plot()self.btn_start.config(state='normal')self.btn_pause.config(state='disabled')messagebox.showinfo("模拟结果", "物体处于静止状态,无运动轨迹")returnelse:self.animation_acceleration = motion_params['acceleration']self.ax.set_title(f"动摩擦状态: a={self.animation_acceleration:.2f}m/s² (F={force:.1f}N > f_max={motion_params['max_static_friction']:.1f}N)", fontsize=14, fontweight='bold', pad=15)self.animation_color = self.color_cycle[self.current_color_idx % len(self.color_cycle)]self.current_color_idx += 1self.current_trajectory = (self.animation_color, mass, mu, force, [], [])self.ax.clear()self.setup_plot()for c, m, u, f, t, p in self.finished_trajectories:self.ax.plot(t, p, '-', lw=1.5, color=c, label=f"F={f:.1f}N, μ={u:.2f}, m={m:.1f}kg")self.animation_line, = self.ax.plot([], [], '-', lw=2, color=self.animation_color, label=f"F={force:.1f}N, μ={mu:.2f}, m={mass:.1f}kg")self.ax.set_xlim(0, self.max_time)estimated_y = 0.5 * self.animation_acceleration * self.max_time**2self.ax.set_ylim(0, max(estimated_y * 1.1, 10))self.ax.legend(loc='upper left', fontsize=10)self.figure.tight_layout()self.animation_time = 0self.animation_velocity = 0self.animation_position = 0self.animation = FuncAnimation(self.figure,self.update_animation,frames=int(self.max_time/self.dt),interval=50,blit=True,repeat=False)self.canvas.draw()def update_animation(self, frame):"""更新动画帧"""if self.is_paused:return [self.animation_line, self.last_point] if self.last_point else [self.animation_line]self.animation_time += self.dtself.animation_velocity += self.animation_acceleration * self.dtself.animation_position += self.animation_velocity * self.dtself.current_trajectory[4].append(self.animation_time)self.current_trajectory[5].append(self.animation_position)self.animation_line.set_data(self.current_trajectory[4], self.current_trajectory[5])self.update_visual_plot(self.animation_position)if self.last_point is not None:self.last_point.remove()if len(self.current_trajectory[4]) > 0:self.last_point = self.ax.plot(self.current_trajectory[4][-1], self.current_trajectory[5][-1], 'o', color=self.animation_color, markersize=6)[0]if self.animation_time >= self.max_time:self.animation.event_source.stop()self.btn_start.config(state='normal')self.btn_pause.config(state='disabled')messagebox.showinfo("模拟完成", f"模拟已完成!\n最终位移: {self.animation_position:.2f} m\n最终速度: {self.animation_velocity:.2f} m/s")return [self.animation_line, self.last_point] if self.last_point else [self.animation_line]def toggle_pause(self):"""暂停/继续模拟"""if self.animation is not None:if not self.is_paused:self.is_paused = Trueself.btn_pause.config(text="继续")else:self.is_paused = Falseself.btn_pause.config(text="暂停")def clear_trajectories(self):"""清除所有轨迹"""if self.animation is not None:self.animation.event_source.stop()self.animation = Noneif self.last_point is not None:self.last_point.remove()self.last_point = Noneself.ax.clear()self.setup_plot()self.setup_visual_plot()self.finished_trajectories = []self.current_trajectory = Noneself.current_color_idx = 0self.is_paused = Falseself.btn_start.config(state='normal')self.btn_pause.config(state='disabled', text="暂停")self.canvas.draw()messagebox.showinfo("清除完成", "所有轨迹已清除")if __name__ == "__main__":try:root = tk.Tk()app = FrictionSimulationApp(root)root.mainloop()except Exception as e:print(f"程序启动失败: {e}")input("按回车键退出...")

http://www.lryc.cn/news/574861.html

相关文章:

  • Vue 英雄列表搜索与排序功能实现
  • 基于 LCD1602 的超声波测距仪设计与实现:从原理到应用
  • uniapp项目之小兔鲜儿小程序商城(六) 地址模块:地址管理页的实现,地址表单页的实现
  • Metasploit常用命令详解
  • 2023年全国青少年信息素养大赛Python 复赛真题——玩石头游戏
  • 2025.6.16-实习
  • 搭建智能问答系统,有哪些解决方案,比如使用Dify,LangChain4j+RAG等
  • JVM(11)——详解CMS垃圾回收器
  • 猿人学js逆向比赛第一届第十二题
  • CDN+OSS边缘加速实践:动态压缩+智能路由降低30%视频流量成本(含带宽峰值监控与告警配置)
  • RSS解析并转换为JSON的API集成指南
  • SQL Server从入门到项目实践(超值版)读书笔记 18
  • [学习] C语言编程中线程安全的实现方法(示例)
  • 【Datawhale组队学习202506】YOLO-Master task04 YOLO典型网络模块
  • Python训练营-Day40-训练和测试的规范写法
  • 【Python-Day 29】万物皆对象:详解 Python 类的定义、实例化与 `__init__` 方法
  • 【Linux网络与网络编程】15.DNS与ICMP协议
  • 性能测试-jmeter实战4
  • 集成学习基础:Bagging 原理与应用
  • PyEcharts教程(009):PyEcharts绘制水球图
  • 60天python训练营打卡day41
  • Linux系统---Nginx配置nginx状态统计
  • 鸿蒙 Stack 组件深度解析:层叠布局的核心应用与实战技巧
  • AI时代工具:AIGC导航——AI工具集合
  • 接口自动化测试之pytest 运行方式及前置后置封装
  • 爬取小红书相关数据导入到excel
  • 项目需求评审报告参考模板
  • 图的拓扑排序管理 Go 服务启动时的组件初始化顺序
  • 飞往大厂梦之算法提升-day08
  • sqlserver怎样动态执行存储过程,并且返回报错