Vector Differentiation¶
Note
You can download this example as a Python script:
differentiation.py
or Jupyter Notebook:
differentiation.ipynb
.
import sympy as sm
import sympy.physics.mechanics as me
sm.init_printing(use_latex='mathjax')
class ReferenceFrame(me.ReferenceFrame):
def __init__(self, *args, **kwargs):
kwargs.pop('latexs', None)
lab = args[0].lower()
tex = r'\hat{{{}}}_{}'
super(ReferenceFrame, self).__init__(*args,
latexs=(tex.format(lab, 'x'),
tex.format(lab, 'y'),
tex.format(lab, 'z')),
**kwargs)
me.ReferenceFrame = ReferenceFrame
Learning Objectives¶
After completing this chapter readers will be able to:
Calculate the partial derivative of a vector with respect to any variable when viewed from any reference frame.
Use the product rule to find the relationship of changing measure numbers and changing unit vectors.
Explain the difference in expressing a vector in a reference frame and taking the derivative of the vector when observed from the reference frame.
Calculate second partial derivatives.
Calculate time derivatives of vector functions.
Partial Derivatives¶
If a vector \(\bar{v}\) is a function of \(n\) scalar variables \(q_1,q_2,\ldots,q_n\) in reference frame \(A\) then the first partial derivatives of \(\bar{v}\) in \(A\) with respect to \(q_r\) where \(r=1\ldots n\) can be formed by applying the product rule of differentation and taking into account that the mutually perpendicular unit vectors fixed in \(A\) do not change when observed from \(A\). The partial derivatives are then:
where \(v_i\) are the measure numbers of \(\bar{v}\) expressed in \(A\) associated with the mutually perpendicular unit vectors \(\hat{a}_1,\hat{a}_2,\hat{a}_3\).
If \(\bar{v}=v_x\hat{a}_x+v_y\hat{a}_y+v_z\hat{a}_z\) the above definition expands to:
Many of the vectors we will work with in multibody dynamics will be a function of a single variable, most often time \(t\). If that is the case, the partial derivative reduces to a single variate derivative:
Warning
A derivative written as \(\frac{\partial \bar{v}}{\partial q_r}\) is meaningless because no reference frame is indicated. The derivative is dependent on which reference frame the change is observed from, so without a reference frame, the derivative cannot be calculated. This is not the case for partial derivatives of scalar expressions, as no reference frame is involved.
The above definition implies that a vector must be expressed in the reference frame one is observing the change from before calculating the partial derivatives of the scalar measure numbers. For example, here is a vector that is expressed with unit vectors from three different reference frames:
alpha, beta = sm.symbols('alpha, beta')
a, b, c, d, e, f = sm.symbols('a, b, c, d, e, f')
A = me.ReferenceFrame('A')
B = me.ReferenceFrame('B')
C = me.ReferenceFrame('C')
B.orient_axis(A, alpha, A.x)
C.orient_axis(B, beta, B.y)
v = a*A.x + b*A.y + c*B.x + d*B.y + e*C.x + f*C.y
v
To calculate \(\frac{{}^A\partial\bar{v}}{\partial \alpha}\) we first need to project the vector \(\bar{v}\) onto the unit vectors of \(A\) and take the partial derivative of those measure numbers with respect to \(\alpha\). The dot product provides the projection and the resulting scalar is differentiated:
dvdalphaAx = v.dot(A.x).diff(alpha)
dvdalphaAx
dvdalphaAy = v.dot(A.y).diff(alpha)
dvdalphaAy
dvdalphaAz = v.dot(A.z).diff(alpha)
dvdalphaAz
We can then construct the vector \(\frac{{}^A\partial \bar{v}}{\partial \alpha}\) from the new measure numbers know that the \(A\) unit vectors are fixed:
dvdalphaA = dvdalphaAx*A.x + dvdalphaAy*A.y + dvdalphaAz*A.z
dvdalphaA
SymPy Mechanics vectors have a special
diff()
method that
manages taking partial derivatives from different reference frames. For the
vector .diff()
method you provide first the variable \(\alpha\)
followed by the reference frame you are observing from:
v.diff(alpha, A)
This gives the identical result as our manually constructed partial derivative above.
Exercise
Calculate \(\frac{{}^B\partial \bar{v}}{\partial e}\) manually and with
diff()
and show the
results are the same.
Solution
dvdeBx = v.dot(B.x).diff(e)
dvdeBy = v.dot(B.y).diff(e)
dvdeBz = v.dot(B.z).diff(e)
dvdeBx*B.x + dvdeBy*B.y + dvdeBz*B.z
v.diff(e, B).express(B)
Warning
What’s the difference in .express() and .diff()?
Any vector can be “expressed” in any reference frame. To express a vector in a reference frame means to project it onto the three mutually perpendicular unit vectors fixed in the reference frame and then to rewrite the vector in terms of measure numbers associated with those three unit vectors using the relevant direction cosine matrix entries. This has nothing to do with differentiation.
We can also take the derivative of a vector when viewed from a specific reference frame. To do so, we observe how the vector changes when viewed from the reference frame and formulate that derivative. Once the derivative is taken, we can express the new vector in any reference frame we desire.
Expressing a vector in a reference frame and taking a derivative of a vector when observered from a reference frame are two different things! Try not to get tripped up by this important distinction.
Product Rule¶
Consider again vector \(\bar{v}=v_x\hat{a}_x+v_y\hat{a}_y+v_z\hat{a}_z\). Previously, only the measure numbers of this vector were scalar functions of \(q_r\). Now consider a reference frame \(N\) that is oriented relative to \(A\) such that the relative orientation also depends on \(q_r\). This means, that when observed from \(N\), the unit vectors \(\hat{a}_x,\hat{a}_y,\hat{a}_z\) may be a function of \(q_r\). With both the measure numbers and unit vectors dependent on \(q_r\) the derivative of \(\bar{v}\) in \(N\) requires the use of the product rule when taking the partial derivative. For example:
The three similar terms with scalar derivatives have the same interpretation of the ones in the prior section.
But the part with unit vector derivatives is more interesting. The partial derivative of a unit vector depends on how it changes. But unit vectors do not change in length, only in orientation.
You will learn in the next chapter how to interpret and use these terms to simplify the calculations of common derivatives. But for now, just be aware of the nature of this partial derivative in \(N\).
The product rule also applies to the dot and cross products:
and generalizes to any series of products. Let \(G=f_1 \cdots f_n\) be a series of products, then:
Second Derivatives¶
\(\frac{{}^A\partial \bar{v}}{\partial q_r}\) is also a vector and, just like \(\bar{v}\), may be a vector function. We can thus calculate the second partial derivative with respect to \(q_s\) where \(s=1\ldots n\). This second partial derivative need not be taken with respect to the same reference frame as the first partial derivative. If we first differentiate with when viewed from \(A\) and then when viewed from \(B\), the second partial derivative is:
Second partials in different reference frames do not necessarily commute:
If the reference frames of each partial derivative are the same, then mixed partials do commute.
Vector Functions of Time¶
In multibody dynamics we are primarily concerned with how motion changes with respect to time \(t\) and our vectors and measure numbers will often be implicit functions of time, i.e. \(q_r(t)\). When that is the case the chain rule can be used to take total derivatives:
Note
We will typically use the “dot” notation for time derivatives, i.e. \(\frac{dq}{dt}\) as \(\dot{q}\) and \(\frac{d^2q}{dt^2}\) as \(\ddot{q}\) and so on.
In SymPy Mechanics, scalar functions of time can be created like so:
t = sm.symbols('t')
q_of = sm.Function('q')
q = q_of(t)
q
And these scalar functions can be differentiated:
q.diff(t)
SymPy Mechanics provides the convenience function
dynamicsymbols()
to create scalar
functions of time just like symbols()
:
q1, q2, q3 = me.dynamicsymbols('q1, q2, q3')
q1, q2, q3
The time variable used in q1,q2,q3
can be accessed like so:
t = me.dynamicsymbols._t
SymPy Mechanics also provide a special printing function
init_vprinting()
which enables
the dot notation on functions of time:
me.init_vprinting(use_latex='mathjax')
q1.diff(t), q2.diff(t, 2), q3.diff(t, 3)
Now these scalar functions of time can be used to formulate vectors:
A = me.ReferenceFrame('A')
B = me.ReferenceFrame('B')
B.orient_body_fixed(A, (q1, q2, q3), 'ZXZ')
v = q1*A.x + q2*A.y + t**2*A.z
v
And the time derivative can be found with:
v.diff(t, A)
Lastly, vectors have a
dt()
method that
calculates time derivatives when viewed from a reference frame, saving a few
characters of typing:
v.dt(A)
We will use time derivatives in the next chapters to formulate velocity and acceleration.