Wednesday, March 6, 2019

Involute gear simulator built with QT and OpenGL, Part 2

Part 1

This time we will review a few key concepts which are needed to understand involute gears, and then have a look at a few things we can learn, using our simulator. The code and a 32 bit Windows compiled binary are available at my GitHub page. You will need an OpenGL compatible graphics card with drivers to run this application. Compiling the code on Linux is straightforward, it should also be possible on OSX but I haven't tried that yet.

As I described previously, an involute curve is obtained by unwinding an imaginary string from a circle (well for gears it's a circle). To proceed we need to define the terminology for the parameters that specify a given gear. There is quite a bit of redundancy in the terminology used to specify gears. Lets start with the size of the teeth, this may be specified in terms of the pitch, the diametrical pitch or the module, and each of these three parameters are related to each other by simple equations because they all obtain the same information. A given gear will have N teeth, and the pitch is the distance between neighbouring teeth for a gear with very large N. The diameter of the gear is proportional to the number of teeth and the module is the diameter per tooth while the diametrical pitch is the inverse of the module; diametrical pitch = 1 / module. Now as we are working on a simulation, we don't care about the absolute size of a gear, so long as we draw everything in proper proportion and thus we don't care about the module or the diametrical pitch or the pitch and so we will just work with module = 1, but we do care about the number of teeth N.

Now to proceed imagine we have two wheels, one driving the other using friction, and for the sake of this argument there can be no slip. Each diameter of these two wheels coincides with what is called the pitch diameter in a gear. The involute diameter used to generate the gear teeth is smaller than the pitch diameter. This is illustrated in the diagram, where the involute generating circles are also depicted and if we imagine the smaller gear A is winding the string onto its involute circle which is being unwound from the involute circle of gear B we see the essence of how the involute gear scheme works. Another important parameter is the pressure angle, as shown in the diagram. The pressure angle determines the ratio between the involute diameter and the pitch diameter, which depends upon the pressure angle but not the number of teeth.

Click to enlarge


We are now in a position to appreciate the magic of involute gears: that being why they don't depend upon the distance between them to maintain constant velocity. Constant velocity refers to the driven gear always rotating at the same speed relative to the driving gear as the teeth move in and out of mesh, as opposed to the driven gear rapidly speeding up and slowing down. The shapes of the involute teeth are solely determined from their involute generating circles and note that if we move the gears further apart these circles don't change. What does change is the pressure angle, because the angle of the string connecting the two circles obviously changes (refer to diagram). But it is clear that the rotation of the two gears determined by the string unwinding from one involute circle and onto the other doesn't change upon altering the distance between the gears. But it is this string that traces out the involute teeth and obviously it also maintains the constant velocity ratio when the separation between the gears is changed, that is how the magic works.

There is one more thing we need to cover here, and it is rather easy. A gear's outside diameter needs to be bigger than its pitch diameter for the teeth of mating gears to engage with each other. The convention with involute gears is to make this outside diameter for a gear equal to the pitch diameter of a gear which has two more teeth. That is a value of twice the module is added to the pitch diameter to obtain the outside diameter. The teeth need some clearance down the bottom, so rather than cutting them to a depth of twice the module a value of 2.157 is used. Side clearance is also needed for the teeth and the standard here is to make the tooth width 0.48 of the pitch leaving 0.52 for the gap between teeth.

So the teeth are always the same depth for a given pitch regardless of N, but the difference between the outside diameter and the involute diameter gets smaller as N is reduced. When N is too small the teeth will penetrate the involute circle. The convention to deal with this is to continue the tooth below the involute circle by using a straight radial line for the rest of the profile. Whether a real gear has this feature or not will depend on how it is manufactured. If it is made using a form cutter it will likely have the feature, but mass produced gears are more often made by a generating method such as hobbing, and then they won't. Regardless: the section of the gear tooth below the involute circle is completely useless, it can't be used for transmission of torque and is only there to provide clearance. But when is there enough clearance and when isn't there? This will depend upon what gear meshes with what and we will learn more about this soon enough.

The standard pressure angle for involute gears used to be 14.5 degrees. Despite being deprecated many decades back these gears are still around (imperial gears, not metric). These days the standard pressure angles are 20 and 25 degrees with the former being far more common. But why where things changed and what are the pros and cons? When the pressure angle is increased the degree to which the teeth slide against each other becomes more pronounced resulting in more friction, but the problems with teeth penetrating inside the involute circle are reduced. With this in mind let us look at a small gear of pressure angle 14.5 degrees running with a larger gear and see what happens

Fig. 1. (Click to enlarge) A 16 tooth gear engaged with a 80 tooth gear at
the correct depth. The gears have a pressure angle of 14.5 degrees

Fig. 2. (Click to enlarge) Same as Fig. 1. but zoomed in. Note the pair of teeth
towards the top, which have an overlap problem due to a lack of clearance
towards the root of the 16 tooth pinion's gullet.

In Fig. 1. & 2. we can see that there is a problem with teeth overlapping. This is due to a lack of clearance at the root of the pinion's (the smaller 16 tooth gear) tooth. This occurs because the simulation rotates the driven (80 tooth gear) at a constant velocity; in a real situation there would be no overlap but the constant velocity would be lost.

Fig. 3 (Click to enlarge) same as above except the pinion has been reduced to
8 teeth.


This overlap problem gets worse if we make the pinion smaller which we can see in Fig. 3 where the pinion has only 8 teeth. Obviously it gets less if we increase the pinion size and by the time we get to 20 teeth there is very little overlap left.

Fig. 4 (Click to enlarge) same as Fig. 3 except the pressure angle has been
increased from 14.5 to 25 degrees


A way to deal with the overlap problem is to increase the pressure angle. Fig. 4 is the same as Fig. 3 with the pressure angle increased to 25 degrees. The resulting overlap is drastically reduced.

Fig. 5. (Click to enlarge) Same as Fig. 3 except the gears are moved a
further 0.75 units apart.


Another way to deal with the overlap problem is to separate the gears further from each other. Doing this results in no lost engagement because a pinion tooth can only engage on its involute section anyway, and we haven't moved it far enough to loose any of the involute. In Fig. 5 this can be seen with the gears moved a further 0.75 units apart. Recall that the pitch diameter is 1 unit per tooth and the depth of tooth engagement is 2 units. Because the involute curve is invariant to the separation distance, this is a really good solution, and if you get this simulation going on your own computer you can see how well it works.


Fig. 6. (Click to enlarge) Same as Fig. 5 but using the circle approximation
for the involute curve.
A final point we will look at is the circle approximation that is often used to make form cutters to manufacture gears. This approximation works by using a circle for the involute curve, which has its gradient and curvature matched to the involute's at the point where the involute is on the pitch circle. The errors in this approximation gets worse as the number of teeth in the gear are reduced and as the pressure angle is reduced. Fig. 6 shows the extreme case where the pinion has only 8 teeth and a low pressure angle of 14.5 degrees. While the dramatic changes to the tooth profile of the pinion are easily spotted, the bigger 80 tooth gear is very different. For the 80 tooth gear it is very difficult to see any change at all. I should mention that an 8 tooth pinion is extremely small for an involute gear, and so things are really being pushed to the limit. I believe there are better circle approximations for really small gears. If I was to try and derive one, I would calculate the average of the least square difference along the involute section of the tooth flank and find the circle which minimises it. This is a standard mathematical technique. The benefit of doing this is that it would focus on finding the best circle over the entire tooth rather than just extrapolating at the pitch circle.

In the final part of this series we will look at an overview of how the some of the more interesting aspects of the code work.


Tuesday, February 19, 2019

Involute gear simulator built with QT and OpenGL, Part 1

Part 2

I have built a C++ application to provide a high resolution  rendering of a pair of involute gears running together. This post will focus on introducing what it is and what it does. An image of the application is shown below and the source code (along with 32 bit Windows binaries) is available at: https://github.com/wilstep/OpenGL-involute-gears-simulation/releases/tag/v1.3.1
This simulation allows one to slow the gears right down and examine them under great magnification, and from any direction to observe in great detail how the contacts between the gear teeth change in time and how the involute gear form actually works. In effect it lets one examine the gears rotating as if they are being viewed under a high powered microscope, but from any direction and at any chosen speed, and with perfectly sharp edges. So one can view the detailed interaction of the gear teeth with a full virtual reality visualisation.


The simulation is highly realistic and detailed with the correct involute form being calculated to all the standards used in gear specifications including the correct amount of clearance, correct tooth depths and standard pressure angles. There is also a fillet radius at the bottom of the teeth, which was surprisingly difficult to implement. Things are arranged such that one gear drives the other, and the speed at which they rotate may be adjusted. The number of teeth on each of the gears can be changed and the rendering may be zoomed in, shifted and rotated with the mouse. Further the distance separating the two gears may be tweaked and the light source for the rendering may be moved. The above image is shown below after rotating, zooming and moving the light source.


There are two common types of tooth profile systems used in gear design. The first is the cycloidal gear form that is used mainly in horology (mechanical clocks and watches) and the second is the involute form which is far more common. My simulation only deals with involute gears.

An involute is a mathematical curve which is obtained from some generating shape. In the case of gears the generating shape is a circle. Imagine we wrap a string around this circle with a pencil tied to the end, and then draw a spiral as the string unwraps. This spiral is the involute and a very short piece of this forms the profile on the gear tooth. There are several nuances that need to be appreciated to form a gear from the involute curve, which I will discuss in the next post. The special feature of the involute gear form is that the driven gear rotates at constant velocity, regardless of errors in the separation distance between the two gears.

I plan to make a further two posts subsequent to this. The next one discussing more details about the gear profiles, their calculation and also demonstrating some important insights that can be gained from this tool, and then a final one looking at some of the more interesting snippets of the code.

Part 2

Thursday, February 7, 2019

Object Orientated Design to build a sphere in OpenGL

Introduction

The motivation for this project is to do some object oriented design with C++. The problem I have chosen is to draw a sphere in OpenGL without repeating vertices. Modern OpenGL does not provide any direct means to draw a sphere, rather the user needs to render a collection of triangles to achieve this. Each triangle is specified by three vertices which coincide with its corners. So it turns out that each vertex is shared by several triangles and we are left with the problem of not repeating the same vertex.

The approach I will use provides an efficient O(n log(n)) algorithm (n being the final number of vertices or number of triangles). However the algorithm or code will be special to this type of problem where we are dividing triangles. There are more general approaches such as searching for repeated vertices and removing them, or perhaps  making the vertices first and then finding the triangles second. But with these methods you will tend to get an O(n^2) algorithm, although this is probably not such a major drawback in practice and things can be sped up with the use of a hash table or some other search mechanism. Regardless; at the end of the day we will end up with a good little API to make a sphere in OpenGL.

The Problem

The basic approach to make a sphere by dividing triangles is easy to find on the web. We will start with an octahedron which consists of eight triangles. A topological graph of an octahedron is shown in Fig. 1. An octahedron is formed from eight triangles, which neighbour each other, and we wish to divide these triangles into more triangles to get something that is much closer to a sphere.

Fig. 1. Topological graph for an octahedron.

We can divide each of these triangles into four smaller triangles as shown in Fig. 2. We then normalise the lengths of all the vertices to unity to approximate a sphere of unit radius. Thus for each triangle we make three new vertices, but these vertices are shared with neighbouring vertices, and  we don't want to repeat ourself. We are left with the task of assigning the new vertices when needed and asking neighbouring triangles for them when they already exist. To do this we will use some object oriented design, by making a sphere object which is formed from a collection of triangle objects.

Fig. 2. Diagram showing how a triangle is divided into four smaller triangles.

The Objects

The sphere

Our sphere class has the following data:

    std::vector<GLfloat> verts;
    std::vector<GLuint> inds;
    std::vector<triangle> trigs;
    const GLuint order, nVerts, nTri;
    GLuint triCnt, triCntOld, vertCnt, vertCntOld;


There a three vectors, the first of which (vert) represents the set of vertices for our sphere which specify the corners of the triangles, the second (inds) is the set of indices which specify the vertices to form each triangle (as used by OpenGL), and the third (trigs) is the set of triangles. We also have (order) the number of iterations where each triangle is divided into four new triangles. If order = 0 we simply use the octahedron and if order = 1 we divide each of the octahedron's triangles into 4 new triangles only, then stop. If order is bigger we iterate the same procedure order times. The constants nVerts and nTri are the total number of vertices and triangles we end up with, and the last line of variables are just for counting these numbers as we iteratively build our sphere.

The triangles

Our triangle class encapsulates the following data:

    std::array<GLuint, 3> index; 
    std::array<GLuint, 3> neigh_tri; 
    std::stack<std::array<GLuint, 3> > lifo;

The first two arrays of length 3 hold the indices (index)  for the triangles three vertices and the indices (neigh_tri) for the three neighbouring triangles. These indices refer to the vectors in the sphere object, and only triangles which share an edge are considered neighbours. There is also a stack (called lifo) which these two arrays can be pushed onto.

Labelling conventions for triangles and Dividing triangles

We employ the following labelling convention. When we look at the face of a triangle from outside the sphere it either points up or down as shown in Fig. 3. The vertex indices (for the triangles' array: index) are ordered according to whether they are left, right, or top/bottom as per Fig. 3. In addition the neighbouring triangles (for the triangles' array: neigh_tri) are also ordered according to the orientations given in Fig. 3. These conventions are then used to subdivide our triangles while keeping tack of all the vertices and neighbours. Keeping these conventions in mind is crucial if you wish to delve into the full details of the source code.



Fig. 3. Diagram showing how the indices are ordered for both the vertices and the neighbouring triangles, when the triangles are pointing up or down.


When we subdivide a triangle the triangle object pushes its two array objects onto its stack. This triangle object will then become the smaller triangle in the centre of the subdivided triangle as shown in Fig. 2. But first we make all the new vertices and store the corresponding indices in the triangles array (index) that they belong to. 

To do this without repeating vertices we use the size of the triangles' index in the sphere class to establish a precedence. If the index of the neighbouring triangle is less than the one we are currently working on, then this vertex has already been made and we get the required index off the neighbour. Otherwise we make a new vertex.

After doing this we make three new triangles for each existing triangle, then re-establish which triangles neighbour each other while popping the stacks at appropriate times. This is all easier said than done, but you may read the code for full details.

The Code

The C++ code is available from my GitHub page: https://github.com/wilstep/OpenGL-sphere/releases/tag/v1.0. The code is arranged so that it can easily be reused as an API if you want to draw a sphere in some other application. As it stands it is set up for use on Linux. There is a single command line argument which sets the order variable. The resulting approximate sphere is rendered with lighting and shading, and the approximate sphere rotates on its y-axis. The figure below, Fig. 4, shows the order-0 sphere which is a octahedron. However this octahedron has the normal vectors for a sphere and so the lighting is all incorrect for an octahedron.

Fig. 4. An order-0 sphere, which is actually an octahedron. The edges are hard to see (in places) because the shading normals are for a sphere, not an octahedron.


The next figure, Fig. 5, shows an order-4 sphere that looks very convincing (2,048 triangles), however if you run this on your computer and focus on the spot where the light is reflected it will still be apparent that the sphere is rotating. If we go to order-6 (32,768 triangles) then this artefact disappears.



Fig. 5. An order-4 sphere which is composed of 2,048 triangles.
 That is all for this little project.