Создание матрицы преобразования из тензоров скалярных углов - PullRequest
0 голосов
/ 06 июня 2018

Исходный вопрос

Я хочу создать пользовательскую функцию Lambda с использованием керас, которая выполняет прямую кинематику шарнирного рычага.

Эта функция имеет набор углов в качестве входных данных и должнавывести вектор, содержащий положение и ориентацию конечного эффектора.

Я мог бы легко создать эту функцию в numpy;но когда я захотел переместить его в Keras, все стало сложно.

Поскольку вход и выход лямбда-функции являются тензорами, все операции должны выполняться с использованием тензоров и внутренних операций.

Проблема в том, что мне нужно создать матрицу преобразования из входных углов.

Я мог бы использовать K.cos и K.sin (K - тензор потока бэкэнда) для вычисления косинусов и синусовуглы.Но проблема в том, как создать тензор, который представляет собой матрицу 4X4, которая содержит несколько ячеек, которые являются просто числами (0 или 1), а другие являются частями тензора.Например, для вращения Z:

T = tf.convert_to_tensor( [[c, -s, 0, dX],
                           [s,  c, 0, dY],
                           [0,  0, 1, dZ],
                           [0,  0, 0, 1]])

Здесь c и s вычисляются с использованием K.cos(input[3]) и K.sin(input[3]).Это не работает.Я получаю:

ValueError: Фигуры должны иметь одинаковый ранг, но равны 1 и 0 От слияния фигуры 1 с другими фигурами.для 'lambda_1 / pack / 0' (op: 'Pack') с формами ввода: [5], [5], [], [].

Есть предложения?


Дальнейшие проблемы

Код, предоставленный @Aldream, работал нормально.Проблема заключается в том, что когда я встраиваю это в слой Lambda, я получаю сообщение об ошибке при компиляции модели.

...
self.model.add(Lambda(self.FK_Keras))
self.model.compile(optimizer="adam", loss='mse', metrics=['mse'])

Как видите, я использую класс, который содержит модель и различные функции.Сначала у меня есть вспомогательная функция, которая вычисляет матрицу преобразования:

def trig_K( angle):
    r = angle*np.pi/180.0
    return K.cos(r), K.sin(r)

def T_matrix_K(rotation, axis="z", translation=K.constant([0,0,0])):
    c, s = trig_K(rotation)

    dX = translation[0]
    dY = translation[1]
    dZ = translation[2]

    if(axis=="z"):
        T = K.stack(  [[c, -s, 0., dX],
                           [s,  c, 0., dY],
                           [0.,  0., 1., dZ],
                           [0.,  0., 0., 1.]], axis=0)
    if(axis=="y"):
        T  = K.stack( [ [c,  0.,-s,  dX],
                           [0., 1., 0., dY],
                           [s,  0., c,  dZ],
                           [0., 0., 0., .1]], axis=0)
    if(axis=="x"):
        T = K.stack( [  [1., 0.,  0., dX],
                           [0., c, -s, dY],
                           [0., s,  c, dZ],
                           [0., 0.,  0., 1.]], axis=0)

    return T

Затем FK_keras вычисляет преобразование конечного эффектора:

def FK_Keras(self, angs):
    # Compute local transformations            
    base_T=T_matrix_K(angs[0],"z",self.base_pos_K)
    shoulder_T=T_matrix_K(angs[1],"y",self.shoulder_pos_K)
    elbow_T=T_matrix_K(angs[2],"y",self.elbow_pos_K)
    wrist_1_T=T_matrix_K(angs[3],"y",self.wrist_1_pos_K)
    wrist_2_T=T_matrix_K(angs[4],"x",self.wrist_2_pos_K)

    # Compute end effector transformation   
    end_effector_T=K.dot(base_T,K.dot(shoulder_T,K.dot(elbow_T,K.dot(wrist_1_T,wrist_2_T))))

    # Compute Yaw, Pitch, Roll of end effector
    y=K.tf.atan2(end_effector_T[1,0],end_effector_T[1,1])
    p=K.tf.atan2(-end_effector_T[2,0],K.tf.sqrt(end_effector_T[2,1]*end_effector_T[2,1]+end_effector_T[2,2]*end_effector_T[2,2]))
    r=K.tf.atan2(end_effector_T[2,1],end_effector_T[2,2])

    # Construct the output tensor [x,y,z,y,p,r]
    output = K.stack([end_effector_T[0,3],end_effector_T[1,3],end_effector_T[2,3], y, p, r], axis=0)
    return output

Здесь self.base_pos_K и другие векторы переводов являются константами:

self.base_pos_K = K.constant(np.array([x,y,z]))

Код Tle застревает в функции компиляции и возвращает эту ошибку:

ValueError: Фигуры должны иметь одинаковый ранг, но равны 1 и 0 Из слияния фигуры 1 с другими фигурами,для 'lambda_1 / stack_1' (op: 'Pack') с формами ввода: [5], [5], [], [].

Я попытался создать быстрый тестовый код, подобный этому:

arm = Bot("")
# Articulation angles
input_data =np.array([90., 180., 45., 25., 25.])
sess = K.get_session()
inp = K.placeholder(shape=(5), name="inp")#)
res = sess.run(arm.FK_Keras(inp),{inp: input_data})

Этот код работает без ошибок.Есть кое-что о интеграции этого в лямбда-слой последовательной модели.

Решенные проблемы

Действительно, проблема была связана с тем, как Keras работает с данными.Он добавляет пакетное измерение, которое следует учитывать при реализации функции.

