用python的scipy中的odeint来解常微分方程中的一些细节问题(适用于小白)

2023-05-16

用python的scipy中的odeint来解常微分方程中的一些细节问题(适用于小白)

写在前面

最近有些需要解决常微分方程的问题,网上查了很多教程都不是很明晰,便自己研究了一段时间,写一点小白初次接触这个方法应该如何理解,有哪些需要注意的点。
odeint在官网的参数很多,如下所示:

scipy.integrate.odeint(func, y0, t, args=(), Dfun=None, col_deriv=0, full_output=0, ml=None, 
mu=None, rtol=None, atol=None, tcrit=None, h0=0.0, hmax=0.0, hmin=0.0, ixpr=0, mxstep=0, 
mxhnil=0, mxordn=12, mxords=5, printmessg=0, tfirst=False)

我们这次简单点只使用前三个参数scipy.integrate.odeint(func, y0, t),如果想了解更多,其他教程应该有更详细的教学,这里只说一点最简单的原理和注意事项。

  • 第一个参数是我们自行定义的需要求解的微分方程的函数
  • 第二个参数代表求解的微分方程的初值,没有初值微分方程的解不能唯一确定
  • 第三个参数是求解的微分方程中的自变量,应该是一个连续的序列值

我们必须明确,求解微分方程最终我们得到的是一个关于自变量的函数,而不是一个值。

在此我们说一点基础的数学知识点,对于一个函数,比如
y = 4 x 3 + 100 y=4x^3+100 y=4x3+100
我们对它求导得到:
y ′ = 12 x 2 y'=12x^2 y=12x2
那么对应的,求解第二个微分方程就能得到第一个方程吗?
答案是否定的,因为对第二个导数式子积分,得到的是:
y = 4 x 3 + a ( 常 数 ) y = 4x^3 + a(常数) y=4x3+a()
只有求解出常数a,才能唯一确定一个微分方程的解;否则我们得到的是一系列相差常数的解

官网例子说明

在odeint的官网里面有个具体的例子,我们拿出来讲一讲,
θ ′ ′ ( t ) + b ∗ θ ′ ( t ) + c ∗ s i n ( θ ( t ) ) = 0 \theta''(t) + b*\theta'(t) + c*sin(\theta(t)) = 0 θ(t)+bθ(t)+csin(θ(t))=0
官网的办法是说把这个二阶的微分方程先变换作一阶的方程组:
θ ′ ( t ) = ω ( t ) \theta'(t) = \omega(t) θ(t)=ω(t) ω ′ ( t ) = − b ∗ ω ( t ) − c ∗ s i n ( θ ( t ) ) \omega'(t) = -b*\omega(t) - c*sin(\theta(t)) ω(t)=bω(t)csin(θ(t))
这里边有一个代换过程:

ω ( t ) = θ ′ ( t ) \omega(t) = \theta'(t) ω(t)=θ(t)
因此有: ω ′ ( t ) = θ ′ ′ ( t ) = − b ∗ ω ( t ) − c ∗ s i n ( θ ( t ) ) \omega'(t)= \theta''(t) = -b*\omega(t) - c*sin(\theta(t)) ω(t)=θ(t)=bω(t)csin(θ(t))
最终我们需要的两个方程是: θ ′ ( t ) = ω ( t ) \theta'(t)=\omega(t) θ(t)=ω(t) ω ′ ( t ) = − b ∗ ω ( t ) − c ∗ s i n ( θ ( t ) ) \omega'(t)= -b*\omega(t) - c*sin(\theta(t)) ω(t)=bω(t)csin(θ(t))

随后官网根据这个代换过程定义了一个函数:

def pend(y, t, b, c):
    theta, omega = y
    dydt = [omega, -b*omega - c*np.sin(theta)]
    return dydt

或许有些python基础不是很好的同学对于这个函数比较陌生,我先来解释下,

  • 参数b,c是系统的常数我们暂且承认即可
  • t是微分方程的自变量,这个没有争议
  • theta, omega = y,这句可能会有疑惑,我们传入的y到底是个啥?

