Alrighty! Last time I posted about SymPy we weren’t worrying too much about computation speed. It was worth the time saved by automating the Jacobian, inertia matrix, etc calculations and it didn’t affect simulation speed really all that much. But! When working with a real arm it suddenly becomes critical to have highly efficient calculations.
Turns out that I still don’t want to code those equations by hand. There are probably very nice options for generating optimized code using Mathematica or MapleSim or whatever pay software, but SymPy is free and already Python compatible so my goal is to stick with that.
Options
I did some internet scouring, and looked at installing various packages to help speed things up, including
- trying out the Sympy
simplify
function, - trying out SymEngine,
- trying out the Sympy compile to Theano function,
- trying out the Sympy autowrap function, and
- various combinations of the above.
The SymEngine backend and Theano functions really didn’t give any improvements for the kind of low-dimensional vector calculations performed for control here. Disclaimer, it could be the case that I gave up on these implementations too quickly, but from some basic testing it didn’t seem like they were the way to go for this kind of problem.
So down to the simplify
function and compiling to the Cython backend options. First thing you’ll notice when using the simplify
is that the generation time for the function can take orders of magnitude longer (up to 12ish hours for inertia matrices for the Kinova Jaco 2 arm with simplify
vs about 1-2 minutes without simplify
, for example). And then, as a nice kick in the teeth after that, there’s almost no difference between straight Cython of the non-simplified functions and the simplified functions. Here are some graphs:
So you can see how the addition of the simplify
drastically increases the compile time in the top half of the graphs there. In the lower half, you can see that the simplify
does save some execution time relative to the baseline straight lambdify
function, but once you start compiling to Cython with the autowrap
the difference is on the order of hundredths to thousandths of milliseconds.
Results
So! My recommendation is
- Don’t use
simplify
, - do use
autowrap
with the Cython backend.
To compile using the Cython backend:
from sympy.utilities.autowrap import autowrap function = autowrap(sympy_expression, backend="cython", args=parameters)
In the last Sympy post I looked at a hard-coded calculation against the Sympy generated function, using the timeit
function, which runs a given chunk of code 1,000,000 times and tells you how long it took. To save tracking down the results from that post, here are the timeit
results of a Sympy function generated using just the lambdify
function vs the hard-coding the function in straight Numpy:
And now using the autowrap
function to compile to Cython code, compared to hard-coding the function in Numpy:
This is a pretty crazy improvement, and means that we can have the best of both worlds. We can declare our transforms in SymPy and automate the calculation of our Jacobians and inertia matrices, and still have the code execute fast enough that we can use it to control real robots.
That said, this is all from my basic experimenting with some different optimisations, which is a far from thorough exploration of the space. If you know of any other ways to speed up execution, please comment below!
You can find the code I used to generate the above plots up on my GitHub.