2. MATLAB基础速通:矩阵运算、脚本与函数、绘图基础、数据类型与结构体

各位同学,欢迎来到第二章。说实话,很多初学者一上来就啃飞控算法,结果连MATLAB的基本操作都磕磕绊绊,最后代码跑不通,心态先崩了。我个人习惯是:先把工具箱的底子打牢,后面建模才顺手。这一章,我们就用最快的速度,把MATLAB最核心的几个技能点过一遍。

MATLAB 基础速通 矩阵运算 脚本与函数 绘图基础 数据类型与结构体 创建 运算 2D 3D 结构体 元胞

2.1 矩阵运算:MATLAB的看家本领

MATLAB全名叫Matrix Laboratory,说白了就是为矩阵而生的。你在飞控里看到的姿态矩阵、旋转矩阵、协方差矩阵,本质上都是矩阵。所以这一关必须过。

2.1.1 矩阵的创建

创建矩阵最简单的方式,就是直接敲。我刚开始学的时候,总觉得这玩意儿跟C语言数组差不多,但用起来才发现——真香。

% 直接输入
A = [1, 2, 3; 4, 5, 6; 7, 8, 9];

% 常用快捷函数
zeros(3,4)      % 全零矩阵
ones(2,5)       % 全一矩阵
eye(4)          % 单位矩阵
rand(3,3)       % 随机矩阵(均匀分布)
linspace(0,10,5) % 从0到10均匀取5个点
小技巧: 我在项目中经常用 linspace 生成时间序列,比如仿真步长0.01秒,跑10秒,就是 t = linspace(0,10,1001)。比手动写循环快多了。

2.1.2 矩阵运算的核心操作

这里有个坑,我必须要讲清楚。MATLAB里 *.* 是两码事。前者是矩阵乘法,后者是对应元素相乘。你想想看,飞控里你要做坐标变换,用的是矩阵乘法;你要做增益缩放,用的是点乘。搞混了,结果全错。

% 矩阵乘法(线性代数意义)
A = [1,2; 3,4];
B = [5,6; 7,8];
C = A * B;  % 结果是 [19,22; 43,50]

% 对应元素相乘(点乘)
D = A .* B; % 结果是 [5,12; 21,32]

% 转置
A_trans = A';  % 单引号就是转置

% 求逆
A_inv = inv(A);

% 特征值与特征向量(飞控模态分析常用)
[V, D] = eig(A);
避坑指南: 我曾经在写卡尔曼滤波时,把协方差矩阵更新里的点乘写成了矩阵乘,结果滤波结果直接发散。排查了整整一个下午才发现是符号问题。记住:矩阵乘法用 *,元素运算用 .*

2.2 脚本与函数:让代码活起来

写飞控算法,你不能把所有代码都堆在一个文件里。那样调试起来,你会想砸电脑的。脚本和函数,就是帮你把代码组织起来的工具。

2.2.1 脚本文件(.m)

脚本就是按顺序执行的一堆命令。适合做一次性计算或者快速验证。我个人习惯是:做原型验证时用脚本,因为改起来快。

% 文件名:test_angle.m
% 一个简单的角度转换脚本
deg = 45;
rad = deg * pi / 180;
fprintf('%d 度 = %.4f 弧度\n', deg, rad);

2.2.2 函数文件

函数就不一样了。它有输入输出,有独立的工作空间。你想想看,飞控里的姿态解算、控制律计算,每个模块都应该是一个函数。这样你改一个模块,不会影响到其他部分。

% 文件名:deg2rad_custom.m
function rad = deg2rad_custom(deg)
    % 角度转弧度
    % 输入:deg - 角度值(标量或向量)
    % 输出:rad - 弧度值
    rad = deg * pi / 180;
end

% 调用方式
result = deg2rad_custom(45);
关键区别: 脚本里的变量会留在工作区,函数里的变量是局部的。我建议:凡是需要重复使用的代码,一律写成函数。这是专业工程师的习惯。

2.3 绘图基础:用眼睛看数据

做飞控调试,光看数字是看不出问题的。你得把数据画出来。比如姿态角的变化曲线、控制输出的响应、传感器的噪声特性——这些都得靠图。

2.3.1 二维绘图

plot 函数是最常用的。嗯,这里要注意:画图之前,先想清楚横轴是什么,纵轴是什么。

% 画一条正弦曲线
t = 0:0.01:2*pi;
y = sin(t);
plot(t, y, 'b-', 'LineWidth', 2);
xlabel('时间 (s)');
ylabel('幅值');
title('正弦波');
grid on;  % 加网格,方便读数

2.3.2 多图与子图

飞控里经常要同时看多个信号,比如同时看滚转、俯仰、偏航角。用 subplot 可以在一张图里放多个子图。

% 子图示例
t = 0:0.01:10;
roll = sin(0.5*t);
pitch = cos(0.3*t);
yaw = sin(0.8*t + pi/4);

subplot(3,1,1);
plot(t, roll); ylabel('滚转角');

subplot(3,1,2);
plot(t, pitch); ylabel('俯仰角');

subplot(3,1,3);
plot(t, yaw); ylabel('偏航角');
xlabel('时间 (s)');
个人经验: 我习惯在画图时加上 grid onlegend。有一次给客户看仿真结果,图里没加图例,客户问哪条线是期望值、哪条是实际值——场面一度很尴尬。

2.4 数据类型与结构体:管理复杂数据

飞控系统里数据种类很多:传感器数据、状态估计值、控制指令、参数配置……如果都用单独的变量,代码会乱成一锅粥。这时候就需要结构体和元胞数组了。

2.4.1 数值类型

MATLAB默认是double类型,也就是双精度浮点数。做飞控仿真,double完全够用。但如果你要部署到嵌入式平台,就得考虑单精度甚至定点数了。

数据类型说明飞控应用场景
double双精度浮点(64位)仿真、离线分析
single单精度浮点(32位)嵌入式实时计算
int8/int16/int32有符号整数传感器原始数据
logical逻辑值(true/false)状态标志、模式切换

2.4.2 结构体(struct)

结构体就像是一个文件夹,可以把相关的数据打包在一起。我在写飞控代码时,会把所有的传感器数据放在一个结构体里,所有的控制参数放在另一个结构体里。

% 定义飞控参数结构体
fc_param = struct();
fc_param.Kp = 2.5;      % 比例增益
fc_param.Ki = 0.1;      % 积分增益
fc_param.Kd = 0.05;     % 微分增益
fc_param.dt = 0.01;     % 控制周期(秒)
fc_param.enable = true; % 使能标志

% 访问结构体字段
disp(fc_param.Kp);  % 输出 2.5

2.4.3 元胞数组(cell array)

元胞数组可以存放不同类型的数据。比如你要存一组不同长度的传感器数据序列,用元胞数组就很方便。

% 元胞数组示例
data = cell(3,1);
data{1} = [1,2,3,4];        % 向量
data{2} = 'IMU数据';        % 字符串
data{3} = struct('x',1,'y',2); % 结构体

% 访问元胞内容
disp(data{1});  % 输出 1 2 3 4
注意: 元胞数组用花括号 {} 访问内容,圆括号 () 访问的是元胞本身。这个区别我刚开始也搞混过,后来养成了习惯:取内容用花括号,取子集用圆括号。

好了,这一章的内容就到这里。矩阵运算是基础中的基础,脚本和函数帮你组织代码,绘图让你看得见数据,结构体和元胞数组帮你管理复杂信息。把这些练熟了,后面学飞控建模就会顺畅很多。