Python使用SymPy自动求解追及问题的方程

 更新时间:2026年06月11日 08:34:27   作者:databook  
做追及问题动画时,需要根据题意列方程求出相遇时间,再手动计算两个物体在每个时刻的坐标,本文用 SymPy 把列方程和求解都自动化,直接得到相遇时间和运动轨迹,动画代码只需拿到结果去画图,需要的朋友可以参考下

引言

做追及问题动画时,需要根据题意列方程求出相遇时间,再手动计算两个物体在每个时刻的坐标。

题意中速度、初始距离、出发时间差这些参数一改变,就得重新手算一遍,整个过程繁琐且易错。

本文用 SymPy 把列方程和求解都自动化,直接得到相遇时间和运动轨迹,动画代码只需拿到结果去画图。

1. 痛点场景还原

假设做一个最经典的追及动画:甲从原点出发,速度 v1=2;乙从 x=10处同向出发,速度 v2,问多久追上。

如果用纯手工方式写 Manim

class PainfulCatchUp(Scene):
    def construct(self):
        # 手动列方程并求解
        # 设 t 为乙出发后的时间,甲的位置:2*(t+?),乙的位置:10+5*t
        # 如果同时出发:2t = 10+5t → t = -10/3 负数无意义
        # 改甲先出发2秒:2(t+2) = 10+5t → 2t+4=10+5t → -3t=6 → t=-2 还是负
        # 必须反复调整题设,手算满足实际情况的初始条件
        # 这里干脆让乙追甲,甲在乙前面:
        # 甲在x=10以v=2向前,乙在x=0以v=5同时出发 → 5t = 10+2t → 3t=10 → t=10/3
        v1, v2 = 2, 5
        t_meet = 10/3   # 手动解出的结果
        meet_x = 5 * t_meet  # 再手动算相遇位置

        # 甲和乙的轨迹只能硬编码
        def pos1(t):
            return 10 + v1 * t
        def pos2(t):
            return v2 * t

        # 然后创建动画……

痛点很明显:

  • 每次改变速度或初始距离,都要重新手写方程、求解、算相遇坐标。
  • 题目条件稍微变化(比如“甲先走1分钟”、“乙在中途休息”),手算的过程就得全部推翻重来。
  • 容易在单位换算、正方向等细节上出错,动画一旦跑起来发现不对,排查起来也费劲。

这些计算本质上就是根据文字描述建立代数方程并求解,正是 SymPy 最擅长的事。

2. SymPy 解决方案介绍

SymPy 可以让我们用符号把追及问题“翻译”成方程,然后自动求解。

import sympy as sp

# 符号定义:t 为乙出发后经过的时间
t = sp.symbols('t', positive=True)
v1, v2 = 2, 5          # 速度
s0 = 10                # 初始距离(甲在乙前面10米)

# 甲的位置:先出发0秒(即同时出发),位置 = s0 + v1*t
pos1 = s0 + v1 * t
# 乙的位置:从0开始,位置 = v2*t
pos2 = v2 * t

# 相遇条件:位置相等
eq = sp.Eq(pos1, pos2)
solution = sp.solve(eq, t)
# 输出: [10/3]

如果甲先出发 2 秒,方程只需改一下:

t_delay = 2  # 甲早出发2秒
pos1 = s0 + v1 * (t + t_delay)   # 甲多走2秒
eq = sp.Eq(pos1, pos2)
solution = sp.solve(eq, t)
# 输出: [14/3]

无论怎么变化,我们只需要修改符号表达式的构建逻辑,求解交给 solve,相遇坐标直接代入即可。

接下来把这个思想嵌入 Manim,动画就能自适应任意追及条件。

3. Manim 联动实战

下面是一个完整的动画场景:给定甲、乙的初始位置、速度和出发延迟,自动计算相遇点,并动态展示追及过程。

from manim import *
import sympy as sp


