SLAM位姿矩阵左乘右乘详解:从坐标系语义到工程实践

📅 发布时间:2026/9/9 1:28:21
SLAM位姿矩阵左乘右乘详解:从坐标系语义到工程实践 1. 从旋转矩阵左乘右乘到SLAM位姿矩阵这个坑为什么值得专门写一篇先说一个我自己的真实经历。前几年用ORB-SLAM2跑数据集的时候我想在全局坐标系下给相机轨迹加一个固定偏移让整个地图挪到指定位置。当时想都没想直接在位姿矩阵左边乘了一个变换矩阵结果地图全部飞到天上去了。排查了半天最后发现问题不是出在数值计算上而是我根本没搞明白左乘和右乘在SLAM位姿语义上的区别。这个事说起来很基础但几乎每一个做SLAM的人都会在某一个瞬间被它绊一下。包括我后来带过的实习生包括在一些技术社群里看到的提问大家反复在为什么左乘是全局变换右乘是局部变换这个点上纠结。很多人看完《视觉SLAM十四讲》里旋转矩阵左乘右乘那一节觉得看懂了但一写代码、一调试位姿就懵。所以这篇东西不是从零开始教大家矩阵乘法怎么算而是把位姿矩阵左乘和右乘这件事放到SLAM的实际应用场景里拆开讲清楚。适合正在看视觉SLAM十四讲、刚开始跑ORB-SLAM2或者自己写位姿解算代码的人也适合那些已经写了很久代码、但遇到相机坐标和世界坐标到底该左乘还是右乘时依然要停下来想一想的同学。这篇文章的核心只围绕一件事当你面对一个位姿矩阵T在它左边乘一个矩阵和右边乘一个矩阵到底分别意味着什么在什么场景下应该用哪一个以及最容易踩的坑在哪里。2. 先建立坐标系直觉全局坐标系与局部坐标系的分水岭在讲左乘和右乘之前我强烈建议先把坐标系这件事在脑子里钉死。SLAM里面所有的位姿矩阵本质上都是在描述一个刚体在三维空间中的位置和朝向。而位置和朝向必须有参考系才有意义。2.1 位姿矩阵T到底在表达什么一个4x4的齐次变换矩阵T通常长这样T [ R t ] [ 0 1 ]其中R是3x3旋转矩阵t是3x1平移向量。它表达的含义是把一个点从某个坐标系下的坐标变换到另一个坐标系下的坐标。这里就引出了第一个关键点位姿矩阵永远描述的是两个坐标系之间的相对关系而不是绝对概念。比如雷达坐标系到相机坐标系的外参是一个T相机坐标系到世界坐标系的变换也是一个T。在SLAM里最常见的写法是T_wc意思是从相机坐标系到世界坐标系的变换也可以理解为相机在世界坐标系下的位姿。这个下标顺序非常值得多说几句。T_wc的读法是左边的下标是目标坐标系w右边的下标是源坐标系c。也就是说一个在相机坐标系下的点p_c想要得到它在世界坐标系下的坐标p_w计算方式是p_w T_wc * p_c。这个约定在《视觉SLAM十四讲》里讲得很清楚但很多初学者容易搞反把T_wc当成从世界坐标到相机坐标来用后面全乱了。2.2 全局变换与局部变换的分界线到底在哪现在来到核心问题当我们对位姿矩阵T_wc做变换时在左边乘一个矩阵和在右边乘一个矩阵分别意味着什么假设我们有一个位姿矩阵T_wc现在想对它施加一个额外的变换ΔT。如果写成 ΔT * T_wc这就是左乘。左乘的结果是在**世界坐标系全局坐标系**下对这个位姿施加变换。如果写成 T_wc * ΔT这就是右乘。右乘的结果是在**相机自身坐标系局部坐标系**下对这个位姿施加变换。这个结论本身就是整个话题的核心。但是光记住这句话没有用得理解为什么。我习惯用一个生活化类比来说明。假设你站在一个房间里面朝北。现在你面前有一堵墙墙在你身体的局部坐标系的前方。如果你想在全局坐标系下移动这堵墙让它从房间北边挪到东边这个变换是在房间坐标系中定义的对应左乘。如果你想让你自己刚体先向右转90度再往前走这个动作是相对于你自己的朝向定义的对应右乘。对于相机位姿T_wc来说左乘相当于把整个世界坐标系固定住然后我问你如果把这个相机在世界上平移一段距离、旋转一个角度它新的位姿是什么右乘则相当于把相机坐标系固定住然后我问你如果相机的中心不动只是相对于它自己的坐标轴转动了一下或者沿着自己的坐标轴平移了一段它新的位姿是什么这两种想法都是合法的但它们描述的物理情景完全不同。SLAM中很多bug就是从这里来的——你以为自己在做全局变换实际写出来的代码却是在做局部变换或者反过来。2.3 为什么矩阵乘法不满足交换律在这里是个好事矩阵乘法本来就不满足交换律AB和BA通常不相等。在SLAM位姿变换这个场景里这种不相等恰恰很好地反映了物理世界的真实差异。比如一个位姿T_wc相机在世界坐标系的(1, 0, 0)位置朝向是世界坐标系X轴正方向。如果我们在左边乘上一个沿世界坐标系Z轴平移1个单位的矩阵ΔT_trans_z新位姿会跑到(1, 0, 1)处如果我们在右边乘上同样的ΔT_trans_z因为ΔT的旋转部分是单位阵平移部分在左乘和右乘时表现出的效果是一样的都是(1, 0, 1)这容易让人产生左乘右乘没区别的错觉。一旦涉及到旋转区别立刻凸显。假设ΔT是一个绕X轴旋转90度的旋转矩阵左乘和右乘的结果差异会非常明显。左乘会让位姿在世界坐标系下绕X轴转90度右乘则会让位姿在自己的相机坐标系下绕X轴转90度。前者会把相机朝向从指向世界X轴变成指向世界Z轴后者则不会改变相机在世界坐标系中的朝向因为当相机朝向与世界X轴一致时相机自己的X轴和世界X轴重合两者效果恰好一样。但一旦初始朝向不是正好对齐差异就会显现。这也是为什么在调试SLAM轨迹的时候很多人会遇到旋转方向反了转的角度对但是轴不对这类诡异问题多半就是左乘和右乘用错了地方。3. 数学推导与几何直觉为什么左乘是全局、右乘是局部搞清楚结论之后接下来要解决的是为什么。这一节我会把数学推导过程完整走一遍帮助那些需要在代码里推导公式、写优化代码的人打好底子。3.1 用点的变换来倒推位姿的变换位姿矩阵的物理含义是把一个点从一个坐标系映射到另一个坐标系。我们从这个角度出发来推导左乘右乘的含义。假设当前相机位姿是T_wc空间中有个点P在世界坐标系下坐标是p_w在相机坐标系下坐标是p_c那么满足p_w T_wc * p_c这是在SLAM里出现频率最高的公式之一。现在我要给这个位姿左乘一个新变换ΔT得到T_new ΔT * T_wc此时p_w_new T_new * p_c ΔT * T_wc * p_c ΔT * p_w看出来了吗左乘ΔT之后新的位姿对同一个相机坐标系下的点P会先按原来的T_wc映射到世界坐标p_w然后再在世界坐标系下按ΔT进行变换。因此左乘的结果是先保持点的相机坐标不变但最终它在世界坐标系里的位置被ΔT重新调整了。从位姿的运动角度看这就是在全局坐标系下移动了相机。再看右乘设T_new T_wc * ΔT那么p_w_new T_new * p_c T_wc * ΔT * p_c我们可以先计算ΔT * p_c。因为ΔT是在T_wc的右侧它首先作用于相机坐标系下的点p_c在相机坐标系内对点进行了一次变换然后再由T_wc映射到世界坐标系。从位姿的运动角度看这个ΔT是在相机坐标系里做变换影响的是相机自身的局部运动。一个简单的推导把左乘右乘的差别暴露无遗左乘是把变换直接作用在世界坐标系的点上右乘是先把点在相机坐标系内做变换再映射到世界坐标系。3.2 旋转矩阵左乘右乘与位姿矩阵的对应关系很多人之前在学旋转矩阵的时候已经接触过左乘是固定坐标系下的旋转右乘是自身坐标系下的旋转这个结论。位姿矩阵的左乘和右乘其实是一回事的推广。但有一个容易被忽略的细节位姿矩阵包含了平移分量所以左乘和右乘的影响不仅仅是旋转方向问题平移部分也遵循同样的规律。具体来说把ΔT拆成旋转ΔR和平移Δt分情况推导一下。左乘的情况ΔT * T_wc [ ΔR * R_wc, ΔR * t_wc Δt ] [ 0 , 1 ]右乘的情况我们把ΔT写开T_wc * ΔT [ R_wc * ΔR, R_wc * Δt t_wc ] [ 0 , 1 ]对比这两个结果信息量很大旋转部分左乘后的新旋转是ΔR * R_wc相当于在世界坐标系下先转ΔR再转到原朝向右乘后的新旋转是R_wc * ΔR相当于先按原朝向转到相机朝向再在相机坐标系下转ΔR。平移部分左乘时Δt直接加在世界坐标系的坐标上并且原有的平移向量t_wc还会被ΔR旋转一遍右乘时Δt先被R_wc旋转到世界坐标系方向再加上原来的平移。这些公式解释了一个很常见的现象当你给一个位姿左乘一个纯平移矩阵相机会在全局坐标系下沿某个轴移动当你右乘一个纯平移矩阵相机会沿着自己当前朝向的方向移动。如果相机已经旋转了90度右乘平移的效果看起来就像是在侧移左乘平移则永远是沿世界坐标轴走。3.3 用李群视角看左乘右乘如果你的研究或者工作涉及位姿优化比如图优化、Bundle Adjustment那么大概率会接触到李群和李代数。这时候左乘右乘的概念会被进一步推广。在李群语境下左乘扰动模型和右乘扰动模型是两种常见的求导方式。它们在数学本质上的区别和本文讨论的坐标系差异完全一致左扰动在位姿T左侧乘一个微小变换exp(ξ^)δ扰动被解释为在世界坐标系/全局坐标系下的扰动对应全局优化的雅可比。右扰动在位姿T右侧乘一个微小变换扰动被解释为在局部坐标系下的扰动对应局部参数化的雅可比。这个区别直接影响到你写代码时得到的雅可比矩阵形式。比如在g2o或者GTSAM里不同边定义的误差对位姿的雅可比是左扰动形式还是右扰动形式在编程时不能搞混。我见过不少人因为把左扰动雅可比直接套到右扰动更新上导致优化发散。在代码层面Eigen和Sophus库对左乘右乘的支持都非常直接。Sophus中SE3d的*运算符支持两个位姿矩阵相乘也支持位姿矩阵乘以点。如果你构造一个SE3d delta然后写T_new delta * T那就是左乘写T_new T * delta那就是右乘。代码写起来很容易難的是在写之前想清楚自己要的是哪种。4. SLAM实际场景里的左乘右乘建图、定位与轨迹处理的典型案例理论讲了一大堆最终还是要落到实际场景中。这一节我用几个SLAM开发中最高频的场景来举例每一个都是我自己实际写代码时遇到过的。4.1 场景一给整条轨迹加上一个全局偏移这是最典型的左乘应用场景。假设你用ORB-SLAM2跑完了一段数据集得到了每一帧的相机位姿T_wc现在因为地图对齐、坐标系变换、或者要与地面真值比较需要把整条轨迹按某个固定的变换ΔT做一次整体移动。做法很简单每一帧做左乘T_wc_aligned ΔT * T_wc_original为什么是左乘而不是右乘因为这里ΔT是在世界坐标系下定义的世界坐标系的原点要移到别的地方整个世界坐标系的轴要重新定向。每一帧相机在世界坐标系里的位姿自然也要跟着做同样的变换。这个场景如果用了右乘轨迹会发生奇怪的扭曲——每一帧都在自己的相机坐标系下做了变换导致轨迹的形状完全改变而不仅仅是被平移旋转。我遇到过一种容易混淆的情况用evo工具评估SLAM轨迹和真值的差异时需要先做轨迹对齐。如果你自己手动写对齐代码不小心用了右乘评估出来的ATE/APE误差会大得离谱让人误以为算法跑崩了。实际上就是坐标系变换方向搞反了。4.2 场景二机器人在自身坐标系下的运动控制这个场景对应机器人底盘运动学与SLAM位姿更新的结合。假设你有一个差速驱动机器人底盘发布的速度指令是相对于机器人自身坐标系的前进1米、左转30度、右转15度这些都是在机器人当前朝向下的动作。那么每次更新位姿的时候应该右乘T_wc_new T_wc_old * ΔT_odom其中ΔT_odom是机器人坐标系下观测到的运动增量来自轮式里程计。因为里程计的两个轮子速度天然是在机器人坐标系下积分出来的运动增量描述的是相对于机器人当前朝向走了多少所以必须右乘。这个场景最容易犯的错是把里程计数据直接加到世界坐标系的平移量上。比如相机朝向北的时候走了1米然后转了90度朝东又走了1米。如果搞成左乘第二次的1米可能还是会加到世界坐标系的北方向上轨迹就歪了。我之前调试一个视觉惯导融合的demo时就是在这个问题上卡了一个下午。IMU预积分出来的增量是在IMU坐标系下的直接左乘到世界坐标系位姿上跑出来的轨迹像喝醉了酒一样。4.3 场景三相机外参标定与坐标变换链在做多传感器融合时经常涉及到相机坐标系、IMU坐标系、雷达坐标系之间的变换。假设我们知道了雷达坐标系到相机坐标系的变换T_lc又知道相机坐标系到世界坐标系的变换T_wc那么雷达在世界坐标系下的位姿是T_wl T_wc * T_lc这其实是一个右乘的典型应用。这里没有对位姿进行额外变换的意思而是纯粹的坐标系链路叠加先把点从雷达坐标系转到相机坐标系再从相机坐标系转到世界坐标系。这种链式变换遵循的规则就是坐标变换的组合顺序和前面讨论的左乘右乘是统一的每次变换作用的都是当前坐标系下的点所以从左到右乘。这个例子提醒我们不要机械地记右乘局部运动这个结论有时候右乘仅仅是坐标系链路的自然写法。理解透之后看到T_wc * T_lc这样的式子你应该立刻能说出来这是先把雷达系下的点转到相机系再转到世界系T_lc的平移和旋转是在雷达自身坐标系里定义的。4.4 场景四相机位姿优化中的扰动更新在图优化和BA里位姿更新通常有两种方式。如果你用的是左乘更新那么每次迭代时T_new exp(ξ^) * T_old如果你的优化库采用的是右乘更新约定则是T_new T_old * exp(ξ^)这两种更新方式对应的雅可比矩阵形式不一样。ORB-SLAM2里的g2o优化按照实现细节通常是对位姿的扰动求导其雅可比形式往往对应一种特定的扰动放置位置。如果你要自己写一个自定义边必须先弄清楚这个优化器约定的是左扰动还是右扰动否则会出现误差在下降但位姿更新完全错误的诡异现象。判断方法其实不复杂你把一个微小扰动分别放在左边和右边分别算出误差对扰动的导数哪个形式和优化器内部更新公式一致就选哪个。但很多人会偷懒不推导直接抄网上代码抄对了能跑抄错了就陷入玄学调参。5. 最容易踩的3个坑我自己和身边人反复犯过的错误这一节算是经验之谈专门挑出那些不难但特别容易错的细节。这些坑不会让你写出编译不过的代码但会让你的仿真结果或者实机表现出一堆莫名其妙的偏差。5.1 坑一混淆T_wc和T_cw的物理含义这个坑实在太常见了。很多人在代码里定义了T_wc但在做坐标变换时又错误地用它去乘一个本来应该乘T_cw的点。比如在ORB-SLAM2里Tcw表示从世界坐标系到相机坐标系的变换矩阵也就是把世界系下的地图点转到相机系下。官方代码里追踪线程经常用Tcw乘以地图点坐标来得到投影后的像素坐标。如果你自定义代码时把变量名写反或者在赋值时混淆了来源整个投影过程就会乱套。关于T_wc和T_cw的互转有一个很实用的公式如果你只有T_cw想得到T_wc相机在世界坐标系下的位姿直接取逆即可。因为T_cw的逆就是T_wcT_wc T_cw.inverse()在Eigen中就是T_cw.inverse()这么一行。但要注意虽然矩阵求逆在数学上是精确的但在数值层面对于4x4齐次矩阵直接用.inverse()会比用分块公式慢一点。对于SLAM性能敏感的场景更高效的做法是利用齐次变换矩阵的求逆性质T_wc [ R_cw^T, -R_cw^T * t_cw ] [ 0 , 1 ]手动构造这个矩阵可以省掉一次通用的4x4求逆计算。这个优化在地图点数量庞大、频繁需要坐标变换的时候性能提升还是比较明显的。5.2 坑二忘记旋转矩阵的正交性对平移部分的影响回顾右乘公式里的平移部分t_new R_wc * Δt t_wc很多人在推导右乘平移效果时会下意识地写成Δt t_wc忘了前面还要乘一个R_wc。这个错误在做局部运动累加时尤其致命。比如机器人在世界系的(1, 2, 3)处朝向旋转了某个角度然后沿自身坐标系x轴前进了1米。正确的平移更新是先把自身的(1,0,0)方向旋转到世界坐标系下再叠加到原来的位置上。如果漏了旋转里程计积分就会在旋转后完全失真。我之前写一个简单的视觉里程计测试程序时正好卡在这个地方旋转角度超过45度后轨迹就开始偏角度越大偏得越离谱。后来打印中间变量才发现平移更新少乘了一个旋转矩阵平移量的方向在相机旋转后没有跟着转。5.3 坑三在轨迹评测时忽略坐标变换约定用evo评测SLAM轨迹时很多人会从算法里直接导出位姿文件然后与数据集提供的groundtruth做对比。但不同数据集、不同SLAM系统的位姿约定可能不同。举个具体例子TUM数据集提供的groundtruth是相机在世界坐标系下的位姿对应T_wc形式。而ORB-SLAM2输出的轨迹在默认情况下也是T_wc或者严格说是两帧之间的相对运动转换后的世界系位姿两者约定一致可以直接评测。但有些数据集或者算法输出的是T_cw如果你不转换直接喂给evo评测结果会非常糟糕。正确的做法是在导出轨迹文件前确保每一行位姿矩阵表达的是同一个坐标变换语义。如果不一致就用上面提到的取逆操作统一成T_wc。再提醒一句evo本身有--align参数做轨迹对齐但它解决的是轨迹之间的整体刚体变换问题不能解决你导出的语义本身就不对的问题。上下文里那些搜索热词反复出现evo说明很多人在用这个工具我真心建议大家先用小数据集确认坐标语义再开大量评测不然很容易得出错误结论。6. 一个加深理解的实战小例子手写坐标变换来验证左乘右乘光说不练还是容易忘。我建议你亲手做一次这个小实验它花费的时间不超过10分钟但能让你对左乘右乘的理解牢固很多。6.1 构造场景与预期结果假设相机在世界坐标系的初始位姿为位置(0, 0, 0)朝向世界坐标系X轴正方向即相机朝向世界X轴现在做两步操作绕世界坐标系的Z轴旋转90度。沿当前相机自身的X轴前进了1米。请你想一下这两种操作如果分别用左乘和右乘实现最后相机应该在哪里如果第一步旋转用左乘实现绕世界Z轴转90度第二步沿自身X轴前进用右乘实现因为自身X轴就是局部坐标系的定义那么位姿更新公式为T_1 ΔT_rot_z * T_0 // 左乘绕世界Z轴转90度 T_2 T_1 * ΔT_trans_x // 右乘沿自身X轴走1米预期结果第一步后相机朝向世界Y轴正方向因为绕Z轴转90度后X轴指向Y方向第二步沿自身X轴走1米相当于沿世界Y轴走1米。最终位置应该是(0, 1, 0)。如果你把第二步写成左乘T_2_wrong ΔT_trans_x * T_1由于ΔT_trans_x是沿世界X轴平移1米最终位置会变成(1, 0, 0)。方向完全错误。6.2 代码验证用Eigen实现这个验证非常直观。我用Sophus库因为它在表达SE3时更贴合SLAM习惯。#include iostream #include Eigen/Dense #include sophus/se3.hpp int main() { using Sophus::SE3d; using Eigen::Vector3d; using Eigen::AngleAxisd; using Eigen::Matrix3d; // 初始位姿原点朝向X轴正方向 SE3d T0(Matrix3d::Identity(), Vector3d(0, 0, 0)); // 绕世界Z轴旋转90度 Matrix3d Rz AngleAxisd(M_PI / 2.0, Vector3d::UnitZ()).toRotationMatrix(); SE3d delta_rot_z(Rz, Vector3d::Zero()); // 沿X轴平移1米 SE3d delta_trans_x(Matrix3d::Identity(), Vector3d(1, 0, 0)); // 正确旋转左乘 平移右乘 SE3d T1 delta_rot_z * T0; SE3d T2_correct T1 * delta_trans_x; // 错误平移也写成左乘 SE3d T2_wrong delta_trans_x * T1; std::cout correct translation: T2_correct.translation().transpose() std::endl; std::cout wrong translation: T2_wrong.translation().transpose() std::endl; return 0; }输出结果应该是correct translation: 0 1 0 wrong translation: 1 0 0这一步跑通之后你就能从背结论升级为能验证结论。下次遇到任何左乘右乘的疑问直接写个几行的小demo验证一下比自己反复琢磨要高效得多。6.3 进阶从旋转矩阵推广到任意SE3这个验证办法可以推广到任意的SE3变换。比如你可以试试先绕世界X轴转45度再绕自身Y轴转30度分别用左乘和右乘实现看看结果差别在哪里。先沿世界Z轴平移2米再沿自身Z轴平移2米在初始朝向不同的情况下看看效果差异。多做几组组合你的空间直觉会明显变好。我自己带人入门SLAM时经常让他们先做个这样的小实验再去看ORB-SLAM2里PnP求解时对位姿的更新代码理解速度会快很多。7. 从原理到代码如何快速判断一个场景该用左乘还是右乘最后这部分我想分享一套我自己用下来的判断方法论。不保证百分之百适用所有情形但对绝大多数SLAM开发场景都有效。当你面对这个位姿变换该左乘还是右乘的问题时按顺序问自己三个问题第一这个变换ΔT是在哪个坐标系下定义的如果ΔT的旋转轴、平移方向是在世界坐标系里描述的比如沿全局坐标系的Z轴旋转、沿世界地图的东北方向移动那用左乘如果ΔT是传感器测量得到的、在自身坐标系下的增量比如轮式里程计的左右轮积分、IMU预积分、视觉帧间运动估计那用右乘。第二变换作用的语义是什么如果变换是在已有位姿上额外叠加一个运动而这个运动的参考系就是该刚体当前所在的参考系那么几乎可以确定用右乘。如果变换是把整个刚体连同它所在的参考系一起在世界坐标系里做刚体运动用左乘。第三点坐标变换的链路是什么如果你在处理的是p_w T_wc * p_c这样的链路想知道一个点在多个坐标系之间怎么转那这种纯粹的多坐标系链式变换通常就是老老实实按矩阵乘法从左到右叠乘不用刻意区分左乘右乘因为它们的组合顺序已经由坐标变换链路本身决定了。还有一个非常实用的检查手段把ΔT设成纯旋转看旋转之后相机的朝向变化是否符合你的预期。比如你期望绕世界Z轴转90度左乘后相机在世界坐标系的朝向应该明确改变如果相机朝向没变或者方向反了基本就是乘反了。在实际工程中我更推荐的做法是在关键变换代码处写清注释把坐标系、变换语义、左乘还是右乘都标出来。很多时候代码本身并没有错是过了一个月回头看的人包括自己忘了当时的语义然后在上面乱改才引入了bug。这些注释看起来不起眼关键时刻比一大堆文档都好用。8. 从我自己的调试经历里总结的几条经验文章写到这主要的内容已经讲完了。再分享几个我从实际项目中沉淀下来、常规文档里不会写的点。第一个是调试顺序问题。排查位姿相关bug时不要一上来就怀疑随机噪声、回调丢数据、线程不同步先把坐标系和左乘右乘链捋一遍。我自己的经验是这种基础性错误通常是症状五花八门、根源只有一个比如轨迹旋转方向反了、地图整体偏移、局部建图漂移都有可能只是某个T乘反了。看起来是玄学的问题最后往往落在一个很小的矩阵乘法顺序上。第二个是输出中间量定位问题。很多位姿bug是看起来怪怪的但说不清哪里怪。这时候我一般会在关键位姿更新处打印中间量分别输出左乘和右乘两个结果和预期值对比。前面那个10分钟的小实验代码就是一个很有效的可复用脚手架。在不同项目里我都写过一个类似的变换验证函数输入一个T和一个ΔT输出左乘和右乘的结果一目了然。第三个是代码规范问题。建议在类或者函数命名里就把坐标变换语义写清楚比如updatePoseWithOdomDelta()、transformGlobal()、applyLocalMotion()不要只写updatePose。我曾经接手过一个代码库里面有好几个updatePose有的内部左乘有的内部右乘完全靠调用顺序去猜维护成本极高。后来全部改成带语义的命名后整体开发效率高了一大截。第四个是关于工具链的使用。《视觉SLAM十四讲》的读者大多会接触到Eigen、Sophus、g2o、evo这些工具这些工具本身的文档可能不会告诉你这个接口默认是左扰动还是右扰动但它们的源码和示例里一定会暴露出来。比如g2o的顶点更新函数oplusImpl里如果传入的增量是被左乘到位姿上的那这个顶点的求导约定就是左扰动。看源码虽然累但对理解框架的约定帮助巨大。遇到模棱两可时直接在源码里搜*this Sophus::SE3d::exp(delta) * (*this)还是*this (*this) * Sophus::SE3d::exp(delta)一目了然。最后说一句左乘还是右乘本质上是坐标系参考系的问题。SLAM里面的坐标系多到让人眼花缭乱但只要抓住变换是在哪个坐标系下定义的这一条线绝大多数问题都能迎刃而解。希望这篇内容对你有用祝调试顺利。