零基础:离散数据积分与 Python 实现保姆级教程

《零基础:离散数据积分与 Python 实现保姆级教程》

一、 概念扫盲:什么是"离散数据"与"积分"?

1.1 什么是离散数据?

无人机上的传感器(如速度计、GPS)是离散采样的。它只能每隔一段时间(比如 0.1 秒)"拍个照",记录下那一瞬间的数据。

在 Python 中,离散数据就是两个长度一模一样的列表(数组):

  • 一个是时间戳表 ttt:[0秒, 1秒, 2秒, 3秒]
  • 一个是传感器读数 vvv:[0m/s, 2m/s, 4m/s, 2m/s]

在这 1秒 到 2秒 之间发生了什么?我们完全不知道,这就叫离散数据的盲区

1.2 积分到底是什么?(白话解释)

在工程中,积分最直观的物理意义就是:积累量

  • 数学意义:求曲线和横轴围成的面积。
  • 物理意义:已知"速度",求"位移"(速度在时间上的累积);已知"功率",求"耗电量"。

哪怕你忘了牛顿-莱布尼茨公式,只要记住:积分 = 算面积 = 算累积量

二、 核心数学原理:如何"猜"出面积?

既然点与点之间是盲区,我们怎么算总面积(总位移)呢?只能把相邻的两个点连起来,拼成一个个小几何图形来"猜"。

2.1 推导:为什么工程上最爱用"梯形法"?

假设我们从无人机上拿到了两个相邻的采样点:

  • 第 1 个点:时刻 t1t_1t1,速度 v1v_1v1
  • 第 2 个点:时刻 t2t_2t2,速度 v2v_2v2

这两个点在图纸上怎么连?

  1. 矩形法(太粗糙) :假装从 t1t_1t1 到 t2t_2t2 速度一直不变(一直是 v1v_1v1)。面积 = 宽 ×\times× 高 = (t2−t1)×v1(t_2 - t_1) \times v_1(t2−t1)×v1。这样误差极大。
  2. 梯形法(最实用) :用一条直线 把 (t1,v1)(t_1, v_1)(t1,v1) 和 (t2,v2)(t_2, v_2)(t2,v2) 连起来。这就形成了一个梯形

梯形面积公式推导:

  • 梯形面积 = (((上底 + 下底)×) \times)× 高 ÷2\div 2÷2
  • 把梯形竖着看:
    • 上底 = 时刻 t1t_1t1 的速度 v1v_1v1
    • 下底 = 时刻 t2t_2t2 的速度 v2v_2v2
    • = 两个点之间经历的时间 Δt=t2−t1\Delta t = t_2 - t_1Δt=t2−t1

所以,这两个点之间走过的位移(小梯形面积)就是:

面积=(v1+v2)×(t2−t1)2\text{面积} = \frac{(v_1 + v_2) \times (t_2 - t_1)}{2}面积=2(v1+v2)×(t2−t1)

只要我们把所有相邻两点组成的小梯形面积全部加起来,就是总位移!这就是梯形积分法的全部数学原理。

三、 从零手写 Python 积分代码

在调用高级库之前,我们先纯手工用 Python 写一遍上述逻辑,彻底搞懂底层的 for 循环是怎么运行的。

3.1 数组索引的对应关系 (Mermaid 图解)