class CatchUpLab(Scene):
    def construct(self):
        # ========== 题目参数(任意修改这里即可) ==========
        v1 = 1.5  # 甲的速度
        v2 = 2.5  # 乙的速度
        init_gap = 8  # 初始距离(甲在乙前面)
        delay = 1  # 甲早出发的时间

        # ========== SymPy 自动求解 ==========
        t = sp.symbols("t", positive=True)
        pos1_expr = init_gap + v1 * (t + delay)  # 甲的位置
        pos2_expr = v2 * t  # 乙的位置
        eq = sp.Eq(pos1_expr, pos2_expr)
        t_meet = sp.solve(eq, t)[0]  # 精确解
        meet_x = float(pos2_expr.subs(t, t_meet))  # 相遇位置

        # 为了动画流畅,预先计算两个运动函数(可以直接用lambda)
        def pos1_func(time):
            return init_gap + v1 * (time + delay)

        def pos2_func(time):
            return v2 * time

        # ========== 场景搭建 ==========
        axes = NumberLine(
            x_range=[0, 30, 5],
            length=8,
            include_numbers=True,
            label_direction=DOWN,
        )
        self.play(Create(axes))

        # 甲和乙的点
        dot1 = Dot(color=RED, radius=0.2)
        dot2 = Dot(color=BLUE, radius=0.2)
        # 初始放置
        dot1.move_to(axes.number_to_point(pos1_func(0)))
        dot2.move_to(axes.number_to_point(pos2_func(0)))
        self.add(dot1, dot2)

        # 标签
        label1 = Text("甲", color=RED).next_to(dot1, UP * 2)
        label2 = Text("乙", color=BLUE).next_to(dot2, UP * 2)
        self.add(label1, label2)

        # 轨迹虚线(预留)
        trace1 = TracedPath(dot1.get_center, stroke_color=RED, stroke_width=2)
        trace2 = TracedPath(dot2.get_center, stroke_color=BLUE, stroke_width=2)
        self.add(trace1, trace2)

        # 相遇点标记(先隐藏,等追到时再显示)
        meet_dot = Dot(point=axes.number_to_point(meet_x), color=YELLOW)
        meet_label = Text(f"相遇点: {meet_x:.2f}", font_size=24, color=YELLOW)
        meet_label.next_to(meet_dot, UP * 1.5)

        # 动态更新的时间显示
        time_text = MathTex("t=0.0").shift(UL * 2)
        self.add(time_text)

        # 追击动画
        total_time = float(t_meet) + 2  # 多跑2秒

        def update_dots(mob, alpha):
            # alpha 从0到1,对应时间从0到total_time
            t_now = alpha * total_time
            dot1.move_to(axes.number_to_point(pos1_func(t_now)))
            dot2.move_to(axes.number_to_point(pos2_func(t_now)))
            # 更新标签位置
            label1.next_to(dot1, UP * 2)
            label2.next_to(dot2, UP * 2)
            # 更新时间显示
            time_text.become(MathTex(f"t={t_now:.1f}").shift(UL * 2))
            # 判断是否到达相遇点
            if t_now >= float(t_meet):
                self.add(meet_dot, meet_label)  # 显示相遇标记

        self.play(
            UpdateFromAlphaFunc(
                VGroup(dot1, dot2, label1, label2, time_text),
                update_dots,
                run_time=total_time,
                rate_func=linear,
            )
        )
        self.wait(1)

关键点解释:

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

4. 效果展示说明

运行这个场景,你会看到:

  • 一条水平数轴,红点(甲)在蓝点(乙)的前方。
  • 动画开始后,两点同时向右移动,蓝点速度更快,逐渐逼近红点。
  • 在精确的相遇时刻,一个黄色圆点出现在相遇位置,并标注坐标,同时甲和乙重合。
  • 即使改变参数——比如甲的速度从 1.5 改成 1.2、初始距离改成 12、甲提前出发 3 秒,只需修改脚本顶部的几个数字,无需任何手动计算,动画仍然能够准确呈现追及过程,并自动在正确的位置标记相遇点。
  • 如果参数设置使得无法追上(如乙的速度小于甲),sp.solve 返回空或负解,我们可以加入逻辑判断提示无法相遇,动画就不会标记错误时刻。

5. 小结

追及问题的动画化,真正耗时的往往不是画图,而是根据不断变化的题设反复列方程、解方程、算坐标。

SymPy 的价值在于把“根据题意列方程”和“求解”这两步都程序化了,你只需要用符号描述位置关系,剩下的事交给计算机。

这种思路同样适用于其他行程问题:相遇、环形跑道、顺流逆流……只要你能把物理情景转化为代数方程,

SymPy 就能帮你解出关键节点,而 Manim 负责把这些节点变成流畅的视觉呈现。

两个工具分工明确,做出来的动画既精准又灵活。

以上就是Python使用SymPy自动求解追及问题的方程的详细内容,更多关于Python SymPy求解追及问题方程的资料请关注脚本之家其它相关文章!

相关文章

  • Pandas按周/月/年统计数据介绍

    Pandas按周/月/年统计数据介绍

    大家好,本篇文章主要讲的是Pandas按周/月/年统计数据介绍,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • python中decimal模块的具体使用

    python中decimal模块的具体使用

    本文主要介绍了python中decimal模块的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • python实现数组求和与平均值

    python实现数组求和与平均值

    这篇文章主要介绍了python实现数组求和与平均值方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • 常用python爬虫库介绍与简要说明

    常用python爬虫库介绍与简要说明

    本文介绍了一些常用的python爬虫库其中包括python网络库,python网络爬虫框架,python HTML解析,python文本处理,python 自然语言处理,python 浏览器模拟等各种常用的python库
    2020-01-01
  • Python调用GPU算力的实现步骤

    Python调用GPU算力的实现步骤

    本文介绍了在Python中调用GPU算力的基本步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-03-03
  • 浅析Python中signal包的使用

    浅析Python中signal包的使用

    这篇文章主要介绍了Python中signal包的使用,主要在Linux系统下对进程信号进行相关操作,需要的朋友可以参考下
    2015-11-11
  • Python实现语音合成功能详解

    Python实现语音合成功能详解

    这篇文章主要为大家介绍了一个通过Python制作的小工具,可以实现语音识别以及文字转语音的功能,文中的实现步骤讲解详细,感兴趣的可以动手试一试
    2022-01-01
  • 用python打开摄像头并把图像传回qq邮箱(Pyinstaller打包)

    用python打开摄像头并把图像传回qq邮箱(Pyinstaller打包)

    这篇文章主要介绍了用python打开摄像头并把图像传回qq邮箱,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • Python实现把json格式转换成文本或sql文件

    Python实现把json格式转换成文本或sql文件

    这篇文章主要介绍了Python实现把json格式转换成文本或sql文件,本文直接给出代码实例,需要的朋友可以参考下
    2015-07-07
  • python解析xml模块封装代码

    python解析xml模块封装代码

    这篇文章主要分享下在python中解析xml文件的模块用法,以及对模块封装的方法,有需要的朋友参考下
    2014-02-02

最新评论