===================== Holonomic Constraints ===================== .. note:: You can download this example as a Python script: :jupyter-download-script:`configuration` or Jupyter Notebook: :jupyter-download-notebook:`configuration`. .. jupyter-execute:: import sympy as sm import sympy.physics.mechanics as me me.init_vprinting(use_latex='mathjax') .. container:: invisible .. jupyter-execute:: 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: - derive and specify the configuration constraint (holonomic constraint) equations for a system of connected rigid bodies - numerically solve a set of holonomic constraints for the dependent coordinates - apply point configuration constraints as a general approach to constraining a system - calculate the number of generalized coordinates - choose generalized coordinates - calculate velocities when holonomic constraints are present Four-Bar Linkage ================ .. todo:: This chapter would be clearer if I made a P_4* and P_4 so we can talk about the points that come into existence when the linkage is cut. Consider the linkage shown below: .. _configuration-four-bar: .. figure:: figures/configuration-four-bar.svg :align: center :width: 600px a) Shows four links in a plane :math:`A`, :math:`B`, :math:`C`, and :math:`N` with respective lengths :math:`l_a,l_b,l_c,l_n` connected in a closed loop at points :math:`P_1,P_2,P_3,P_4`. b) Shows the same linkage that has been separated at point :math:`P_4` to make it an open chain of links. This is a planar `four-bar linkage`_ with reference frames :math:`N,A,B,C` attached to each bar. Four bar linkages are used in a wide variety of mechanisms. One you may be familiar with is this rear suspension on a mountain bicycle: .. _mountain-bike-suspension: .. figure:: https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/MtbFrameGeometry_FSR.png/320px-MtbFrameGeometry_FSR.png :align: center Four bar linkage shown in blue, red, orange, and green used in the rear suspension mechanism of a mountain bicycle. Cartemere, CC BY-SA 3.0 https://creativecommons.org/licenses/by-sa/3.0, via Wikimedia Commons .. _four-bar linkage: https://en.wikipedia.org/wiki/Four-bar_linkage Depending on the length of the links, different motion types are possible. :numref:`grashof-animation` shows some of the possible motions. .. _grashof-animation: .. figure:: https://upload.wikimedia.org/wikipedia/commons/c/ca/Grashof_Type_I_Four-Bar_Kinematic_Inversions.gif :align: center :width: 100% Pasimi, CC BY-SA 4.0 https://creativecommons.org/licenses/by-sa/4.0, via Wikimedia Commons A four bar linkage is an example of a *closed kinematic loop*. We can define this loop by disconnecting the loop at some location, :math:`P_4` in our case, and forming the *open loop* vector equations to points that should coincide. In the case of :numref:`configuration-four-bar` there are two vector paths to point :math:`P_4` from :math:`P_1`, for example: .. math:: :label: vector-loop \bar{r}^{P_4/P_1} & = l_n \hat{n}_x \\ \bar{r}^{P_4/P_1} & = \bar{r}^{P_2/P_1} + \bar{r}^{P_3/P_2} + \bar{r}^{P_4/P_3} = l_a\hat{a}_x + l_b\hat{b}_x + l_c\hat{c}_x For the loop to close, the two vector paths must equate. Keep in mind that we assume that the lengths are constant and the angles change with time. To define this loop in SymPy, setup the variables, reference frames, and points: .. jupyter-execute:: q1, q2, q3 = me.dynamicsymbols('q1, q2, q3') la, lb, lc, ln = sm.symbols('l_a, l_b, l_c, l_n') N = me.ReferenceFrame('N') A = me.ReferenceFrame('A') B = me.ReferenceFrame('B') C = me.ReferenceFrame('C') A.orient_axis(N, q1, N.z) B.orient_axis(A, q2, A.z) C.orient_axis(B, q3, B.z) P1 = me.Point('P1') P2 = me.Point('P2') P3 = me.Point('P3') P4 = me.Point('P4') SymPy Mechanics will warn you if you try to establish a closed loop among a set of points and you should not do that because functions that use points have no way to know which vector path you desire to use. Instead you will establish positions among points on one open leg of the chain: .. jupyter-execute:: P2.set_pos(P1, la*A.x) P3.set_pos(P2, lb*B.x) P4.set_pos(P3, lc*C.x) P4.pos_from(P1) Now, create a vector for the other path to :math:`P_4` outside of the ``Point`` position relationships: .. jupyter-execute:: r_P1_P4 = ln*N.x With both vector paths written, we can form the left hand side of the following equation: .. math:: :label: constraint-expression \left( \bar{r}^{P_2/P_1} + \bar{r}^{P_3/P_2} + \bar{r}^{P_4/P_3} \right) - \bar{r}^{P_4/P_1} = 0 Use :external:py:meth:`~sympy.physics.vector.point.Point.pos_from` for the open loop leg made of points and the additional vector: .. jupyter-execute:: loop = P4.pos_from(P1) - r_P1_P4 loop This "loop" vector expression must equate to zero for our linkage to always be a closed loop. We have a planar mechanism, so we can extract two scalar equations associated with a pair of unit vectors in the plane of the mechanism. We can pick any two non-parallel unit vectors to express the components in, with the intuitive choice being :math:`\hat{n}_x` and :math:`\hat{n}_y`. .. jupyter-execute:: fhx = sm.trigsimp(loop.dot(N.x)) fhx .. jupyter-execute:: fhy = sm.trigsimp(loop.dot(N.y)) fhy For the loop to close, these two expressions must equal zero for all values :math:`q_1,q_2,q_3`. These are two nonlinear equations in three time varying variables called coordinates. The solution can be found if we solve for two of the time varying variables. For example, :math:`q_2` and :math:`q_3` can be solved for in terms of :math:`q_1`. We would then say that :math:`q_2` and :math:`q_3` depend on :math:`q_1`. These two equations are called holonomic constraints, or configuration constraints, because they constrain the kinematic configuration to be a loop. Holonomic constraints take the form of a real valued vector function: .. math:: :label: configuration-constraint \bar{f}_h(q_1, \ldots, q_N, t) = 0 \textrm{ where } \bar{f}_h \in \mathbb{R}^M :math:`N` is number of coordinates that you have used to describe the system and :math:`M` is the number of scalar constraint equations. .. warning:: Holonomic constraints are defined strictly as equations that are function of the :math:`N` time varying coordinates. It is true that these equations are only valid for a limited set of ranges for the constants in the equations, i.e. the lengths of the bars, but the range and combination constraints on the constants are not what we are considering here. Secondly, Eq. :math:numref:`configuration-constraint` does not represent inequality constraints. A coordinate may be constrained to a specific range, e.g. :math:`-\pi