4. NumPy核心操作:数组创建与索引、矩阵运算、统计函数、随机数生成
各位同学,咱们今天聊点硬核的。NumPy,说白了就是Python科学计算的基石。我刚开始做运动控制仿真那会儿,数据量一大,纯Python列表处理起来慢得让人抓狂。后来用了NumPy,才真正体会到什么叫「飞一般的感觉」。
这一节,我带你过一遍NumPy最核心的四个操作。你想想看,搞运动控制,哪个环节离得开数组、矩阵、统计和随机数?一个都跑不掉。
核心要点:NumPy的核心是ndarray对象。它比Python原生列表快几十倍,原因在于底层用C语言实现,并且支持向量化操作。说白了,就是一次处理一批数据,而不是一个个循环。
4.1 数组创建与索引
创建数组是第一步。我个人习惯用 np.array() 从列表转换,但更常用的是 np.zeros()、np.ones() 和 np.arange()。
import numpy as np
# 从列表创建
arr1 = np.array([1, 2, 3, 4, 5])
# 全零数组,常用于初始化
zeros = np.zeros((3, 4))
# 等差数列,类似range
arr2 = np.arange(0, 10, 2) # [0, 2, 4, 6, 8]
# 等间隔数组,指定个数
arr3 = np.linspace(0, 1, 5) # [0. , 0.25, 0.5, 0.75, 1. ]
索引这块,我踩过坑。有一次做轨迹规划,需要提取某一段位置数据,直接用 arr[1:3] 切片,结果发现修改切片会影响原数组。为什么?因为NumPy切片返回的是视图,不是副本。嗯,这里要注意:
避坑指南:NumPy切片默认是浅拷贝(视图)。如果你需要独立的数据,一定要用 .copy() 方法。我曾经因为这个问题,调试了一下午才发现数据被意外修改了。
# 索引示例
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 取单个元素
print(arr[1, 2]) # 6
# 取一行
print(arr[0, :]) # [1, 2, 3]
# 布尔索引——这个在运动控制中非常实用
mask = arr > 5
print(arr[mask]) # [6, 7, 8, 9]
4.2 矩阵运算
运动控制里,矩阵运算无处不在。坐标变换、雅可比矩阵、动力学方程……全是矩阵。NumPy的矩阵运算,我总结为三类:
| 运算类型 | 运算符/函数 | 说明 |
|---|---|---|
| 逐元素运算 | +, -, *, / |
对应位置元素直接运算 |
| 矩阵乘法 | @ 或 np.dot() |
线性代数中的矩阵乘法 |
| 转置/逆 | .T, np.linalg.inv() |
矩阵转置和求逆 |
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
# 逐元素乘法
C = A * B # [[5, 12], [21, 32]]
# 真正的矩阵乘法
D = A @ B # [[19, 22], [43, 50]]
# 转置
print(A.T) # [[1, 3], [2, 4]]
# 求逆
inv_A = np.linalg.inv(A)
我记得有一次做六轴机器人的逆解,需要频繁计算3x3旋转矩阵的逆。用 np.linalg.inv() 虽然方便,但计算量不小。后来我改用解析公式,速度提升了近10倍。所以,能用解析解就别用数值解,这是性能优化的铁律。
4.3 统计函数
统计函数在仿真分析中用来评估性能。比如,分析电机转速的波动、计算轨迹跟踪误差的均值方差等。
data = np.array([2.1, 2.3, 2.0, 2.4, 2.2, 2.5, 2.1])
# 基本统计量
mean = np.mean(data) # 均值
std = np.std(data) # 标准差
max_val = np.max(data) # 最大值
min_val = np.min(data) # 最小值
# 百分位数——我常用它来评估控制精度
p50 = np.percentile(data, 50) # 中位数
p95 = np.percentile(data, 95) # 95%分位点
print(f"均值: {mean:.3f}, 标准差: {std:.3f}")
print(f"95%误差在: {p95:.3f} 以内")
实用技巧:在运动控制仿真中,我习惯用 np.percentile(data, [5, 95]) 来评估误差的分布范围。这比单纯看最大值更靠谱,因为最大值可能只是某个瞬间的毛刺。
4.4 随机数生成
随机数在仿真中用来模拟噪声、初始化参数、做蒙特卡洛分析。NumPy的随机模块 np.random 功能很全。
# 设置种子,保证结果可复现
np.random.seed(42)
# 均匀分布 [0, 1)
rand_uniform = np.random.rand(3, 3)
# 正态分布
rand_normal = np.random.randn(1000) # 均值0,标准差1
# 整数随机
rand_int = np.random.randint(0, 10, size=(2, 3))
# 打乱数组——做数据增强时常用
arr = np.array([1, 2, 3, 4, 5])
np.random.shuffle(arr)
你想想看,做运动控制仿真时,传感器噪声怎么模拟?我一般用 np.random.normal(0, 0.01, size=data.shape) 来生成高斯白噪声,然后叠加到真实信号上。这样仿真结果才贴近实际。
注意:每次运行前设置 np.random.seed() 是个好习惯。否则两次运行结果不同,你根本没法判断是算法改进带来的提升,还是随机性导致的波动。我早期吃过这个亏,后来就养成了固定种子的习惯。
知识体系总览
下面这张图,是我梳理的NumPy核心操作知识结构。你可以把它当作一个快速索引图,用到哪个知识点,回来查一下就行。
好了,这一节的内容就这些。数组创建是基础,矩阵运算是核心,统计函数帮你分析数据,随机数生成让仿真更真实。这四个模块,你在运动控制仿真中几乎每天都会用到。
我个人建议,学完这一节后,马上打开Jupyter Notebook,把上面的代码敲一遍。光看不练,等于白学。你想想看,代码这东西,敲一遍和看十遍,效果天差地别。