Управления роботом Moveo в ROS ч.2 - инверсная кинематика с IKPy, управление роботом с помощью джойстика в Rviz
Теперь необходимо решить две задачи:
- задавать с помощью контролера координаты цели в трехмерном пространстве, куда должен попасть захват манипулятора: положение и ориентация;
- решить обратную кинематическую задачу по преобразованию координат цели в углы между звеньями робота;
- и здесь же подзадача - оценить ошибку позиционирования захвата относительно цели.
Пакет moveo_ps3 для этого этапа можно скачать на GitHub. Пройдемся по его основным моментам. Скажу сразу, что без изменений остались только 3D модели (meshes).
Начать нужно с файла URDF. На этом этапе используется дополненный moveo_ps3_extended.urdf, в котором добавлено два пустых звена, позволяющих определить новые системы координат (СК, frames): "world" - глобальная СК сцены и "end_effector" - СК захвата, она находится между его пальцами:
Пакет запускается с помощью launch файла moveo_ps3_ikpy_tf.launch:
$ roslaunch moveo_ps3 moveo_ps3_ikpy_tf.launch
Граф узлов пакета (rqt_graph) выглядит следующим образом:
Состояние осей и кнопок контроллера передается узлом /joy в топике /joy (подробней об этом можно посмотреть в документации joy_node, а также в одном из моих предыдущих постов про пакет urdf_test).
В контроллере я использую следующие элементы:
Обработчик входящих сообщений joy_collback() транслирует состояния элементов контроллера в переменные класса, откуда их берет для публикации паблишер pub_target_state(). При этом параметры change_position / orientation имеют смысл смещений цели, которые далее в узле /target_positioner с учетом скорости пересчитываются в глобальные координаты цели. Посмотреть содержимое сообщений в топике /ps3_state можно с помощью команды
$ rostopic echo /ps3_state
Еще из особенностей работы узла /ps3_handle: реакция на все кнопки, кроме, reset pos / orient сделана в виде триггера-защелки - их состояние переключается и удерживается. Состояния reset меняются однократно после нажатия кнопок, после чего сбрасываются.
Далее по графу узлов, сообщения в топике /ps3_state поступают в три узла: /target_positioner, /target_marker и /moveo_ikpy.
Узел /target_positioner отвечает за пересчет смещений цели в их координаты в глобальной СК "world" - см. функцию ps3_state_callback():
Полученное перемещение просто суммируется с текущим, учитывая скорость. С ориентацией несколько сложнее: она определяется как три угла Эйлера (отн. X - тангаж, отн. Y - крен, отн. Z - рыскание), при этом, мне хотелось сделать сферическое вращение, при котором в качестве осей вращения используются оси подвижной СК цели. Так, на мой взгляд, удобней поворачивать цель - всегда есть возможность сделать поворот относительно какой-то одной из осей. Однако, есть и другие способы определить поворот, например, если переставить в функции quaternion_multiply() местами кватернионы, то вращение будет происходить относительно осей глобальной СК. Также при конвертации ушлов Эйлера в кватернион выбрать преобразование 'rXYZ' - вращающаяся СК, в
которой поворот отн. осей X, Y рассчитывается в глобальной СК, а поворот
отн. Z - в СК, вращающейся вместе с целью. Т.к. ось Z направлена вдоль
пальцев захвата (его продольная ось), то таким образом можно задавать
вращение захвата, независимо от того, как ориентирована цель. Однако, т.к. вращение захвата производится не в последнем суставе, а в предыдущем, то чтобы обеспечить такое вращение, необходимо, чтобы сразу два звена оказались на одной оси, что далеко не всегда достижимо.
Также в узле /target_positioner определена исходная позиция цели, выставляемая в начале работы и при получении команд reset:
Положение цели в глобальной СК, рассчитанное в узле /target_positioner передается в узел /target_tf2_publisher, который публикует их в единое пространство СК - /tf2. Для этого используется топик /target_pose: координаты в нем передаются в метрах, а ориентация в виде кватерниона. Это позволяет просто транслировать их в /tf2 без дополнительных преобразований.
Оба узла в этой цепочке: /target_positioner и /target_tf2_publisher работают в режиме отклика (spin mode), генератором частоты для которого является публикация сообщений в топике /ps3_state (10 Гц). На данном этапе, параметры частоты работы узлов, а также названия СК задаются непосредственно в узлах, в будущем планирую вынести их в сервер параметров ROS, чтобы все значимые параметры были определены в launch-файле.
Последний узел, необходимый для создания цели - /target_marker, отвечающий за ее визуализацию в Rviz в виде маркера. Он построен на базе класса TargetMarker, в его переменных определены максимальные отклонения перемещения / ориентации, при которых считается, что захват не достиг цели.
Относительное положение систем координат цели "target" и захвата "end_effector" узел получает из /tf2:
на основании чего и подсчитывается отклонение как норма векторов перемещения и вращения:
В зависимости от величины ошибки, а также от тех параметров, коорые узел получает в топике /ps3_state, принята следующая цветовая кодировка:
- зеленый - ошибки позиционирования (перемещение и вращение) не превышают допуска,
- оранжевый - ошибка перемещения в допуске, ошибка вращения больше допуска,
- синий - ошибка перемещения в допуске, ориентирование отключено,
- красный - ошибка перемещения больше допуска,
- серый - расчет кинематики отключен, перемещается только маркер.
Позиция маркера и его параметры публикуются в топике /visualization_marker, на который подписан Rviz (почему-то на графе эта связь отсутствует). Координаты маркера также берутся из /tf2 - и в этом его сила ☺.
Вторая ветка графа обеспечивает, собственно, расчет углов между звеньями робота. Ее элементы также завязаны на /tf2: расчет инверсной кинематики производится в узле /moveo_ikpy, который передает эти значения в узел /robot_state_publisher, по сути, выполняющий функции расчета прямой кинематики и публикации СК звеньев в /tf2.
Рассматривая узел /moveo_ikpy, стоит подробней остановиться на двух моментах:
1. в нем сделана загрузка пути к urdf-файлу из сервера параметров
2. расчет инверсной кинематики с помощью модуля ikpy
В зависимости от параметра calc_orient рассчитывается либо полное позиционирование захвата, либо только совпадение с координатами цели (узел получает их из /tf2).
Для того, чтобы движение манипулятора было плавным, в функцию inverse_kinematics() передается предыдущая позиция робота - initial_position. Дело в том, что данная функция является "оберткой" над основной расчетной функцией inverse_kinematic_optimization, в которой присутствует параметр "starting_nodes_angles (numpy.array) – The initial pose of your chain". Если его не задать, то при каждом обращении обратная кинематика будет рассчитываться от исходной точки (все углы = 0), что приводит к внезапным "скачкам", обусловленным кинематической неопределенностью - когда робот резко перескакивает в другую позицию, удовлетворяющую решению задачи.
Также необходимо обратить внимание на то, что ikpy при расчете использует все соединения (joints) в цепочке urdf, при этом в /robot_state_publisher необходимо передавать только значения подвижных.
На этом по данному этапу, пожалуй, все. Далее буду пробовать управлять уже самим манипулятором - посмотрим, как это будет выглядеть.
Посмотреть как выглядит пакет в действии - можно в этом видео:
Let's go design!
Комментарии
Отправить комментарий