# Purpose
[![PyPI version](https://badge.fury.io/py/mechanism.svg)](https://badge.fury.io/py/mechanism)
[![Downloads](https://static.pepy.tech/badge/mechanism)](https://pepy.tech/project/mechanism)
[![Downloads](https://static.pepy.tech/badge/mechanism/month)](https://pepy.tech/project/mechanism)
This package was created to aid with the designing process of mechanisms involving linkages, cams, and gears. In regard
to linkages, it is capable of implementing a kinematic analysis with the knowledge of the degrees of freedom for the
vectors that make up the mechanism. With the aid of numerical solving and iteration, the position, velocity, and
acceleration of these vectors and points may be acquired.
In regard to cams, this package is capable of supplying coordinates of a cam profile, plotting SVAJ diagrams, and
getting a cam and follower animation for roller and flat faced followers. In turn, the coordinates may be supplied to a
machinist or imported into SolidWorks. All that is needed to know is the motion description (i.e. rise 2 inches in 1
second, dwell for 1.5 seconds, fall 2 inches in 3 seconds). As of right now, the kinds of motion supported are
naive/uniform motion (how the cam shouldn't be designed), harmonic motion, and cycloidal motion. It is possible that
this gets updated in the future with better options such as modified sinusoidal motion.
For gears, this package is capable of providing the coordinates of a spur gear tooth profile given a set of properties.
The analysis is based on the diametral pitch, number of teeth, and pitch diameter if desired over the number of teeth.
An argument for AGMA standards may be set to `True` if desired.
Install this package via pip: `pip install mechanism`.
# Results/Examples
`fourbarlinkage.py`
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/fourbarlinkage.gif)
`fivebarlinkage.py`
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/fivebarlinkage.gif)
`crunode_coupler.py`
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/crunode_coupler.gif)
`crankslider.py`
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/crankslider.gif)
`engine.py`
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/engine.gif)
`non_grashof.py`
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/non_grashof.gif)
`offset_crankslider.py`
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/offset_crankslider.gif)
`cam2_example.py`
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/cam2.gif)
# Linkages, Cranks, Couplers, and Rockers
In order to use the contents of `mechanism.py`, a basic knowledge of vector loops must be known. The structure of the
vector loops function is shown in several files under the `examples` folder. To gain a greater understanding of this
package's usage, this walk through is provided.
## Four Bar Linkage Example
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/fourbarlinkage.PNG)
A four bar linkage is the basic building block of all mechanisms. This is similar to how the triangle is the basic
building block of all structures. What defines a mechanism or structure is the system's overall number of degrees of
freedom, and the number of degrees of freedom is determined via Kutzbach’s equation.
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/fourbarlinkage_dof.PNG)
Kutzbach's equation is: *total degrees of freedom = 3(#links - 1) - 2(J1) - J2* where J1 is the number of full joints
(also known as a revolute joint) and J2 is the number of half joints. For this four bar linkage, there are 4 full
joints.
The number of degrees of freedom is: 3(4 - 1) - 2(4) = 1
This means that we need one known input to find the unknowns of the system. This can be explained further with a diagram
of the vectors that make up the four bar linkage.
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/fourbarlinkage_loop.PNG)
From the above image, the vector "a" is the crank. The speed at which it rotates will be considered as the input to the
system, and thus, it is the defining parameter to the system.
The lengths of all the vectors are known. The only two unknowns are the angle that corresponds to vector "b" and "d". It
is important to note that the objects that make up this package are vectors, and the polar form of the vectors is the
main interest.
There is only one loop equation which provides two equations when breaking down the vectors into its components. With
two equations and two unknowns, this system becomes solvable.
### Problem Statement
Consider the four bar linkage shown above. The lengths of a, b, c, and d are 5", 8", 8" and 9". The crank (a) rotates at
a constant 500 RPM. Use `mechanism` to get an animation of this linkage system and plot the angles, angular velocity,
and angular acceleration of vector d as a function of time.
### Solution
The four bar linkage is a grashof linkage because it satisfies the grashof condition (9 + 5 < 8 + 8). This means that
the crank is able to fully rotate. The input can be deduced by integrating and differentiating the constant value of the
constant angular velocity of the crank.
Always begin with defining the joints and vectors.
```python
from mechanism import *
import numpy as np
import matplotlib.pyplot as plt
# Declare the joints that make up the system.
O, A, B, C = get_joints('O A B C')
# Declare the vectors and keep in mind that angles are in radians and start from the positive x-axis.
a = Vector((O, A), r=5)
b = Vector((A, B), r=8)
c = Vector((O, C), r=8, theta=0, style='ground')
d = Vector((C, B), r=9)
```
Always define the vectors in the polar form. The first argument is the joints, and the first joint is the tail of the
vector, and the second is the head. Additionally, extra keyword arguments will be passed to plt.plot() for styling.
By not defining the angles for a vector (like `a`, `b`, and `c`) you are saying that this vector will have a varying
angle and the same is true for the length argument (`r`). If both the length and the angle are defined, as with `c`,
then the vector is stationary and will remain at this length and angle. If niether `r` or `theta` is specified, then you
are saying that the vector changes in length and angle, so you should expect two degrees of freedom for the input of
this vector in the vector loop equations. There should be half as many loop equations as there are unknown. The input
vector "a" does not need to have its known values at its declaration. Instead, it's values will be accounted for in the
loop equation. The next thing to do is to define the known input and guesses for the first iteration of the unknown
values.
```python
# Define the known input to the system.
# For a 500 RMP crank, the time it takes to rotate one rev is 0.12s
time = np.linspace(0, 0.12, 300)
angular_velocity = 50*np.pi/3 # This is 500 RPM in rad/s
theta = angular_velocity*time # Integrate to find the theta
omega = np.full((time.size,), angular_velocity) # Just an array of the same angular velocity
alpha = np.zeros(time.size)
# Guess the unknowns
pos_guess = np.deg2rad([45, 90])
vel_guess = np.array([1000, 1000])
acc_guess = np.array([1000, 1000])
```
The guess values need to be arrays of the same length as the number of unknowns. These arrays will be passed as the
first iteration. The next thing to do is to define the loop function and create the mechanism object.
```python
# Define the loop equation(s)
def loop(x, i):
return a(i) + b(x[0]) - c() - d(x[1])
# Create the mechanism object
mechanism = Mechanism(vectors=(a, b, c, d), origin=O, loops=loop, pos=theta, vel=omega, acc=alpha,
guess=(pos_guess, vel_guess, acc_guess))
```
This example is simpler than most others because there is only one loop equation. For multiple loop equations, it is
important that the function returns a flattened array of the same length as there are unknown, and the indexing of the
first array argument to the loop corresponds to the input guess values. The second argument is the input. It is strongly
encouraged to view the examples for the more rigorous structure of the loop function. The last thing to do is to
call `mechanism.iterate()`, which is necessary if the input from `pos`, `vel`, and `acc` are arrays. If they are not
arrays, then it is assumed that the mechanism at an instant is desired. If this is the case, then
call `mechanism.calculate()` then call `mechanism.plot()` (see `plot_at_instant.py`).
```python
# Call mechanism.iterate() then get and show the animation
mechanism.iterate()
ani, fig_, ax_ = mechanism.get_animation()
# Plot the angles, angular velocity, and angular acceleration of vector d
fig, ax = plt.subplots(nrows=3, ncols=1)
ax[0].plot(time, d.pos.thetas, color='maroon')
ax[1].plot(time, d.vel.omegas, color='maroon')
ax[2].plot(time, d.acc.alphas, color='maroon')
ax[0].set_ylabel(r'$\theta$')
ax[1].set_ylabel(r'$\omega$')
ax[2].set_ylabel(r'$\alpha$')
ax[2].set_xlabel(r'Time (s)')
ax[0].set_title(r'Analysis of $\vec{d}$')
for a in (ax[0], ax[1], ax[2]):
a.minorticks_on()
a.grid(which='both')
fig.set_size_inches(7, 7)
# fig.savefig('../images/analysis_d.png')
plt.show()
```
This will produce the following output:
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/fourbar_animation.gif)
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/analysis_d.png)
# Cams
There are several kinds of motion types for a cam, but there is an important corollary when designing cams: *The jerk
function must be finite across the entire interval (360 degrees)* (Robert Norton's *Design of Machinery*). Usually, the
cycloidal motion type achieves this corollary, but it comes at a cost. It produces an acceleration and velocity that is
typically higher than the other motion types. More motion types are to come later (hopefully).
## Problem Statement
Design a cam using cycloidal motion that has the following motion description:
* Dwell at zero displacement for 90 degrees
* Rise 1 inch in 90 degrees
* Dwell for 90 degrees
* Fall 1 inch in 90 degrees
The cam's angular velocity is 2*pi radians per second. Show the SVAJ diagram as well as the cam's profile. Size the cam
for a roller follower with a radius of 1/2" with a maximum pressure angle of 30 degrees. Also size the cam for a flat
faced follower. Get an animation for both a roller/flat faced follower. Finally, save the coordinates of the profile to
a text file and show the steps for creating a part in SolidWorks.
## Solution
Begin by creating a cam object with the correct motion description.
```python
import numpy as np
from mechanism import Cam
import matplotlib.pyplot as plt
cam = Cam(motion=[
('Dwell', 90),
('Rise', 1, 90),
('Dwell', 90),
('Fall', 1, 90)
], degrees=True, omega=2*np.pi)
```
The motion description is a list of tuples. Each tuple must contain 3 items for rising and falling and two items for
dwelling. The first item of the tuple is a string equal to "Rise", "Fall", or "Dwell" (not case-sensitive). For rise and
fall motion, the second item in the tuple is the distance at which the follower falls or rises. For dwelling, the second
item in the tuple is either the time (in seconds) or angle (in degrees) for which the displacement remains constant. The
third item in the tuple for rising and falling is equivalent to the second item for dwelling. If degrees is set to true,
then the last item in each tuple is interpreted as the angle for which the action occurs. A manual input for the angular
velocity is then required if conducting further analysis via SVAJ.
This is all that's required to call the following methods.
```python
fig1, ax1 = cam.plot(kind='all')
fig2, ax2 = cam.svaj(kind='cycloidal')
plt.show()
```
This produces the following:
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/displacement_plot.png)
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/svaj.png)
Looking at the acceleration plot, there are no vertical lines. This means that there is no infinite derivative at any
instant along the cam's profile; the jerk function is finite across each instant, making this an acceptable motion type.
If a roller follower with a 1/2" radius is desired, an analysis depending on the cam's radius of curvature and pressure
angle can be conducted to determine the base circle of the cam.
```python
roller_analysis = cam.get_base_circle(kind='cycloidal', follower='roller', roller_radius=1/2, max_pressure_angle=30,
plot=True)
fig3, ax3 = cam.profile(kind='cycloidal', base=roller_analysis['Rb'], show_base=True, roller_radius=1/2,
show_pitch=True)
plt.show()
```
Output:
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/pressure_angle.png)
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/roller_profile.png)
For a flat faced follower, the radius of curvature at the point of contact should be positive (or greater than 0.25")
for all theta. There is an option to return the base radius such that the radius of curvature of the cam's profile is
positive for all values of theta (this is the conservative approach).
```python
flat_analysis = cam.get_base_circle(kind='cycloidal', follower='flat', desired_min_rho=0.25)
print(flat_analysis['Rb'])
print(flat_analysis['Min Face Width'])
fig4, ax4 = cam.profile(kind='cycloidal', base=flat_analysis['Rb'], show_base=True)
plt.show()
```
Output:
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/flat_profile.png)
The base circle radius was found to be 1.893" and the minimum face width for the follower was found to be 2.55".
To get the roller animation, call this:
```python
ani, fig5, ax5, follower = cam.get_animation(kind='cycloidal', base=roller_analysis['Rb'], roller_radius=1/2, length=2,
width=3/8, inc=5)
fig6, ax6 = follower.plot()
plt.show()
```
Output:
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/cam_roller.gif)
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/roller_follower_displacement.png)
The graph above shows the actual follower displacement due to the circle having to always be tangent to the surface of
the cam. Note that as a result of this physical limitation, the follower will have higher magnitudes of velocity and
acceleration.
For the flat faced follower,
```python
ani_flat, fig7, ax7, follower = cam.get_animation(kind='cycloidal', base=flat_analysis['Rb'], face_width=2.75, length=2,
width=3/8, inc=5)
fig8, ax8 = follower.plot()
plt.show()
```
Output:
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/cam_flat.gif)
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/flat_follower_displacement.png)
### Getting Coordinates into SolidWorks
Save the coordinates to a text file.
```python
cam.save_coordinates('cam_coordinates.txt', kind='cycloidal', base=1.3, solidworks=True)
```
Select `Curve Through XYZ Points`
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/curve_xyz.png)
The cam profile will always be extended to the front plane due to the manner in which SolidWorks defines the global
coordinates. Next, select browse and choose the saved coordinate file, making sure that text files are able to be seen.
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/select_file.PNG)
Create a sketch on the front plane. Select the curve and then convert entities. The sketch is now projected to the front
plane.
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/front_plane.PNG)
Notice that the sketch is not closed. Add a line to close the sketch, then extrude the sketch.
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/solidworks_cam.PNG)
# Gears
To use this feature, a knowledge of gear nomenclature must be known. Here is a figure from Robert Norton's *Design of
Machinery*:
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/gear_nomenclature.PNG)
For gears, a general rule of thumb is that the base circle must fall below the dedendum circle because the curve below
base circle cannot be an involute curve. This package will send a warning if this occurs, and if it is desired to
continue, the curve below the circle is just a straight line, and undercutting will occur.
For a reference, here are the AGMA (American Gear Manufacturers Association) standards from *Design of Machinery*:
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/agma.PNG)
## Problem Statement
Design a gear that has a diametral pitch of 32 and has 60 teeth using `mechanism`. The gear follows the AGMA standards.
Compare the gear to SolidWorks' gear from the tool library.
## Solution
Define a gear object with the known information and save the coordinates to a file.
```python
from mechanism import SpurGear
import matplotlib.pyplot as plt
gear = SpurGear(N=60, pd=32, agma=True, size=500)
fig, ax = gear.plot()
fig.savefig('../images/gear60.PNG', dpi=240)
plt.show()
gear.save_coordinates(file='gear_tooth_coordinates.txt', solidworks=True)
gear.rundown()
```
output:
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/gear60.PNG)
| Property | Value |
|--------------------------|----------|
| Number of Teeth (N) | 60 |
| Diametral Pitch (pd) | 32.00000 |
| Pitch Diameter (d) | 1.87500 |
| Pitch Radius (r) | 0.93750 |
| Pressure Angle (phi) | 20.00000 |
| Base Radius | 0.88096 |
| Addendum (a) | 0.03125 |
| Dedendum (b) | 0.03906 |
| Circular Tooth Thickness | 0.04846 |
| Circular Space Width | 0.04971 |
| Circular Backlash | 0.00125 |
Keep in mind that the `size` argument refers to the size of the coordinates that make up the involute curve. The more
points, the sharper it is, but SolidWorks sometimes struggles with points being too close together. To fix this issue,
make the size smaller. The default value is 1000.
### SolidWorks Results
Follow the same steps to get the curve into SolidWorks from the cam example. Make sure that the units in SolidWorks
matches the units of the analysis.
![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/gear60_compare.PNG)
The results are a near identical match, and the addendum and dedendum fit perfectly. If analyzed closely, the only
difference is the tooth thickness. The gray gear (the resulting gear from this package) has a slightly larger tooth
thickness compared to SolidWorks' gear. This is due to the fact that SolidWorks doesn't use an involute gear tooth
profile, as gears from the SolidWorks toolbox are for visuals only. Instead, the tooth profile is circular. Their gears
should not be used for manufacturing as this is not accurate at all. The purpose of the involute tooth profile is that
the meshing of gears will always produce a constant angular velocity, even when the gears aren't perfectly placed
tangent to the pitch circles.
Raw data
{
"_id": null,
"home_page": "https://github.com/gabemorris12/mechanism",
"name": "mechanism",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": "mechanism, kinematic, cams, linkages, analysis, animations",
"author": "Gabe Morris",
"author_email": "gabemorris1231@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/57/b9/16bc82fd64baabb53566993b512ae297f770cefe5be35248d1467de1dae9/mechanism-1.1.8.tar.gz",
"platform": null,
"description": "# Purpose\n[![PyPI version](https://badge.fury.io/py/mechanism.svg)](https://badge.fury.io/py/mechanism)\n[![Downloads](https://static.pepy.tech/badge/mechanism)](https://pepy.tech/project/mechanism)\n[![Downloads](https://static.pepy.tech/badge/mechanism/month)](https://pepy.tech/project/mechanism)\n\nThis package was created to aid with the designing process of mechanisms involving linkages, cams, and gears. In regard\nto linkages, it is capable of implementing a kinematic analysis with the knowledge of the degrees of freedom for the\nvectors that make up the mechanism. With the aid of numerical solving and iteration, the position, velocity, and\nacceleration of these vectors and points may be acquired.\n\nIn regard to cams, this package is capable of supplying coordinates of a cam profile, plotting SVAJ diagrams, and\ngetting a cam and follower animation for roller and flat faced followers. In turn, the coordinates may be supplied to a\nmachinist or imported into SolidWorks. All that is needed to know is the motion description (i.e. rise 2 inches in 1\nsecond, dwell for 1.5 seconds, fall 2 inches in 3 seconds). As of right now, the kinds of motion supported are\nnaive/uniform motion (how the cam shouldn't be designed), harmonic motion, and cycloidal motion. It is possible that\nthis gets updated in the future with better options such as modified sinusoidal motion.\n\nFor gears, this package is capable of providing the coordinates of a spur gear tooth profile given a set of properties.\nThe analysis is based on the diametral pitch, number of teeth, and pitch diameter if desired over the number of teeth.\nAn argument for AGMA standards may be set to `True` if desired.\n\nInstall this package via pip: `pip install mechanism`.\n\n# Results/Examples\n\n`fourbarlinkage.py`\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/fourbarlinkage.gif)\n\n`fivebarlinkage.py`\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/fivebarlinkage.gif)\n\n`crunode_coupler.py`\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/crunode_coupler.gif)\n\n`crankslider.py`\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/crankslider.gif)\n\n`engine.py`\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/engine.gif)\n\n`non_grashof.py`\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/non_grashof.gif)\n\n`offset_crankslider.py`\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/offset_crankslider.gif)\n\n`cam2_example.py`\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/cam2.gif)\n\n# Linkages, Cranks, Couplers, and Rockers\n\nIn order to use the contents of `mechanism.py`, a basic knowledge of vector loops must be known. The structure of the\nvector loops function is shown in several files under the `examples` folder. To gain a greater understanding of this\npackage's usage, this walk through is provided.\n\n## Four Bar Linkage Example\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/fourbarlinkage.PNG)\n\nA four bar linkage is the basic building block of all mechanisms. This is similar to how the triangle is the basic\nbuilding block of all structures. What defines a mechanism or structure is the system's overall number of degrees of\nfreedom, and the number of degrees of freedom is determined via Kutzbach\u2019s equation.\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/fourbarlinkage_dof.PNG)\n\nKutzbach's equation is: *total degrees of freedom = 3(#links - 1) - 2(J1) - J2* where J1 is the number of full joints\n(also known as a revolute joint) and J2 is the number of half joints. For this four bar linkage, there are 4 full\njoints.\n\nThe number of degrees of freedom is: 3(4 - 1) - 2(4) = 1\n\nThis means that we need one known input to find the unknowns of the system. This can be explained further with a diagram\nof the vectors that make up the four bar linkage.\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/fourbarlinkage_loop.PNG)\n\nFrom the above image, the vector \"a\" is the crank. The speed at which it rotates will be considered as the input to the\nsystem, and thus, it is the defining parameter to the system.\n\nThe lengths of all the vectors are known. The only two unknowns are the angle that corresponds to vector \"b\" and \"d\". It\nis important to note that the objects that make up this package are vectors, and the polar form of the vectors is the\nmain interest.\n\nThere is only one loop equation which provides two equations when breaking down the vectors into its components. With\ntwo equations and two unknowns, this system becomes solvable.\n\n### Problem Statement\n\nConsider the four bar linkage shown above. The lengths of a, b, c, and d are 5\", 8\", 8\" and 9\". The crank (a) rotates at\na constant 500 RPM. Use `mechanism` to get an animation of this linkage system and plot the angles, angular velocity,\nand angular acceleration of vector d as a function of time.\n\n### Solution\n\nThe four bar linkage is a grashof linkage because it satisfies the grashof condition (9 + 5 < 8 + 8). This means that\nthe crank is able to fully rotate. The input can be deduced by integrating and differentiating the constant value of the\nconstant angular velocity of the crank.\n\nAlways begin with defining the joints and vectors.\n\n```python\nfrom mechanism import *\nimport numpy as np\nimport matplotlib.pyplot as plt\n\n# Declare the joints that make up the system.\nO, A, B, C = get_joints('O A B C')\n\n# Declare the vectors and keep in mind that angles are in radians and start from the positive x-axis.\na = Vector((O, A), r=5)\nb = Vector((A, B), r=8)\nc = Vector((O, C), r=8, theta=0, style='ground')\nd = Vector((C, B), r=9)\n```\n\nAlways define the vectors in the polar form. The first argument is the joints, and the first joint is the tail of the\nvector, and the second is the head. Additionally, extra keyword arguments will be passed to plt.plot() for styling.\n\nBy not defining the angles for a vector (like `a`, `b`, and `c`) you are saying that this vector will have a varying\nangle and the same is true for the length argument (`r`). If both the length and the angle are defined, as with `c`,\nthen the vector is stationary and will remain at this length and angle. If niether `r` or `theta` is specified, then you\nare saying that the vector changes in length and angle, so you should expect two degrees of freedom for the input of\nthis vector in the vector loop equations. There should be half as many loop equations as there are unknown. The input\nvector \"a\" does not need to have its known values at its declaration. Instead, it's values will be accounted for in the\nloop equation. The next thing to do is to define the known input and guesses for the first iteration of the unknown\nvalues.\n\n```python\n# Define the known input to the system.\n# For a 500 RMP crank, the time it takes to rotate one rev is 0.12s\ntime = np.linspace(0, 0.12, 300)\nangular_velocity = 50*np.pi/3 # This is 500 RPM in rad/s\n\ntheta = angular_velocity*time # Integrate to find the theta\nomega = np.full((time.size,), angular_velocity) # Just an array of the same angular velocity\nalpha = np.zeros(time.size)\n\n# Guess the unknowns\npos_guess = np.deg2rad([45, 90])\nvel_guess = np.array([1000, 1000])\nacc_guess = np.array([1000, 1000])\n```\n\nThe guess values need to be arrays of the same length as the number of unknowns. These arrays will be passed as the\nfirst iteration. The next thing to do is to define the loop function and create the mechanism object.\n\n```python\n# Define the loop equation(s)\ndef loop(x, i):\n return a(i) + b(x[0]) - c() - d(x[1])\n\n\n# Create the mechanism object\nmechanism = Mechanism(vectors=(a, b, c, d), origin=O, loops=loop, pos=theta, vel=omega, acc=alpha,\n guess=(pos_guess, vel_guess, acc_guess))\n```\n\nThis example is simpler than most others because there is only one loop equation. For multiple loop equations, it is\nimportant that the function returns a flattened array of the same length as there are unknown, and the indexing of the\nfirst array argument to the loop corresponds to the input guess values. The second argument is the input. It is strongly\nencouraged to view the examples for the more rigorous structure of the loop function. The last thing to do is to\ncall `mechanism.iterate()`, which is necessary if the input from `pos`, `vel`, and `acc` are arrays. If they are not\narrays, then it is assumed that the mechanism at an instant is desired. If this is the case, then\ncall `mechanism.calculate()` then call `mechanism.plot()` (see `plot_at_instant.py`).\n\n```python\n# Call mechanism.iterate() then get and show the animation\nmechanism.iterate()\nani, fig_, ax_ = mechanism.get_animation()\n\n# Plot the angles, angular velocity, and angular acceleration of vector d\nfig, ax = plt.subplots(nrows=3, ncols=1)\nax[0].plot(time, d.pos.thetas, color='maroon')\nax[1].plot(time, d.vel.omegas, color='maroon')\nax[2].plot(time, d.acc.alphas, color='maroon')\n\nax[0].set_ylabel(r'$\\theta$')\nax[1].set_ylabel(r'$\\omega$')\nax[2].set_ylabel(r'$\\alpha$')\n\nax[2].set_xlabel(r'Time (s)')\nax[0].set_title(r'Analysis of $\\vec{d}$')\n\nfor a in (ax[0], ax[1], ax[2]):\n a.minorticks_on()\n a.grid(which='both')\n\nfig.set_size_inches(7, 7)\n# fig.savefig('../images/analysis_d.png')\n\nplt.show()\n```\n\nThis will produce the following output:\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/fourbar_animation.gif)\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/analysis_d.png)\n\n# Cams\n\nThere are several kinds of motion types for a cam, but there is an important corollary when designing cams: *The jerk\nfunction must be finite across the entire interval (360 degrees)* (Robert Norton's *Design of Machinery*). Usually, the\ncycloidal motion type achieves this corollary, but it comes at a cost. It produces an acceleration and velocity that is\ntypically higher than the other motion types. More motion types are to come later (hopefully).\n\n## Problem Statement\n\nDesign a cam using cycloidal motion that has the following motion description:\n\n* Dwell at zero displacement for 90 degrees\n* Rise 1 inch in 90 degrees\n* Dwell for 90 degrees\n* Fall 1 inch in 90 degrees\n\nThe cam's angular velocity is 2*pi radians per second. Show the SVAJ diagram as well as the cam's profile. Size the cam\nfor a roller follower with a radius of 1/2\" with a maximum pressure angle of 30 degrees. Also size the cam for a flat\nfaced follower. Get an animation for both a roller/flat faced follower. Finally, save the coordinates of the profile to\na text file and show the steps for creating a part in SolidWorks.\n\n## Solution\n\nBegin by creating a cam object with the correct motion description.\n\n```python\nimport numpy as np\nfrom mechanism import Cam\nimport matplotlib.pyplot as plt\n\ncam = Cam(motion=[\n ('Dwell', 90),\n ('Rise', 1, 90),\n ('Dwell', 90),\n ('Fall', 1, 90)\n], degrees=True, omega=2*np.pi)\n```\n\nThe motion description is a list of tuples. Each tuple must contain 3 items for rising and falling and two items for\ndwelling. The first item of the tuple is a string equal to \"Rise\", \"Fall\", or \"Dwell\" (not case-sensitive). For rise and\nfall motion, the second item in the tuple is the distance at which the follower falls or rises. For dwelling, the second\nitem in the tuple is either the time (in seconds) or angle (in degrees) for which the displacement remains constant. The\nthird item in the tuple for rising and falling is equivalent to the second item for dwelling. If degrees is set to true,\nthen the last item in each tuple is interpreted as the angle for which the action occurs. A manual input for the angular\nvelocity is then required if conducting further analysis via SVAJ.\n\nThis is all that's required to call the following methods.\n\n```python\nfig1, ax1 = cam.plot(kind='all')\nfig2, ax2 = cam.svaj(kind='cycloidal')\nplt.show()\n```\n\nThis produces the following:\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/displacement_plot.png)\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/svaj.png)\n\nLooking at the acceleration plot, there are no vertical lines. This means that there is no infinite derivative at any\ninstant along the cam's profile; the jerk function is finite across each instant, making this an acceptable motion type.\n\nIf a roller follower with a 1/2\" radius is desired, an analysis depending on the cam's radius of curvature and pressure\nangle can be conducted to determine the base circle of the cam.\n\n```python\nroller_analysis = cam.get_base_circle(kind='cycloidal', follower='roller', roller_radius=1/2, max_pressure_angle=30,\n plot=True)\nfig3, ax3 = cam.profile(kind='cycloidal', base=roller_analysis['Rb'], show_base=True, roller_radius=1/2,\n show_pitch=True)\nplt.show()\n```\n\nOutput:\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/pressure_angle.png)\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/roller_profile.png)\n\nFor a flat faced follower, the radius of curvature at the point of contact should be positive (or greater than 0.25\")\nfor all theta. There is an option to return the base radius such that the radius of curvature of the cam's profile is\npositive for all values of theta (this is the conservative approach).\n\n```python\nflat_analysis = cam.get_base_circle(kind='cycloidal', follower='flat', desired_min_rho=0.25)\nprint(flat_analysis['Rb'])\nprint(flat_analysis['Min Face Width'])\nfig4, ax4 = cam.profile(kind='cycloidal', base=flat_analysis['Rb'], show_base=True)\nplt.show()\n```\n\nOutput:\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/flat_profile.png)\n\nThe base circle radius was found to be 1.893\" and the minimum face width for the follower was found to be 2.55\".\n\nTo get the roller animation, call this:\n\n```python\nani, fig5, ax5, follower = cam.get_animation(kind='cycloidal', base=roller_analysis['Rb'], roller_radius=1/2, length=2,\n width=3/8, inc=5)\nfig6, ax6 = follower.plot()\nplt.show()\n```\n\nOutput:\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/cam_roller.gif)\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/roller_follower_displacement.png)\n\nThe graph above shows the actual follower displacement due to the circle having to always be tangent to the surface of\nthe cam. Note that as a result of this physical limitation, the follower will have higher magnitudes of velocity and\nacceleration.\n\nFor the flat faced follower,\n\n```python\nani_flat, fig7, ax7, follower = cam.get_animation(kind='cycloidal', base=flat_analysis['Rb'], face_width=2.75, length=2,\n width=3/8, inc=5)\nfig8, ax8 = follower.plot()\nplt.show()\n```\n\nOutput:\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/cam_flat.gif)\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/flat_follower_displacement.png)\n\n### Getting Coordinates into SolidWorks\n\nSave the coordinates to a text file.\n\n```python\ncam.save_coordinates('cam_coordinates.txt', kind='cycloidal', base=1.3, solidworks=True)\n```\n\nSelect `Curve Through XYZ Points`\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/curve_xyz.png)\n\nThe cam profile will always be extended to the front plane due to the manner in which SolidWorks defines the global\ncoordinates. Next, select browse and choose the saved coordinate file, making sure that text files are able to be seen.\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/select_file.PNG)\n\nCreate a sketch on the front plane. Select the curve and then convert entities. The sketch is now projected to the front\nplane.\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/front_plane.PNG)\n\nNotice that the sketch is not closed. Add a line to close the sketch, then extrude the sketch.\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/solidworks_cam.PNG)\n\n# Gears\n\nTo use this feature, a knowledge of gear nomenclature must be known. Here is a figure from Robert Norton's *Design of\nMachinery*:\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/gear_nomenclature.PNG)\n\nFor gears, a general rule of thumb is that the base circle must fall below the dedendum circle because the curve below\nbase circle cannot be an involute curve. This package will send a warning if this occurs, and if it is desired to\ncontinue, the curve below the circle is just a straight line, and undercutting will occur.\n\nFor a reference, here are the AGMA (American Gear Manufacturers Association) standards from *Design of Machinery*:\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/agma.PNG)\n\n## Problem Statement\n\nDesign a gear that has a diametral pitch of 32 and has 60 teeth using `mechanism`. The gear follows the AGMA standards.\nCompare the gear to SolidWorks' gear from the tool library.\n\n## Solution\n\nDefine a gear object with the known information and save the coordinates to a file.\n\n```python\nfrom mechanism import SpurGear\nimport matplotlib.pyplot as plt\n\ngear = SpurGear(N=60, pd=32, agma=True, size=500)\nfig, ax = gear.plot()\nfig.savefig('../images/gear60.PNG', dpi=240)\nplt.show()\ngear.save_coordinates(file='gear_tooth_coordinates.txt', solidworks=True)\ngear.rundown()\n```\n\noutput:\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/gear60.PNG)\n\n| Property | Value |\n|--------------------------|----------|\n| Number of Teeth (N) | 60 |\n| Diametral Pitch (pd) | 32.00000 |\n| Pitch Diameter (d) | 1.87500 |\n| Pitch Radius (r) | 0.93750 |\n| Pressure Angle (phi) | 20.00000 |\n| Base Radius | 0.88096 |\n| Addendum (a) | 0.03125 |\n| Dedendum (b) | 0.03906 |\n| Circular Tooth Thickness | 0.04846 |\n| Circular Space Width | 0.04971 |\n| Circular Backlash | 0.00125 |\n\nKeep in mind that the `size` argument refers to the size of the coordinates that make up the involute curve. The more\npoints, the sharper it is, but SolidWorks sometimes struggles with points being too close together. To fix this issue,\nmake the size smaller. The default value is 1000.\n\n### SolidWorks Results\n\nFollow the same steps to get the curve into SolidWorks from the cam example. Make sure that the units in SolidWorks\nmatches the units of the analysis.\n\n![image not found](https://github.com/gabemorris12/mechanism/raw/master/images/gear60_compare.PNG)\n\nThe results are a near identical match, and the addendum and dedendum fit perfectly. If analyzed closely, the only\ndifference is the tooth thickness. The gray gear (the resulting gear from this package) has a slightly larger tooth\nthickness compared to SolidWorks' gear. This is due to the fact that SolidWorks doesn't use an involute gear tooth\nprofile, as gears from the SolidWorks toolbox are for visuals only. Instead, the tooth profile is circular. Their gears\nshould not be used for manufacturing as this is not accurate at all. The purpose of the involute tooth profile is that\nthe meshing of gears will always produce a constant angular velocity, even when the gears aren't perfectly placed\ntangent to the pitch circles.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A package that provides a kinematic analysis of mechanisms and cams and custom tooth profile for spur gears.",
"version": "1.1.8",
"project_urls": {
"Homepage": "https://github.com/gabemorris12/mechanism"
},
"split_keywords": [
"mechanism",
" kinematic",
" cams",
" linkages",
" analysis",
" animations"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "7dc15b51a13d52e6929bb96fecf49a8a74c1fc5236d979f2bd4e8eefc52213f2",
"md5": "810a9bbc200d4fe6339909b29107f35e",
"sha256": "4d75844fd03ad29f3b51bdd0adb124c7268e6055af9195b4c3c82a33ad980c6d"
},
"downloads": -1,
"filename": "mechanism-1.1.8-py3-none-any.whl",
"has_sig": false,
"md5_digest": "810a9bbc200d4fe6339909b29107f35e",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 35268,
"upload_time": "2024-06-01T00:53:05",
"upload_time_iso_8601": "2024-06-01T00:53:05.202398Z",
"url": "https://files.pythonhosted.org/packages/7d/c1/5b51a13d52e6929bb96fecf49a8a74c1fc5236d979f2bd4e8eefc52213f2/mechanism-1.1.8-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "57b916bc82fd64baabb53566993b512ae297f770cefe5be35248d1467de1dae9",
"md5": "7aef6dbf8c2be2b1a9b94e47cd670760",
"sha256": "de43ef39dea5159f18940709ab5ae38ac4e147a882554a8ce56e9620aad01190"
},
"downloads": -1,
"filename": "mechanism-1.1.8.tar.gz",
"has_sig": false,
"md5_digest": "7aef6dbf8c2be2b1a9b94e47cd670760",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 39639,
"upload_time": "2024-06-01T00:53:07",
"upload_time_iso_8601": "2024-06-01T00:53:07.401718Z",
"url": "https://files.pythonhosted.org/packages/57/b9/16bc82fd64baabb53566993b512ae297f770cefe5be35248d1467de1dae9/mechanism-1.1.8.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-06-01 00:53:07",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "gabemorris12",
"github_project": "mechanism",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "mechanism"
}