Я имел дело с этим по-другому, в том числе переопределением T_matrix_K для работы с этим дополнительным измерением, но я думаю, что способ, предложенный @Aldream более элегантен.

Большое спасибо @Aldream.Его ответы были весьма полезны.

1 Ответ

0 голосов
/ 06 июня 2018

Использование K.stack():

import keras
import keras.backend as K

input = K.constant([3.14, 0., 0, 3.14])
dX, dY, dZ = K.constant(1.), K.constant(2.), K.constant(3.)
c, s = K.cos(input[3]), K.sin(input[3])

T = K.stack([[ c, -s, 0., dX],
             [ s,  c, 0., dY],
             [0., 0., 1., dZ],
             [0., 0., 0., 1.]], axis=0
            )

sess = K.get_session()
res = sess.run(T)
print(res)
# [[ -9.99998748e-01  -1.59254798e-03   0.00000000e+00   1.00000000e+00]
#  [  1.59254798e-03  -9.99998748e-01   0.00000000e+00   2.00000000e+00]
#  [  0.00000000e+00   0.00000000e+00   1.00000000e+00   3.00000000e+00]
#  [  0.00000000e+00   0.00000000e+00   0.00000000e+00   1.00000000e+00]]

Как использовать с Lambda:

Слои Keras ожидают / обрабатывают пакетные данные.Керас, например, предположил бы, что вход (angs) вашего слоя Lambda(FK_Keras) имеет форму (batch_size, 5).Таким образом, ваш FK_Keras() должен быть адаптирован для работы с такими входными данными.

Довольно простой способ сделать это, требующий лишь незначительных правок для вашего T_matrix_K(), заключается в использовании K.map_fn() для циклического перемещения по каждому списку.углы в партии и применить соответствующую функцию T_matrix_K() для каждого.

Другие незначительные изменения, связанные с партиями:

  • Использование K.batch_dot() вместо K.dot()
  • Вещание в соответствии с вашими постоянными тензорами, например self.base_pos_K
  • Принимая во внимание дополнительное 1-е измерение для пакетных тензоров, например, заменив end_effector_T[1,0] на end_effector_T[:, 1,0]

Найдите ниже сокращенный рабочий код (расширение для всех соединений оставлено вам):

import keras
import keras.backend as K
from keras.layers import Lambda, Dense
from keras.models import Model, Sequential
import numpy as np


def trig_K( angle):
    r = angle*np.pi/180.0
    return K.cos(r), K.sin(r)

def T_matrix_K_z(x):
    rotation, translation = x[0], x[1]
    c, s = trig_K(rotation)
    T = K.stack(   [[c, -s, 0., translation[0]],
                       [s,  c, 0., translation[1]],
                       [0.,  0., 1., translation[2]],
                       [0.,  0., 0., 1.]], axis=0)
    # We have 2 inputs, so have to return 2 outputs for `K.map_fn()`:
    return T, 0.

def T_matrix_K_y(x):
    rotation, translation = x[0], x[1]
    c, s = trig_K(rotation)
    T = K.stack( [ [c,  0.,-s,  translation[0]],
                       [0., 1., 0., translation[1]],
                       [s,  0., c,  translation[2]],
                       [0., 0., 0., .1]], axis=0)
    # We have 2 inputs, so have to return 2 outputs for `K.map_fn()`:
    return T, 0.

def FK_Keras(angs):
    base_pos_K = K.constant(np.array([1, 2, 3])) # replace with your self.base_pos_K
    shoulder_pos_K = K.constant(np.array([1, 2, 3])) # replace with your self.shoulder_pos_K

    # Manually broadcast your constants to batches:
    batch_size = K.shape(angs)[0]
    base_pos_K = K.tile(K.expand_dims(base_pos_K, 0), (batch_size, 1))
    shoulder_pos_K = K.tile(K.expand_dims(shoulder_pos_K, 0), (batch_size, 1))

    # Compute local transformations, for each list of angles in the batch:
    base_T, _ = K.map_fn(T_matrix_K_z, (angs[:, 0], base_pos_K))
    shoulder_T, _ = K.map_fn(T_matrix_K_y, (angs[:, 1], shoulder_pos_K))
    # ... (repeat with your other joints)

    # Compute end effector transformation, over batch:
    end_effector_T = K.batch_dot(base_T,shoulder_T) # add your other joints

    # Compute Yaw, Pitch, Roll of end effector
    y=K.tf.atan2(end_effector_T[:, 1,0],end_effector_T[:, 1,1])
    p=K.tf.atan2(-end_effector_T[:, 2,0],K.tf.sqrt(end_effector_T[:, 2,1]*end_effector_T[:, 2,1]+end_effector_T[:, 2,2]*end_effector_T[:, 2,2]))
    r=K.tf.atan2(end_effector_T[:, 2,1],end_effector_T[:, 2,2])

    # Construct the output tensor [x,y,z,y,p,r]
    output = K.stack([end_effector_T[:, 0,3],end_effector_T[:, 1,3],end_effector_T[:, 2,3], y, p, r], axis=1)
    return output


# Demonstration:
input_data =np.array([[90., 180., 45., 25., 25.],[90., 180., 45., 25., 25.]])
sess = K.get_session()
inp = K.placeholder(shape=(None, 5), name="inp")#)
res = sess.run(FK_Keras(inp),{inp: input_data})

model = Sequential()
model.add(Dense(5, input_dim=5))
model.add(Lambda(FK_Keras))
model.compile(optimizer="adam", loss='mse', metrics=['mse'])
...