Кинематика
Данная библиотека является дополнением к системе сборок и позволяет управлять перемещением элементов сборок в терминах обобщенных кинематических координат, а так же осуществлять некоторые действия на кинематическими цепями.
Два юнита одного звена
Кинематический компонент реализован парой юнитов, находящихся в относительном движении:
- Сам объект звена, наследующий
kinematic_unit, служит входным юнитом. Егоlocationзадаёт установку звена относительно родителя. joint.output— выходной юнит, дочерний по отношению к входному. Его положение относительно входа определяется кинематическими координатами.
parent
└── joint вход: установка шарнира
└── joint.output выход: вращение или перемещение
└── payload деталь или следующее звено
joint.add(shape) прикрепляет геометрию к входу, например корпус привода. joint.output.add(shape) прикрепляет её к подвижному выходу. Вызов joint.link(child) также присоединяет child к выходу, в отличие от unit.link(child), который присоединяет непосредственно к текущему юниту.
При создании потомка через конструктор пишите parent=joint.output, если он должен двигаться вместе с выходом. parent=joint прикрепляет его к входному юниту.
Вращательное и поступательное звенья
Классы импортируются из zencad.assemble:
| Звено | Координата | Движение выхода |
|---|---|---|
rotator(axis=...) |
set_coord(angle), угол в радианах |
Вращение вокруг локальной оси входа |
actuator(axis=...) |
set_coord(distance), длина в единицах модели |
Перемещение вдоль локальной оси входа |
Ось нормируется. Параметр mul масштабирует координату: фактический угол или ход равен coord * mul. location устанавливает вход звена, а set_coord() изменяет относительное положение выхода. У однокоординатных звеньев dim() возвращает 1; доступна также запись set_coords([value]).
Пример поворотного рычага:
from zencad import *
from zencad.assemble import unit, rotator
base = unit()
joint = rotator(axis=(0, 0, 1), parent=base)
joint.add(cylinder(3, 2, center=True))
joint.output.add(box(20, 2, 2).back(1).down(1))
tip = unit(parent=joint.output, location=translate(20, 0, 0))
base.location_update(deep=True, view=False)
joint.set_coord(deg(90), view=False)
position = tip.global_location.translation()
assert abs(position.x) < 1e-7
assert abs(position.y - 20) < 1e-7
display(base)
show()
Геометрия рычага остаётся прежней, меняется её размещение. global_location учитывает всех родителей. После ручного изменения структуры или локальных положений используйте base.location_update(deep=True). view=False подходит для расчётов без обновления отображения; при работе с показанной моделью используйте обычное обновление.
Для анимации создайте сборку до show(), а в callback меняйте координаты звеньев. Примеры находятся в zencad/examples/4.Assemble; взаимодействие с вводом описано в разделе «Анимация».
Кинематическая цепь и дерево
В самом ZenCad есть класс kinematic_chain из zencad.libs.kinematic. Это алгоритмическое представление одного пути в дереве сборки, а не контейнер, в который нужно повторно добавлять детали.
Конструктор kinematic_chain(distant, proxymal=None) идёт от конечного юнита distant по ссылкам parent к начальному proxymal, включая его. Если начало не указано, обход продолжается до корня. Имя аргумента proxymal приведено в том написании, которое используется в API; указанный юнит должен быть предком конечного.
getchain()возвращает все юниты пути, включая неподвижные промежуточные.kinematic_pairsсодержит только кинематические звенья;chain[i]обращается к этому списку.- Порядок — от выхода к основанию. В таком же порядке задаются приращения и располагаются столбцы матриц для однокоординатных звеньев.
Отдельного класса kinematic_tree нет. Дерево задаётся связями unit: у одного входа или выхода может быть несколько потомков. Для каждого интересующего рабочего органа можно построить свою цепь к общему основанию. При изменении общей для ветвей координаты меняются положения всех зависимых потомков.
Чувствительности и матрица Якоби
Цепь вычисляет локальную зависимость движения рабочего органа от изменения координат звеньев:
| Метод | Результат |
|---|---|
sensivity(basis=None) |
Список объектов screw с угловой частью .ang и линейной .lin |
sensivity_jacobian(basis=None) |
NumPy-матрица 6 × N: угловые компоненты в первых трёх строках, линейные в последних |
translation_sensivity_jacobian(basis=None) |
NumPy-матрица 3 × N линейных компонентов |
apply_step(increments) |
Прибавляет приращения к координатам в порядке столбцов Якобиана |
apply(speeds, delta) |
Прибавляет speed * delta к каждой координате |
Написание sensivity сохранено в именах методов API. Без basis чувствительности выражены в системе конечного юнита; basis=base выражает их в системе указанного юнита. Положения дерева должны быть обновлены перед расчётом. Приращения и скорости передавайте по одному значению на столбец Якобиана: звенья идут в порядке kinematic_pairs, координаты внутри каждого звена — в обратном порядке относительно set_coords().
from zencad import *
from zencad.assemble import unit, rotator, actuator
from zencad.libs.kinematic import kinematic_chain
base = unit()
hinge = rotator(axis=(0, 0, 1), parent=base)
slide = actuator(
axis=(1, 0, 0), parent=hinge.output, location=translate(10, 0, 0)
)
tip = unit(parent=slide.output)
base.location_update(deep=True, view=False)
slide.set_coord(2, view=False)
chain = kinematic_chain(tip, proxymal=base)
assert chain.kinematic_pairs == [slide, hinge]
jacobian = chain.translation_sensivity_jacobian(basis=base)
assert jacobian.shape == (3, 2)
assert abs(jacobian[0, 0] - 1) < 1e-7
assert abs(jacobian[1, 1] - 12) < 1e-7
chain.apply_step([1, 0])
assert abs(tip.global_location.translation().x - 13) < 1e-7
Здесь первый столбец описывает линейный привод, второй — шарнир. При исходном вылете 12 малый поворот шарнира даёт линейную скорость по Y с коэффициентом 12.
Есть и методы для отслеживания локальной системы внутри промежуточного юнита: sensivity2(body, local, basis=None), sensitivity_jacobian2(...) и translation_sensitivity_jacobian2(...). Они позволяют анализировать положение, отличающееся от конечной системы цепи, в том числе вход звена или юнит соседней ветви. Координаты, не влияющие на выбранный юнит, дают нулевые столбцы. basis меняет систему выражения абсолютной скорости; движение самого базиса из результата не вычитается.
В zencad/examples/4.Assemble/robot-arm.py рука из поворотных звеньев автоматически следует за красным шариком на замкнутой пространственной траектории. Линейный Якобиан и метод наименьших квадратов с демпфированием задают скорости суставов; chain.apply() применяет их в порядке столбцов матрицы. Пример управляет положением конца руки, не его ориентацией.
Двухкоординатные звенья
spherical_rotator имеет две координаты: set_coords([yaw, pitch]) задаёт поворот rotateZ(yaw) * rotateY(pitch). Углы задаются в радианах; доступны отдельные set_yaw() и set_pitch(). dim() возвращает 2, get_coords() — (yaw, pitch). senses() возвращает угловые чувствительности по yaw и pitch в системе выхода звена; они учитывают текущие углы. Это двухосевой поворот, а не параметризация произвольной ориентации тремя координатами.
planemover.set_coords([x, y]) задаёт перемещение по XY; get_coords() возвращает (x, y), а senses() — линейные чувствительности по X и Y.
Цепь сохраняет обратный порядок чувствительностей внутри пары: для spherical_rotator это pitch, затем yaw; для planemover — Y, затем X. apply_step() и apply() поддерживают смешанные цепи из этих звеньев, rotator и actuator. Длина входного вектора равна сумме dim() звеньев. Размер и конечность значений проверяются до изменения координат.