Робототехніка · Лінійна алгебра
📅 Липень 2026 ⏱ ≈ 14 хв читання 🎯 Середній–Просунутий · Останнє оновлення: 9 липня 2026 р.

Матриці обертання та кватерніони в роботиці

Кінцевому ефектору робота недостатньо лише позиції — йому потрібна орієнтація: у який бік дивиться захват. Матриці обертання, кути Ейлера, вісь-кут та кватерніони — це чотири різні способи закодувати одне й те саме обертання у 3D, кожен зі своїми компромісами. Ця стаття веде від SO(3) (математичної групи всіх можливих обертань у 3D) до кватерніонів і пояснює, чому майже кожен сучасний робототехнічний стек (ROS, Unity, Unreal, Three.js) зрештою зберігає орієнтацію саме як кватерніон.

Коротко: Роботам потрібно якось представляти орієнтацію у 3D, і є чотири способи це зробити: матриці обертання, кути Ейлера, вісь-кут і кватерніони. Кути Ейлера зрозумілі, але страждають від "gimbal lock" (втрати одного ступеня свободи); кватерніони цього уникають, займають менше пам'яті і плавно інтерполюються (SLERP), тому більшість робототехнічних стеків зберігають орієнтацію саме як кватерніон, а в матрицю конвертують лише для масової трансформації хмар точок.

Група обертань SO(3)

Будь-яку жорстку орієнтацію у 3D-просторі можна представити матрицею обертання 3×3 R, що належить спеціальній ортогональній групі SO(3). Належність до неї визначають дві умови:

Rᵀ · R = I (ортогональна — стовпці одиничні й взаємно перпендикулярні)
det(R) = +1 (власне обертання, а не відображення)

Композиція обертань — це просто множення матриць: R_total = R₂ · R₁ спочатку застосовує R₁, потім R₂. Обертання точки — добуток матриці на вектор: p' = R · p. Матриця обертання легко застосовується до векторів і легко компонується, але вона зберігає 9 чисел для 3 ступенів свободи — надлишково, і схильна поступово втрачати ортогональність після багаторазового множення з плаваючою комою (числова похибка накопичується, і R поступово перестає бути справжнім обертанням).

Реортогоналізація: довготривалі контролери роботів періодично реортогоналізують R (наприклад, через Грам-Шмідт або SVD), щоб компенсувати накопичену похибку — інакше «матриця обертання» поступово перетворюється на довільне лінійне перетворення.

Кути Ейлера та gimbal lock

Кути Ейлера описують будь-яку орієнтацію як три послідовні обертання навколо координатних осей — наприклад крен (X), тангаж (Y), рискання (Z), або аерокосмічна конвенція Z-Y-X. Вони інтуїтивні для людини (три числа в градусах), але страждають на gimbal lock: коли середнє обертання досягає ±90°, перша і третя осі вирівнюються, і один ступінь обертальної свободи втрачається.

R = Rz(рискання) · Ry(тангаж) · Rx(крен)
при тангажі = ±90° рискання і крен обертаються навколо тієї самої осі → gimbal lock

Для 6-DOF руки-маніпулятора це має конкретний наслідок: біля пози gimbal lock нескінченно мала зміна цільової орієнтації може вимагати величезного, розривного стрибка одного з кутів Ейлера — рука «перекидається» замість плавного руху. Тому кути Ейлера використовують для людиночитаного відображення в UI (повзунки, показники), але рідко — для внутрішнього стану орієнтації.

Вісь-кут і формула Родріга

Будь-яке 3D-обертання можна описати єдиною одиничною віссю n̂ та кутом θ обертання навколо неї (теорема обертання Ейлера). Формула обертання Родріга перетворює вісь-кут безпосередньо на матрицю обертання, зовсім оминаючи кути Ейлера:

R = I + sin(θ)·K + (1 − cos(θ))·K²
де K — кососиметрична матриця векторного добутку для n̂:
K = [ 0, −nz, ny; nz, 0, −nx; −ny, nx, 0 ]

