用C++的odeint库求解微分方程
目录
- 1、集成方程
- 2、求解单摆模型
- 2.1 微分方程标准化
- 2.2 代码实现
文章图片
即:
\dot{\boldsymbol{x}} = \boldsymbol{f}(\boldsymbol{x}, t),\, \boldsymbol{x}(0) = \boldsymbol{x_0}
这是一阶微分方程组,
\boldsymbol{x}
和 \boldsymbol{f}(\boldsymbol{x}, t)
均为向量。如果要求解高阶微分方程,需要先转换成一阶微分方程组后再用odeint
求解。1、集成方程 API中最重要的是集成函数(
integrate functions
),一共有5种,它们的调用接口很类似。 integrate_const 的函数调用方式为:integrate_const(stepper, system, x0, t0, t1, dt, observer)
其中:
stepper
是求解器,也就是所使用的数值算法(例如Runge-Kutta或Euler法)system
是待求解的微分方程x0
是初始条件t0
和 t1 分别是初始时间和结束时间dt
是时间间隔,它重要与否取决于求解器的类型observer
是每N个时间间隔调用一次的函数,可用来打印实时的解,该参数是可选的,如果没有此参数,集成函数会从 t0 计算到 t1 ,不产生任何输出就返回
x0
,集成函数从初始时间 t0
到结束时间 t1
不断地调用给定的 stepper
,计算微分方程在不同时刻的解,用户还可以提供 observer
以分析某个时刻的状态值。具体选择哪个集成函数取决于你想要什么类型的结果,也就是调用 observer
的频次。integrate_const
每过相等的时间间隔 dt 会调用一次 observer
,语法为:integrate_const(stepper, system, x0, t0, t1, dt, observer)
integrate_n_steps
和前面的类似,但它不需要知道结束时间,它只需要知道要计算的步数,语法为:integrate_n_steps(stepper, system, x0, t0, dt, n, observer)
integrate_times
计算在用户给定时间点的值,语法为:integrate_times(stepper, system, x0, times_start, times_end, dt, observer)integrate_times(stepper, system, x0, time_range, dt, observer)
integrate_adaptive
用于需要在每个时间间隔调用 observer
的场合,语法为:integrate_adaptive(stepper, system, x0, t0, t1, dt, observer)
integrate 是最方便的集成函数, 不需要指定 stepper ,简单快捷,语法为:
integrate(system, x0, t0, t1, dt, observer)
求解器
stepper
的选择(比如自适应方式会根据误差修改时间间隔)会改变计算的具体实现方式, 但是observer
的调用(也就是你的输出结果)依然遵循上述规则。2、求解单摆模型
2.1 微分方程标准化
现在求单摆系统微分方程的解,以得出单摆角度随时间变化的规律。其微分方程
文章图片
即:
\ddot{\theta}(t) = -\mu \dot{\theta}(t) - \frac{g}{L} \sin \theta(t)
文章图片
文章图片
即:
\begin{cases} \dot{\theta}(t) & = \omega(t) \\ \dot{\omega}(t) & = -\mu \omega(t) - g/L \sin \theta(t) \end{cases}
令状态变量
文章图片
即:
\boldsymbol{x} = \begin{bmatrix} x_1(t)\\ x_2(t) \end{bmatrix} = \begin{bmatrix} \theta(t)\\ \omega(t) \end{bmatrix}
微分方程组变为
文章图片
即:
\frac{\mathrm{d}\boldsymbol{x}}{\mathrm{d}t}= \frac{\mathrm{d}}{\mathrm{d}t} \begin{bmatrix} x_1(t)\\ x_2(t) \end{bmatrix} = \begin{bmatrix} x_2(t)\\ -\mu x_2(t) - g/L \sin x_1(t) \end{bmatrix}
2.2 代码实现
【用C++的odeint库求解微分方程】代码中有如下几个关键点:
- 要定义状态变量的类型
state_type
,定义为std::vector
即可 - 要用方程表示微分方程模型,和
MATLAB
中模型方程的写法非常类似 - 要写一个
Observer
以打印出计算结果,Observer
函数也可以直接将数据写入文件中 - 要选择合适的求解器
stepper
,各种stepper
的特点总结可以看 这里 - 要根据需要选择合适的集成函数,一般选择
integrate_const
即可满足要求
#include#include #include using namespace std; using namespace boost::numeric::odeint; const double g= 9.81; // 重力加速度const double L= 1.00; // 摆线长度const double mu = 0.80; // 阻力系数// 定义状态变量的类型typedef std::vector state_type; // 要求解的微分方程void pendulum(const state_type &x, state_type &dxdt, double t){dxdt[0] = x[1]; dxdt[1] = -mu*x[1] - g/L*sin(x[0]); }// Observer打印状态值void write_pendulum(const state_type &x, const double t){cout << t << '\t' << x[0] << '\t' << x[1] << endl; }int main(int argc, char **argv){// 初始条件,二维向量state_type x = {0.10 , 0.00}; // 求解方法为runge_kutta4integrate_const(runge_kutta4(), pendulum, x , 0.0 , 5.0 , 0.01 , write_pendulum); }
编译该程序依赖
boost
库,要在 CMakeLists.txt
中添加相应的内容。编译成功后运行,会得到如下的结果:00.100.010.0999512-0.0097530.020.0998052-0.01941880.030.0995631-0.02898870.040.0992258-0.03845420.050.0987944-0.04780690.060.0982701-0.05703850.070.0976541-0.06614120.080.0969477-0.0751070.090.0961524-0.08392830.10.0952696-0.09259770.110.094301-0.101108----many lines ommitted----
可以将输出数据重定向到文本文件
data.txt
中,然后使用Python
等脚本语言提取数据并画图显示。下面是实现该功能的参考代码:import numpy as npimport matplotlib.pyplot as pltlines = tuple(open("data.txt", 'r')) # 读取文件行到tuple中rows = len(lines)time= np.zeros(rows)theta = np.zeros(rows)omega = np.zeros(rows)for r in range(rows):[str1, str2, str3] = lines[r].split()time[r]= float(str1)theta[r] = float(str2)omega[r] = float(str3)plt.plot(time, theta, time, omega) # 角度和角速度变化# plt.plot(theta, omega) # 相图plt.show()
到此这篇关于用C++的odeint库求解微分方程的文章就介绍到这了,更多相关C++的odeint库求解微分方程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
推荐阅读
- 热闹中的孤独
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 放屁有这三个特征的,请注意啦!这说明你的身体毒素太多
- 一个人的旅行,三亚
- 布丽吉特,人生绝对的赢家
- 慢慢的美丽
- 尽力
- 一个小故事,我的思考。
- 家乡的那条小河
- Docker应用:容器间通信与Mariadb数据库主从复制