2013-07-26 3 views
0

Я создал простую роботизированную руку, используя 3 RC Servos и Arduino. Я просто хочу поиграть с ним и узнать кое-что о робототехнике.Моделирование данных прямой кинематики

В настоящее время я пытаюсь вычислить положение наконечника роботизированной руки, используя три угловых положения сервоприводов. «Передовая кинематика» Я думаю, это технический термин для этого. Кстати, кончик руки - это ручка, мне показалось, что я попытаюсь что-то сделать с ней позже.

В диапазоне движений руки я установил декартову систему координат и записал образцы 24 (угол => положение). pastebin.com/ESqWzJJB

Теперь, я пытаюсь смоделировать эти данные, но я немного из глубины. Вот мой подход:

Я использую уравнения Денавита-Хартенберга, найденные в Википедии en.wikipedia.org/wiki/Denavit-Hartenberg_parameters. Затем я пытаюсь определить параметры, используя оптимизацию наименьших квадратов.

minimize(sum(norm(f(x,P)-y)^2)) 

Я также добавил линейных членов к входу и выходу модели для компенсации возможных искажений (например, фазовый сдвиг в угле серво):

y = f(ax+b)*c+d 

Мой код Python: Pastebin.com/gQF72mQn

from numpy import * 
from scipy.optimize import minimize 

# Denavit-Hartenberg Matrix as found on Wikipedia "Denavit-Hartenberg parameters" 
def DenHarMat(theta, alpha, a, d): 
    cos_theta = cos(theta) 
    sin_theta = sin(theta) 
    cos_alpha = cos(alpha) 
    sin_alpha = sin(alpha) 


    return array([ 
     [cos_theta, -sin_theta*cos_alpha, sin_theta*sin_alpha, a*cos_theta], 
     [sin_theta, cos_theta*cos_alpha, -cos_theta*sin_alpha, a*sin_theta], 
     [0, sin_alpha, cos_alpha, d], 
     [0, 0, 0, 1], 
    ]) 


def model_function(parameters, x): 
    # split parameter vector 
    scale_input, parameters = split(parameters,[3]) 
    translate_input, parameters = split(parameters,[3]) 

    scale_output, parameters = split(parameters,[3]) 
    translate_output, parameters = split(parameters,[3]) 

    p_T1, parameters = split(parameters,[3]) 
    p_T2, parameters = split(parameters,[3]) 
    p_T3, parameters = split(parameters,[3]) 

    # compute linear input distortions 
    theta = x * scale_input + translate_input 

    # load Denavit-Hartenberg Matricies 
    T1 = DenHarMat(theta[0], p_T1[0], p_T1[1], p_T1[2]) 
    T2 = DenHarMat(theta[1], p_T2[0], p_T2[1], p_T2[2]) 
    T3 = DenHarMat(theta[2], p_T3[0], p_T3[1], p_T3[2]) 

    # compute joint transformations 
    # y = T1 * T2 * T3 * [0 0 0 1] 
    y = dot(T1,dot(T2,dot(T3,array([0,0,0,1])))) 

    # compute linear output distortions 
    return y[0:3] * scale_output + translate_output 


# least squares cost function 
def cost_function(parameters, X, Y): 
    return sum(sum(square(model_function(parameters, X[i]) - Y[i])) for i in range(X.shape[0]))/X.shape[0] 


# ========== main script start =========== 

# load data 
data = genfromtxt('data.txt', delimiter=',', dtype='float32') 
X = data[:,0:3] 
Y = data[:,3:6] 


cost = 9999999 

#try: 
# parameters = genfromtxt('parameters.txt', delimiter=',', dtype='float32') 
# cost = cost_function(parameters, X, Y) 
#except IOError: 
# pass 


