LInKAlphabetDemo / LInK /Solver.py
Open-TO's picture
init
460c05d
import numpy as np
# Dyadic Solution Path Finder
def find_path(A, motor = [0,1], fixed_nodes=[0, 1]):
'''
This function finds the solution path of a dyadic mechanism.
Parameters:
A (np.array): Adjacency matrix of the mechanism.
motor (list): motor nodes.
fixed_nodes (list): List of fixed nodes.
Returns:
path (np.array): Solution path of the mechanism.
status (bool): True if the mechanism is dyadic and has a solution path, False otherwise.
'''
path = []
A,fixed_nodes,motor = np.array(A),np.array(fixed_nodes),np.array(motor)
unkowns = np.array(list(range(A.shape[0])))
knowns = np.concatenate([fixed_nodes,[motor[-1]]])
unkowns = unkowns[np.logical_not(np.isin(unkowns,knowns))]
counter = 0
while unkowns.shape[0] != 0:
if counter == unkowns.shape[0]:
# Non dyadic or DOF larger than 1
return [], False
n = unkowns[counter]
ne = np.where(A[n])[0]
kne = knowns[np.isin(knowns,ne)]
# print(kne.shape[0])
if kne.shape[0] == 2:
path.append([n,kne[0],kne[1]])
counter = 0
knowns = np.concatenate([knowns,[n]])
unkowns = unkowns[unkowns!=n]
elif kne.shape[0] > 2:
#redundant or overconstraint
return [], False
else:
counter += 1
return np.array(path), True
# Dyadic Mechanism Sorting
def get_order(A, motor = [0,1], fixed_nodes=[0, 1]):
'''
This function sorts the mechanism based on the solution path.
Parameters:
A (np.array): Adjacency matrix of the mechanism.
motor (list): motor nodes.
fixed_nodes (list): List of fixed nodes.
Returns:
joint order (np.array): Sorted order of the joints in a mechanism.
'''
path, status = find_path(A, motor, fixed_nodes)
fixed_nodes = np.array(fixed_nodes)
if status:
return np.concatenate([motor,fixed_nodes[fixed_nodes!=motor[0]],path[:,0]])
else:
raise Exception("Non Dyadic or Dof larger than 1")
def sort_mechanism(A, x0, motor = [0,1], fixed_nodes=[0, 1]):
'''
This function sorts the mechanism based on the solution path.
Parameters:
A (np.array): Adjacency matrix of the mechanism.
x0 (np.array): Initial positions of the joints.
motor (list): motor nodes.
fixed_nodes (list): List of fixed nodes.
Returns:
A_s (np.array): Sorted adjacency matrix of the mechanism.
x0 (np.array): Sorted initial positions of the joints.
motor (np.array): Motor nodes.
fixed_nodes (np.array): Fixed nodes.
ord (np.array): Sorted order of the joints in a mechanism.
'''
ord = get_order(A, motor, fixed_nodes)
n_t = np.zeros(A.shape[0])
n_t[fixed_nodes] = 1
A_s = A[ord,:][:,ord]
n_t_s = n_t[ord]
return A_s, x0[ord], np.array([0,1]), np.where(n_t_s)[0], ord
# Vectorized Dyadic Solver
def solve_rev_vectorized_batch_CPU(As,x0s,node_types,thetas):
Gs = np.square((np.expand_dims(x0s,1) - np.expand_dims(x0s,2))).sum(-1)
x = np.zeros([x0s.shape[0],x0s.shape[1],thetas.shape[0],2])
x = x + np.expand_dims(node_types * x0s,2)
m = x[:,0] + np.tile(np.expand_dims(np.swapaxes(np.concatenate([np.expand_dims(np.cos(thetas),0),np.expand_dims(np.sin(thetas),0)],0),0,1),0),[x0s.shape[0],1,1]) * np.expand_dims(np.expand_dims(np.sqrt(Gs[:,0,1]),-1),-1)
m = np.expand_dims(m,1)
m = np.pad(m,[[0,0],[1,x0s.shape[1]-2],[0,0],[0,0]],mode='constant')
x += m
for k in range(3,x0s.shape[1]):
inds = np.argsort(As[:,k,0:k])[:,-2:]
l_ijs = np.linalg.norm(x[np.arange(x0s.shape[0]),inds[:,0]] - x[np.arange(x0s.shape[0]),inds[:,1]], axis=-1)
gik = np.sqrt(np.expand_dims(Gs[np.arange(x0s.shape[0]),inds[:,0],np.ones(shape=[x0s.shape[0]],dtype=int)*k],-1))
gjk = np.sqrt(np.expand_dims(Gs[np.arange(x0s.shape[0]),inds[:,1],np.ones(shape=[x0s.shape[0]],dtype=int)*k],-1))
cosphis = (np.square(l_ijs) + np.square(gik) - np.square(gjk))/(2 * l_ijs * gik)
cosphis = np.where(np.tile(node_types[:,k],[1,thetas.shape[0]])==0.0,cosphis,np.zeros_like(cosphis))
x0i1 = x0s[np.arange(x0s.shape[0]),inds[:,0],np.ones(shape=[x0s.shape[0]]).astype(np.int32)]
x0i0 = x0s[np.arange(x0s.shape[0]),inds[:,0],np.zeros(shape=[x0s.shape[0]]).astype(np.int32)]
x0j1 = x0s[np.arange(x0s.shape[0]),inds[:,1],np.ones(shape=[x0s.shape[0]]).astype(np.int32)]
x0j0 = x0s[np.arange(x0s.shape[0]),inds[:,1],np.zeros(shape=[x0s.shape[0]]).astype(np.int32)]
x0k1 = x0s[:,k,1]
x0k0 = x0s[:,k,0]
s = np.expand_dims(np.sign((x0i1-x0k1)*(x0i0-x0j0) - (x0i1-x0j1)*(x0i0-x0k0)),-1)
phi = s * np.arccos(cosphis)
a = np.transpose(np.concatenate([np.expand_dims(np.cos(phi),0),np.expand_dims(-np.sin(phi),0)],0),axes=[1,2,0])
b = np.transpose(np.concatenate([np.expand_dims(np.sin(phi),0),np.expand_dims(np.cos(phi),0)],0),axes=[1,2,0])
R = np.einsum("ijk...->jki...", np.concatenate([np.expand_dims(a,0),np.expand_dims(b,0)],0))
xi = x[np.arange(x0s.shape[0]),inds[:,0]]
xj = x[np.arange(x0s.shape[0]),inds[:,1]]
scaled_ij = (xj-xi)/np.expand_dims(l_ijs,-1) * np.expand_dims(gik,-1)
x_k = np.squeeze(np.matmul(R, np.expand_dims(scaled_ij,-1))) + xi
x_k = np.where(np.tile(np.expand_dims(node_types[:,k],-1),[1,thetas.shape[0],2])==0.0,x_k,np.zeros_like(x_k))
x_k = np.expand_dims(x_k,1)
x_k = np.pad(x_k,[[0,0],[k,x0s.shape[1]-k-1],[0,0],[0,0]],mode='constant')
x += x_k
return x
# Solve a single mechanism
def solve_mechanism(A, x0 , motor = [0,1], fixed_nodes=[0, 1], thetas = np.linspace(0,2*np.pi,200)):
A,x0,motor,fixed_nodes,ord = sort_mechanism(A, x0, motor, fixed_nodes)
n_t = np.zeros([A.shape[0],1])
n_t[fixed_nodes] = 1
A = np.expand_dims(A,0)
x0 = np.expand_dims(x0,0)
n_t = np.expand_dims(n_t,0)
sol = solve_rev_vectorized_batch_CPU(A,x0,n_t,thetas)
return sol[0], ord