查看: 77|回复: 1

SymPy+Manim自动求解追及问题方程与动画联动实践

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
在制作物理追及问题的动画时,常需要根据题意手动列方程求相遇时间,再逐一计算每时刻坐标。参数(速度、初始距离、出发时间差)一旦改变,所有计算就得重来,过程繁琐且易错。本文基于 Python 的 SymPy 符号计算库,将列方程和求解自动化,并联动 Manim 动画引擎,实现任意参数下追及问题的自动求解与可视化。
  1. import sympy as sp
复制代码

一、痛点场景

假设经典追及问题:甲从原点出发,速度 v1=2;乙从 x=10 处同向出发,速度 v2=5,问多久追上?若手工编写 Manim 动画,通常需要手动解方程:
  1. 5t = 10+2t → t=10/3
复制代码
然后手动算出相遇位置,再硬编码轨迹函数。每次修改速度或初始距离,都需要重复上述过程,且容易在单位换算、正方向等细节上出错。

二、SymPy 自动求解

SymPy 可以将物理情景“翻译”成符号方程,自动求解。核心思路:定义时间符号 t,用符号表达式表示甲、乙的位置,令二者相等,调用 sp.solve() 直接得到相遇时间。

示例:同时出发,甲初始在乙前面 10 米,速度 v1=2,v2=5。
  1. t = sp.symbols('t', positive=True)
  2. v1, v2 = 2, 5
  3. s0 = 10  # 初始距离
  4. pos1 = s0 + v1*t  # 甲位置
  5. pos2 = v2*t       # 乙位置
  6. eq = sp.Eq(pos1, pos2)
  7. solution = sp.solve(eq, t)
  8. print(solution)  # 输出 [10/3]
复制代码

若甲先出发 2 秒(即乙出发时甲已运动 2 秒),只需调整表达式:
  1. t_delay = 2
  2. pos1 = s0 + v1*(t + t_delay)
  3. eq = sp.Eq(pos1, pos2)
  4. solution = sp.solve(eq, t)
  5. print(solution)  # 输出 [14/3]
复制代码

无论初始距离、速度差值还是出发延迟如何变化,只需修改符号表达式构建逻辑,求解交给 solve,相遇坐标代入即可获得精确值。

三、Manim 联动实战

将 SymPy 求解嵌入 Manim 动画场景,实现全自动追及动画。以下是一个完整示例,参数集中定义,动画完全自适应。
  1. from manim import *
  2. import sympy as sp
  3. class CatchUpLab(Scene):
  4.     def construct(self):
  5.         # ====== 可修改参数 ======
  6.         v1 = 1.5          # 甲速度
  7.         v2 = 2.5          # 乙速度
  8.         init_gap = 8      # 初始距离(甲在乙前面)
  9.         delay = 1         # 甲早出发时间
  10.         # ====== SymPy 自动求解 ======
  11.         t = sp.symbols("t", positive=True)
  12.         pos1_expr = init_gap + v1 * (t + delay)
  13.         pos2_expr = v2 * t
  14.         eq = sp.Eq(pos1_expr, pos2_expr)
  15.         t_meet = sp.solve(eq, t)[0]  # 精确相遇时间
  16.         meet_x = float(pos2_expr.subs(t, t_meet))  # 相遇位置
  17.         def pos1_func(time):
  18.             return init_gap + v1 * (time + delay)
  19.         def pos2_func(time):
  20.             return v2 * time
  21.         # ====== 场景搭建 ======
  22.         axes = NumberLine(
  23.             x_range=[0, 30, 5],
  24.             length=8,
  25.             include_numbers=True,
  26.             label_direction=DOWN,
  27.         )
  28.         self.play(Create(axes))
  29.         dot1 = Dot(color=RED, radius=0.2)
  30.         dot2 = Dot(color=BLUE, radius=0.2)
  31.         dot1.move_to(axes.number_to_point(pos1_func(0)))
  32.         dot2.move_to(axes.number_to_point(pos2_func(0)))
  33.         self.add(dot1, dot2)
  34.         label1 = Text("甲", color=RED).next_to(dot1, UP*2)
  35.         label2 = Text("乙", color=BLUE).next_to(dot2, UP*2)
  36.         self.add(label1, label2)
  37.         trace1 = TracedPath(dot1.get_center, stroke_color=RED, stroke_width=2)
  38.         trace2 = TracedPath(dot2.get_center, stroke_color=BLUE, stroke_width=2)
  39.         self.add(trace1, trace2)
  40.         meet_dot = Dot(point=axes.number_to_point(meet_x), color=YELLOW)
  41.         meet_label = Text(f"相遇点: {meet_x:.2f}", font_size=24, color=YELLOW)
  42.         meet_label.next_to(meet_dot, UP*1.5)
  43.         time_text = MathTex("t=0.0").shift(UL*2)
  44.         self.add(time_text)
  45.         total_time = float(t_meet) + 2
  46.         def update_dots(mob, alpha):
  47.             t_now = alpha * total_time
  48.             dot1.move_to(axes.number_to_point(pos1_func(t_now)))
  49.             dot2.move_to(axes.number_to_point(pos2_func(t_now)))
  50.             label1.next_to(dot1, UP*2)
  51.             label2.next_to(dot2, UP*2)
  52.             time_text.become(MathTex(f"t={t_now:.1f}").shift(UL*2))
  53.             if t_now >= float(t_meet):
  54.                 self.add(meet_dot, meet_label)
  55.         self.play(
  56.             UpdateFromAlphaFunc(
  57.                 VGroup(dot1, dot2, label1, label2, time_text),
  58.                 update_dots,
  59.                 run_time=total_time,
  60.                 rate_func=linear,
  61.             )
  62.         )
  63.         self.wait(1)