还记得前面的一阶代换过程嘛,简单来说就是把y先分成theta和omega,一个代表原微分方程theta第二个代表theta的一阶导;再对这两个求导就分别得到了一阶导和二阶导。

  • 此函数返回的dydt是一个列表,分别代表theta的一阶导 和 二阶导 ,但是两阶导数是通过中间变量omega的一阶导数来表示的。

这里面牵扯了一些数学代换,最终的式子为了变量减少,只能出现不带导数的theta和omega,看不懂先看下去,后面会有一个简单的例子来说明,我们先大概了解官网这个例子在说什么即可。

有些同学或许觉得现在我们就可以调用方程求解了,其实认真看的同学知道,我们此时还缺少关键的初始条件,否则无法确定唯一的一个解。为了方便此时我们把系统变量b,c同时定义好:

b = 0.25
c = 5.0
y0 = [np.pi - 0.1, 0.0] #初始条件

此处初始条件有两个是因为,我们有一个方程组(两个方程),原方程和一阶方程都需要初始条件(同样可以看到后面的简单例子)。

在编程时我们还需要定义自变量,这个也很重要,

t = np.linspace(0, 10, 101)

到此为止,准备工作全部完成,我们可以求解了。

sol = odeint(pend, y0, t, args=(b, c))

其实我们会发现这个结果sol,是一个形状为(101,2)的数组。第一列是theta(t),第二列是omega(t),也就是说得到了两个解,theta(t)是原微分方程的解,omega(t)是原方程的一阶导数方程。

plt.plot(t, sol[:, 0], 'b', label='theta(t)')
plt.plot(t, sol[:, 1], 'g', label='omega(t)')
plt.legend(loc='best')
plt.xlabel('t')
plt.grid()
plt.show()

通过调用以上语句,官网分别画出了结果的图像,如下所示,
sol图像
官网的介绍到此为止了,我不知道有没有小伙伴看完和我一样很懵,定义微分方程为什么是这样的,还有没有别的可能,关于这个odeint有没有别的需要注意的点,这一个例子感觉说的不那么明白,比如我有以下疑问:

  1. 首先,自定义的微分方程的函数,如果我们需要求解的是一阶的怎么办?
  2. 如果是二阶的微分方程,theta, omega = yomega ,theta,= y 和下面的返回值列表有没有一一对应关系
  3. 如果关系,应该如何组织二者的顺序。
  4. 对于二阶微分方程的初值那个列表,第一个数代表得是原方程的初值吗?那第二个是不是应该代表一阶微分方程的初值,为了保证结果的正确,到底应该如何安排顺序?

这是我看完的几个疑问,如果你愿意跟我一起,通过下面的代码进行测试,我相信,你会对这个odeint有更深一点的理解。

测试

1先定义好数学问题

就拿我们前面举过的例子来说吧,对于下面的函数【1】,
y = 4 x 3 + 100 y=4x^3+100 y=4x3+100
我们对它求导得到表达式【2】:
y ′ = 12 x 2 y'=12x^2 y=12x2

这是一个一阶微分方程,表达式【1】是它的一个解(别忘记常数)

我们在此求导得到表达式【3】,
y ′ ′ = 24 x y'' = 24x y=24x

这是一个二阶微分方程,表达式【1】是它的一个解(同样别忘记常数)

先求一阶的结果

如果我们想求表达式【2】y’=12x^2 在初值为100(本文指的是x=0时)的情况下,这个微分方程的解是多少,我们编写如下函数,

initial = 100				# 定义原方程初始值
def f1(y,x):
    return 12*x**2
x = np.linspace(0,10,100) 	#定义自变量
result1 = odeint(f1,initial,x) #调用求解
plt.plot(x,result1)				#求解值
plt.plot(x,4*x**3+initial,'--k') #真实值

