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.
I did some internet scouring, and looked at installing various packages to help speed things up, including
- trying out the Sympy
- 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.
So! My recommendation is
- Don’t use
- do use
autowrapwith 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.