复制代码

关键设计:
- 所有参数集中在脚本开头,修改 v1、v2、init_gap、delay 即可改变题目,SymPy 自动重新求解相遇时间和位置,动画完全自适应。
- 使用 UpdateFromAlphaFunc 驱动动画,每帧根据当前时间计算两个点的坐标,坐标函数直接由 SymPy 表达式生成,精确无误差。
- 相遇点预先通过 sp.solve 得到精确值,当动画时间超过相遇时刻时,黄色标记出现,直观展示“乙追上甲”的瞬间。
- 未采用 always_redraw 而使用 UpdateFromAlphaFunc,更好地控制动画进度和时间显示,避免复杂依赖更新。

四、效果说明

运行上述场景,你将看到:一条水平数轴,红点(甲)在蓝点(乙)前方。动画开始后,两点同时向右移动,蓝点速度更快逐渐逼近红点。到达精确相遇时刻,黄色圆点出现在相遇位置并标注坐标,甲和乙重合。

若修改参数——例如甲速度从 1.5 改为 1.2,初始距离改为 12,甲提前出发 3 秒——只需修改脚本顶部几个数字,无需任何手动计算,动画仍准确呈现追及过程,并在正确位置标记相遇点。

注意:若参数设置导致无法追上(如乙速度小于甲),sp.solve 将返回空或负解,可在代码中增加判断逻辑,提示无法相遇,避免误标。

五、拓展思路

追及问题的自动化求解与动画联动,核心价值在于将“根据题意建立方程”和“求解”这两步程序化。对于其他行程问题(相遇、环形跑道、顺流逆流等),只要能将物理情景转化为代数方程,SymPy 就能自动解出关键节点,Manim 负责将节点转化为流畅的视觉呈现。两个工具分工明确,既精准又灵活。

(参考来源:脚本之家《Python使用SymPy自动求解追及问题的方程》)
回复

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: SymPy+Manim自动求解追及问题方程与动画联动实践

这个思路很实用!之前手动解方程确实容易在参数调整时反复改代码,SymPy 自动求解直接省掉了中间步骤,而且保持精确值,对非整数参数尤其友好。Manim 联动也做得干净,参数集中定义、函数自动生成,修改起来很舒服。 有一个小好奇:如果追及方向相反(比如相向而行),或者涉及加速度,这套框架能直接扩展吗?另外,遇到多解(比如超过一次相遇)时,`sp.solve` 会如何处理?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-11 10:19 , Processed in 0.028895 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部