先说结论:Stewart平台这东西,听着像是实验室里才碰得到的精密机构,但实际上它的身影早就在飞行模拟器、并联机床、射电望远镜、汽车测试台架里转悠了。我最早接触它是在做六自由度运动模拟台的预研,当时手里只有MATLAB和一个SolidWorks模型,硬是靠着MATLAB把运动学仿真跑通了。今天这篇就把我踩过的坑、验证过的代码思路、还有最容易卡住人的细节全部摊开讲,希望能让想入门或正在调参的你少走几周弯路。
这篇文章适合三类人:刚接触并联机构、被正解困扰的学生;需要用MATLAB快速验证Stewart平台方案可行性的工程师;以及想在Simulink/Simscape里把运动学和控制打通的人。我不讲晦涩的张量推导,只讲能直接上手的建模流程、逆解代码、可视化方法,以及那些文档里查不到但实操中一定会遇到的坑。
我把整个仿真拆成三层:第一层是运动学模型,解决“给定上平台位姿,六根杆该伸长多少”;第二层是动力学与控制接口,解决“怎么让平台按轨迹动起来”;第三层是可视化与验证,解决“仿真结果到底靠不靠谱”。这三层弄清楚,再回头看那些花哨的控制算法,会觉得地基稳了很多。
1. 先把Stewart平台讲明白:这东西是什么,仿真到底在仿什么
1.1 一个会动的六条腿
Stewart平台最简单的描述是:一个下平台(基座)、一个上平台(运动平台),中间用六根可伸缩的支腿连接,每根支腿两端各有一个铰链——下端通常是万向铰或球铰,上端也是球铰。靠改变六根杆的长度,就能让上平台在空间里做六个自由度的运动:沿X、Y、Z轴的平移,加上绕这三个轴的转动(横滚、俯仰、偏航)。
这个结构最迷人的地方在于,它不像串联机械臂那样把误差逐级放大,而是六根腿共同分担载荷,刚度和精度都能做到很高。代价就是运动学关系复杂——你让某根腿伸长一点,上平台不只是上下动,还会带着倾斜和旋转。这就决定了仿真的核心目标:搞清楚“腿长变化”和“平台位姿变化”之间的映射关系。
1.2 为什么大家选MATLAB做并联平台仿真
我见过不少用Adams或者ANSYS做并联机构分析的人,它们在做动力学和结构强度分析上确实强,但说到快速验证运动学算法、调试控制策略,MATLAB的优势太明显了:矩阵运算利落,绘图和动画方便,Simulink里搭闭环控制只要拖几个模块。最关键的是,MATLAB的代码写出来几乎是伪代码级别的可读性,公式长什么样,代码就长什么样。
举个例子,位置逆解的核心公式就那么几行矩阵运算,在MATLAB里可以直接按数学表达式的思路写,不需要像C++那样考虑内存和指针。对于做算法验证和方案论证阶段的工作,这效率高得不是一点半点。我当时的做法是先在MATLAB里把逆解、正解、工作空间全部跑通,再用MATLAB Coder转成C++交给下位机,整个过程衔接很顺。
有人可能会问,为什么不直接用Simulink里的Simscape Multibody搭一个三维模型?我的回答是:可以,而且我后面会讲,但前提是你得先用手写的运动学代码验证模型的正确性。否则你拖了一堆模块进去,结果运动不对,你根本分不清是模型搭错了还是控制算法错了。
1.3 仿真之前,先弄清楚你要仿什么
很多新手上来就问“怎么仿真Stewart平台”,但这个问题太大。我得先帮你理清层次:
- 运动学仿真:给定位姿轨迹,算出六根杆的长度变化曲线,或者反过来给定杆长,求解平台位姿。这是基础。
- 动力学仿真:考虑质量、惯性、驱动力,看电机需要输出多大的力/力矩,杆件受多大的力。这通常要借助Simscape或ADAMS。
- 控制仿真:加入PID、滑模、自适应等控制算法,让平台实际运动跟上目标轨迹。这时候Simulink闭环就是主角。
- 工作空间分析:扫描平台能到达的空间范围,判断铰链设计和杆长行程是否满足需求。
这篇文章主要覆盖第一层和第三层的基础部分,因为这是大多数起步项目的核心痛点。动力学部分我会做一个简单的Simscape引入,够用就行。
2. 运动学建模:位置逆解是整个仿真的地基
2.1 坐标系、铰点坐标和关键参数
建模第一步是定义坐标系。我的习惯是:
- 固定坐标系(基座坐标系){B}:原点在下平台几何中心,Z轴竖直向上。
- 动坐标系(平台坐标系){P}:原点在上平台几何中心,初始时刻与{B}重合,随平台运动。
然后需要用几何参数把六根腿的位置描述清楚。这里有一个大多数教程不会强调的坑:上下平台的铰点不是随便均匀分布的,而是成对布置的。常见的布局是六个铰点分布在圆周上,但相邻两个铰点之间夹角通常有“窄角对”和“宽角对”交替出现。比如上平台铰点角度为0°、60°、120°、180°、240°、300°,但相邻铰点对的角度间隔是30°和90°交替。这样做是为了避免奇异位形、提升刚度均匀性。
我用过的一组典型几何参数:
| 参数 | 符号 | 数值 | 说明 |
|---|---|---|---|
| 下平台铰点分布半径 | R_b | 0.5 m | 基座铰接点所在圆半径 |
| 上平台铰点分布半径 | R_p | 0.35 m | 平台铰接点所在圆半径 |
| 下平台短边对应夹角 | θ_b | 30° | 每对下铰点之间的窄角 |
| 上平台短边对应夹角 | θ_p | 30° | 每对上铰点之间的窄角 |
| 初始平台高度 | h | 1.0 m | 上下平台原点距离 |
| 杆长范围 | L | [0.85, 1.2] m | 根据行程确定 |
这里解释一下为什么上平台半径要比下平台小:一是为了获得更大的倾斜能力,二是避免杆件在运动中发生干涉。至于短边夹角为什么取30°,这是工程上相当常见的取值,太大或太小都会让工作空间变得畸形。
2.2 位置逆解:给定位姿求杆长
所谓位置逆解,就是已知上平台在{B}中的位置向量t= [x, y, z]^T和姿态角(通常是ZYX欧拉角:偏航ψ、俯仰θ、横滚φ),求六根杆的长度。
计算流程如下:
根据三个欧拉角,写出旋转矩阵R。MATLAB里直接用角度转方向余弦矩阵就行,但不建议用欧拉角做内部持续运算,因为会遇到万向节锁问题。这里只做逆解展示,够用。
对于第i根腿(i=1...6),上铰点在动坐标系里的坐标是p_i(已知几何参数可算出),转换到固定坐标系:
P_i=R*p_i+t
下铰点在固定坐标系的坐标B_i恒定不变。杆长向量就是:
L_i=P_i-B_i
杆长就是该向量的模:L_i = ||L_i||。
整个过程零迭代、零非线性求解,这就是逆解迷人之处——不到十行核心代码就能搞定。这也意味着实时控制时,逆解可以极高频地刷新,非常适合做伺服控制。
2.3 正解为什么难,以及你需不需要它
正解正好反过来:已知六根杆长,求平台位姿。这需要求解一组非线性方程组,通常用牛顿-拉夫逊迭代或者数值优化来做。正解的难点在于收敛性:初值离真解远了会发散,而且方程组有多个解。
我个人的建议是:如果你主要做控制仿真,避重就轻地绕开实时正解。控制回路里用逆解就够了——你期望平台到某个位姿,逆向算出该给每根腿多长,然后用腿长做闭环。真正需要正解的场景通常是传感器只有杆长计(比如用编码器测六根腿的长度,再推算平台位姿),这时才需要跑正解算法。
我后来在做半实物仿真时遇到过一次正解需求,当时的做法是用上一时刻的位姿作为迭代初值,再用lsqnonlin求解,每步迭代不超过10次。这方法工程上稳得很,但绝对不适合用来给新手入门讲模型——容易把自己绕晕。
3. 姿态描述的核心:旋转矩阵、欧拉角与四元数
3.1 三者怎么选,会直接影响仿真结果
Stewart平台仿真里最容易出bug的不是矩阵运算,而是姿态描述方式不一致。我自己就栽过:在MATLAB里用欧拉角序列算出的旋转矩阵,和Simulink里姿态模块默认的旋转矩阵不一致,结果平台乱转一通,查了一下午才发现是旋转顺序定义搞混了。
工程上常用三种姿态描述:
- 欧拉角(ZYX/ZYZ等):直观,但存在万向节锁,且不同行业对旋转顺序的约定不同。航空上常用ZYX,机器人领域常见ZYZ。写代码之前,先想清楚自己用的是哪个序列。
- 旋转矩阵:计算简单直接,但9个元素有冗余,不适合插值和长期积分。
- 四元数:无万向节锁,插值平滑,适合控制回路和滤波。缺点是没那么直观。
我的建议:人机交互和轨迹规划用欧拉角,数值计算内部通通用四元数或旋转矩阵。具体到MATLAB,我推荐把旋转矩阵作为“通用语言”,欧拉角只在输入和显示时转换一次。
3.2 用旋转矩阵来建模,代码怎么写
给定欧拉角,我用的是ZYX顺序,即先绕Z轴旋转(偏航),再绕新的Y轴旋转(俯仰),最后绕新的X轴旋转(横滚)。旋转矩阵公式如下:
R = Rz(ψ) * Ry(θ) * Rx(φ)
MATLAB里可以直接用angle2dcm函数,注意它返回的是一个方向的旋转矩阵,要和你的坐标变换方向匹配:
% 欧拉角转旋转矩阵 (ZYX顺序) psi = 10 * pi/180; % 偏航 theta = -5 * pi/180; % 俯仰 phi = 3 * pi/180; % 横滚 R = angle2dcm([psi theta phi], 'ZYX'); % 这是“向量旋转”方式的dcm另一个常用函数是rotz、roty、rotx,它们构造的是基本旋转矩阵,用它们手动乘起来会更看得懂:
R = rotz(psi) * roty(theta) * rotx(phi);坑点提醒:angle2dcm和手写的rotz*roty*rotx在某些MATLAB版本里返回的矩阵方向是相反的(一个是坐标系旋转,一个是向量旋转)。做验证的时候,我强烈建议先用一个已知位姿(比如只绕X轴转30°)去检查矩阵是否正确,不要直接开跑全姿态。
3.3 给个能直接用的小函数
我封装了一个函数用于转换欧拉角到旋转矩阵,顺手还加了输入检查:
function R = eulerZYX2Rot(phi, theta, psi) % 输入:横滚phi(rad)、俯仰theta(rad)、偏航psi(rad) Rz = [cos(psi) -sin(psi) 0; sin(psi) cos(psi) 0; 0 0 1]; Ry = [cos(theta) 0 sin(theta); 0 1 0; -sin(theta) 0 cos(theta)]; Rx = [1 0 0; 0 cos(phi) -sin(phi); 0 sin(phi) cos(phi)]; R = Rz * Ry * Rx; end到这里,你已经有了姿态描述的“标准件”。接下来把它塞进逆解函数里,整个Stewart运动学模型的核心就跑起来了。
4. 把逆解写成代码:MATLAB实现全流程
4.1 主程序结构设计
我写Stewart仿真程序时,习惯把功能模块化到极致,因为后面要对接Simulink和不同的轨迹规划器。主程序逻辑大致如下:
- 初始化几何参数(上下平台铰点坐标、杆长范围、初始高度)。
- 定义目标轨迹(时间序列上的位姿)。
- 对轨迹的每个采样点调逆解函数,算出六根杆长。
- 做杆长范围检查,判断轨迹是否超出伸缩范围。
- 绘图/动画展示。
这样设计的好处是:你可以随时替换轨迹生成器,或者替换逆解模块,而不影响其他代码。这也是我后来把代码从验证阶段顺利过渡到实时控制阶段的原因之一。
% ========== 初始化 ========== clear; clc; close all; R_b = 0.5; R_p = 0.35; theta_b = 30*pi/180; theta_p = 30*pi/180; h0 = 1.0; % 生成上下铰点坐标 [B, P0] = StewartHingePoints(R_b, R_p, theta_b, theta_p, h0); % ========== 生成轨迹 ========== t = linspace(0, 10, 1000)'; z_traj = h0 + 0.1 * sin(0.5 * t); % 上下正弦运动 phi_traj = 5 * pi/180 * sin(0.3 * t); % 横滚小角度摆动 % ========== 逆解 ========== L_hist = zeros(length(t), 6); for k = 1:length(t) [L, ~] = StewartInverseKinematics(B, P0, ... [0 0 z_traj(k)]', [phi_traj(k) 0 0]'); L_hist(k, :) = L'; end % ========== 杆长检查 ========== L_min = 0.85; L_max = 1.2; if any(L_hist(:) < L_min) || any(L_hist(:) > L_max) warning('存在杆长超出行程范围,请检查轨迹参数!'); end % ========== 绘制 ========== figure; plot(t, L_hist, 'LineWidth', 1.5); xlabel('时间 (s)'); ylabel('杆长 (m)'); legend('L1','L2','L3','L4','L5','L6'); grid on;4.2 铰点坐标生成函数
上平台铰点在动坐标系里的坐标,以及下平台铰点在固定坐标系里的坐标,都需要提前算好。这里的核心就是角度循环加上成对布置。
function [B, P0] = StewartHingePoints(R_b, R_p, theta_b, theta_p, h0) % 下平台铰点:固定坐标系 B = zeros(6, 3); for i = 0:2 base_angle = i * 2*pi/3; % 三组均布,间隔120° angle1 = base_angle - theta_b/2; angle2 = base_angle + theta_b/2; B(2*i+1, :) = [R_b*cos(angle1), R_b*sin(angle1), 0]; B(2*i+2, :) = [R_b*cos(angle2), R_b*sin(angle2), 0]; end % 上平台铰点:动坐标系(初始与固定系平行,高度h0) P0 = zeros(6, 3); for i = 0:2 base_angle = i * 2*pi/3 + pi/3; % 让上下铰点错开一个夹角,改善奇异性 angle1 = base_angle - theta_p/2; angle2 = base_angle + theta_p/2; P0(2*i+1, :) = [R_p*cos(angle1), R_p*sin(angle1), h0]; P0(2*i+2, :) = [R_p*cos(angle2), R_p*sin(angle2), h0]; end end这里有一个我特别提醒自己的点:上下铰点必须错开相位。如果上下铰点完全对应,平台在某些姿态下会直接掉进奇异位形——杆力趋近无穷、机构卡死。我的代码里把上平台的基角偏移了60°,工程上常见。
4.3 逆解核心函数
function [L, P_global] = StewartInverseKinematics(B, P0, t, euler_angles) % 输入: % B - 6x3 下铰点坐标矩阵 % P0 - 6x3 上铰点初始坐标(动坐标系) % t - 3x1 上平台原点在固定系中的位置 [x,y,z] % euler_angles - 3x1 [phi, theta, psi] 旋转角 % 输出: % L - 6x1 杆长向量 % P_global - 6x3 上铰点在固定系中的坐标 R = eulerZYX2Rot(euler_angles(1), euler_angles(2), euler_angles(3)); P_global = (R * P0')'; % 旋转后加上平移 P_global = P_global + repmat(t', 6, 1); L_vec = P_global - B; % 6x3,每条腿的杆向量 L = sqrt(sum(L_vec.^2, 2)); % 取模得到杆长 end一共不到10行,位置逆解就完成了。要验证这段代码对不对,有个土办法:给初始位姿t=[0,0,1.0],欧拉角全是0,算出来的六根杆长应该完全一样。如果不一致,说明铰点坐标或者上平台初始高度没对好。
4.4 雅可比矩阵:速度和力的桥梁
如果只想做位置分析,逆解就够了。但做控制的时候,你得知道“杆长变化速度”和“平台运动速度”之间的关系,又想知道每条腿该输出多大的力来抵抗负载,这两件事都要用到雅可比矩阵。
对于Stewart平台,雅可比矩阵J把上平台速度旋量v和杆长变化速度dL/dt联系起来:dL/dt = J * v。在MATLAB里,最稳妥的做法是数值雅可比——用微扰法,给位姿加一个小增量,重算杆长,差商就是雅可比的一列。这个方法代码量小、不容易错,适合验证阶段使用。
function J = StewartJacobianNumeric(B, P0, t, euler_angles, delta) % 数值雅可比:dL = J * [vx vy vz wx wy wz]' if nargin < 5, delta = 1e-6; end J = zeros(6, 6); % 位置扰动 for j = 1:3 tp = t; tm = t; tp(j) = tp(j) + delta; tm(j) = tm(j) - delta; Lp = StewartInverseKinematics(B, P0, tp, euler_angles); Lm = StewartInverseKinematics(B, P0, tm, euler_angles); J(:, j) = (Lp - Lm) / (2 * delta); end % 姿态扰动(近似用角速度扰动) for j = 1:3 ep = euler_angles; em = euler_angles; ep(j) = ep(j) + delta; em(j) = em(j) - delta; Lp = StewartInverseKinematics(B, P0, t, ep); Lm = StewartInverseKinematics(B, P0, t, em); J(:, j + 3) = (Lp - Lm) / (2 * delta); end end这段代码的速度可能不是最优,但正确性容易验证。等你把控制器跑通,再回头优化成解析雅可比也不迟。这就是我常说的“先让它对,再让它快”。
5. 把模型动起来:Simulink与Simscape联合仿真
5.1 Simscape Multibody的价值:验证运动学代码的“标尺”
手写运动学代码有个隐患:构建铰点坐标时算错一个角度,逆解结果会错得无声无息。这时候用Simscape Multibody搭一个机构模型,导入杆件三维几何和铰链,直接驱动六根杆,看平台运动是否符合预期,是最好的验证手段。
用Simscape搭Stewart平台的主要步骤:
- 在Simulink里拖入
Simscape Multibody库的Rigid Transform和Solid模块,分别建立基座、上平台、六根支腿。 - 铰链用
Spherical Joint模拟球铰,注意上下铰链配对不同。 - 支腿用
Prismatic Joint(移动副)+Actuation设置为位移输入,这样你可以把逆解算出来的杆长信号直接给到移动副。 - 在平台本体上加一个
Transform Sensor,测量上平台的实际位姿。
这里最大的坑是方向余弦矩阵和Simscape内部姿态约定不一致。Simscape的旋转模块需要的是“旋转矩阵”或“四元数”,而逆解模块输出的是欧拉角,中间必须转换。我用的是四元数作为接口,先把欧拉角转成四元数,再喂给Simscape。
5.2 轨迹跟踪仿真:闭环控制怎么搭
运动学仿真验证通过后,下一步就是加控制闭环。以最简单的位姿PD控制为例:
- 给定期望位姿(轨迹生成器输出)。
- 逆解算出期望杆长。
- 与当前实际杆长(Simscape里测量得到)做差。
- PD控制器输出驱动信号,作用在移动副上。
在Simulink里搭起来就是一套很清晰的信号流。这里我想多说一句:并联平台的动平台位姿控制,“解耦”是个巨大的坑。六根腿看起来是独立的,但平台上任何方向的运动会牵动所有腿,所以如果你只看单腿误差做控制,会出现严重的相互干扰。一个有效的做法是:在任务空间(位姿空间)里做PD控制,得到期望加速度后折算到腿空间。这个思路可以先用MATLAB脚本验证,再移植到Simulink。
5.3 动力学仿真能告诉你什么
加上了质量、惯量、重力之后,仿真就能输出每根腿的驱动力曲线。在设计选型阶段,我用它来做两件事:一是验证电机峰值扭矩是否够,二是看机构在高速运动时有没有冲击。用Adams当然更专业,但Simscape Multibody的好处是和MATLAB控制代码无缝衔接,改参数方便。
搭建动力学模型时,质量参数别拍脑袋。我把上平台和负载折算成一个等效刚体,重心放在平台上表面以上某处——这个细节对仿真结果影响很大,因为重心越高,平台在倾斜时需要的力矩越大。
6. 常见问题与排查技巧实录
6.1 问题速查表
| 现象 | 可能原因 | 排查方法 | 解决办法 |
|---|---|---|---|
| 六根杆长初始不相等 | 铰点错开角没设置,或上平台高度没对齐 | 检查几何参数、打印铰点坐标 | 让上平台基角偏移60°,核对初始高度 |
| 平台动画出现穿透 | 杆件几何模型与运动学模型不一致 | 检查铰链连接点坐标 | Simscape里用Rigid Transform逐一核对 |
| 轨迹后半段杆长超出范围 | 目标轨迹超出工作空间 | 用逆解批跑并扫描杆长极值 | 缩小振幅或调整初始高度 |
| 控制发散、杆长剧烈震荡 | PD参数过大,或控制信号没有限幅 | 加信号限幅、降低增益 | 从P很小开始调,逐步增大 |
| 逆解速度不够快(实时控制) | 用了符号计算或大量循环 | 用tic/toc测单次逆解耗时 | 向量化计算,或改用MATLAB Coder生成C代码 |
| 欧拉角在±90°俯仰附近乱跳 | 万向节锁问题 | 打印欧拉角序列检查跳变 | 内部换四元数,只在接口处用欧拉角 |
6.2 工作空间扫描:避免调参靠玄学
有个很好用的经验:在跑正式轨迹前,先把目标工作空间扫一遍。具体做法是在上平台高度方向上取若干层,每层用网格扫描X、Y位置,再叠加一个姿态范围,逐点做逆解并检查六根杆是否超出行程。这样能得到一张“可达域”图。
我写过一次扫描程序,跑了大约10万个位姿点,耗时不到两分钟,但换来了极其宝贵的设计结论:某个平台的横滚能力被杆长行程卡在±18°,而不是设计者拍脑袋想的±25°。类似的问题,用仿真提前暴露出来,省下的改版成本是巨大的。
6.3 我的调试顺序建议
最后分享一个我个人觉得特别实用的调试顺序,它帮我避开了大量“葫芦娃救爷爷”式的低级错误:
- 先静态后动态:初始位姿下,逆解输出是否合理?先让平台不带轨迹跑。
- 先单自由度后六自由度:先只做Z轴上下运动,确认六根杆同步伸缩;再单独做横滚,确认杆长变化有规律。
- 先开环后闭环:用逆解信号直接驱动Simscape模型,看开环跟踪是否准确;确认无误再上控制器。
- 先低增益后高增益:调PD参数时,先给一个很小比例增益,确认闭环稳定,再逐步提增益,找到临界值。
踩过几次坑之后我最大的体会是:Stewart平台仿真的大多数问题都出在几何参数和坐标系定义上,而不是算法本身。把铰点坐标、旋转顺序、正反解定义这三件事在一开始就固化下来,后面会顺畅很多。
这趟仿真做下来,我最大的感受是:MATLAB真正的优势不在某个具体函数多强大,而在于从运动学建模、控制调试到可视化验证的闭环足够短。你可以在一个环境里快速迭代,等到方案成熟再考虑工程化。这种“先快速验证再投入成本”的工作方式,对于并联机构这种复杂系统尤其重要。希望这篇文章能帮你少踩几个坑,早点把自己平台的仿真跑起来。