假设我们有 4 个数据点。在 Python 中,列表的索引(下标)是从 0 开始的。
#mermaid-svg-BhhR2zgb8ffdVGet{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BhhR2zgb8ffdVGet .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BhhR2zgb8ffdVGet .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BhhR2zgb8ffdVGet .error-icon{fill:#552222;}#mermaid-svg-BhhR2zgb8ffdVGet .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BhhR2zgb8ffdVGet .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BhhR2zgb8ffdVGet .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BhhR2zgb8ffdVGet .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BhhR2zgb8ffdVGet .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BhhR2zgb8ffdVGet .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BhhR2zgb8ffdVGet .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BhhR2zgb8ffdVGet .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BhhR2zgb8ffdVGet .marker.cross{stroke:#333333;}#mermaid-svg-BhhR2zgb8ffdVGet svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BhhR2zgb8ffdVGet p{margin:0;}#mermaid-svg-BhhR2zgb8ffdVGet .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BhhR2zgb8ffdVGet .cluster-label text{fill:#333;}#mermaid-svg-BhhR2zgb8ffdVGet .cluster-label span{color:#333;}#mermaid-svg-BhhR2zgb8ffdVGet .cluster-label span p{background-color:transparent;}#mermaid-svg-BhhR2zgb8ffdVGet .label text,#mermaid-svg-BhhR2zgb8ffdVGet span{fill:#333;color:#333;}#mermaid-svg-BhhR2zgb8ffdVGet .node rect,#mermaid-svg-BhhR2zgb8ffdVGet .node circle,#mermaid-svg-BhhR2zgb8ffdVGet .node ellipse,#mermaid-svg-BhhR2zgb8ffdVGet .node polygon,#mermaid-svg-BhhR2zgb8ffdVGet .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BhhR2zgb8ffdVGet .rough-node .label text,#mermaid-svg-BhhR2zgb8ffdVGet .node .label text,#mermaid-svg-BhhR2zgb8ffdVGet .image-shape .label,#mermaid-svg-BhhR2zgb8ffdVGet .icon-shape .label{text-anchor:middle;}#mermaid-svg-BhhR2zgb8ffdVGet .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BhhR2zgb8ffdVGet .rough-node .label,#mermaid-svg-BhhR2zgb8ffdVGet .node .label,#mermaid-svg-BhhR2zgb8ffdVGet .image-shape .label,#mermaid-svg-BhhR2zgb8ffdVGet .icon-shape .label{text-align:center;}#mermaid-svg-BhhR2zgb8ffdVGet .node.clickable{cursor:pointer;}#mermaid-svg-BhhR2zgb8ffdVGet .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BhhR2zgb8ffdVGet .arrowheadPath{fill:#333333;}#mermaid-svg-BhhR2zgb8ffdVGet .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BhhR2zgb8ffdVGet .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BhhR2zgb8ffdVGet .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BhhR2zgb8ffdVGet .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BhhR2zgb8ffdVGet .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BhhR2zgb8ffdVGet .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BhhR2zgb8ffdVGet .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BhhR2zgb8ffdVGet .cluster text{fill:#333;}#mermaid-svg-BhhR2zgb8ffdVGet .cluster span{color:#333;}#mermaid-svg-BhhR2zgb8ffdVGet div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BhhR2zgb8ffdVGet .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BhhR2zgb8ffdVGet rect.text{fill:none;stroke-width:0;}#mermaid-svg-BhhR2zgb8ffdVGet .icon-shape,#mermaid-svg-BhhR2zgb8ffdVGet .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BhhR2zgb8ffdVGet .icon-shape p,#mermaid-svg-BhhR2zgb8ffdVGet .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BhhR2zgb8ffdVGet .icon-shape .label rect,#mermaid-svg-BhhR2zgb8ffdVGet .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BhhR2zgb8ffdVGet .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BhhR2zgb8ffdVGet .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BhhR2zgb8ffdVGet :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 第一块小梯形计算 (i=0)
索引 i 的含义
对应时间
对应速度
对应时间
对应速度
对应时间
对应速度
对应时间
对应速度
i = 0
t0 = 0.0s
v0 = 0.0
i = 1
t1 = 1.0s
v1 = 2.0
i = 2
t2 = 2.0s
v2 = 4.0
i = 3
t3 = 3.0s
v3 = 1.0
上底: v0
下底: v1
高: t1 - t0
公式: (上+下)*高/2

3.2 纯手工 Python 代码推导

python 复制代码
# 1. 准备离散数据(一共 4 个点)
t = [0.0, 1.0, 2.0, 3.0]  # 时间戳 (秒)
v = [0.0, 2.0, 4.0, 1.0]  # 速度 (m/s)

# 2. 初始化总面积(总位移)为 0
total_distance = 0.0

# 3. 核心:写 for 循环遍历所有相邻的点
# 为什么是 len(t) - 1?
# 因为 4 个点只能连成 3 个梯形!如果 i 走到最后一个点,就找不到 i+1 了,会报错。
for i in range(len(t) - 1):
    
    # 第 i 步:提取当前点和下一个点的时间与速度
    t_current = t[i]
    t_next = t[i+1]
    v_current = v[i]
    v_next = v[i+1]
    
    # 第 i 步:套用梯形面积公式
    height = t_next - t_current       # 高 (时间差)
    top = v_current                   # 上底 (当前速度)
    bottom = v_next                   # 下底 (下一个速度)
    
    area = (top + bottom) * height / 2.0  # 算出一个小梯形面积
    
    # 把这块小面积累加到总面积中
    total_distance = total_distance + area
    
    # 打印过程,方便你观察
    print(f"第 {i} 个梯形: 时间段 {t_current}s 到 {t_next}s, 增加位移 {area}m")