这里我们会发现我们需要定义的微分方程函数,返回的都是某个变量的一阶导数,所以说这个odeint在调用时填入的参数fun句柄,应该是以一阶微分形式给出的,所以以后定义这种函数我们应该都化为一阶然后写好自定义函数,再传入odeint里面。
本文初值为100,指的是x=0时,y=100。请思考如果是x=1时,y=104该如何表示!

在这里插入图片描述

上图结果说明一阶的求解结果完全正确。

二阶微分方程的测试

如果我们想求解表达式【3】y’’ = 24x 在初始值100 和 一阶初值 0 的情况下的解,
此时应先化为一阶方程组,令

ω = y ′ \omega = y' ω=y y ′ ′ = ω ′ = 24 x y'' = \omega' =24x y=ω=24x

确认如下结果方程组:
y ′ = ω y' = \omega y=ω ω ′ = 24 x \omega' =24x ω=24x
如此保证方程左边是一阶组,右边是不含导数项。

我们应该编写如下函数,

initial = 100	#原初值
derivative1 = 0 #一阶初值
def f2(y,x):
    y1,w = y
    return np.array([w,24*x]) #返回值列阵 先一阶 再 二阶 由低到高
x = np.linspace(0,10,100)		#定义自变量
result2 = odeint(f2,(initial,derivative1),x)  #初值对应 先原方程 再 一阶 由低到高
plt.plot(x,result2)
plt.plot(x,4*x**3+initial,'--k')

在这里插入图片描述
可以发现原方程的结果是吻合的。

如果我们调换一下初值的顺序(initial,derivative1)(derivative1,initial),即先一阶导再二阶导,会发生什么呢?结果还正确嘛?

initial = 100	
derivative1 = 0 
def f2(y,x):
    y1,w = y
    return np.array([w,24*x]) 
x = np.linspace(0,10,100)
#result2 = odeint(f2,(initial,derivative1),x)  #初值对应 先原方程 再 一阶 由低到高 【注释掉】
result2 = odeint(f2,(derivative1,initial),x)  #初值对应 先一阶 再 原方程  会出现错误【如下图】

plt.plot(x,result2)
plt.plot(x,4*x**3+initial,'--k')

在这里插入图片描述

可以看出来,如果改变初值顺序结果是错误的。那么这个顺序应该跟谁对应呢?

那尝试如果改变初值顺序改变的情况下,把自定义函数的返回值也调换,会不会得到正确结果呢?
我们看下面的代码:

def f2(y,x):
    y1,w = y
    # return np.array([w,24*x]) #返回值列阵 先一阶 再 二阶 由低到高 【注释掉】
    return np.array(24*x,w) #返回值列阵 先 二阶  再 一阶 由高到低
x = np.linspace(0,10,100)

result2 = odeint(f2,(derivative1,initial),x)  #初值对应 先一阶 再 原方程  会出现错误

plt.plot(x,result2)
plt.plot(x,4*x**3+initial,'--k')

结果会报错:

RuntimeError: The size of the array returned by func (1) does not match the size of y0 (2).

这是因为在自定义函数中y1,w = y应该和返回值对应,如此应该调换为w,y1 = y

def f2(y,x):
    # y1,w = y #【注释掉】
    w,y1 = y
    # return np.array([w,24*x]) #返回值列阵 先一阶 再 二阶 由低到高 【注释掉】
    return np.array([24*x,w]) #返回值列阵 先 二阶  再 一阶 由高到低
x = np.linspace(0,10,100)

result2 = odeint(f2,(derivative1,initial),x)  #初值对应 先一阶 再 原方程  会出现错误的结果

plt.plot(x,result2)
plt.plot(x,4*x**3+initial,'--k')

在这里插入图片描述

这样我们得到了正确的结果

总结

这么麻烦,我们其实只说明了一个很简单的问题,初始值、返回值、中间变量这 三个位置的顺序必须完全相同, 正序或者逆序 都可以,但三个必须保持一致,才能出现正确的结果。

这个顺序我的理解是 原方程-一阶导-二阶导 这样的顺序。 初始值对应的 以及 返回值和中间变量替换对应的 应该是同样的次序, 比如 如果返回值列表是[一阶导 二阶导] 那么初始值对应的应该为[原方程初值 一阶导初值],变量替换对应的元组也应该同一阶导和二阶导对应。

