ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ABB机器人四元数与欧拉角转换实战:公式推导、代码实现与调试避坑

ABB机器人四元数与欧拉角转换实战:公式推导、代码实现与调试避坑 去年夏天在现场调试一台ABB机器人弧焊工位时甲方希望用离线仿真软件导出的路径点直接跑起来但仿真软件里导出的姿态数据是四元数到ABB示教器上手动录入又只能显示欧拉角。折腾了一下午我才意识到所谓ABB机器人姿态表示这件事远没有说明书里一句使用四元数表示姿态那么简单数据的顺序、坐标系约定、转换公式里asin和atan2的边界处理任何一个细节错了机器人就会在示教器里给出一个让现场电工师傅都看不下去的奇异姿态。后来我把这套从四元数到欧拉角的转换方法整理成了固定套路项目里再遇到类似需求基本十分钟搞定。这篇文章就把这套方法完整讲清楚先拆ABB机器人里四元数数据的真实形态再讲从四元数转欧拉角的公式推导和可以直接抄的代码然后讲反向转换和验证方法最后聊几个现场最容易踩的坑。适合正在做ABB机器人二次开发、离线编程、上位机数据对接的工程师也适合刚接触机器人姿态控制、对四元数一直似懂非懂的朋友。1. 示教器上显示欧拉角程序里存的却是四元数绕不开的数据形态问题ABB机器人给人最直观的姿态印象来自示教器。手动操纵时会看到当前TCP的朝向显示为RX、RY、RZ三个角度看起来非常简单直接。但只要你打开RAPID程序编辑器看一眼robtarget的定义就会发现姿态数据其实是用四个数表示的。这四个数就是四元数ABB在底层存储、轨迹插补、TCP通信中都使用四元数作为姿态的标准表达方式欧拉角只是给人看的“界面”。这个设计在工程上有它的道理。欧拉角在插补计算中会出现万向锁问题在数学上存在奇异点而且在跨平台传输时不同厂商的约定千奇百怪四元数则没有这些问题它的插补可以做到平滑连续数据格式也相对统一。ABB把四元数作为内部标准但又在所有可视化界面里显示欧拉角这就导致调试人员和二次开发工程师经常要在两套表达方式之间来回切换。1.1 robtarget到底长什么样RAPID程序里一个典型的目标位置长这样CONST robtarget pPick : [[400, 200, 500], [0.707107, 0, 0.707107, 0], [0, 0, 0, 0], [9E9, 9E9, 9E9, 9E9, 9E9, 9E9]];第一个方括号是位置xyz单位毫米。第二个方括号就是姿态四元数四个分量。后面两个方括号分别对应外轴位置和轴配置参数普通人不需要关心。这里的关键问题是第二个方括号里的四个数顺序究竟代表什么ABB官方的标准定义中q1对应用来表示旋转的四元数的实部可以理解为 ( w )q2对应虚部第一个分量可以理解为绕x轴的旋转分量 ( x )q3对应绕y轴的 ( y )q4对应绕z轴的 ( z )。也就是说在RAPID里看到的[q1, q2, q3, q4]等价于数学里常见的(w, x, y, z)顺序。这个顺序看起来简单实操中特别容易翻车。有一次我从第三方传感器读取姿态数据对方文档里写的是(x, y, z, w)顺序我直接硬填进robtarget里机器人姿态立刻变成完全不合理的角度差点以为传感器坏了。所以无论是从仿真软件导出、从PLC读取还是从视觉系统接收姿态数据第一件事不是写转换公式而是确认对方的四元数分量顺序。顺序不对后面做的都是无用功。1.2 什么情况下必须自己做转换很多人觉得示教器上可以直接手动输入欧拉角为什么还要费劲写转换代码这个需求在日常调试中有几个真实场景。第一个场景是离线编程和外部路径规划。大多数离线仿真软件在导出路径时默认给的是基于自身坐标系的姿态表达有时候是四元数有时候是旋转矩阵。落到ABB机器人里如果不做转换生成的robtarget根本无法直接用。第二个场景是视觉引导抓取视觉系统输出的通常是一个旋转矩阵或者四元数而我们希望机器人末端执行器的姿态是某个具体的欧拉角朝向这中间必然存在转换关系。第三个场景是通过Socket通信做上位机控制外部控制器直接下发欧拉角给机器人而RAPID这边要用四元数构造目标姿态并写入robtarget这就需要把欧拉角转换回四元数。理解了场景下面的事就简单了先把姿态数学搞明白然后代码怎么简洁怎么来。2. 动手转换前先搞清楚ABB的坐标系约定很多人拿到公式就开写结果算出来的角度和示教器显示的对不上于是怀疑公式有问题其实是坐标系约定没对齐。这类坑我踩过太多次这里把ABB的姿态约定完整梳理一遍。2.1 ZYX欧拉角约定和旋转顺序ABB示教器上显示的RX、RY、RZ表面上是三个独立角度实际上它们对应一个明确的旋转顺序先绕Z轴旋转RZ再绕新的Y轴旋转RY最后绕新的X轴旋转RX。这组约定在机器人运动学里通常叫ZYX欧拉角也叫作“绕运动轴依次旋转”。你可以这样理解想象一个固定在工具末端的坐标系先让它绕基座的Z轴转个角度然后绕自己当前已经偏转后的Y轴再转一个角度最后绕进一步偏转后的X轴转第三个角度。三个角度的“旋转顺序”是Z、Y、X。为什么要强调顺序因为同样的三个角度数值如果先绕X再绕Y再绕Z最终姿态是完全不同的。ABB内部轨迹规划算法默认这套ZYX规则示教器显示的角度数值也是按照这套规则反算出来的。所以我们做坐标转换时也必须使用ZYX顺序反解欧拉角否则结果显示必然错位。还有一个容易混淆的点是“固定轴旋转”和“欧拉角旋转”的等价关系。ZYX欧拉角绕运动轴先Z后Y后X在效果上等价于绕固定轴先X后Y后Z的旋转。也就是说你可以把RX、RY、RZ理解为“先绕固定世界坐标系的X轴转RX再绕世界坐标系的Y轴转RY最后绕世界坐标系的Z轴转RZ”。这两种理解方式在数学上是同一组旋转矩阵对应的角度值也一致。搞明白这一层很多专业书里“固定角”和“欧拉角”的差异就不会再让人头晕。ABB实际显示用的就是这层等价关系对应的角度。2.2 四元数在ABB里的符号和归一化四元数本身是一个带四个分量的超复数系统ABB内部用它表示姿态时要求模长恒为1。模长等于1意味着这个四元数是一个纯旋转不包含缩放分量。如果从外部拿到一个四元数模长不是1数值从TCP通信、文件导入或传感器读取进来经过各种浮点运算后出现了偏差转换出的欧拉角就会失真严重时会出现NaN。所以任何外部四元数进入转换公式之前统一做一次归一化是最稳妥的防御性处理。归一化的公式很简单四个分量除以模长即可$$ w \frac{w}{\sqrt{w^2x^2y^2z^2}}, \quad x \frac{x}{\sqrt{w^2x^2y^2z^2}}, \quad y \frac{y}{\sqrt{w^2x^2y^2z^2}}, \quad z \frac{z}{\sqrt{w^2x^2y^2z^2}} $$模长不是1公式里asin的参数就会超出[-1, 1]的有效区间导致计算结果为NaN。这个细节在线调试时尤其重要因为真实通信数据从来不会像教科书那么干净。我在后面第五节会再详细讲一个真实的NaN排查过程。另外注意符号约定。单位四元数存在一个“双重覆盖”特性( q ) 和 ( -q ) 表示完全相同的姿态。ABB示教器上显示欧拉角为0度、45度、90度时底层四元数可能是正的一组数也可能是全负的同一组数这两者等效。所以在做数据比较或结果验证时不能只比较四元数分量是否一致而要先确定符号方向或者直接通过转换出的欧拉角来验证是否一致。3. 四元数转欧拉角的完整推导和代码实现这一节是核心实战内容先讲公式从哪来再给出可以直接用的代码最后说清楚每个函数参数的含义。3.1 旋转矩阵怎么推出来的四元数转欧拉角的本质是把四元数先还原成旋转矩阵再从旋转矩阵里反解出三个角度。单位四元数(w, x, y, z)对应的旋转矩阵标准形式如下[ R \begin{bmatrix} 1 - 2(y^2z^2) 2(xy - wz) 2(xz wy) \ 2(xy wz) 1 - 2(x^2z^2) 2(yz - wx) \ 2(xz - wy) 2(yz wx) 1 - 2(x^2y^2) \end{bmatrix} ]这个矩阵描述了“从机器人基座坐标系到工具坐标系的旋转变换”。它是由四元数旋转公式展开得到的推导过程不需要背但知道矩阵里每个元素对应哪个四元数分量很重要因为后面的反解公式都是从这里读出来的。如果你用的是ZYX欧拉角约定也就是ABB默认的姿态表达方式那么旋转矩阵也可以写成三个基本旋转矩阵连乘的形式先绕Z转再绕新Y转最后绕新X转。连乘展开后矩阵的元素表达式与四元数矩阵存在一一对应关系我们就利用这些对应关系反解角度。3.2 反解公式对应ZYX欧拉角约定ABB的RX、RY、RZ分别对应旋转矩阵里特定的元素RZ绕Z轴转角也就是通常说的偏航角yaw[ RZ \text{atan2}(2(wz xy), 1 - 2(y^2z^2)) ]RY绕Y轴转角也就是俯仰角pitch[ RY \text{asin}(2(wy - zx)) ]RX绕X轴转角也就是横滚角roll[ RX \text{atan2}(2(wx yz), 1 - 2(x^2y^2)) ]这里要特别提醒三件事。第一atan2的两个参数顺序不能写反。很多语言里的atan2(y, x)第一个参数是分子第二个参数是分母如果写成atan2(x, y)结果会差90度而且极难排查。第二asin函数的输入区间是 [-1, 1]。如果四元数没有归一化或者浮点误差导致 2(wy - zx) 的值刚好是 1.0000000001直接调用asin会返回NaN。所以一定要先做clamp操作把输入限制在[-1,1]之间。第三角度单位问题。四元数转换出来的欧拉角默认是弧度ABB示教器显示的是度。如果你要做自动写入或者与示教器数值对比必须先统一单位。RAPID里面也可以通过OrientZYX等函数直接使用弧度但人工核对时几乎总是直接用度。3.3 可直接用的C#和Python代码现场开发最常见的场景是上位机用C#离线计算或脚本用Python。我给出两个版本的实现逻辑完全一致按需取用。先看C#版本using System; public static class AbbPoseUtils { // 输入四元数分量 (w, x, y, z)输出弧度制的 RX、RY、RZ public static (double rx, double ry, double rz) QuatToEulerZyx( double w, double x, double y, double z) { // 防御性归一化避免模长偏差导致 asin 越界 double norm Math.Sqrt(w*w x*x y*y z*z); w / norm; x / norm; y / norm; z / norm; // RZ绕Z轴转角 double rz Math.Atan2(2*(w*z x*y), 1 - 2*(y*y z*z)); // RY绕Y轴转角注意 clamp double sinPitch 2*(w*y - z*x); sinPitch Math.Max(-1.0, Math.Min(1.0, sinPitch)); double ry Math.Asin(sinPitch); // RX绕X轴转角 double rx Math.Atan2(2*(w*x y*z), 1 - 2*(x*x y*y)); return (rx, ry, rz); } }再看Python版本import math def quat_to_euler_zyx(w, x, y, z): norm math.sqrt(w*w x*x y*y z*z) w / norm x / norm y / norm z / norm rz math.atan2(2*(w*z x*y), 1 - 2*(y*y z*z)) sin_pitch 2*(w*y - z*x) sin_pitch max(-1.0, min(1.0, sin_pitch)) ry math.asin(sin_pitch) rx math.atan2(2*(w*x y*z), 1 - 2*(x*x y*y)) return rx, ry, rz这两个函数是我在实际项目里反复用过的版本唯一改动是加了归一化和clamp。把返回值乘以180 / Math.PI就能得到角度制方便和示教器显示值对照。用的时候特别注意如果从robtarget里读到的是[q1, q2, q3, q4]对应到函数参数就是QuatToEulerZyx(q1, q2, q3, q4)其中q1是实部w。不要因为自己习惯(x, y, z, w)格式就顺手改掉要严格按照ABB的顺序来。4. 反向操作欧拉角转回四元数外部上位机需要下发姿态指令时往往要提供四元数给RAPID程序。RAPID里可以直接用四元数构造robtarget但很多第三方系统或数据库里存的是欧拉角转换逻辑就变成反向的三个角度合成为一个四元数。4.1 拆分旋转和四元数乘法反向转换的原理其实更直观一次ZYX旋转可以拆成三次独立的旋转每一次旋转对应一个基础四元数。先把三个基础四元数算出来然后按顺序做四元数乘法。三个基础四元数分别是绕Z轴转RZ角度 [ q_z \left(\cos\frac{RZ}{2}, 0, 0, \sin\frac{RZ}{2}\right) ]绕新Y轴转RY角度 [ q_y \left(\cos\frac{RY}{2}, 0, \sin\frac{RY}{2}, 0\right) ]绕新X轴转RX角度 [ q_x \left(\cos\frac{RX}{2}, \sin\frac{RX}{2}, 0, 0\right) ]整个旋转的四元数就是 ( q_z \cdot q_y \cdot q_x )其中四元数乘法定义如下[ (w_1, x_1, y_1, z_1) \cdot (w_2, x_2, y_2, z_2) (w_1w_2 - x_1x_2 - y_1y_2 - z_1z_2, \ w_1x_2 x_1w_2 y_1z_2 - z_1y_2, \ w_1y_2 - x_1z_2 y_1w_2 z_1x_2, \ w_1z_2 x_1y_2 - y_1x_2 z_1w_2) ]这个乘法公式看着复杂但写成代码之后一劳永逸。我也见过有人直接记组合后的展开公式但那样顺序一错就容易全盘皆输。我倾向先写基础四元数和乘法函数再组合出问题好排查。4.2 代码实现和自校验方法完整的反向转换函数如下def euler_zyx_to_quat(rx, ry, rz): cx math.cos(rx / 2) sx math.sin(rx / 2) cy math.cos(ry / 2) sy math.sin(ry / 2) cz math.cos(rz / 2) sz math.sin(rz / 2) # q_z * q_y * q_x w cz*cy*cx sz*sy*sx x cz*cy*sx - sz*sy*cx y cz*sy*cx sz*cy*sx z sz*cy*cx - cz*sy*sx return w, x, y, z这个函数里三个角度必须传弧度制。如果你从数据库里读的是角度制先乘以math.pi / 180再传进来。写完反向转换后最好做一个自校验随机生成一组欧拉角转成四元数再转回欧拉角对比前后角度是否一致。注意由于四元数和非唯一性的存在直接对比四元数数值不一定相同但欧拉角在限定范围内应当一致。我通常把RX、RY、RZ限定在[-180, 180]范围内做校验误差在1e-6以内就算通过。自校验还有一个作用检查你的正向转换和反向转换是否使用了同一套旋转约定。如果正向用ZYX、反向用了XYZ自校验一定失败。这一步能在开发早期拦截绝大多数约定错误。5. 现场调试里最容易翻车的几个细节代码能跑通只是第一步真正在现场不出问题才是目的。这一节把我实际调试中碰到过的几个高频问题完整列出来每条都有对应的处理经验。5.1 顺手就能避开的NaN问题有一次视觉系统反馈的抓取点四元数通过TCP传到上位机我的转换程序偶尔会输出NaN。当时第一个反应是通信数据丢包查了半天网络结果发现视觉系统里四元数在传输过程中没有做归一化某个数值的模长变成了1.000001。由于我最初的代码没有做归一化和clampasin函数直接越界返回了NaN。从那以后我给自己定了一条规矩任何外部输入的四元数进入姿态转换逻辑之前强制归一化不管它看起来是否正常asin函数调用前一律对参数做clamp到[-1,1]的处理。这两行代码成本几乎为零但能解决现场一大半莫名其妙的姿态错误。5.2 万向锁临界姿态怎么处理当俯仰角RY接近±90度时ZYX欧拉角体系进入万向锁状态此时RX和RZ的旋转轴重合两个角度的区分变得不唯一。数学上体现为反解公式中的atan2失去了稳定的角度分辨率误差被急剧放大。这不是ABB机器人的bug而是所有使用欧拉角表达的系统的固有问题。实际工程中如果某个工位的轨迹点在作业过程里必然经过RY接近±90度的姿态建议不要直接用欧拉角做插补或路径规划。正确做法是全程使用四元数进行轨迹计算只有在需要人工查看或记录时才临时转换成欧拉角展示。ABB机器人底层本身就是四元数插补所以只要你不在上层把姿态转成欧拉角重新下发轨迹就是平滑可靠的。这个原则一定要记住四元数用于计算和传输欧拉角用于显示和人工理解二者职责分离是绕开万向锁问题的最佳工程实践。5.3 用RAPID内置指令做交叉验证如果对自己写的转换算法没有十足把握可以在RAPID端用官方内置函数做交叉验证。RAPID里与姿态转换相关的常用指令有OrientZYX、Orient和四元数取值函数。你可以在机器人控制器里先定义一个已知欧拉角的目标姿态然后通过RAPID读取它的四元数再拿这个四元数到你的上位机代码里做反向转换对比是否得到相同的欧拉角。交叉验证步骤如下在示教器手动操纵界面把机器人转到某个姿态记录下RX、RY、RZ。在RAPID程序里读取当前设备姿态的robtarget.rot也就是四元数。把这个四元数拿到你的转换程序里算一下欧拉角。与示教器的显示数值对比误差在0.01度以内说明算法约定一致。这个方法比纯理论验算可靠得多因为它验证的是你的代码和ABB控制器使用的约定是否完全一致。我每次新做一套转换工具都会先用这个流程对几个典型姿态做验证包括RY为0度、45度、接近90度的姿态。确认无误后再接入正式流程。5.4 float精度对二次开发的影响上位机里C#往往默认使用double但RAPID变量有时使用float存储尤其通过OPC UA或Socket读取时数据可能被截断成单精度浮点。单精度带来的误差通常在1e-6量级对于绝大多数姿态显示来说足够了。但在做传感器引导的精密装配时1e-6的角度误差也会导致末端位置偏差几十微米这时就要确保整个链路都用double传输。另外一个容易忽略的点是四元数在TCP传输过程中的字节序。用Socket通信时不同设备间的字节序可能不同读取到的四元数看起来数值差不多但分量顺序彻底乱掉。这类问题比算法问题更隐蔽排查时要先确认数据包协议再讨论姿态算法。6. 从四元数到欧拉角真正省时间的是流程化整套方法落地之后我在后续项目里只需要遵循几个固定的检查步骤第一步确认数据来源的四元数分量顺序第二步确认目标欧拉角旋转顺序是ZYX第三步统一角度单位第四步做归一化和clamp第五步用已知姿态做交叉验证。这五步走完转换工具基本不会出问题。再分享一个我个人的习惯我始终保留一张“已知姿态对照表”里面记录了10组典型姿态的欧拉角和对应四元数例如零姿态、绕单轴旋转、双轴组合旋转、接近万向锁的姿态等。每次写完新的转换代码先跑一遍这张表通过就算入门。这个习惯救过我很多次尤其是隔了大半年重新打开旧项目、换了一台新电脑、或者换了另一种编程语言重写工具的时候对照表可以快速帮助恢复状态。你如果经常和ABB机器人打交道强烈建议自己攒一张这样的表。
返回列表