Вісь-кут компактний (4 числа: 3 для осі + 1 для кута, або 3, якщо масштабувати вісь на θ), не має gimbal lock і саме це представлення внутрішньо використовують більшість інтеграторів кутової швидкості — кутова швидкість ω — це, по суті, швидкість вісь-кута (вісь = ω/|ω|, швидкість = |ω|).

Кватерніони

Одиничний кватерніон q = w + xi + yj + zk (з w² + x² + y² + z² = 1) кодує рівно ту саму інформацію, що й вісь-кут, але у формі, яка компонується та інтерполюється значно чистіше:

q = ( cos(θ/2), n̂ · sin(θ/2) ) = (w, x, y, z)
w = скалярна (дійсна) частина, (x,y,z) = векторна (уявна) частина

Кватерніони мають рівно 4 числа на 3 ступені свободи (одне обмеження: одиничну норму) — менш надлишкові за 9-числову матрицю, компактніші за окреме зберігання осі й кута, і головне: без gimbal lock, оскільки немає «середньої осі», яка може вирівнятися з іншими.

Подвійне покриття: q і −q представляють точно те саме обертання. Це не завадить при обертанні векторів, але важливо для інтерполяції — завжди перевіряйте скалярний добуток двох кватерніонів перед SLERP (див. нижче), і заперечуйте один із них, якщо добуток від'ємний, щоб піти коротшим шляхом.

Добуток Гамільтона та обертання векторів

Композиція двох обертань — спочатку q₁, потім q₂ — це добуток Гамільтона q₂ * q₁ (множення кватерніонів некомутативне — порядок важливий, точно як і при множенні матриць):

(w₁,v₁) * (w₂,v₂) = ( w₁w₂ − v₁·v₂, w₁v₂ + w₂v₁ + v₁×v₂ )

Щоб обернути вектор p кватерніоном q, «затискаємо» його між q і спряженим q⁻¹ (для одиничного кватерніона спряжений q* = (w,−v) дорівнює оберненому):

p' = q * (0, p) * q⁻¹

На практиці цей «сендвіч» ніколи не реалізують буквально — його алгебраїчно розкривають один раз і отримують пряму формулу (див. код нижче) — саме її й виконує кожен ігровий рушій та робототехнічна бібліотека.

Інтерполяція: SLERP проти LERP

Інтерполяція орієнтації — наприклад, плавний рух кінцевого ефектора від орієнтації q_a до q_b по траєкторії — саме там, де кватерніони справді розкриваються. Наївна лінійна інтерполяція (LERP) чотирьох компонент з подальшою нормалізацією дає візуально нерівномірну швидкість; SLERP (сферична лінійна інтерполяція) рухається з постійною кутовою швидкістю вздовж найкоротшої дуги великого кола на 4D одиничній гіперсфері:

slerp(q_a, q_b, t) = [sin((1−t)Ω)·q_a + sin(tΩ)·q_b] / sin(Ω)
Ω = кут між q_a та q_b: cos(Ω) = q_a · q_b

Коли Ω дуже мале (майже однакові орієнтації), SLERP чисельно нестабільний (ділення майже на нуль) — виробничий код у такому випадку переходить на нормалізований LERP, оскільки різниця на такому крихітному куті непомітна.

Порівняльна таблиця

Представлення Чисел Gimbal lock Вартість композиції Найкраще для
Матриця обертання 9 Немає 9 множень × 3 Застосування до багатьох векторів (GPU, пакетні перетворення)
Кути Ейлера 3 Так Спочатку конвертація в матрицю UI для людини, повзунки
Вісь-кут 3–4 Немає Спочатку конвертація в матрицю/кватерніон Кутова швидкість, швидкості суглобів
Кватерніон 4 Немає 16 множень (добуток Гамільтона) Зберігання, композиція, інтерполяція (SLERP)

Більшість робототехнічних стеків внутрішньо зберігають орієнтацію як кватерніон, конвертують у кути Ейлера лише для відображення, і конвертують у матрицю обертання безпосередньо перед пакетним перетворенням великих масивів точок (наприклад, хмари точок) — бо множення матриці на вектор краще векторизується на GPU/SIMD, ніж повторні добутки Гамільтона.