这是关于这个函数的第一篇,我也是刚接触这个东西第二天,不确定以后会不会继续写,希望不会误人子弟吧。有些地方表述可能不清楚,以后会修改。欢迎批评指正,另外不要杠我,你杠就是你对。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用python的scipy中的odeint来解常微分方程中的一些细节问题(适用于小白) 的相关文章

  • 如何替换 Pandas Dataframe 中不在列表中的所有值? [复制]

    这个问题在这里已经有答案了 我有一个值列表 如何替换 Dataframe 列中不在给定值列表中的所有值 例如 gt gt gt df pd DataFrame D ND D garbage columns S gt gt gt df S 0
  • Pandas set_levels,如何避免标签排序?

    我使用时遇到问题set levels多索引 from io import StringIO txt Name Height Age Metres A 1 25 B 95 1 df pd read csv StringIO txt heade
  • 如何在 __init__ 中使用await设置类属性

    我如何定义一个类await在构造函数或类体中 例如我想要的 import asyncio some code class Foo object async def init self settings self settings setti
  • 在 Python 中将列表元素作为单独的项目返回

    Stackoverflow 的朋友们大家好 我有一个计算列表的函数 我想单独返回列表的每个元素 如下所示 接收此返回的函数旨在处理未定义数量的参数 def foo my list 1 2 3 4 return 1 2 3 4 列表中的元素数
  • pandas DataFrame.join 的运行时间是多少(大“O”顺序)?

    这个问题更具概念性 理论性 与非常大的数据集的运行时间有关 所以我很抱歉没有一个最小的例子来展示 我有一堆来自两个不同传感器的数据帧 我需要最终将它们连接成两个very来自两个不同传感器的大数据帧 df snsr1 and df snsr2
  • PyQt 使用 ctrl+Enter 触发按钮

    我正在尝试在我的应用程序中触发 确定 按钮 我当前尝试的代码是这样的 self okPushButton setShortcut ctrl Enter 然而 它不起作用 这是有道理的 我尝试查找一些按键序列here http ftp ics
  • Tensorboard SyntaxError:语法无效

    当我尝试制作张量板时 出现语法错误 尽管开源代码我还是无法理解 我尝试搜索张量板的代码 但不清楚 即使我不擅长Python 我这样写路径C Users jh902 Documents logs因为我正在使用 Windows 10 但我不确定
  • 打印数字时添加千位分隔符[重复]

    这个问题在这里已经有答案了 我真的不知道这个问题的 名称 所以它可能是一个不正确的标题 但问题很简单 如果我有一个数字 例如 number 23543 second 68471243 我想要它使print 像这样 23 54368 471
  • 从 Powershell 脚本安装 Python

    当以管理员身份从 PowerShell 命令行运行以下命令时 可以在 Windows 11 上成功安装 Python c temp python 3 11 4 amd64 exe quiet InstallAllUsers 0 Instal
  • 使用 python/numpy 重塑数组

    我想重塑以下数组 gt gt gt test array 11 12 13 14 21 22 23 24 31 32 33 34 41 42 43 44 为了得到 gt gt gt test2 array 11 12 21 22 13 14
  • 导入错误:没有名为flask.ext.login的模块

    我的flask login 模块有问题 我已经成功安装了flask login模块 另外 从命令提示符我可以轻松运行此脚本 不会出现错误 Python 2 7 r27 82525 Jul 4 2010 07 43 08 MSC v 1500
  • 嵌套作用域和 Lambda

    def funct x 4 action lambda n x n return action x funct print x 2 prints 16 我不太明白为什么2会自动分配给n n是返回的匿名函数的参数funct 完全等价的定义fu
  • Pandas 组合不同索引的数据帧

    我有两个数据框df 1 and df 2具有不同的索引和列 但是 有一些索引和列重叠 我创建了一个数据框df索引和列的并集 因此不存在重复的索引或列 我想填写数据框df通过以下方式 for x in df index for y in df
  • 找到一个数字所属的一组范围

    我有一个 200k 行的数字范围列表 例如开始位置 停止位置 该列表包括除了非重叠的重叠之外的所有类型的重叠 列表看起来像这样 3 5 10 30 15 25 5 15 25 35 我需要找到给定数字所属的范围 并对 100k 个数字重复该
  • Protobuf 如何编码 oneof 消息结构

    对于这个 python 程序 在编码时运行 protobuf 编码会给出以下输出 0a 10 08 7f8a 0104 08 02 10 0392 0104 08 02 10 03 18 01 我不明白的是为什么8a后面有一个01 为什么9
  • 在 Google App Engine 中,如何避免创建具有相同属性的重复实体?

    我正在尝试添加一个事务 以避免创建具有相同属性的两个实体 在我的应用程序中 每次看到新的 Google 用户登录时 我都会创建一个新的播放器 当新的 Google 用户在几毫秒内进行多个 json 调用时 我当前的实现偶尔会创建重复的播放器
  • Spider 必须返回 Request、BaseItem、dict 或 None,已“设置”

    我正在尝试从以下位置下载所有产品的图像 我的蜘蛛看起来像 from shopclues items import ImgData import scrapy class multipleImages scrapy Spider name m
  • 如何使用 AWS Lambda Python 读取 AWS S3 存储的 Word 文档(.doc 和 .docx)文件内容?

    我的场景是 我尝试使用 python 实现从 Aws Lambda 读取 AWS 存储的 S3 word 文档 doc 和 docx 文件内容 下面的代码是我使用的 我的问题是我可以获取文件名 但无法读取内容 def lambda hand
  • python 中的“槽包装器”是什么?

    object dict 和其他地方的隐藏方法设置为这样的
  • 如何在 Flask 中的视图函数/会话之间传递复杂对象

    我正在编写一个 Web 应用程序 当 且仅当 用户登录时 该应用程序从第三方服务器接收大量数据 这些数据被解析为自定义对象并存储在list 现在 用户在应用程序中使用这些数据 调用不同的视图 例如发送不同的请求 我不确定什么是最好的模式在视

