1. 从一次翻车实测说起:轮式里程计为什么会骗我
去年年中我接手了一个AGV底盘项目,四轮麦克纳姆轮布局,电机编码器精度也不差,理论上轮子转多少圈、车就走多远,这套逻辑在PPT上完美无缺。结果第一次跑直线测试就翻车了——我下发指令让车以0.5m/s的速度笔直前进两米,实际轨迹却向右偏了差不多15厘米。第一反应是四个轮子的转速不一致,于是我把每路电机的编码器数据全部打出来对比,结果四个轮子的转速误差都在0.5%以内,这已经是很漂亮的调速表现了。那问题出在哪?答案是轮子在地面上发生了微小滑移,而编码器只认轮子转了多少圈,它根本不关心轮子是不是真的“抓”住了地面。轮胎磨损、地面摩擦系数不均匀、加速瞬间的轻微打滑,这些误差在短距离内不起眼,但累积起来足以让一台看起来控制精准的底盘变成一个乱跑的莽夫。
这个实测结果让我对“轮式里程计够用”这句话产生了严重怀疑。纯靠编码器推算位置本质上是一个开环积分过程,它的前提假设是“轮子与地面之间全程纯滚动、无滑动”,但现实世界几乎不存在这样的地面。相比之下,惯性导航解算利用加速度计和陀螺仪测量载体自身的运动状态,不需要依赖外部环境接触,它是一种完全独立的定位信息来源。虽然惯导也存在误差,但它的误差特性和轮式里程计完全不同,两者互补之后的效果远远好于任何单一方。所以这个项目的核心结论从一开始就定了:不能只靠轮子,必须把惯性导航解算加进系统里。
后面我把整个调试过程梳理了一遍,发现“惯性导航”这个词听起来高大上,但落地时真正卡住人的往往不是算法本身,而是坐标系混乱、数据没调理干净、以及不知道惯导和底盘运动学之间到底谁该信谁。这篇文章就按我实际踩坑的顺序,把姿态解算、位置解算、以及与麦克纳姆轮运动学解算的融合过程完整写出来,希望能帮正在做类似底盘或机器人定位的朋友省掉几个月的弯路。
2. 姿态解算的地基:四元数、欧拉角与坐标系约定
2.1 为什么我一开始用欧拉角差点把自己绕晕
我最早接触惯性导航时,脑子里第一个概念就是欧拉角——俯仰角、横滚角、偏航角,多直观,三个角度就能描述物体姿态。MPU6050这种六轴传感器又自带DMP库,直接输出欧拉角,拿来就能用。但实际做解算时,欧拉角最大的坑是万向锁:当俯仰角接近±90°时,横滚和偏航的旋转轴重合,系统会丢失一个自由度,角度解算会剧烈跳变。轮式机器人虽然很少真的垂直抬头,但AGV在坡道、颠簸路面上偶尔会触发接近90°的姿态,一旦出现万向锁,整个姿态解算就会瞬间崩掉。
更麻烦的是欧拉角的姿态更新依赖三角函数的反复计算,不仅效率低,而且在连续旋转时角度插值会产生不连续跳变。所以在这个项目里我彻底放弃用欧拉角做姿态表示,只在最后输出给用户看的时候才把四元数转成欧拉角。
2.2 四元数运算:从数学符号到代码实现
四元数本质上是一个四维复数扩展,形式上写成
$$q = w + xi + yj + zk$$
其中w是实部,x、y、z是虚部,并且满足i²=j²=k²=ijk=-1。对姿态解算而言,四元数描述的是从导航坐标系到载体坐标系的旋转,归一化后满足w²+x²+y²+z²=1。四元数最让我喜欢的一点是它没有奇异性,任何姿态都能用唯一的单位四元数表示,更新时只需要做乘法就行。
核心的姿态更新方程是四元数微分方程:
$$ \dot{q} = \frac{1}{2} q \otimes \omega $$
其中ω是陀螺仪测得的角速度四元数形式,即(0, ωx, ωy, ωz)。在代码里,我用一阶龙格库塔法来离散更新:
void quaternionUpdate(Quat* q, float gx, float gy, float gz, float dt) { float q0 = q->w, q1 = q->x, q2 = q->y, q3 = q->z; // 角速度转换为四元数增量 float dq0 = 0.5f * (-q1*gx - q2*gy - q3*gz) * dt; float dq1 = 0.5f * ( q0*gx + q2*gz - q3*gy) * dt; float dq2 = 0.5f * ( q0*gy - q1*gz + q3*gx) * dt; float dq3 = 0.5f * ( q0*gz + q1*gy - q2*gx) * dt; // 更新四元数 q->w += dq0; q->x += dq1; q->y += dq2; q->z += dq3; // 归一化防止误差累积 float norm = sqrt(q->w*q->w + q->x*q->x + q->y*q->y + q->z*q->z); q->w /= norm; q->x /= norm; q->y /= norm; q->z /= norm; }很多初学者会忽略最后一步归一化,但这一步恰恰特别关键。陀螺仪数据有噪声和量化误差,如果长时间不归一化,四元数模长会逐渐偏离1,导致旋转矩阵不再是正交矩阵,姿态解算就会缓慢“飘走”。我一开始就是忘了归一化,结果发现静止时角度在以很慢的速度漂移,一度还以为是陀螺仪零偏问题,排查了半天。
2.3 坐标系约定:b系和n系,一个容易搞反的细节
姿态解算里最先要约定的是坐标系。我这里统一采用右前上坐标系作为载体坐标系(b系),即x轴指向载体右方,y轴指向前方,z轴垂直向上;导航坐标系(n系)则采用“东-北-天”(ENU),在地面测试时近似认为n系就是大地固定坐标系。
这个约定直接决定了旋转矩阵长什么样。从b系旋转到n系的旋转矩阵可以写成:
$$ C_b^n = \begin{bmatrix} 1-2(y^2+z^2) & 2(xy-wz) & 2(xz+wy) \ 2(xy+wz) & 1-2(x^2+z^2) & 2(yz-wx) \ 2(xz-wy) & 2(yz+wx) & 1-2(x^2+y^2) \end{bmatrix} $$
这段矩阵看起来长,但不要背,直接用四元数旋转公式推就行。实际编码时有现成库就直接调用,没有就照着这个矩阵实现。我建议大家一起手写一次这个矩阵,因为后面做重力消除和加速度坐标系变换时,都要反复用到它。一个小技巧是:单位四元数对应的旋转矩阵有一个快速记忆法,第一列是(1-2(y²+z²), 2(xy+wz), 2(xz-wy)),第二列、第三列按规律类推,写代码时逐个对照不容易出错。
3. MPU6050数据调理:零偏、标定与滤波
3.1 加速度计的静态标定:不标定后面全是空中楼阁
MPU6050出厂时虽然有一定校准,但焊接温度、贴片应力、安装姿态都会引入误差。我上一个项目就是直接拿未标定的加速度数据去做姿态解算,结果俯仰角静止时读数偏移了2度左右,这个偏差在解算位移时会带来灾难性后果——因为加速度做二次积分时,一个微小的常值偏差会导致位移随时间平方增长。
加速度计标定的思路很简单:把传感器在多个已知姿态下静止,采集数据,拟合出零偏和尺度因子。实际上我用的是一种更省事的六面静态标定法:把传感器分别朝上、朝下、朝前、朝后、朝左、朝右静止放置约10秒,采集每个面的加速度均值。理论上每个面有一个轴比重力g,另外两个轴接近0。根据这些数据可以解出三轴的零偏b_a和三轴尺度因子s_a,校正公式为:
$$ a_{cal} = s_a \cdot (a_{raw} - b_a) $$
我实际采集后的数据大致如下:
| 姿态 | Ax原始(m/s²) | Ay原始(m/s²) | Az原始(m/s²) |
|---|---|---|---|
| X轴朝上 | 9.72 | 0.08 | 0.12 |
| X轴朝下 | -9.85 | 0.05 | 0.09 |
| Y轴朝上 | 0.03 | 9.69 | -0.07 |
| Y轴朝下 | -0.04 | -9.92 | 0.06 |
| Z轴朝上 | -0.02 | 0.03 | 9.76 |
| Z轴朝下 | 0.05 | -0.04 | -9.88 |
由这些数据可以看出,X轴正方向有约-0.1g的偏差,Y轴和Z轴也有少量偏差。通过最小二乘拟合后得到标定参数,校正后的数据就非常接近理论值了。这里提醒一句:标定后一定要重新上电测试,因为有些零偏和温度、供电电压有关,环境变了需要重新标定。
3.2 陀螺仪零偏:静止时也在“转”的错觉
陀螺仪测量的是角速度,静止时理论上应该输出0,但实际会有一个稳定的偏置输出。比如我这里MPU6050在室温下静止时,gyro_z的平均值大约是16 LSB(对应约0.5°/s),如果直接拿这个数据去积分,一秒钟就会多出0.5°,一分钟偏航角就偏了30°。所以零偏估计是所有惯导解算的第一步。
最常用的零偏估计方法是静态采集后取平均:让设备水平静止放置至少20秒,采集N个样本,计算每个轴的均值作为零偏。注意采集时间不能太短,因为陀螺仪噪声是高频的,但均值收敛需要一定样本量。我一般采集1000个样本,大约20秒,然后对每个轴取平均。实际操作中还可以采集多组不同温度下的零偏,做温度补偿,但这个项目里我们底盘工作温度范围不算极端,直接用常温零偏就够用了。
一个常见的坑:陀螺仪在上电后的前几秒数据往往不稳定,因为内部振荡器还在起振,此时采集零偏会得到错误结果。所以零偏标定一定要在上电稳定后至少等2秒再开始采集。
标定完成后的零偏修正很简单:
gx_cal = gx_raw - gyro_zero_x;把这个修正后的角速度送入姿态更新,静止时积分出来的角度才能维持在0附近。
3.3 低通滤波与数据融合前的准备
MPU6050的原始数据噪声其实不小。加速度计的噪声还好,主要是高频抖动;陀螺仪的噪声虽小,但积分后会累积。我实测过,不滤波直接做姿态解算,静止时的偏航角抖动量大约在±0.8°,虽然勉强能用,但底盘在振动环境下会放大到±2°以上。所以数据预处理环节一定要加低通滤波。
最简单的是一阶低通滤波器,公式:
$$ y[n] = \alpha y[n-1] + (1-\alpha) x[n] $$
其中α=τ/(τ+Ts),τ是滤波时间常数,Ts是采样周期。对于100Hz采样,加速度计我取τ=0.1s,陀螺仪取τ=0.05s。注意滤波会引入相位延迟,所以加速度计和陀螺仪的时间常数要尽量匹配,否则后续融合时会出现“加速度计反应慢半拍”的现象。
我还尝试过滑动窗口滤波,但实际效果在姿态解算中不如一阶低通滤波平滑,而且会占用额外的内存,对MCU不友好。还有一种更高级的做法是用二阶巴特沃斯低通滤波器,但截止频率选取需要重新调参,对普通项目性价比不高。我最终选了一阶低通,代码只有三行:
float lowpass(float input, float prevOutput, float alpha) { return alpha * prevOutput + (1.0f - alpha) * input; }数据调理做完后,最直观的变化是:把MPU6050放在桌面上,用上位机观察加速度和角速度曲线,高频毛刺明显减少,静止时曲线基本是一条直线。到这里,数据质量才算满足姿态解算的输入要求。
4. 姿态解算三种主流算法的实测对比
4.1 互补滤波:为什么它是入门首选
互补滤波的核心思想特别朴素:陀螺仪动态响应快但会漂移,加速度计静态稳定但有噪声,在频域上二者正好互补。高通滤波器让陀螺仪的高频角速度信息通过,低通滤波器让加速度计的低频姿态信息通过,两者相加得到完整姿态。公式可以表达为:
$$ \theta = \alpha \cdot (\theta_{gyro}) + (1-\alpha) \cdot \theta_{acc} $$
其中θ_gyro通过对角速度积分得到,θ_acc通过加速度计反算得到。α一般取0.98左右,也就是98%信任陀螺仪、2%信任加速度计。
我用互补滤波跑起来后,静态姿态抖动大约±0.3°,动态跟随也基本没有延迟。它的优势是计算量极小、代码实现简单、参数调起来直观,对STM32这类MCU非常友好。缺点也明显:它本质上是频域加权平均,没有利用系统的状态模型,所以对一些剧烈运动或振动场景,精度上限不高。
4.2 卡尔曼滤波:性能更好,但调试成本高
卡尔曼滤波把姿态角当成系统状态,建立状态方程和观测方程。最简化的模型是以角度和陀螺仪零偏为状态向量:
$$ x = \begin{bmatrix} \theta \ bias \end{bmatrix} $$
状态转移方程是角度等于上一时刻角度加上(角速度减去零偏)乘以dt,观测方程是加速度计算出的角度。卡尔曼滤波的核心流程是预测与更新两个步骤交替进行,预测利用陀螺仪积分,更新利用加速度计的观测修正。它的优势是能显式估计陀螺仪零偏,并且在数据融合时能根据噪声方差动态调整权重。
不过卡尔曼滤波的调试比互补滤波麻烦不少。Q矩阵和R矩阵的取值直接影响结果,Q过大则滤波太“相信”陀螺仪,角度会漂移;Q过小则滤波太“相信”加速度计,角度噪声会变大。我调了一整天,最终在静止状态下卡尔曼滤波的输出稳定度是±0.15°,动态响应比互补滤波快一点,但R矩阵如果设得太激进,角度会有轻微过冲。如果项目对姿态精度要求不高,卡尔曼滤波带来的收益其实并不划算,性价比不如互补滤波。
4.3 Mahony算法:工程界最常用的“隐藏大佬”
Mahony算法是目前很多开源飞控和机器人项目里默认的姿态解算方案。它的本质是互补滤波的改进版本:通过比例积分控制器,把加速度计和陀螺仪之间的叉积误差反馈到陀螺仪角速度上,从而修正四元数。公式核心是:
$$ \omega_{correct} = \omega_{gyro} + K_p \cdot e_{error} + K_i \int e_{error} , dt $$
其中e_error是加速度计测得的重力方向与四元数推算出的重力方向之间的叉积误差。Kp越大,修正越快但噪声越大;Ki负责消除稳态误差。在MPU6050上跑Mahony,我把Kp设为2.0,Ki设为0.05,整体效果非常平稳,静态抖动±0.2°,动态响应也够快。
三种算法实装之后,我搞了一个简单对比,在同样的底盘上跑同样的动作,记录偏航角输出的标准差:
| 算法 | 静态偏航角标准差(°) | 动态最大偏差(°) | 计算耗时(us@100Hz) | 调试难度 |
|---|---|---|---|---|
| 互补滤波 | 0.32 | 3.5 | 12 | 低 |
| 卡尔曼滤波 | 0.15 | 2.1 | 210 | 高 |
| Mahony | 0.20 | 2.6 | 28 | 中 |
最终我选择Mahony作为主姿态解算方案,因为它综合表现最好,而且对MCU的算力需求非常低。如果你是在树莓派或Jetson这类高性能平台上跑,卡尔曼滤波的算力开销也无所谓,那追求精度就选卡尔曼;如果是在STM32F103这种小单片机里,我建议无脑上Mahony。
5. 从姿态到位移:位置解算与误差控制
5.1 加速度积分:最“简单”也最“简单粗暴”的环节
有了姿态后,位置解算的原理其实很简单:把载体坐标系下的加速度转换到导航坐标系,减去重力加速度,然后对时间做二次积分得到速度和位移。公式是:
$$ a_n = C_b^n \cdot a_b $$
$$ v(t) = \int_0^t (a_n(t) - g) , dt $$
$$ p(t) = \int_0^t v(t) , dt $$
看起来再清楚不过,但实现起来全是坑。第一次跑这个流程时,我把MPU6050放在桌面上静止不动,结果速度曲线在前5秒内漂到了0.3m/s,位移直接飞到了0.5米。问题出在两方面:一是加速度计的零偏虽然标定过,但仍有残余误差,二次积分把这些误差放大了;二是重力消除计算中,如果姿态角哪怕只有0.5°的误差,重力在水平方向上的分量就会产生约0.085m/s²的虚假加速度,这个值看起来不大,但积分后就是灾难。
我的解决方案是加一个静止检测:当角速度模长和加速度模长都低于阈值时,判定机器人处于静止状态,此时强制把速度清零。这个在轮式机器人上尤其好用,因为小车经常会启停,静止时机不少。阈值取角速度模长小于0.5°/s,加速度模长在9.0到10.0 m/s²之间。每次静止检测到后,就把积分速度置零,这样位移积分就从“纯二次积分”变成了“分段积分”,误差被限制在每一段运动区间内。
5.2 零速修正:让误差“归零”的魔法
零速修正(ZUPT)是行人惯导里常用的手段,但用在地面机器人类似。实现逻辑是:只要判断机器人静止,就认为实际速度为0,把计算速度硬清零。这样即使加速度计有零偏,它也只会在运动过程中累积误差,一旦静止就被“充值重置”了。
不过ZUPT有一个前提:静止检测必须足够可靠。如果检测阈值太宽,机器人还在缓慢移动时就被误判为静止,速度被清零,位置就会丢失;阈值太窄,静止状态检测不到,误差就会一直累积。我在底盘上实测,轮式机器人的加速度模长在静止时非常接近9.8 m/s²,但振动环境下会有小幅波动,所以我用了“连续N个样本满足条件”的判定逻辑,N取50帧(也就是500毫秒),这样既不会太灵敏也不会太迟钝。
5.3 位置解算的误差累积实验数据
我专门做过一个测试:让小车沿直线走20米,比较纯惯导位置解算、纯轮式里程计、以及惯导+ZUPT三种方案的位置误差。结果如下:
| 方案 | 终点误差(m) | 误差占比(%) | 最大瞬时偏差(m) |
|---|---|---|---|
| 纯惯导(无ZUPT) | 5.6 | 28% | 0.8 |
| 纯轮式里程计 | 0.42 | 2.1% | 0.15 |
| 惯导+ZUPT | 1.2 | 6% | 0.3 |
这个数据很能说明问题:纯惯导在20米级别的距离上误差巨大,根本不适用于长时间定位;但轮式里程计在平坦路面上表现不错,误差只有2.1%。然而一旦路面打滑或轮胎磨损,轮式里程计的误差会迅速飙升。所以最终结论不是“二选一”,而是“融合”:惯导提供姿态和短时运动趋势,轮式里程计提供长时位置参考,两者各取所长。
6. 与麦克纳姆轮运动学解算的融合实践
6.1 麦轮运动学正逆解:从轮速到底盘速度
麦克纳姆轮的优势是能实现全向移动:前后、左右、旋转可以同时进行。但前提是要把底盘速度分解成四个轮子的转速,这就是麦轮运动学逆解。假设底盘中心在载体坐标系下的速度是(vx, vy, ωz),四个轮子分别位于底盘的四个角,轮距一半为a,轴距一半为b,轮子半径为r,那么逆解公式为:
轮1(左前):ω1 = (vx - vy - (a+b)·ωz) / r 轮2(右前):ω2 = (vx + vy + (a+b)·ωz) / r 轮3(左后):ω3 = (vx + vy - (a+b)·ωz) / r 轮4(右后):ω4 = (vx - vy + (a+b)·ωz) / r反过来,如果已知四个轮速,也能解算出底盘的实际速度,这就是正解。实际上我更常用的是把惯导的姿态角与麦轮逆解结合:先根据目标速度生成轮速指令,然后通过惯导实时监测底盘的实际运动状态,计算二者差异并修正。
这里有个关键点:麦轮逆解是基于“底盘在水平面上运动”的假设,如果底盘在坡道上,重力分量会影响轮子的载荷和摩擦,单纯用逆解公式去控制会出现侧滑。这时惯导的姿态信息就很有用,比如检测到底盘俯仰角超过3°时,我会降低目标速度或切换到爬坡模式,避免车轮打滑导致运动学解算失真。
6.2 惯导数据和轮式里程计的融合策略
我最终采用的融合策略不是复杂的卡尔曼滤波,而是根据工况动态切换权重。整体思路如下:
- 在平坦路面上,轮式里程计的短时精度远高于惯导,所以位置输出以轮式里程计为主,惯导的姿态信息用于修正轮子打滑造成的偏差;
- 当检测到打滑(比如惯导解算的速度与轮速差值超过阈值)时,切换到以惯导速度为参考,直到轮速恢复到与惯导速度一致。
- 姿态角则始终以惯导的Mahony解算结果为准,因为轮子不打滑还好,一旦打滑,编码器推不出任何有用的角度信息。
阈值的选择需要实际测量。我在底盘上分别测试了空载和满载状态下的打滑特征,发现空载时惯导速度与轮速的差值通常在0.15m/s以内,满载时这个值会增大到0.3m/s。所以我将打滑检测阈值设为0.25m/s,连续触发超过100ms才判定为打滑,避免瞬时噪声造成误切换。
这个融合策略的代码结构大致是:
float posX_wheel, posY_wheel, posYaw_wheel; // 轮式里程计推算 float posX_imu, posY_imu; // 惯导推算 float velX_wheel, velY_wheel, velX_imu, velY_imu; if ( fabs(velX_wheel - velX_imu) > SLIP_THRESHOLD ) { slipFlag = true; posX = posX_imu; // 打滑时信任惯导 } else { slipFlag = false; posX = posX_wheel; // 正常时信任轮式里程计 }实测下来,这种简单策略比直接卡尔曼融合在工程上更好调试,因为更容易理解系统在什么状态下“更信谁”,出问题时也能一眼定位。
6.3 实战标定流程:从硬件安装到联调
融合效果的好坏,一半取决于算法,另一半取决于标定是否到位。我在项目总结时列了一个供自己复用的标定清单,分享出来:
- 传感器安装校准:MPU6050尽量安装在底盘几何中心,三个轴与底盘运动方向对齐,用水平尺校准安装平面,安装误差控制在0.5°以内。
- 加速度计六面标定:如第3章所述,采集六个静态面的数据,算出零偏和尺度因子。
- 陀螺仪零偏标定:静止20秒取均值,并在上电稳定后执行。
- 轮距和轴距测量:麦轮运动学逆解里的a和b会在很多场景用到,不要用产品手册给的公称值,要实际测量轮子接地点的距离,误差每1毫米都会导致旋转解算偏差。
- 轮半径标定:轮胎胎压不同会导致有效半径变化,我采用的办法是让小车走一段固定距离,根据编码器实际脉冲数反推有效轮半径,误差能控制在0.5%以内。
- 整体跑合验证:让小车走一个2mx2m的方形轨迹,检查终点误差,如果误差偏大,优先检查轮半径和轮距测量,而不是急着调算法参数。
整个融合系统跑通后,在平整地面上做2米直线往复运动,终点误差能控制在3厘米以内;在光滑瓷砖地面故意诱导打滑的场景里,融合方案的终点误差比纯轮式里程计低了约40%,这个提升对于AGV对接这样的场景足够了。
7. 几个值得注意的坑和我的最终建议
第一个坑是太早追求高精度算法。我刚上手时一头扎进卡尔曼滤波和因子图优化,结果基础的数据调理没做,姿态角一直在飘,位置解算完全没有参考价值。后来退回来老老实实做标定、滤波、Mahony解算,效果反而立竿见影。惯性导航这行有个经验,大部分精度问题不是算法不够先进,而是输入数据不够干净。
第二个坑是坐标系不统一导致的混淆。IMU的b系、底盘的坐标系、导航的n系,如果不在一开始约定清楚并贯穿始终,后面写融合代码时很容易出现符号错误。我的经验是:所有数据传进解算模块前统一转换到约定的坐标系,宁可多花几个周期做变换,也不要在代码里留下“这个符号代表哪个系”的模糊地带。
第三个坑是温度变化对陀螺仪零偏的影响。我最初标定是在室内常温环境,后来把车推到室外暴晒后测试,零偏漂了将近50%,姿态角也随之出现明显漂移。后来我在代码里加入了简单的温度补偿线性模型,虽然精度有限,但比完全不补偿强得多。如果你要长期在室外使用,建议还是选带温度补偿的高端IMU,MPU6050在这个场景下确实有点吃力。
最后一个建议是关于调试工具的。写惯导解算代码,没有上位机可视化调试纯属自虐。我推荐至少要有三个显示界面:一是三维姿态显示,用来直观检查姿态解算是否正确;二是实时波形显示,用来观察加速度、角速度、滤波前后的对比;三是轨迹显示,把推算出的位置画出来,方便在地图上对照实际路线。这些工具代价不高,但能让排查效率提升一个量级。
这个项目做下来,我最深的体会是:惯性导航解算的核心难点不在于“解算”本身,而在于把数据质量、坐标系、误差特性这些基础环节真正搞清楚。前期花的每一分钟标定和校准时间,后期都会以几倍的精度回报回来。如果有人正在做类似的底盘项目,我的建议很简单:先静下心把姿态解算练扎实,再谈位置解算和融合,这条路虽然绕,但走完之后你会发现自己对运动控制的理解会完全不一样。