# Введение

Данные интерактивные тетради основаны на языке Python.

Для выполнения кода выберите ячейку с кодом и нажмите `Ctrl + Enter`.


```python
from platform import python_version
print("Используемая версия Python:", python_version())
```

Ячейки подразумевают последовательное исполнение.


```python
l = [1, 2, 3]
```


```python
l[0]
```


```python
type(l)
```


```python
help(l)
```

## Математический аппарат

В этих интерактивных тетрадях используется математический аппарат, основанный на парах вектор-кватернион.

Вектор (`Vector`) представлен тремя чиселами, кватернион (`Quaternion`) - четыремя.
Пара вектор-кватернион (`Transformation`) соостоит из вектора и кватерниона и описывает последовательные перемещение и поворот.
$$ T = 
\begin{bmatrix}
    [v_x, v_y, v_z] \\
    [q_w, q_x, q_y, q_z]
\end{bmatrix}
$$

Математический аппарат расположен в файле [kinematics.py](../edit/kinematics.py)

### Vector

Вектор - тройка чисел, описывает перемещение:
$$ v = [v_x, v_y, v_z] $$


```python
from kinematics import Vector
```

Создание вектора требует трех чисел:


```python
v1 = Vector(1, 2, 3)
v2 = Vector(-2, 4, -3)
```

Вектора можно складывать поэлементно:


```python
v1 + v2
```

А также умножать на скаляр:


```python
2.5 * v1
```

Нулевой вектор создается через `Vector.zero()`:


```python
Vector.zero()
```

### Quaternion

Кватернион - четверка чисел, описывает поворот:
$$ q = [q_w, q_x, q_y, q_z] $$


```python
from kinematics import Quaternion
from numpy import pi
```

Кватернион создается из угла и оси поворота:


```python
q1 = Quaternion.from_angle_axis(0.5 * pi, Vector(0, 0, 1))
q2 = Quaternion.from_angle_axis(0.5 * pi, Vector(1, 0, 0))
print(q1)
print(q2)
```

Перемножение кватернионов соответствует последовательному приложению поворотов, в данном случае - повороту вокруг оси, проходящей через точку `(1, 1, 1)` на угол 120 градусов:


```python
q1 * q2
```


```python
Quaternion.from_angle_axis(2 / 3 * pi, Vector(1, 1, 1).normalized())
```

Поворот вектора сокращен до оператора `*`:


```python
q = Quaternion.from_angle_axis(pi / 2, Vector(0, 0, 1))
q * Vector(1, 2, 3)
```

Кватернион нулевого поворота создается `Quaternion.identity()`:


```python
Quaternion.identity() * Vector(1, 2, 3)
```

### Transform


```python
from kinematics import Transform
```

Пара вектор-кватернион собирается из вектора и кватерниона:


```python
t1 = Transform(v1, q1)
t2 = Transform(v2, q2)
```

Пара состоит из смещения и поворота:


```python
t1.translation
```


```python
t1.rotation
```

Пара с нулевыми смещением и поворотом создается через `Transform.identity()`:


```python
Transform.identity()
```

Суммирование двух пар описывет последовательное применение смещения - поворота - смещения - поворота:


```python
t1 + t2
```

Суммирование пары и ветора описывает применение преобразования, записанного в паре к вектору:


```python
t1 + Vector(1, 0, 0)
```

## Графика

Подключим магию для работы с графикой:


```python
from matplotlib import pyplot as plt
from matplotlib import animation
import numpy as np
from IPython.display import HTML
import graphics
%matplotlib notebook
```

Отрисовка систем координат производится через `graphics.axis`.
Преобразование цепочки в отдельные массивы точек `X, Y, Z` производится через `graphics.chain_to_points`.


```python
fig = plt.figure()
ax = fig.add_subplot(projection="3d")
ax.set_xlim([-3, 3]); ax.set_ylim([-3, 3]); ax.set_zlim([-3, 3]);
graphics.axis(ax, Transform.identity(), 3)
graphics.axis(ax, t1)
graphics.axis(ax, t1 + t2)
x, y, z = graphics.chain_to_points([Transform.identity(), t1, t1 + t2])
ax.plot(x, y, z)
fig.show()
```

## Анимация

Анимация будет сохраняться в переменную, например в `ani`, которую потом можно будет отобразить в виде видеоролика через `HTML(ani.to_jshtml())`.
Перед сохранением в виде ролика можно заранее повернуть сцену мышкой.
Обратите внимание что перерисовка каждого кадра требует работы ядра.
Для остановки нажмите кнопку выключения в правом верхнем углу трехмерной сцены.