# 4. 输出最终结果
print(f"最终算出的总位移是: {total_distance} 米")

四、 掌握 Python 的"魔法工具":SciPy 库

上面写了十几行代码,在实际科研中,我们直接使用 SciPy 库,只需一行代码即可完成。

首先需要导入库:

复制代码
import numpy as np
from scipy.integrate import trapezoid, cumulative_trapezoid

4.1 一步求总和:trapezoid

这个函数做的事情,和我们上面手写的 for 循环一模一样。

python 复制代码
import numpy as np
from scipy.integrate import trapezoid

t = np.array([0.0, 1.0, 2.0, 3.0])
v = np.array([0.0, 2.0, 4.0, 1.0])

# 一行代码搞定总位移
total_dist = trapezoid(y=v, x=t) 
print(f"SciPy 梯形积分总位移: {total_dist} 米") 
# 结果同样是 6.5 米

4.2 科研重头戏:求过程轨迹 cumulative_trapezoid

在控制系统仿真中,我们不仅要知道"最后飞了多远"(一个孤立的数字 6.5),更需要知道"每一秒无人机在什么位置"(我们需要一条曲线)。

cumulative_trapezoid (累积梯形法)就是干这个的。它会把每一步累加的结果保存成一个数组。

代码段
#mermaid-svg-32nRXODz24SIQzcP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-32nRXODz24SIQzcP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-32nRXODz24SIQzcP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-32nRXODz24SIQzcP .error-icon{fill:#552222;}#mermaid-svg-32nRXODz24SIQzcP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-32nRXODz24SIQzcP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-32nRXODz24SIQzcP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-32nRXODz24SIQzcP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-32nRXODz24SIQzcP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-32nRXODz24SIQzcP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-32nRXODz24SIQzcP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-32nRXODz24SIQzcP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-32nRXODz24SIQzcP .marker.cross{stroke:#333333;}#mermaid-svg-32nRXODz24SIQzcP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-32nRXODz24SIQzcP p{margin:0;}#mermaid-svg-32nRXODz24SIQzcP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-32nRXODz24SIQzcP .cluster-label text{fill:#333;}#mermaid-svg-32nRXODz24SIQzcP .cluster-label span{color:#333;}#mermaid-svg-32nRXODz24SIQzcP .cluster-label span p{background-color:transparent;}#mermaid-svg-32nRXODz24SIQzcP .label text,#mermaid-svg-32nRXODz24SIQzcP span{fill:#333;color:#333;}#mermaid-svg-32nRXODz24SIQzcP .node rect,#mermaid-svg-32nRXODz24SIQzcP .node circle,#mermaid-svg-32nRXODz24SIQzcP .node ellipse,#mermaid-svg-32nRXODz24SIQzcP .node polygon,#mermaid-svg-32nRXODz24SIQzcP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-32nRXODz24SIQzcP .rough-node .label text,#mermaid-svg-32nRXODz24SIQzcP .node .label text,#mermaid-svg-32nRXODz24SIQzcP .image-shape .label,#mermaid-svg-32nRXODz24SIQzcP .icon-shape .label{text-anchor:middle;}#mermaid-svg-32nRXODz24SIQzcP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-32nRXODz24SIQzcP .rough-node .label,#mermaid-svg-32nRXODz24SIQzcP .node .label,#mermaid-svg-32nRXODz24SIQzcP .image-shape .label,#mermaid-svg-32nRXODz24SIQzcP .icon-shape .label{text-align:center;}#mermaid-svg-32nRXODz24SIQzcP .node.clickable{cursor:pointer;}#mermaid-svg-32nRXODz24SIQzcP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-32nRXODz24SIQzcP .arrowheadPath{fill:#333333;}#mermaid-svg-32nRXODz24SIQzcP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-32nRXODz24SIQzcP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-32nRXODz24SIQzcP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-32nRXODz24SIQzcP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-32nRXODz24SIQzcP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-32nRXODz24SIQzcP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-32nRXODz24SIQzcP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-32nRXODz24SIQzcP .cluster text{fill:#333;}#mermaid-svg-32nRXODz24SIQzcP .cluster span{color:#333;}#mermaid-svg-32nRXODz24SIQzcP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-32nRXODz24SIQzcP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-32nRXODz24SIQzcP rect.text{fill:none;stroke-width:0;}#mermaid-svg-32nRXODz24SIQzcP .icon-shape,#mermaid-svg-32nRXODz24SIQzcP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-32nRXODz24SIQzcP .icon-shape p,#mermaid-svg-32nRXODz24SIQzcP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-32nRXODz24SIQzcP .icon-shape .label rect,#mermaid-svg-32nRXODz24SIQzcP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-32nRXODz24SIQzcP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-32nRXODz24SIQzcP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-32nRXODz24SIQzcP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 累加过程 (初始位移 0)
速度数组 v (4个点)
0.0
2.0
4.0
1.0
起点: 0
0 + 梯形1面积(1.0) = 1.0
1.0 + 梯形2面积(3.0) = 4.0
4.0 + 梯形3面积(2.5) = 6.5

