5.scipy Integrate


title: "调用ode对lorenz进行求解, 用两个不同的初始值" date: 2026-05-24T14:11:14Z

1import pylab as pl
2import numpy as np
3from scipy import integrate
4from scipy.integrate import odeint
5import matplotlib as mpl
6mpl.rcParams['font.sans-serif'] = ['SimHei']

数值积分-integrate

球的体积

1def half_circle(x):
2    return (1-x**2)**0.5
1N = 10000
2x = np.linspace(-1, 1, N)
3dx = x[1] - x[0]
4y = half_circle(x)
52 * dx * np.sum(y) # 面积的两倍 
3.1415893269307373
1np.trapz(y, x) * 2 # 面积的两倍
3.1415893269315975
1from scipy import integrate
2pi_half, err = integrate.quad(half_circle, -1, 1)
3pi_half * 2
3.141592653589797
1def half_sphere(x, y):
2    return (1-x**2-y**2)**0.5
1volume, error = integrate.dblquad(half_sphere, -1, 1, 
2        lambda x:-half_circle(x), 
3        lambda x:half_circle(x))
4
5print (volume, error, np.pi*4/3/2)
2.094395102393199 1.0002356720661965e-09 2.0943951023931953

解常微分方程组

 1#%fig=洛伦茨吸引子:微小的初值差别也会显著地影响运动轨迹
 2from scipy.integrate import odeint 
 3import numpy as np 
 4
 5def lorenz(w, t, p, r, b): #❶
 6    # 给出位置矢量w,和三个参数p, r, b计算出
 7    # dx/dt, dy/dt, dz/dt的值
 8    x, y, z = w.tolist()
 9    # 直接与lorenz的计算公式对应 
10    return p*(y-x), x*(r-z)-y, x*y-b*z
11
12t = np.arange(0, 30, 0.02) # 创建时间点 
13# 调用ode对lorenz进行求解, 用两个不同的初始值 
14track1 = odeint(lorenz, (0.0, 1.00, 0.0), t, args=(10.0, 28.0, 3.0)) #❷
15track2 = odeint(lorenz, (0.0, 1.01, 0.0), t, args=(10.0, 28.0, 3.0)) #❸
16#%hide
17from mpl_toolkits.mplot3d import Axes3D
18fig = pl.figure()
19ax = Axes3D(fig)
20ax.plot(track1[:,0], track1[:,1], track1[:,2], lw=1)
21ax.plot(track2[:,0], track2[:,1], track2[:,2], lw=1);

png

ode类

1def mass_spring_damper(xu, t, m, k, b, F):
2    x, u = xu.tolist()
3    dx = u
4    du = (F - k*x - b*u)/m
5    return dx, du 
 1#%fig=滑块的速度和位移曲线
 2m, b, k, F = 1.0, 10.0, 20.0, 1.0
 3init_status = 0.0, 0.0
 4args = m, k, b, F
 5t = np.arange(0, 2, 0.01)
 6result = odeint(mass_spring_damper, init_status, t, args)
 7#%hide
 8fig, (ax1, ax2) = pl.subplots(2, 1)
 9ax1.plot(t, result[:, 0], label=u"位移")
10ax1.legend()
11ax2.plot(t, result[:, 1], label=u"速度")
12ax2.legend();

png

 1from scipy.integrate import ode
 2
 3class MassSpringDamper(object): #❶
 4    
 5    def __init__(self, m, k, b, F):
 6        self.m, self.k, self.b, self.F = m, k, b, F
 7        
 8    def f(self, t, xu):
 9        x, u = xu.tolist()
10        dx = u
11        du = (self.F - self.k*x - self.b*u)/self.m
12        return [dx, du] 
13
14system = MassSpringDamper(m=m, k=k, b=b, F=F)
15init_status = 0.0, 0.0
16dt = 0.01
17
18r = ode(system.f) #❷
19r.set_integrator('vode', method='bdf')
20r.set_initial_value(init_status, 0)
21
22t = []
23result2 = [init_status]
24while r.successful() and r.t + dt < 2: #❸
25    r.integrate(r.t + dt)
26    t.append(r.t)
27    result2.append(r.y)
28    
29result2 = np.array(result2)
30np.allclose(result, result2)
True
 1class PID(object):
 2    
 3    def __init__(self, kp, ki, kd, dt):
 4        self.kp, self.ki, self.kd, self.dt = kp, ki, kd, dt
 5        self.last_error = None
 6        self.status = 0.0
 7        
 8    def update(self, error):
 9        p = self.kp * error