# random init 
for i in range(100): 
    tmpParams = (random.rand(7*3)*2-1)*8 
    tmpCost = cost_function(tmpParams, X, Y) 
    if tmpCost < cost: 
     cost = tmpCost 
     parameters = tmpParams 
     print('Random Cost: ' + str(cost)) 
     savetxt('parameters.txt', parameters, delimiter=',') 


# optimization 
continueOptimization = True 
while continueOptimization: 
    res = minimize(cost_function, parameters, args=(X,Y), method='nelder-mead', options={'maxiter':100,'xtol': 1e-5}) 
    parameters = res.x 
    print(res.fun) 
    savetxt('parameters.txt', parameters, delimiter=',') 
    continueOptimization = not res.success 


print(res) 

Но он просто не будет работать, ни один из моих попыток не сходились на хорошее решение. Я также пробовал простое матричное умножение 3x4, что не имеет большого смысла в качестве модели, но, как ни странно, это не делало хуже, чем более сложная модель выше.

Я надеюсь, что есть кто-то, кто может помочь.

+0

Можете ли вы опубликовать свой код в ваш вопрос, а не заставить людей перейти на другой сайт, чтобы увидеть его? –

ответ

1

Если я правильно понял вас, вы пытаетесь решить обратную кинематику (IK) вашего робота. Передвижная кинематика (FK) заключается в том, чтобы определить, где находится ваш конечный эффектор, с учетом совместных углов. Вы хотите найти углы, которые делают конечный эффектор достигать желаемой позиции.

Чтобы решить проблему IK, вам нужно выяснить, как двигаться вперед-кинематику вашей руки. Если вы не уверены в своем текущем FK, вы можете использовать следующий скрипт для получения символических матриц FK для каждого сустава (включая конечный эффектор). Он также генерирует якобиан.

import numpy as np 
from sympy import * 

def pos(matrix): 
list = [0,0,0] 
list[0] = matrix[0,3] 
list[1] = matrix[1,3] 
list[2] = matrix[2,3] 
return np.array(list).astype(float).tolist() 

class KinematicChain: 

def __init__(self): 
    self.i = 1 
    self.syms = [] 
    self.types = [] 
    self.matrices = [] 
    self.fk = [] 

def add(self, type, relPos): 
    """ 
    Parameters: 
     type - the type of joint 
     relpos - the position of the joint relative to the previos one 
    """ 
    mat = self.transMatrix(type, relPos); 
    self.matrices.append(mat) 
    self.types.append(type) 
    if len(self.fk) == 0: 
     self.fk.append(eye(4)*mat) 
    else: 
     self.fk.append(simplify(self.fk[-1]*mat)) 


def jacobian(self): 
    fk = self.fk[-1] 

    px = fk[0,3] 
    py = fk[1,3] 
    pz = fk[2,3] 

    f = Matrix([px, py, pz]) 

    if (len(self.syms) < 1): 
     return eye(4) 
    else: 
     x = Matrix(self.syms) 
     ans = f.jacobian(x) 
     return ans 



def transMatrix(self, type, p): 
    if (type != "FIXED"): 
     s1 = "a" + str(self.i) 
     self.i += 1 
     a = symbols(s1) 
     self.syms.append(a) 

    if (type == "FIXED"): 
     return Matrix([ 
     [1, 0, 0, p[0]], 
     [0, 1, 0, p[1]], 
     [0, 0, 1, p[2]], 
     [0, 0, 0, 1]]) 
    elif (type == "RX"): 
     return Matrix([ 
     [1, 0, 0, p[0]], 
     [0, cos(a), -sin(a), p[1]], 
     [0, sin(a), cos(a), p[2]], 
     [0, 0, 0, 1]]) 
    elif (type == "RY"): 
     return Matrix([ 
     [cos(a), 0, sin(a), p[0]], 
     [0, 1, 0, p[1]], 
     [-sin(a), 0, cos(a), p[2]], 
     [0, 0, 0, 1]]) 
    elif (type == "RZ"): 
     return Matrix([ 
     [cos(a), -sin(a), 0, p[0]], 
     [sin(a), cos(a), 0, p[1]], 
     [0, 0, 1, p[2]], 
     [0, 0, 0, 1]]) 
    elif (type == "PX"): 
     return Matrix([ 
     [1, 0, 0, p[0] + a], 
     [0, 1, 0, p[1]], 
     [0, 0, 1, p[2]], 
     [0, 0, 0, 1]]) 
    elif (type == "PY"): 
     return Matrix([ 
     [1, 0, 0, p[0]], 
     [0, 1, 0, p[1] + a], 
     [0, 0, 1, p[2]], 
     [0, 0, 0, 1]]) 
    elif (type == "PZ"): 
     return Matrix([ 
     [1, 0, 0, p[0]], 
     [0, 1, 0, p[1]], 
     [0, 0, 1, p[2] + a], 
     [0, 0, 0, 1]]) 
    else: 
     return eye(4) 

