Jak wyprowadzić zamkniętą IK dla manipulatorów BEZ sferycznego nadgarstka — case study UR5 (Hawkins/Kufieta) i ES5 (Załącznik A dysertacji Gruszki 2024). Plus krótko o redundancji Franka Panda.
Moduł 1 pokazał piękne, „klasyczne" wyprowadzenie IK dla PUMA 560 — manipulator z trzema osiami nadgarstka schodzącymi w jednym punkcie (forma A warunku Piepera). To pozwoliło na dekompozycję 3+3: najpierw pozycja, potem orientacja. Pięć stron algebry, jeden czysty wynik.
Większość współczesnych cobotów nie spełnia jednak formy A. UR5, UR10, Franka Panda, KUKA iiwa — wszystkie one mają geometrie zaprojektowane pod inne cele (smukłość, lekkość, równa dystrybucja mas), które łamią klasyczną dekompozycję. Czy to znaczy że nie można ich rozwiązać analitycznie? Nie — można, ale trzeba zmienić podejście.
Co wynika z tego modułu
Po przerobieniu modułu student potrafi: (1) rozpoznać formę warunku Piepera na podstawie geometrii DH, (2) wyprowadzić IK dla manipulatora UR5 (forma B, metoda Hawkins/Kufieta), (3) wyprowadzić IK dla ES5 z dysertacji Gruszki, (4) rozumie różnicę między manipulatorami 6-DOF a redundantnym 7-DOF (Franka Panda) i wie dlaczego ten ostatni wymaga zupełnie innego podejścia.
Donald Pieper w 1968 roku pokazał, że istnieje rozwiązanie zamknięte IK dla manipulatora 6-DOF jeśli spełniony jest jeden z dwóch warunków geometrycznych:
W obu przypadkach 6-wymiarowy problem rozpada się na dwa niezależne 3-wymiarowe (pozycja + orientacja w jakimś rozumieniu), a wyniki są wzorami zamkniętymi z funkcjami trygonometrycznymi.Jeśli żaden warunek nie zachodzi — IK nadal może być rozwiązalna analitycznie, ale wymaga zaawansowanych metod algebraicznych (Raghavan–Roth, redukcja do równania 16. stopnia w jednej zmiennej).
Forma A
3 osie schodzą w jednym punkcie
Przykłady: PUMA 560, Stäubli TX, ABB IRB
Forma B
3 osie wzajemnie równoległe
Przykłady: UR5/UR10/UR16, ES5, KUKA iiwa (częściowo)
Brak warunku Piepera
Ani A, ani B
Przykłady: Manipulatory eksperymentalne — wymagana metoda Raghavan–Roth (równanie 16. stopnia)
Zasada Piepera (1968): jeśli manipulator 6-DOF ma 3 kolejne osie spełniające A lub B, to istnieje rozwiązanie zamknięte IK (dekompozycja na 3+3 niewiadome). To warunek wystarczający, nie konieczny — manipulatory bez Piepera też mogą mieć zamknięte rozwiązania, ale wymagają zaawansowanych metod (rezultanty, metoda Raghavan–Roth).
Praktyczna konsekwencja: projektanci robotów zwykle wybierają geometrię spełniającą jedną z form Piepera celowo — żeby IK miało zamknięte rozwiązanie i było obliczane w mikrosekundach zamiast milisekundach. Manipulatory bez Piepera stosuje się rzadko (głównie w eksperymentalnych systemach badawczych).
Manipulatory Universal Robots (UR3, UR5, UR10, UR16) mają geometrię zaprojektowaną pod cele współpracy z człowiekiem: smukłe, bez ostrych krawędzi, równa dystrybucja masy. Cecha kinematyczna: kolejne osie q₂, q₃, q₄ są wzajemnie równoległe (oś pozioma, w jednym kierunku), a osie nadgarstka są przesunięte względem siebie o niezerowy — co oznacza że nie schodzą w jednym punkcie.
Parametry DH UR5 (modyfikowany Craig, [m, rad])
| i | uwagi | ||||
|---|---|---|---|---|---|
| 1 | 0 | 0 | 0,089 | obrót podstawy | |
| 2 | 0 | 0 | bark | ||
| 3 | 0 | −0,425 | 0 | forma B: q₂ ∥ q₃ ∥ q₄ | |
| 4 | 0 | −0,392 | 0,109 | łokieć | |
| 5 | 0 | 0,095 | d₅ ≠ 0 — forma A wykluczona | ||
| 6 | 0 | 0,082 | kołnierz |
Wniosek: dekompozycja 3+3 z M1 nie zadziała — nie istnieje „środek nadgarstka" jako wspólny punkt 3 ostatnich osi. Ale spełniona jest forma B (q₂ ∥ q₃ ∥ q₄), więc da się rozpisać rozwiązanie zamknięte — tylko innym algorytmem.
Metoda Hawkins/Kufieta (2013/2014): kolejność wyprowadzania to . W skrócie: z geometrii „cylindra zakazanego" wokół podstawy wyznacza się q₁ przez przekształcenia atan2 z d_5 jako stałym offsetem. Mając q₁, można wyizolować q₅ i q₆ z elementów macierzy. Mając konfigurację kiści, wracamy do podproblemu 3R-planarnego w pionowej płaszczyźnie po q₁ — i z twierdzenia cosinusów (jak w M1) wyznaczamy q₂, q₃, q₄.
Liczba rozwiązań: 8 — tak samo jak Pumy. Dwa branche shoulder × dwa elbow × dwa wrist. Pomimo zupełnie innej algebry, struktura rozwiązań pozostaje taka sama dla każdego manipulatora spełniającego jakąkolwiek formę Piepera.
ur_kinematics w ROS, ur_rtde w Pythonie, ikfast (autogenerowany z URDF), C++ w ur_robot_driver.W aplikacji nie implementujemy pełnej IK UR5 — to wykraczałoby poza zakres modułu i wymagało osobnego modelu URDF. Skupiamy się tu na ES5 (mamy go już w M9–M11), dla którego mamy gotowe wyprowadzenie z dysertacji [Gruszka 2024, Załącznik A].
ES5 to manipulator firmy EasyRobots, dla którego M9 (dynamika) i M10 (silnik) korzystają z parametrów inercji. Tu domykamy pętlę — pokazujemy jego kinematyczne rozwiązanie IK. Geometrycznie ES5 jest podobny do UR (forma B — równoległość q₂, q₃, q₄), ale ma inne wymiary i konwencję DH.
Parametry DH ES5 (modyfikowany Craig, [m, rad])
źródło: src/lib/robots/es5.ts
| i | uwagi | ||||
|---|---|---|---|---|---|
| 1 | 0 | 0 | 0 | obrót podstawy | |
| 2 | 0 | 0 | bark | ||
| 3 | 0 | 0,425 | 0 | forma B: q₂ ∥ q₃ ∥ q₄ | |
| 4 | 0 | 0,395 | 0,1105 | łokieć z odsadzeniem d₄ | |
| 5 | 0 | 0,101 | kostka nadgarstka | ||
| 6 | 0 | 0,0765 | kołnierz końcówki |
Kolejność wyprowadzania współrzędnych w dysertacji: θ₁ → θ₅ → θ₆ → θ₃ → θ₂ → θ₄. Ta sekwencja nie jest oczywista — jest algebraicznie wymuszona faktem, że pewne równania trygonometryczne dają się rozwiązać tylko gdy znane są inne współrzędne. To kontrastuje z M1 (Puma), gdzie kolejność q₁ → q₂,₃ → q₄,₅,₆ wynikała wprost z dekompozycji formy A.
Poniżej pełne wyprowadzenie 6 współrzędnych zgodnie z Załącznikiem A dysertacji. Każdy krok zawiera referencję do oryginalnego numeru równania w pracy (eq. A.x).
Zadana jest macierz transformacji TCP względem bazy w postaci ogólnej (eq. A.1 z dysertacji):
Dla czytelności zapisu używamy w dysertacji skrótu i , oraz , itd. (skutek równoległości osi q₂, q₃, q₄ — sumy ich kątów zachowują się jak jeden „efektywny" kąt).
Klucz wyprowadzenia: jeśli weźmiemy obliczone na dwa różne sposoby — analitycznie z parametrów DH, oraz przez izolację z zadanego przez mnożenie odwrotnościami — dostaniemy układ równań w którym pewne komórki zależą tylko od θ₁:
Analitycznie (z DH) ma w komórce y wektora translacji (drugi wiersz, czwarta kolumna) wartość stałą: . Z drugiej strony, po wykonaniu mnożenia po lewej i porównaniu — dostajemy równanie w którym jedyną niewiadomą jest :
Podstawienie helpers — pozycja środka nadgarstka (środka układu 5) rzutowana na xy bazy:
Po podstawieniu i wykorzystaniu wzoru na sin różnicy kątów:
Stąd ostateczna postać dla θ₁:
import numpy as np
# T_target: macierz 4x4 zadanej pozy efektora
R = T_target[:3, :3]
px, py = T_target[0, 3], T_target[1, 3]
r13, r23 = R[0, 2], R[1, 2]
# eq. A.6: środek układu 5 w bazie (cofamy się o d6 wzdłuż z6_world)
p5x = px - D6 * r13
p5y = py - D6 * r23
p5xy = np.hypot(p5x, p5y)
# eq. A.13: dwie gałęzie shoulder
asin_val = np.arcsin(np.clip(D4 / p5xy, -1, 1))
alpha = np.arctan2(p5y, p5x)
theta1_candidates = [
(alpha + asin_val, "right"),
(alpha + np.pi - asin_val, "left"),
]Dwa rozwiązania — odpowiadają dwóm konfiguracjom barku (shoulder-left / shoulder-right). Analog do gałęzi shoulder w M1.
Analiza struktury kinematycznej pokazuje, że współrzędna y wektora (translacja od początku ogniwa 2 do początku ogniwa 6) zależy wyłącznie od θ₅:
Z drugiej strony, ten sam można wyrazić przez obrót (eq. A.16–A.18):
Przyrównanie i wyizolowanie :
for theta1, shoulder in theta1_candidates:
c1, s1 = np.cos(theta1), np.sin(theta1)
cos5 = (px * s1 - py * c1 - D4) / D6
if abs(cos5) > 1:
continue
base_t5 = np.arccos(np.clip(cos5, -1, 1))
for wrist_sign in (+1, -1):
theta5 = wrist_sign * base_t5
# ... dalej θ₆ i (θ₃, θ₂, θ₄)Dwa rozwiązania — odpowiadają dwóm orientacjom kiści względem ogniwa 4 (analog do gałęzi wrist flip z M1).
Uwaga o pierwszej osobliwości: gdy , szósta oś obrotu staje się równoległa do osi 2, 3 i 4 — pojawia się nadmiarowość stopni swobody (obrót osi 2, 3, 4 manipuluje TCP niezależnie od rotacji osi 6). To jest klasyczny wrist singularity analogiczny do Pumy.
Wyznaczając jawnie z parametrów DH (eq. A.21) i porównując z formą uzyskaną przez (eq. A.26–A.28), z komórek [2,1] oraz [2,2] dostajemy układ:
Stąd osobno i , i ich złożenie przez atan2:
c5, s5 = np.cos(theta5), np.sin(theta5)
r11, r12 = R[0, 0], R[0, 1]
r21, r22 = R[1, 0], R[1, 1]
if abs(s5) < EPS: # wrist singularity
theta6 = 0.0
else:
sin6 = ( s1 * r12 - c1 * r22) / s5
cos6 = (-s1 * r11 + c1 * r21) / s5
theta6 = np.arctan2(sin6, cos6)atan2 (a nie acos) — żeby zachować pełen zakres . Dzielenie przez jest niesingularne dopóki .
Po znalezieniu θ₁, θ₅, θ₆ wracamy do zadania pozycji. Wyznaczamy przez ciąg mnożeń odwrotnościami:
Z geometrii (Rys. A.1 z dysertacji) — robot rzutowany na płaszczyznę xz tworzy trójkąt o bokach , i przekątnej . Z twierdzenia cosinusów (jak w M1):
Stąd:
# Wylicz T_1_4 = (T_0_1)^-1 · T_target · (T_5_6)^-1 · (T_4_5)^-1
T01 = link_transform(0, theta1)
T45 = link_transform(4, theta5)
T56 = link_transform(5, theta6)
T14 = inv_se3(T01) @ T_target @ inv_se3(T56) @ inv_se3(T45)
p1x_4, _, p1z_4 = T14[:3, 3]
# Twierdzenie cosinusów dla trójkąta 2R w płaszczyźnie xz:
a2, a3 = A3, A4 # ramię i przedramię
p1n2 = p1x_4**2 + p1z_4**2
cos3 = (p1n2 - a2**2 - a3**2) / (2 * a2 * a3)
if abs(cos3) > 1:
continue
base_t3 = np.arccos(np.clip(cos3, -1, 1))
for elbow_sign in (+1, -1):
theta3 = elbow_sign * base_t3
# ... dalej θ₂, θ₄Dwa rozwiązania — gałęzie elbow-up i elbow-down, analog do M1.
Z tego samego rzutu (Rys. A.1) widzimy: , gdzie γ to kąt do , a α to wewnętrzny kąt trójkąta przy O₂. Wyrażając przez znane wielkości:
α z prawa sinusów dla trójkąta O₂O₃O₄:
Stąd:
c3, s3 = np.cos(theta3), np.sin(theta3)
K = a2 + a3 * c3
Mt = a3 * s3
theta2 = np.arctan2(K * p1z_4 - Mt * p1x_4,
K * p1x_4 + Mt * p1z_4)Ostatnia współrzędna — analogicznie do θ₆, ale na innym poziomie dekompozycji macierzy:
Z komórek [1,1] i [1,2] tej macierzy wyciągamy i jako kombinacje liniowe znanych już (eq. A.45 — dwie długie formuły 6-składnikowe). Następnie:
T12 = link_transform(1, theta2)
T23 = link_transform(2, theta3)
T03 = T01 @ T12 @ T23
T34 = inv_se3(T03) @ T_target @ inv_se3(T56) @ inv_se3(T45)
theta4 = np.arctan2(-T34[0, 1], T34[0, 0])
solutions.append((theta1, theta2, theta3, theta4, theta5, theta6))Komplet wzorów (eq. A.47–A.52 z dysertacji) — gotowe do wstawienia do kodu solvera analitycznego dla ES5:
Liczba rozwiązań: 2 (shoulder θ₁) × 2 (elbow θ₃) × 2 (wrist θ₅) = 8 konfiguracji — identycznie jak dla Pumy 560, choć geometria jest zupełnie inna. To uniwersalny wynik dla manipulatorów 6-DOF spełniających jakąkolwiek formę warunku Piepera.
| Cecha | PUMA 560 (M1) | UR5 (Hawkins/Kufieta) | ES5 (Gruszka 2024) |
|---|---|---|---|
| Forma Piepera | A (osie 4,5,6 schodzą) | B (osie 2,3,4 równoległe) | B (osie 2,3,4 równoległe) |
| DOF | 6 | 6 | 6 |
| Liczba rozwiązań | 8 | 8 | 8 |
| Kolejność wyprowadz. | q₁ → q₂,q₃ → q₄,q₅,q₆ | q₁ → q₅,q₆ → q₂,q₃,q₄ | q₁ → q₅ → q₆ → q₃ → q₂ → q₄ |
| Środek nadgarstka | TAK — w punkcie przecięcia osi 4,5,6 | NIE — osie nie schodzą (d_5 ≠ 0) | NIE — osie nie schodzą |
| Singularności | oś 1, łokieć wew./zew., nadgarstek | cylinder zakazany, łokieć, nadgarstek | analogiczne do UR (θ₅=0 → wrist) |
| Typowe zastosowanie | przemysł motoryzacyjny, edukacja | cobot, lekkie zadania, lead-through | cobot/przemysł, dydaktyka dysertacji |
Wniosek dydaktyczny: mimo zupełnie różnych geometrii i algorytmów wyprowadzania, liczba rozwiązań (8) i ogólna struktura wyboru (shoulder × elbow × wrist) są uniwersalne dla każdego manipulatora 6-DOF spełniającego warunek Piepera. To głęboka prawda: forma rozwiązania zależy od geometrii, ale liczba i typ branch'y — od struktury problemu IK.
Franka Emika Panda łamie wszystkie założenia, na których opierał się ten moduł — bo ma siedem stopni swobody zamiast sześciu. Dla zadania SE(3) (6-DOF) oznacza to nadmiarowość 1 DOF: dla każdej osiągalnej pozy końcówki istnieje cała ciągła rodzina konfiguracji (parametryzowana jednym dodatkowym kątem — zwykle nazywanym kątem łokcia albo swivel angle).
Konsekwencje:
Pełne wyprowadzenie 7-DOF IK wykracza poza ten moduł — wymaga osobnego materiału z parametryzacją kąta łokcia, obsługą singularności łokcia (gdy oś 4 jest prawie wyprostowana) i integracją z null-space optymalizacją. Sugestia: prześledź P. Beeson & B. Ames (2015) „TRAC-IK" — niereferencyjna ale praktycznie stosowana biblioteka działająca na Franka, UR i Pumie.
Sześć snippetów Python z kroków 1–6 wyprowadzenia powyżej, połączone w jedną funkcję solve_es5_ik(T_target). Zwraca listę do 8 rozwiązań (krotki 6 kątów), zgodnie z gałęziami shoulder × elbow × wrist.
import numpy as np
# Parametry DH ES5 (modified Craig) — z src/lib/robots/es5.ts:
A3 = 0.425 # ramię
A4 = 0.395 # przedramię
D4 = 0.1105 # odsadzenie przedramienia
D5 = 0.101 # kostka nadgarstka
D6 = 0.0765 # wystawienie końcówki
EPS = 1e-9
def solve_es5_ik(T_target, link_transform, inv_se3):
"""
Analityczne IK dla ES5 (forma B Piepera) wg Załącznika A
dysertacji [Gruszka 2024].
link_transform(i, theta_i) — macierz 4×4 ⁱ⁻¹T_i z DH (Craig).
inv_se3(T) — szybka odwrotność SE(3): R^T blok + -R^T·t.
Obie funkcje musisz dostarczyć (kilka linijek każda).
Zwraca: list[tuple[float, ...]] — do 8 krotek (q1..q6).
"""
R = T_target[:3, :3]
px, py = T_target[0, 3], T_target[1, 3]
r11, r12, r13 = R[0]
r21, r22, r23 = R[1]
# === Krok 1: θ₁ (dwie gałęzie shoulder) ===
p5x = px - D6 * r13
p5y = py - D6 * r23
p5xy = np.hypot(p5x, p5y)
if p5xy < EPS:
return []
ratio = D4 / p5xy
if abs(ratio) > 1:
return []
asin_val = np.arcsin(np.clip(ratio, -1, 1))
alpha = np.arctan2(p5y, p5x)
theta1_candidates = [
(alpha + asin_val, "right"),
(alpha + np.pi - asin_val, "left"),
]
solutions = []
for theta1, shoulder in theta1_candidates:
c1, s1 = np.cos(theta1), np.sin(theta1)
# === Krok 2: θ₅ (dwie gałęzie wrist) ===
cos5 = (px * s1 - py * c1 - D4) / D6
if abs(cos5) > 1:
continue
base_t5 = np.arccos(np.clip(cos5, -1, 1))
for wrist_sign in (+1, -1):
theta5 = wrist_sign * base_t5
c5, s5 = np.cos(theta5), np.sin(theta5)
# === Krok 3: θ₆ — atan2(sin θ₆, cos θ₆) z komórek ⁶R₁ ===
if abs(s5) < EPS:
theta6 = 0.0
else:
sin6 = ( s1 * r12 - c1 * r22) / s5
cos6 = (-s1 * r11 + c1 * r21) / s5
theta6 = np.arctan2(sin6, cos6)
# === T_1_4 numerycznie (cofamy się przez T_5_6, T_4_5) ===
T01 = link_transform(0, theta1)
T45 = link_transform(4, theta5)
T56 = link_transform(5, theta6)
T14 = inv_se3(T01) @ T_target @ inv_se3(T56) @ inv_se3(T45)
p1x_4, _, p1z_4 = T14[:3, 3]
# === Krok 4: θ₃ (dwie gałęzie elbow) ===
a2, a3 = A3, A4
p1n2 = p1x_4**2 + p1z_4**2
cos3 = (p1n2 - a2**2 - a3**2) / (2 * a2 * a3)
if abs(cos3) > 1:
continue
base_t3 = np.arccos(np.clip(cos3, -1, 1))
for elbow_sign in (+1, -1):
theta3 = elbow_sign * base_t3
# === Krok 5: θ₂ (z układu liniowego K·c2 - M·s2 = p1x, ...) ===
c3, s3 = np.cos(theta3), np.sin(theta3)
K = a2 + a3 * c3
Mt = a3 * s3
theta2 = np.arctan2(K * p1z_4 - Mt * p1x_4,
K * p1x_4 + Mt * p1z_4)
# === Krok 6: θ₄ (z elementu T_3_4) ===
T12 = link_transform(1, theta2)
T23 = link_transform(2, theta3)
T03 = T01 @ T12 @ T23
T34 = inv_se3(T03) @ T_target @ inv_se3(T56) @ inv_se3(T45)
theta4 = np.arctan2(-T34[0, 1], T34[0, 0])
solutions.append((theta1, theta2, theta3, theta4, theta5, theta6))
return solutionsCo warto zauważyć: kolejność wyprowadzania współrzędnych jest algebraicznie wymuszona — najpierw θ₁ z geometrii rzutu, potem θ₅ i θ₆ z analizy macierzy , dopiero na końcu θ₃, θ₂, θ₄ z numerycznie wyliczonej . To inna kolejność niż klasyczna dekompozycja 3+3 Pumy z M1 — bo geometria ES5 (forma B) wymaga innego rozdzielenia.
Powyższy algorytm Python jest 1:1 przetłumaczony do TypeScriptu jako src/lib/solvers/analytical-es5.ts w aplikacji. Wersja TS jest używana w playgroundzie poniżej (interaktywne wyznaczanie 8 rozwiązań na żywo). Przechodzi smoke test FK→IK→FK na 5 konfiguracjach z błędem maszynowej precyzji (~10⁻¹⁵ rad).
# Uruchom test:
npx tsx src/lib/solvers/__es5_smoke.ts
# Użycie:
import { solveEs5Analytical } from "@/lib/solvers/analytical-es5";
import { forwardKinematics } from "@/lib/robots/dh";
import { ES5 } from "@/lib/robots/es5";
const target = forwardKinematics(ES5, [0.3, 0.4, 0.5, 0.6, 0.7, 0.8]);
const solutions = solveEs5Analytical(target);
// solutions: do 8 rozwiązań z gałęziami shoulder/elbow/wristPlayground ma dwa równoległe stany:
Wskaźnik TCP odbiega od T* o ... mm mówi czy aktualne q faktycznie trafia w T*. Kliknięcie wiersza w tabeli rozwiązań ładuje wybraną gałąź do sliderów — robot ustawia się tam i wskaźnik staje się zielony.
Eksperymenty do wypróbowania:
z = 0.05 (TCP nisko nad podłożem) — większość rozwiązań prawdopodobnie odpadnie (poza zasięgiem ramienia w dół). Zobaczysz krótszą listę albo komunikat „brak rozwiązań".Rozwiązania IK z pozy docelowej: 0(maks. 8 — shoulder × elbow × wrist)
kliknij żeby załadować
Brak rozwiązań — poza zasięgiem albo geometryczna degeneracja. Spróbuj zmienić pozycję lub orientację w panelu po prawej.
S=shoulder (left/right), E=elbow (up/down), W=wrist (flip/noflip). Wiersz w kolorze zielonym to gałąź, do której aktualnie ustawione są przeguby. Klikając inny wiersz załadujesz tę konfigurację — robot trafi w tę samą pozę docelową inną drogą.
analytical-es5.ts) jest gotowy — może być teraz wpięty do M6 (Benchmark) jako drugi analityk obok Pumy.