```python
fig = plt.figure()
ax = fig.add_subplot(projection="3d")
ax.set_xlim([-1, 1]); ax.set_ylim([-1, 1]); ax.set_zlim([0, 2 * pi])
l, = ax.plot([], [], [])
t = np.arange(1, 2 * pi, 0.1)
frames = 100
def animate(i):
    offs = i / frames * 2 * pi
    z = t
    q = Quaternion.from_angle_axis(t + offs, Vector(0, 0, 1))
    v = q * Vector(1, 0, 0)
    x = v.x
    y = v.y
    l.set_data_3d(x, y, z)
ani = animation.FuncAnimation(
    fig,
    animate,
    frames=frames,
    interval=100
)
```

Не забудьте выключить пересчет модели кнопкой в правом верхнем углу трехмерной сцены.


```python
HTML(ani.to_jshtml())
```

Полученый таким образом ролик можно сохранить в составе всей тетради и выкачать локальную копию через `File -> Download as -> Notebook (.ipynb)`.

## Символьные вычисления

Для работы с символьными вычислениями используется пакет `sympy`.


```python
import sympy as sp
```


```python
x = sp.symbols("x")
x
```

`sympy` позволяет описывать деревья вычислений:


```python
v = sp.sin(x) ** 2 + sp.cos(x) ** 2
v
```

И упрощать их:


```python
sp.simplify(v)
```


```python
u = sp.cos(x) ** 2 - sp.sin(x) ** 2
u
```


```python
sp.simplify(u)
```

Можно легко дифференцировать выражения:


```python
t = sp.symbols("t")
f = sp.sin(t + 2 * x ** 2)
f
```

Производная по $t$:


```python
sp.diff(f, t)
```

Производная по $x$:


```python
sp.diff(f, x)
```

Для того, чтобы описать кватернион в системе `sympy`, нужно передать `sympy`(`sp`) как последний агрумент в `Quaternion.from_angle_axis`:


```python
a, b, c = sp.symbols("a, b, c")
angle = sp.symbols("alpha")
q = Quaternion.from_angle_axis(angle, Vector(0, 0, 1), sp)
v = Vector(a, b, c)
rotated = q * v
```


```python
sp.simplify(rotated.x)
```


```python
sp.simplify(rotated.y)
```


```python
sp.simplify(rotated.z)
```

А еще можно решать уравнения:


```python
alpha, beta = sp.symbols("alpha, beta")
t0 = Transform(
    Vector.zero(),
    Quaternion.from_angle_axis(alpha, Vector(0, 0, 1), sp)
)
t1 = t0 + Transform(
    Vector(beta, 0, 0),
    Quaternion.identity()
)
```


```python
target_x = t1.translation.x
target_x
```


```python
target_y = t1.translation.y
target_y
```


```python
x, y = sp.symbols("x, y")
solution = sp.solve(
    [
        sp.simplify(target_x) - x,
        sp.simplify(target_y) - y
    ],
    [
        alpha,
        beta
    ]
)
```

Первое решение для $\alpha$:


```python
solution[0][0]
```

Первое решение для $\beta$:


```python
solution[0][1]
```

Действительно, если подставить решение, в, например, $y$, получим следущее:


```python
sp.simplify(
    t1.translation.y.replace(alpha, solution[0][0]).replace(beta, solution[0][1])
)
```

Для $x$ такой красоты (пока) не произойдет, придется упрощать вручную:


```python
sp.simplify(
    t1.translation.x.replace(alpha, solution[0][0]).replace(beta, solution[0][1])
)
```

Возможно стоит использовать свое собственное решение, например:
$$ \alpha = \tan^{-1}(y, x) $$
$$ \beta = \sqrt{x^2 + y^2} $$


```python
own_alpha = sp.atan2(y, x)
own_beta = sp.sqrt(x ** 2 + y ** 2)
```


```python
sp.simplify(t1.translation.x.replace(alpha, own_alpha).replace(beta, own_beta))
```


```python
sp.simplify(t1.translation.y.replace(alpha, own_alpha).replace(beta, own_beta))
```

c570847d520f5ca6cff200655e9cf54c2ece1fe3
ipynb
Jupyter Notebook
0 - Intro.ipynb
red-hara/jupyter-kinematics-control
7f55476bef51f915676a1b2f82361fae564f47fa
MIT
2021-03-28T11:36:54.000Z
2022-03-25T04:53:11.000Z
0 - Intro.ipynb
red-hara/jupyter-kinematics-control
7f55476bef51f915676a1b2f82361fae564f47fa
MIT
0 - Intro.ipynb
red-hara/jupyter-kinematics-control
7f55476bef51f915676a1b2f82361fae564f47fa
MIT
2022-03-04T12:19:09.000Z
2022-03-04T12:19:09.000Z
Qwen/Qwen-72B
1. YES
2. YES