Реалізація на JavaScript

Клас Quat

class Quat {
  constructor(w = 1, x = 0, y = 0, z = 0) {
    this.w = w; this.x = x; this.y = y; this.z = z;
  }

  // Побудова з осі (одиничний вектор) + кута в радіанах
  static fromAxisAngle(axis, theta) {
    const h = theta * 0.5, s = Math.sin(h);
    return new Quat(Math.cos(h), axis.x * s, axis.y * s, axis.z * s);
  }

  // Добуток Гамільтона: this * other
  mul(o) {
    return new Quat(
      this.w*o.w - this.x*o.x - this.y*o.y - this.z*o.z,
      this.w*o.x + this.x*o.w + this.y*o.z - this.z*o.y,
      this.w*o.y - this.x*o.z + this.y*o.w + this.z*o.x,
      this.w*o.z + this.x*o.y - this.y*o.x + this.z*o.w
    );
  }

  conjugate() { return new Quat(this.w, -this.x, -this.y, -this.z); }

  // Обертання вектора {x,y,z} цим одиничним кватерніоном (розкритий сендвіч-добуток)
  rotateVec(v) {
    const qv = new Quat(0, v.x, v.y, v.z);
    const r = this.mul(qv).mul(this.conjugate());
    return { x: r.x, y: r.y, z: r.z };
  }

  normalize() {
    const n = Math.hypot(this.w, this.x, this.y, this.z);
    return new Quat(this.w/n, this.x/n, this.y/n, this.z/n);
  }
}

SLERP

function slerp(qa, qb, t) {
  let { w: w2, x: x2, y: y2, z: z2 } = qb;
  let cosOmega = qa.w*w2 + qa.x*x2 + qa.y*y2 + qa.z*z2;

  // Обираємо коротший шлях на гіперсфері (виправлення подвійного покриття)
  if (cosOmega < 0) { cosOmega = -cosOmega; w2 = -w2; x2 = -x2; y2 = -y2; z2 = -z2; }

  let s1, s2;
  if (cosOmega > 0.9995) {
    // майже однакові: переходимо на нормалізований LERP, щоб уникнути 0/0
    s1 = 1 - t; s2 = t;
  } else {
    const omega = Math.acos(cosOmega);
    const sinOmega = Math.sin(omega);
    s1 = Math.sin((1 - t) * omega) / sinOmega;
    s2 = Math.sin(t * omega) / sinOmega;
  }
  return new Quat(
    s1*qa.w + s2*w2, s1*qa.x + s2*x2,
    s1*qa.y + s2*y2, s1*qa.z + s2*z2
  ).normalize();
}

Орієнтація у роботизованій руці

У 6-DOF маніпуляторі кожне суглобове перетворення в ланцюзі прямої кінематики несе і зсув, і обертання. У Three.js кожен Object3D вже зберігає своє обертання внутрішньо як кватерніон (object3d.quaternion) — кути Ейлера (object3d.rotation) — це лише представлення-геттер/сеттер над ним, що конвертується туди й назад. Задавати кути суглобів, щоразу записуючи .rotation.x для обертального суглоба, — цілком нормально (обертання навколо однієї осі ніколи не потрапляє у gimbal lock), але світову орієнтацію кінцевого ефектора — накопичений добуток усіх шести обертань суглобів — варто зчитувати й інтерполювати як кватерніон, а не розкладати на кути Ейлера, коли потрібен плавний рух між цільовими позами.

IMU / сенсорне злиття: оцінювачі орієнтації (Madgwick, Mahony, компліментарні фільтри) також видають кватерніони безпосередньо, бо інтегрування показань кутової швидкості як швидкостей кватерніона уникає тих самих проблем gimbal lock та дрейфу, що й інтегрування швидкостей Ейлера.

🦾 Спробуйте кінематику роботизованої руки

Подивіться на пряму й зворотну кінематику — та поширення орієнтації через ланцюг суглобів — прямо в браузері.

Відкрити симуляцію →