1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
#!/usr/bin/env python3
## Copyright 2009-2021 Intel Corporation
## SPDX-License-Identifier: Apache-2.0
## Used to generate code in kernels/common/motion_derivative.h
import sympy as sp
import numpy as np
import math
################################################################################
#### Utils
################################################################################
def getTerms(map, key):
if key in map.keys():
return map[key]
return 0
# simple linear interpolation wrapper
def lerp(v0,v1,t):
return v0*(1-t)+v1*t
# custom quaternion to matrix conversion
def to_rotation_matrix(q):
return sp.Matrix([[q.a*q.a + q.b*q.b - q.c*q.c - q.d*q.d, 2*(q.b*q.c - q.a*q.d), 2*(q.b*q.d + q.a*q.c), 0],
[2*(q.b*q.c + q.a*q.d), q.a*q.a - q.b*q.b + q.c*q.c - q.d*q.d, 2*(q.c*q.d - q.a*q.b), 0],
[2*(q.b*q.d - q.a*q.c), 2*(q.c*q.d + q.a*q.b), q.a*q.a - q.b*q.b - q.c*q.c + q.d*q.d, 0],
[0, 0, 0, 1]])
################################################################################
#### Set up symbolic objects
################################################################################
t, theta = sp.symbols("t, theta", real = True)
px0, py0, pz0 = sp.symbols("px0, py0, pz0", real=True) # vertex position at t=0
px1, py1, pz1 = sp.symbols("px1, py1, pz1", real=True) # vertex position at t=1
tx0, ty0, tz0 = sp.symbols("tx0, ty0, tz0", real=True) # translation at t=0
tx1, ty1, tz1 = sp.symbols("tx1, ty1, tz1", real=True) # translation at t=1
qx0, qy0, qz0, qw0 = sp.symbols("qx0, qy0, qz0, qw0", real=True) # quaternion at t=0
qx1, qy1, qz1, qw1 = sp.symbols("qx1, qy1, qz1, qw1", real=True) # quaternion at t=1
# coefficients for upper triangular matrices
s000, s001, s002, s003, s011, s012, s013, s022, s023 = sp.symbols("s000, s001, s002, s003, s011, s012, s013, s022, s023", real=True)
s100, s101, s102, s103, s111, s112, s113, s122, s123 = sp.symbols("s100, s101, s102, s103, s111, s112, s113, s122, s123", real=True)
q0 = sp.Quaternion(qw0, qx0, qy0, qz0)
q1 = sp.Quaternion(qw1, qx1, qy1, qz1)
# assuming that q1 is qperp = normalize(q1-q0*cosTheta), where cosTheta=dot(q0, q1) and theta = acos(cosTheta).
# this simplifies the terms of the symbolic expressions later
qt = q0 * sp.cos(t*theta) + q1 * sp.sin(t*theta)
S0 = sp.Matrix([[s000, s001, s002, s003],
[ 0, s011, s012, s013],
[ 0, 0, s022, s023],
[ 0, 0, 0, 1]])
S1 = sp.Matrix([[s100, s101, s102, s103],
[ 0, s111, s112, s113],
[ 0, 0, s122, s123],
[ 0, 0, 0, 1]])
D0 = sp.Matrix([[1, 0, 0, tx0],
[0, 1, 0, ty0],
[0, 0, 1, tz0],
[0, 0, 0, 1]])
D1 = sp.Matrix([[1, 0, 0, tx1],
[0, 1, 0, ty1],
[0, 0, 1, tz1],
[0, 0, 0, 1]])
p0 = sp.Matrix([px0, py0, pz0, 1])
p1 = sp.Matrix([px1, py1, pz1, 1])
Gamma = lerp(D0, D1, t)*to_rotation_matrix(qt)*lerp(S0, S1, t)*lerp(p0, p1, t)
C = sp.Matrix(np.empty(8)) # 8 coefficients
K = sp.Matrix(np.empty(7)) # 7 inputs
A = sp.Matrix(np.empty(8*7*3)) # 8 coefficients, 7 inputs (1, px0, py0, pz0, px1, py1, pz1), 3 dimensions (x, y, z)
dGamma = sp.diff(Gamma, t)
################################################################################
#### Group the coefficients (this might time a couple of seconds)
################################################################################
# loop over dimensions (x, y, z)
for dim in range(3):
dm = sp.expand(dGamma[dim])
dm = dm.subs(sp.sin(t*theta)*sp.sin(t*theta),(1-sp.cos(2*t*theta))/2) # remove sin(t*theta)^2
dm = dm.subs(sp.cos(t*theta)*sp.cos(t*theta),(1+sp.cos(2*t*theta))/2) # remove cos(t*theta)^2
dm = dm.subs(sp.sin(t*theta)*sp.cos(t*theta),sp.sin(2*t*theta)/2) # remove sin(t*theta)*cos(t*theta)
dm = sp.expand(dm)
# group all terms in the form a + b * cos(2*t*theta) + c * sin(2*t*theta)
dm_cos_sin = sp.collect(dm, (sp.cos(2*t*theta), sp.sin(2*t*theta)), evaluate=False)
# get the terms
coeff_cos = getTerms(dm_cos_sin, sp.cos(2*t*theta))
coeff_sin = getTerms(dm_cos_sin, sp.sin(2*t*theta))
coeff_const = getTerms(dm_cos_sin, 1)
# group the term in the form a + b * t
coeff_const_t = sp.collect(coeff_const, t, evaluate=False)
C[0] = getTerms(coeff_const_t, 1)
C[1] = getTerms(coeff_const_t, t)
# group the term in the form a + b * t + c * t^2
coeff_cos_t = sp.collect(coeff_cos, t, evaluate=False)
C[2] = getTerms(coeff_cos_t, 1)
C[3] = getTerms(coeff_cos_t, t)
C[4] = getTerms(coeff_cos_t, t*t)
# group the term in the form a + b * t + c * t^2
coeff_sin_t = sp.collect(coeff_sin, t, evaluate=False)
C[5] = getTerms(coeff_sin_t, 1)
C[6] = getTerms(coeff_sin_t, t)
C[7] = getTerms(coeff_sin_t, t*t)
for c in range(8):
kc = sp.collect(C[c], (px0, py0, pz0, px1, py1, pz1), evaluate=False)
K[0] = getTerms(kc, 1)
K[1] = getTerms(kc, px0)
K[2] = getTerms(kc, py0)
K[3] = getTerms(kc, pz0)
K[4] = getTerms(kc, px1)
K[5] = getTerms(kc, py1)
K[6] = getTerms(kc, pz1)
for k in range(7):
K[k] = sp.expand(K[k])
K[k] = K[k].subs(qw0*qw0, 1-qx0*qx0-qy0*qy0-qz0*qz0) # clean up substitutions
K[k] = K[k].subs(qw1*qw1, 1-qx1*qx1-qy1*qy1-qz1*qz1) # clean up substitutions
K[k] = sp.simplify(K[k])
A[8*7*dim + c*7 + k] = K[k]
################################################################################
#### Write code to file
################################################################################
from sympy.utilities.codegen import codegen, default_datatypes
from sympy.codegen.ast import real, float32
from sympy.printing.ccode import C99CodePrinter
printer = C99CodePrinter()
# custom code printer that will not generate such nonesene as x^2 -> pow(x, 2)
class CustomCodePrinter(C99CodePrinter):
def _print_Pow(self, expr):
if expr.exp.is_integer and expr.exp > 0 and expr.exp < 5:
return '*'.join([self._print(expr.base) for i in range(expr.exp)])
else:
return super()._print_Pow(expr)
customprinter = CustomCodePrinter()
customprinter.type_aliases[real] = float32 # cosf instead of cos
default_datatypes["float"].cname = "float" # float instead of double
params = [
theta,
tx0, ty0, tz0,
tx1, ty1, tz1,
qw0, qx0, qy0, qz0,
qw1, qx1, qy1, qz1,
s000, s001, s002, s003, s011, s012, s013, s022, s023,
s100, s101, s102, s103, s111, s112, s113, s122, s123]
R = sp.MatrixSymbol("coeff", A.shape[0], A.shape[1])
P = sp.MatrixSymbol('p', len(params), 1)
param_map = dict(zip(params, P))
B = A.xreplace(param_map)
codegen(('motion_derivative_coefficients', sp.Eq(R,B)), language='c', printer=customprinter, prefix='motion_derivative_coefficients', to_files=True)
|