随机推荐

  • python断点下载文件

    使用pytohn编码实现文件的断点下载 span class token keyword import span os span class token keyword import span json span class token k
  • 异常检测(1)—初识异常检测

    1 概念 异常一般是指与标准值 xff08 预期值 xff09 有偏离的样本点 xff0c 也就是跟绝大部分数据 长的不一样 异常往往是 有价值 的事情 xff0c 我们对异常的成因感兴趣 2 类别 有监督 xff1a 数据集有标签无监督
  • 【git】在GitHub上下载历史版本

    GitHub上的项目很多都是从简单到复杂 xff0c 一点点迭代的 当我们需要看懂别人的代码时 xff0c 按照别人commit的历史版本一步一步跟踪 xff0c 或许是个好办法 这时候我们就要用到下载历史版本功能了 1 git clone
  • 实时操作系统UCOS学习笔记1----UCOSII简介

    前面我们所有的实验都是跑的裸机程序 xff08 裸奔 xff09 xff0c 从本章开始 xff0c 我们开始介绍UCOSII xff08 实时多任务操作系统内核 xff09 UCOSII简介 UCOSII的前身是UCOS xff0c 最早
  • 欢聚时代YY/测试实习面试

    去到YY大楼 xff0c 虽然在番禺但是附近很多楼在施工中了 大楼就在万达后面 前台登记 xff0c 小姐姐会让你出示邀请邮件 xff0c 然后直接上去就行了 达到楼层 xff0c 二维码签到 然后找地方坐一下等待面试 一轮是技术面 xff
  • 基于卡尔曼滤波的气压计高度解算

    ax ay az为归一化的加速度数据 1代表重力加速度 gx gy gz 为加速度数据 单位rad s altitude为气压计测量得到的海拔数据 欧拉角 float pitch roll yaw 世界坐标系下机体加速度 float ax
  • Kali安装Xfce4

    Kali安装Xface4 一 配置kali源并更新二 解决报错1 签名无效2 依赖报错 三 安装xfce4 一 配置kali源并更新 此处使用的是gedit编辑器 xff0c gedit etc apt sources list xff0c
  • 串口转WIFI的工作方式理解

    串口无线 AP xff08 COM AP xff09 串口无线 STA xff08 COM STA xff09 和 串口无线 AP 43 STA xff08 COM AP 43 STA xff09 3 个模式 串口WIFI模块是基于Uart
  • 典型环节的频率特性(建议收藏)

    自控笔记 5 3典型环节频率特性 控制系统种类繁多 xff0c 传递函数复杂 xff0c 但任何形式的传递函数都是由有限的典型环节组成 因此 xff0c 掌握典型环节的频率特性是使用频域法分析系统的基础 如下表所示 xff0c 构成系统的最
  • 【WINAPI】CreateSemaphore_信号量

    WINAPI CreateSemaphore 信号量 1 注册信号量函数1 1 参数1 2 返回值 2 释放信号量函数2 1 参数2 2 返回值 3 WaitForSingleObject3 2 参数3 3 返回值 4 例子4 1 运行结果
  • MAVROS二次开发(一)MAVROS的安装

    MAVROS二次开发 一 MAVROS的安装 1 参考网址 https dev px4 io v1 10 en ros mavros installation html https github com mavlink mavros tre
  • MAVROS二次开发(二)(三)添加自定义消息

    MAVROS二次开发 二 MAVROS消息添加 1 自定义rostopic消息 路径 xff1a catkin ws src mavros mavros msgs msg 自定义消息文件名称 xff1a CrawlControlStatus
  • MAVROS二次开发(四)添加消息处理插件

    MAVROS二次开发 四 添加消息处理插件 mavros插件所在路径 xff1a catkin ws src mavros mavros src plugins 1 自定义消息处理插件的编写 参考代码 xff1a catkin ws src
  • MAVROS二次开发(五)进行测试

    MAVROS二次开发 五 进行测试 1 测试环境 PX4 xff1a v1 10 1 xff08 含自定义mavlink消息收发 xff09 ROS xff1a KineticUbuntu xff1a 16 04LTSQGC xff1a S
  • ROS2+PX4开发环境配置

    一 ROS2安装 Ubuntu18 04的ros2版本 xff1a Eloquent 参考网址 xff1a https docs ros org en eloquent Installation Linux Install Debians
  • Windows10下Airsim+PX4(WSL2)+MAVROS仿真环境搭建

    一 Windows10下WSL2安装 1 1 WSL2的安装与配置 首先在Windows10下启用WSL xff0c 以管理员身份打开 PowerShell 工具并运行以下命令 dism span class token punctuati
  • Windows10通过vcpkg快速配置PCL库

    1 安装C 43 43 包管理工具vcpkg https github com microsoft vcpkg span class token function git span clone https github com micros
  • 微软Chromium版Edge浏览器正式稳定版

    微软Chromium版Edge浏览器正式稳定版 近期微软Chromium版Edge浏览器正式稳定版下载已经泄露 xff0c 版本77 0 235 9 此版本没有div什么的那些 xff0c 和之前的图标一样 当安装新Edge稳定版之后 xf
  • C++疑难问题

    acwing中的算法疑惑 1 为什么确定范围 要 43 10 在使用归并排序和快速排序等方法时有效率问题 xff0c 确定范围在1e6 但是选择的是1e 43 10 2 C 43 43 除二乘2简单方法以及算法效率问题 算法效率速度排行 x
  • 用python的scipy中的odeint来解常微分方程中的一些细节问题(适用于小白)

    用python的scipy中的odeint来解常微分方程中的一些细节问题 xff08 适用于小白 xff09 写在前面 最近有些需要解决常微分方程的问题 xff0c 网上查了很多教程都不是很明晰 xff0c 便自己研究了一段时间 xff0c