Joshua O'Reilly
Joshua O'Reilly

Reputation: 119

Sympy: 'Mul' object has no attribute 'cos' for layered functions

Summary

I have a Jupyter-Lab notebook that I've been trying to use to develop the Jacobian of a robotic arm symbolically with Sympy. I have expressions that are then used in subsequent expressions, and so forth. I receive the message 'Mul' object has no attribute 'cos' when attempting to run the lambdified final expression

What I've tried

Following the other questions such as

Sympy to numpy causes the AttributeError: 'Symbol' object has no attribute 'cos'

What causes this error (AttributeError: 'Mul' object has no attribute 'cos') in Python?

I've made sure to call sympy using:

import sympy as sp

And then calling everything with the sp prefix:

sp.cos()
sp.pi

This is my code (I apologize for the long bits)

# symbolic analysis
import sympy as sp

# Variables
theta1, theta2, theta3, theta4, theta5, theta6 = sp.symbols('theta1 theta2 theta3 theta4 theta5 theta6')
# Constants (undecided)
a1, a2, a3 = sp.symbols('a1 a2 a3')
d1, d4, d6 = sp.symbols('d1 d4 d6')
# Constants (predetermined)
d2, d3, d5, a4, a5, a6, alpha2, alpha6 = 0, 0, 0, 0, 0, 0, 0, 0
alpha1, alpha3, alpha5 = (sp.pi/2,)*3
alpha4 = -sp.pi/2

# rotation around x
def rotx(angle):
    return sp.Matrix([[1, 0, 0, 0], [0, sp.cos(angle), -sp.sin(angle), 0], [0, sp.sin(angle), sp.cos(angle), 0], [0, 0, 0, 1]])

# rotation around y
def roty(angle):
    return sp.Matrix([[sp.cos(angle), 0, sp.sin(angle), 0], [0, 1, 0, 0], [-sp.sin(angle), 0, sp.cos(angle), 0], [0, 0, 0, 1]])

# rotation around z
def rotz(angle):
    return sp.Matrix([[sp.cos(angle), -sp.sin(angle), 0, 0], [sp.sin(angle), sp.cos(angle), 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])

# Translation in either x, y or z
def trans(x,y,z):
    return sp.Matrix([[1, 0, 0, x], [0, 1, 0, y], [0, 0, 0, z], [0, 0, 0, 1]])

def dh_matrix(d, theta, a, alpha):
    return trans(0,0,d)*rotz(theta)*trans(a,0,0)*rotx(alpha)

t_0_1 = dh_matrix(d1, theta1, a1, alpha1)
t_1_2 = dh_matrix(d2, theta2, a2, alpha2)
t_2_3 = dh_matrix(d3, theta3, a3, alpha3)
t_3_4 = dh_matrix(d4, theta4, a4, alpha4)
t_4_5 = dh_matrix(d5, theta5, a5, alpha5)
t_5_6 = dh_matrix(d6, theta6, a6, alpha6)
t_0_6 = t_0_1 * t_1_2 * t_2_3 * t_3_4 * t_4_5 * t_5_6

last_column = t_0_6.col(-1)
x = last_column.row(0)
y = last_column.row(1)
z = last_column.row(2)

# Partial derivatives of x
dxd1 = sp.diff(x,theta1)
dxd2 = sp.diff(x,theta2)
dxd3 = sp.diff(x,theta3)
dxd4 = sp.diff(x,theta4)
dxd5 = sp.diff(x,theta5)
dxd6 = sp.diff(x,theta6)
# Partial derivates of y
dyd1 = sp.diff(y,theta1)
dyd2 = sp.diff(y,theta2)
dyd3 = sp.diff(y,theta3)
dyd4 = sp.diff(y,theta4)
dyd5 = sp.diff(y,theta5)
dyd6 = sp.diff(y,theta6)
# Partial derivates of z
dzd1 = sp.diff(z,theta1)
dzd2 = sp.diff(z,theta2)
dzd3 = sp.diff(z,theta3)
dzd4 = sp.diff(z,theta4)
dzd5 = sp.diff(z,theta5)
dzd6 = sp.diff(z,theta6)

jacobian = sp.Matrix([[dxd1,dxd2,dxd3,dxd4,dxd5,dxd6], [dyd1,dyd2,dyd3,dyd4,dyd5,dyd6], [dzd1,dzd2,dzd3,dzd4,dzd5,dzd6]])
jacobian_numeric = sp.lambdify([theta1,theta2,theta3,theta4,theta5,theta6,a1,a2,a3,d1,d4,d6],jacobian)
jacobian_numeric(0,sp.pi/2,0,0,0,0,0,0,0,1,0.3,0.1)

Expected Output

A 3x6 matrix containing numeric values

Actual Output

The following error attributed to the final line:

AttributeError: 'Mul' object has no attribute 'cos'

I figure it has to do with how I have functions and multiple expressions all twisted together, but I'm not quite sure how to go about founding where exactly it's failing.

Traceback

It's kind of gigantic; I cut out the rest of the expression with ...

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-21-d0f45c1787c2> in <module>
----> 1 current_jacobian = jacobian_numeric(0,sp.pi/2,0,0,0,0,0,0,0,1,0.3,0.1) # add pi/2 to theta2 because of the initial offset between x_2 and x_3
      2 current_jacobian

<lambdifygenerated-1> in _lambdifygenerated(theta1, theta2, theta3, theta4, theta5, theta6, a1, a2, a3, d1, d4, d6)
      1 def _lambdifygenerated(theta1, theta2, theta3, theta4, theta5, theta6, a1, a2, a3, d1, d4, d6):
----> 2     return (array([[array([[-a1*sin(theta1) - a2*sin(theta1)*cos(theta2) + a3*sin(theta1)*sin(theta2)*sin(theta3) - a3*sin(theta1)*cos(theta2)*cos(theta3) +...

AttributeError: 'Mul' object has no attribute 'cos'

Upvotes: 3

Views: 1250

Answers (2)

Tristan Reid
Tristan Reid

Reputation: 6154

After you use lambdify to create an executable function, you are no longer in the 'symbolic math' world of sympy, and have to pass in numeric values, not symbolic ones.

You can either pass in the 𝜋 from numpy as np.pi (as you noted in BenT's answer), or you can numerically evaluate the 𝜋 from sympy as sp.N(sp.pi). The N function converts a symbolic value into a numeric one, including values like 𝜋.

Upvotes: 1

BenT
BenT

Reputation: 3200

I am not entirely sure why, but when I replace sp.pi with np.pi in the last line of the code jacobian_numeric(0,np.pi/2,0,0,0,0,0,0,0,1,0.3,0.1) the error goes away. I have seen a couple other questions with similar problems but no good answer at this point. This could be a bug in simpy or hopefully someone else has a better explanation.

Upvotes: 2

Related Questions