Существует много способов решения задачи IK. Хорошим является метод Damped Least Squared. См.: http://math.ucsd.edu/~sbuss/ResearchWeb/ikmethods/iksurvey.pdf

Простым методом является Cyclic Coordinate Decent, который вполне управляем для работы с arduino с ограниченной поддержкой матрицы. См.: http://www.cs.cmu.edu/~15464-s13/assignments/assignment2/jlander_gamedev_nov98.pdf

0

Я думаю, что вы пытаетесь сделать что-то вроде «калибровки кинематики»: определение параметров робота из набора данных измерений. Есть множество классических учебников, посвященных этой теме, если вы действительно хотите глубже копать, например [Mooring et al.] "Fundamentals of manipulator calibration".

Возврат к вашему вопросу, многие вещи могут привести к тому, что ваша идентификация параметра не сходится, поэтому помните, что это не ответ на поваренную книгу. ;)

Один из возможных случаев - у вас есть два (или более) соединения с параллельными осями. Обычно такая конфигурация используется для более простых роботов, например, в SCARA или PUMA-подобных механизмах. В этом случае с соглашением DH существует бесконечно много способов выбора осевых линий.

Существуют различные подходы к этому, но YMMV. Одна вещь, которую вы можете попробовать, - использовать Хаяти-модифицированная модель DH. Эта модель добавляет еще один параметр «бета» к базовому DH, чтобы справиться с сингулярностью в случае с параллельной осью.

Или вы можете попробовать создать свои собственные «настраиваемые» матрицы преобразования для моделирования вашего механизма. Например, вы можете использовать углы поворота (или углы Эйлера) для представления вращения между суставными осями, затем добавить один параметр длины для достижения следующего соединения и т. Д.

Еще одна вещь, которая привлекла мое внимание, - _scale_output_ , Я думаю, это означает, что у вас может быть несколько решений «длины руки» для данного набора данных. В качестве иллюстрации оба [scale_output=1, arm_length=100] и [scale_output=100, arm_length=1] будут иметь одинаковое положение с одинаковыми углами соединения. Попробуйте удалить scale_output из модели и посмотреть, поможет ли это.

Также вы можете попробовать другие процедуры оптимизации/минимизации. Я успешно использовал scipy.optimize.leastsq() для калибровки кинематики в прошлом.

Надеюсь, это поможет!

0

Видя, что ваша цель - узнать больше о робототехнике, выстроить прочные основы, в первую очередь, поможет вам в долгосрочной перспективе. Скорее всего, вы захотите сначала погрузиться в мир матриц преобразования, чтобы у вас было что-то, что можно было бы преодолеть, когда вы переходите к более сложным темам, таким как DH-таблицы и обратная кинематика.

Вот некоторые видео, которые могут помочь:

https://www.youtube.com/watch?v=xYQpeKYCfGs&list=PLJtm2YNbaY4_rQApwht0ia5r_sx3vaSxv

Смежные вопросы