Tolerance Stackups. And how to use Monte Carlo Simulations... | by Marguerite Siboni | Towards Data Science

Imagine you have 2 pucks that you want to fit tightly in an opening. If you just needed these three parts to fit together once, you could measure how tall the pucks were and then make your cut to that size and adjust it until it’s perfect. If you are trying to fit these three parts together a lot of times, that’s when things get dicey. Some of your stacks of pucks could be bigger than your opening and not fit. Some of your stacks of pucks could be much smaller than the opening and your parts will fit looser than you’d like.
You may design your yellow puck to be 30 mm tall, but the pucks will more likely be sized somewhere between 29 and 31 mm tall. In this scenario, your yellow puck height has a tolerance1 of ± 1 mm.
You could measure each part and pair the larger yellow pucks with the smaller red pucks and force everything to work. (This is a process known as binning). This does work, but measuring parts takes time. And as the old adage goes, time = money. Incidentally, time also equals time. So if you want to bin, you will need to be willing to spend a lot of time and money doing it.
This is where tolerance stackups come in. When you have parts that you want to fit in an opening, tolerance stackups are a tool that allow you to determine whether or not your parts will always2 fit in your opening, even if you are making hundreds of thousands of these assemblies.
A tolerance stackup is a way to create a loop that includes each critical dimension in the “stack.” It enables you to look at the dimensions and the tolerances for these values to figure out if your design will work and update accordingly. It typically looks like this:
In situations like the example above, a tolerance stackup is fairly easy to put together and it gives you all the information you need to create a good design. A tolerance stackup stops being quite so useful if:
You have many more dimensions in your stackupYou are looking at the tolerance of round parts and their radial fit.You already know the distribution your parts are coming in at
You have many more dimensions in your stackup
You are looking at the tolerance of round parts and their radial fit.
You already know the distribution your parts are coming in at
Each of these scenarios calls for a Monte Carlo simulation.
In a Monte Carlo simulation, you generate realistic values3 for every dimension that you had included in your tolerance stack up. Once you’ve generated these values, you can add and subtract values going through the same loop to generate a distribution for the critical dimension (in our example, the gap from 4 to 1). Finally, you’ll want to set an intended yield4 and see what that critical dimension is for your yield. This may tell you that you can make your design gap smaller or that you need to make it larger. The distribution might tell you there’s a strong case for binning.
Notice that the piece parts follow the specs of the parts as anticipated in the tolerance stackup. However, the resultant gap is distributed tightly enough that the tolerance on the gap is tighter than the RSS value from the tolerance stackup.
To make things more interesting, let’s look at a radial example.
Imagine you are fitting a plastic peg in to a metal opening as shown to the left. This peg has crush ribs on it5. You’d like to make the gap between the top plastic portion and the top metal portion as small as possible all around without ever crashing in to it.
Seems simple, right? Unfortunately, it’s not. If everything was perfectly round, this would work well. But if you consider the scenarios drawn below, you’ll see that the tolerance stack up is missing some information. These images are exaggerations of realistic scenarios.
A Monte Carlo simulation allows you to simulate a radial angle that each part is off center by and a radial angle that each part is the furthest from round at. By simulating your result, you can account for the times that the off center features coincidentally cause parts to fit and the times that they coincidentally cause parts to interfere. This often leads to a tighter and more realistic resultant tolerance than a tolerance stackup. This is because a tolerance stackup forces you to assume that the problem dimensions are all at the same radial angle when in reality, they almost certainly aren’t.
I have pasted the code I used to generate this simulation below:
# importing librariesimport numpy as np import pandas as pd import matplotlib.pyplot as plt%matplotlib inline # Generating part measurement datadf['purple_r'] = np.random.normal(15.05, .1, 1000)df['purple_off_center'] = np.random.normal(0, .02, 1000)df['angle_purple_off_center'] = np.random.uniform(0, 6.283185, 1000)df['grey_off_center'] = np.random.normal(0, .02, 1000)df['grey_r'] = np.random.normal(15.5, .03, 1000)df['angle_grey_off_center'] = np.random.uniform(0, 6.283185, 1000)# Generating assembly measurement data# Using df['angle_purple off center'] as direction of gapdf['Circularity Contribution Purple'] = df['purple_r']df['Concentricity Contribution Purple'] = df['purple_off_center']df['Circularity Contribution Grey'] = df['grey_r'] df['Concentricity Contribution Purple'] = df['grey_off_center'] * \np.cos(df['angle_purple_off_center']-df['angle_grey_off_center'])df['gap'] = np.abs(df['Circularity Contribution Purple']) + \df['Concentricity Contribution Purple'] - np.abs(df['Circularity Contribution Grey']) \- df['Concentricity Contribution Purple']# Part measurement data graphfig, ax = plt.subplots(2, ncols=3, figsize = (14, 8))ax = ax.ravel()ax[0].hist(df['purple_r'], bins =20, color='purple')ax[0].set_title('Distribution of \n- Purple Large Radius')ax[0].set_xlabel('mm')ax[1].hist(df['purple_off_center'], bins =20, color='purple')ax[1].set_title('Distribution of Concentricity\nof Features on Purple Part')ax[1].set_xlabel('mm')ax[2].hist(df['angle_purple_off_center'], bins =20, color='violet')ax[2].set_title('Distribution of Angle of\n Off Center for Purple Part')ax[2].set_xlabel('radians')ax[3].hist(df['grey_off_center'], bins =20, color='dimgray')ax[3].set_title('Distribution of Concentricity\nof Features on Grey Part')ax[3].set_xlabel('mm')ax[4].hist(df['angle_grey_off_center'], bins =20, color='lightgray')ax[4].set_title('Distribution of Angle of\n Off Center for Gray Part')ax[4].set_xlabel('radians')ax[5].hist(df['grey_r'], bins =20, color='dimgray')ax[5].set_title('Distribution of \n - Grey Large Radius')ax[5].set_xlabel('mm')plt.tight_layout();# Assembly measurement data graphfig, ax = plt.subplots(1, ncols=4, figsize = (14, 4))ax = ax.ravel()ax[0].hist(df['Circularity Contribution Purple'], bins =20, color='purple')ax[0].set_title('Circularity Contribution Distribution \n Purple Outer Radius')ax[1].hist(df['Concentricity Contribution Purple'], bins =20, color='violet')ax[1].set_title('Concentricty Contribution Distribution \n Purple Radii Relative to Each Other')ax[2].hist(df['Circularity Contribution Grey'], bins =20, color='dimgray')ax[2].set_title('Circularity Contribution Distribution \n Grey Outer Radius')ax[3].hist(df['Concentricity Contribution Purple'], bins =20, color='lightgray')ax[3].set_title('Concentricty Contribution Distribution \n Purple Radii Relative to Each Other')plt.tight_layout();# Final Gap Graphmu = df['gap'].mean()sigma = df['gap'].std()plt.hist(df['gap'], bins =20, color='black')plt.title('Resultant Gap Distributions', fontsize = 16)plt.axvline((mu-(3*sigma)), color='green', alpha=0.5)plt.axvline((mu+(3*sigma)), color='green', alpha=0.5)plt.axvline((mu+(3*sigma)), color='green', alpha=0.5)plt.xlabel("Gap (mm)")plt.ylabel("Assembly Count")plt.tight_layout();
[1] Tolerances are determined in a number of ways. The design engineer will specify in a part drawing the tolerance for each critical dimension. This tolerance is usually created based on best practices for part tolerances of that manufacturing method and based on feedback from the vendor who is creating the part. If the tolerance stackup indicates that it needs to be tighter than best practices, in many cases that can be done too, but will increase the cost of the part.
[2] By always, I mean almost always. Outliers are always going to happen. Parts will be out of spec. You may even be willing to design knowing that you’ll throw away parts. For the purposes of this blog “always” actually means “as often as your intended yield.”
[3] If this is being done before data is available, you can generate values based on how you would expect them to come in given your manufacturing process and quality. If you are already making parts, you can use the mean and standard deviation of each of your tools to generate data.
[4] If you’ve heard the term “Six Sigma” before and ever wondered what it means, nothing you’ve heard from Jack Donaghy is real. It refers to setting your yield such that ± 3 standard deviations (six sigma) are in spec (i.e. you have a yield of 99.99966%)
[5] Crush ribs are small ribs included in a softer part that will be crushed as it is pushed in to a harder part. Barring major issues with the circularity of the hard part or the crush ribs in the small part, this tends to create a fit close enough to centering the crushed feature within the crushing feature that you can assume the tolerance of the fit is zero.