⚠️ 保姆级排雷:initial=0 参数为什么这么重要?

看上面的图解,4 个速度点,只能算出 3 个梯形面积。

如果不加 initial=0,输出的位置数组只有 [1.0, 4.0, 6.5] 3 个数据。

但你的时间戳 t 是 4 个点。在 Python 里,如果画图(xy 长度不一致),程序会直接崩溃报错!

加上 initial=0,就是在数组最前面强行补上一个"初始位移 0"。

输出就会变成 [0.0, 1.0, 4.0, 6.5],完美与 t 的 4 个点一一对应。

五、 实战演练:多智能体无人机的一段飞行数据

现在,我们把上面的知识串起来。模拟一架无人机从静止加速起飞,然后减速悬停的过程。我们通过速度数据,反推出它的位置曲线,并画图。

python 复制代码
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import cumulative_trapezoid

# 1. 制造模拟数据
# np.linspace(0, 10, 100) 的意思是:在 0 到 10 之间,均匀生成 100 个点(时间戳)
t = np.linspace(0, 10, 100) 

# 假设无人机的速度是一个抛物线形:先加速到 5秒(速度最大),然后减速到 10秒(速度归零)
# 公式随意设定的,只是为了造出一个漂亮的钟形曲线
v = 10 - 0.4 * (t - 5)**2 

# 2. 核心代码:对离散速度进行累积梯形积分,算出离散位置
# y 放被积变量(速度),x 放自变量(时间),务必加上 initial=0
position = cumulative_trapezoid(y=v, x=t, initial=0)

# ================= 下面全都是画图代码,模板可以直接抄 =================
# 创建一个画布,大小为 10x5 英寸
plt.figure(figsize=(10, 5))

# 画第一条线:速度曲线 (用红色虚线表示)
# label 用来在图例中显示名字
plt.plot(t, v, color='red', linestyle='--', linewidth=2, label='Velocity (m/s)')

# 画第二条线:位置曲线 (用蓝色实线表示)
plt.plot(t, position, color='blue', linestyle='-', linewidth=2, label='Position (m)')

# 给图表加上标题和坐标轴说明
plt.title("UAV Flight Data: Integration from Velocity to Position")
plt.xlabel("Time (seconds)")
plt.ylabel("Magnitude")

# 显示网格线,方便人类眼睛对齐数值
plt.grid(True)

# 显示图例 (就是图表角落里解释红线蓝线是什么的小框框)
plt.legend()

# 弹出最终的画面
plt.show()
相关推荐
SunnyDays10111 小时前
使用 Python 将 PowerPoint 转换为视频(无需安装 Microsoft PowerPoint)
python·powerpoint·ppt 转 动画·ppt 转 mp4·ppt 转 wmv·幻灯片转动画·幻灯片转 mp4
用户0332126663671 小时前
使用 Python 将 PDF 转换为 Word(转换单个与多个文件)
python
troy1281 小时前
Visual Studio Code 详细教程:从入门到高效开发
开发语言·python
淡海水1 小时前
11-03-Unity-List-T-和Dictionary-TKey-TValue-的性能调优实战
算法·unity·c#·list·dictionary
土司大王1 小时前
LeetCode hot100——394.字符串解码:Java 双栈模拟
java·算法·leetcode
夜雪一千1 小时前
Python ASR音频转文本 完整代码示例
python
a187927218312 小时前
【算法】双指针与滑动窗口(三):相向双指针——比较、排除、收缩
算法·leetcode·双指针·滑动窗口·原理·相向双指针·算法讲解
JarmanYuo2 小时前
YOLO 涨点研究(十二):具身 CV 进阶篇——Sim2Real 域随机化与真机部署
人工智能·pytorch·python·yolo·计算机视觉
小蒜学长2 小时前
基于Python的微博热搜话题可视化分析系统的设计与实现(代码+数据库+LW)
后端·python·django·数据可视化·情感分析·微博热搜