10        i = self.ki * self.status
11        if self.last_error is None:
12            d = 0.0
13        else:
14            d = self.kd * (error - self.last_error) / self.dt
15        self.status += error * self.dt
16        self.last_error = error
17        return p + i + d
 1#%fig=使用PID控制器让滑块停在位移为1.0处
 2def pid_control_system(kp, ki, kd, dt, target=1.0):
 3    system = MassSpringDamper(m=m, k=k, b=b, F=0.0)
 4    pid = PID(kp, ki, kd, dt)
 5    init_status = 0.0, 0.0
 6
 7    r = ode(system.f)
 8    r.set_integrator('vode', method='bdf')
 9    r.set_initial_value(init_status, 0)
10
11    t = [0]
12    result = [init_status]
13    F_arr = [0]
14
15    while r.successful() and r.t + dt < 2.0:
16        r.integrate(r.t + dt)
17        err = target - r.y[0]  #❶
18        F = pid.update(err)  #❷
19        system.F = F  #❸
20        t.append(r.t)
21        result.append(r.y)
22        F_arr.append(F)
23
24    result = np.array(result)
25    t = np.array(t)
26    F_arr = np.array(F_arr)
27    return t, F_arr, result
28
29
30t, F_arr, result = pid_control_system(50.0, 100.0, 10.0, 0.001)
31print(u"控制力的终值:", F_arr[-1])
32#%hide
33fig, (ax1, ax2, ax3) = pl.subplots(3, 1, figsize=(6, 6))
34ax1.plot(t, result[:, 0], label=u"位移")
35ax1.legend(loc="best")
36ax2.plot(t, result[:, 1], label=u"速度")
37ax2.legend(loc="best")
38ax3.plot(t, F_arr, label=u"控制力")
39ax3.legend(loc="best")
控制力的终值: 19.943404699515057





<matplotlib.legend.Legend at 0x18b42591cc0>

png

 1%%time
 2from scipy import optimize
 3
 4
 5def eval_func(k):
 6    kp, ki, kd = k
 7    t, F_arr, result = pid_control_system(kp, ki, kd, 0.01)
 8    return np.sum(np.abs(result[:, 0] - 1.0))
 9
10
11kwargs = {
12    "method": "L-BFGS-B",
13    "bounds": [(10, 200), (10, 100), (1, 100)],
14    "options": {
15        "approx_grad": True
16    }
17}
18
19opt_k = optimize.basinhopping(
20    eval_func, (10, 10, 10), niter=10, minimizer_kwargs=kwargs)
21print(opt_k.x)
C:\ProgramData\Anaconda3\lib\site-packages\scipy\optimize\_basinhopping.py:282: OptimizeWarning: Unknown solver options: approx_grad
  return self.minimizer(self.func, x0, **self.kwargs)


[56.67106149 99.97434757  1.33963577]
Wall time: 55.1 s
 1#%fig=优化PID的参数降低控制响应时间
 2kp, ki, kd = opt_k.x
 3t, F_arr, result = pid_control_system(kp, ki, kd, 0.01)
 4idx = np.argmin(np.abs(t - 0.5))
 5x, u = result[idx]
 6print ("t={}, x={:g}, u={:g}".format(t[idx], x, u))
 7#%hide
 8fig, (ax1, ax2, ax3) = pl.subplots(3, 1, figsize=(6, 6))
 9ax1.plot(t, result[:, 0], label=u"位移")
10ax1.legend(loc="best")
11ax2.plot(t, result[:, 1], label=u"速度")
12ax2.legend(loc="best")
13ax3.plot(t, F_arr, label=u"控制力")
14ax3.legend(loc="best");
t=0.5000000000000002, x=1.07098, u=0.315352

png