Archive | Uni RSS for this section

Sheet Metal Guillotine

This was designed for a machine design unit.

Requirements were to cut 3 mm mild steel sheet, up to 2400 mm wide, and be powered by one hydraulic cylinder while still maintaining a straight & level cut.

Several iterations led to a design that could be CNC machined from one single 20 x 1800 x 6000 plate, with a few extra small parts.

Double roll crusher

This is the machine I designed for my thesis project. Some native plant species in the south west have seeds with extremely tough outer shells, which help them withstand adverse conditions. But when rehabilitating a minesite, we need seeds ready-to-go, they can’t be sitting around in the soil for years before germinating. This machine is able to crack the outer shell without damaging the seed embryo inside – the gap between the rollers can be adjusted to give the perfect amount of cracking effort. In addition, it’s designed to be ergonomic and easy to use and maintain.


Flow over a backward-facing step

This was a major project for a class in CFD. The aim of the project was to determine the reattachment length (x_R) by developing two- and three-dimensional models of the BFS flow using ANSYS Fluent, and validate the results by referring to prior research.


The general geometry & nomenclature

The model was based on the 1985 experiment by Driver & Seegmiller, which used a wind tunnel with Re_H = 36,800. The reattachment length is strongly dependent on the Reynolds number in the laminar and transitional regimes (Re_H < 3400) but in turbulent flow, settles to x_R/H ~ 7.


Results from the refined 2D model, clearly showing the primary recirculation zone

Results from the refined 2D model, clearly showing the primary recirculation zone

The k-\epsilon model is notoriously bad at predicting x_R, often underestimating by a factor of 2. In this model I compared the k-\epsilon RNG and RSM models for the 2d case, and k-\epsilon standard and realisable for the 3d case. The most realistic results came from the k-\epsilon RNG model in 2D, giving a value x_R/H = 5.33. This is still an underestimation, and I suspect is due to the mesh: I left some high aspect ratio elements in the region behind the step, which would have interfered with calculations of the primary and secondary vortices.

The full report can be found here.


Last year, two friends and I entered the 2014 NI Autonomous Robotics Competition on behalf of UWA. The theme was agriculture: the robots had to navigate a course to a seed pickup point, then plant those seeds in specified locations before returning to the starting point. The robot had to avoid obstacles and (being a competition) had to be fast. We wanted a design that was cheap and easy to manufacture while still being competitive, and this was the outcome:


The 8 x 3.75m world was built from astroturf and cinderblocks, and the “seeds” were 100mm cubes made of stressball foam. The course looked roughly like the picture below, with obstacles placed randomly in the first region, and the “planting rows” in the second region. In order to gain points the robot had to traverse the entire course within 2 minutes, planting all the seeds inside the bounds of the planting rows while avoiding all obstacles and walls.


We were to be funding the robot ourselves, and with no prior parts to use we had to be frugal – so most of our design decisions were based on getting value for money regardless of the work involved. Fortunately the most expensive component was provided by NI – the onboard processor was an NI myRIO which features an ARM Cortex processor (running a real-time OS) integrated with an FPGA, Wi-Fi and plenty of configurable I/O ports. Both the RTOS and FPGA had to be programmed using LAbVIEW (C++ is possible but complicated to set up the development environment) so as the team programmer, I got a lot of practice using LabVIEW.

Early on in the design phase we settled on the layered chassis structure to make it easier to update the robot. We used wood and some standard fastenings from Bunnings for the same reason, and to reduce cost.

We decided that a two-wheeled differential drive would be simpler to program and require fewer parts (hence less risk of breakage) than other drive configurations like tracks or omni-drive. A quick analysis of torque & speed needed to run the course gave us our motor requirements and after considering a few options we settled on some stepper motors – not only would they provide the necessary power, the stepping operation of the motors would give us automatic odometry, eliminating the need for wheel rotation sensors.

As the robot was not allowed to touch any walls or obstacles, it had to be fitted with some kind of proximity or range sensors. We considered every reasonable option: IR proximity sensors, self-made LED/LDR sensors, LIDAR and SONAR sensors, and were about to go ahead with an array of IR distance sensors when we found the HC-SR04 ultrasonic range sensor online for $2 each. These are time-of-flight sensors which don’t have much control circuitry built in, so the distance measured has to be timed by the main robot controller: we used the FPGA for this as the timings between transmit and echo pulses were on the order of microseconds, too fast for the RTOS to be reliable.

The first prototype used a two-layer chassis, with three ultrasonic sensors, wheels made of plywood and rubber bands, and custom designed motor brackets 3d-printed from ABS. We wanted to be able to recycle the parts after the competition, so we kept wires long and used removable mounts.


These wheels had good traction in general, but accurate odometry was difficult on thick carpet or astroturf. We saw two solutions: to use wide tires and spread the load so that the ground didn’t get as deformed, or to use very thin wheels to penetrate through the grass to the base layer. For the competition we decided to go with the thin option, so I modelled some and 3d printed them in ABS.


These thin wheels worked very well on carpet and on the astroturf sample that we’d been given, but in the final competition the wheels didn’t penetrate far enough to the base layer so we lost a lot of traction.

One requirement of the competition was that the robot have an emergency stop button which was easily accessible and would cut power to all moving parts. Rather than use a toggle switch we opted to develop a latching circuit which would provide the emergency stop functionality as well as cutting power in case of a fault – I’ll write about this in another post.

We had two main designs for the seed dispenser: the first was a ferris-wheel type design which loaded the seeds into a circular magazine and dispensed them by placing them gently on the ground – the aim being to prevent it rolling out of the designated zone. This design placed seeds very well but it was very heavy, slowing down the robot substantially and taking the weight off the driving wheels. Despite being heavy, it was not very structurally sound and was mostly held together with duct tape. You can see it in our video for Milestone 4:

The squeaking noise comes from the poor, sad roller ball that sits between the drive wheels and the dispenser – at this point we realised there were major balance issues but we did not have the time to redevelop from scratch. Making the dispenser stronger would have meant even more weight so we redesigned it completely and went for a square tube-shaped hopper made of balsa:


This layout was a bit less delicate in placing the seeds, but it was stronger than the wheel design and was a tiny bit lighter. The final competition was looming and so most of our work from here on was more or less a hack to just keep it running – exemplified by the mechanism one of my teammates built to dispense the seeds from the hopper, which was a Scotch yoke cobbled together using stationery around his house, and worked perfectly:



This also fit very nicely with our goal of keeping things cheap and easy to manufacture.

The robot ended up struggling in the competition and was knocked out in round 1. The round included 3 runs of the competition course: on the first run the robot didn’t move as it couldn’t get enough grip, and on the second and third runs it was too slow and got lost – but it didn’t hit any obstacles! So we were not docked any points and finished the round with zero points. In the end we placed equal 20th with a few other teams who had similar troubles. We stuck with our initial project goal of keeping the robot cheap (final parts cost was ~$200, compared to many teams who used a $1000 LIDAR sensor) and easy to manufacture, and made it to the final competition while meeting all project milestones. Overall this was very rewarding and the design/build/compete process was a lot of fun!

The Method of Joints: Part 2

See Part 1.

With the theory established, the python script was simple enough to write. Prior to this project I hadn’t used numpy before, but after seeing some examples online involving matrix operations, I figured numpy was the perfect tool.

The script is split into 9 major steps:

  1. Read data from the input files
  2. Construct the members table, filled with the coefficients l_{ij} and m_{ij}
  3. Construct the coefficient matrix C and the vector of applied forces P
  4. Solve the matrix equation!
  5. Find the maximum safe load for the truss
  6. Present the results

All the relevant data are kept in a few separate files, which are imported into some ndarrays:

  • containing the (x,y) coordinates of each numbered joint, in mm
  • a list of members with their two associated joints
  • the two supported joints and whether it is a pin or roller support
  • the forces applied to the truss, and their locations
  • the physical properties of the material we’re using

The first four use numpy.genfromtxt() to form their arrays, but is imported into a dictionary:

material = {}
with open(inpath+'', 'r') as fin:
    for line in fin:
        if (line[0] != '#'):
            spl = line.split()
            if len(spl)==2:
                (key, value) = spl 
                material[key] = float(value)

This way the material properties can be accessed easily, for example the Young’s modulus is material['E']

The members table initially has three columns: the member number, the number of joint i, and the number of joint j. The table is extended with a few empty columns, which are filled in with the relevant geometric properties of the member (taken from the (x,y) coordinates of it’s two joints): dx, dy, L, l_{ij}, m_{ij}.

Constructing C is pretty much as described in Part 1: the l_{ij} and m_{ij} values for each member are inserted in the appropriate place, and then the coefficients for the reaction forces are added in to the final three columns. These coefficients are \pm1 as they’re aligned with the axes. The applied forces are inserted into a vector “P
Solving the matrix equation is the easy part and it’s why I chose numpy in the first place:

Q = numpy.linalg.solve(C,P)

I’ve been setting up the applied forces to sum to 1, so Q now contains the portion of the load which is carried by each member. For example if Q[3] = -0.707, this means member #3 is carrying a compressive force of 0.707*(Load).

The maximum load for the truss as a whole is found by iterating through the members and finding the maximum load for the bridge before that member fails, then taking the lowest of these failure limits. This isn’t a rigorous analysis and only yielding and Euler buckling are accounted for, but that’s good enough for a bridge made of plastic straws:

maxL_t = np.zeros([members.shape[0],3])
maxL = 1e6
for k in range(members.shape[0]):
    if Q[k] != 0:
        # yield: F/A &lt;= (safety factor) * s_y
        maxL_t[k,0] = material['sf'] * material['s_y'] * material['A'] / abs(Q[k])
        # buckling: F &lt;= (safety factor) * pi^2 * E * I / l^2
        if Q[k] &lt; 0:
            maxL_t[k,1] = material['sf'] * numpy.pi**2 * material['E'] * material['I'] \
                            / (abs(Q[k]) * members[k,5]**2)
            # which is the smaller limit?
            maxL_t[k,2] = min(maxL_t[k,0], maxL_t[k,1])
            maxL_t[k,2] = maxL_t[k,0]
        if maxL_t[k,2] &lt; maxL:
            maxL = maxL_t[k,2]

And with that the analysis is complete. I’ve set up two modes of output: a plaintext mode, where the results are sent straight to a text file; and a presentation mode, where matplotlib is used to draw the bridge and the results are written to an .html page like this one!

The Method of Joints: Part 1

In one of my courses we’ve been tasked with building a truss bridge out of plastic tubes, slightly thicker than drinking straws, which must span 1 metre and hold 5kg of load. My team had a few basic designs that we wanted to start off with, which meant a whole bunch of calculation to see which one was best. Of course I’m not one to pass up an opportunity like that, and solving systems of linear equations is something that computers are kind-of alright at, so I wrote a python script that solves a given truss and returns the internal forces in each member. With the script complete, a new bridge design takes only about 2 minutes from data input to final results! The slowest step is sketching the bridge design…



The method of joints involves stepping through each joint in a truss and solving the equations of static equilibrium, i.e.

    \begin{align*} \sum{F_x} &= 0,\\ \sum{F_y} &= 0. \end{align*}

These two equations must be solved for each joint, giving 2n equations, where n is the number of joints present. The procedure is quite straightforward but having more than a few joints makes it get tedious very quickly. So, let’s make it faster.

A simple truss

Consider the frame above (with members 1 & 2 the same length). We have six unknowns to solve for: three reaction forces from the supports, and the three internal forces. We also have six equations describing the system, how convenient. These are:

    \begin{align*} 1:\sum{F_x} &= R_{1x} + F_3 \cdot \frac{1}{\sqrt{2}} = 0 \\ 1:\sum{F_y} &= R_{1y} - F_1 - F_3 \cdot \frac{1}{\sqrt{2}} = 0 \\ 2:\sum{F_x} &= R_{2x} + F_2 = 0 \\ 2:\sum{F_y} &= F_1 = 0 \\ 3:\sum{F_x} &= -F_2 - F_3 \cdot \frac{1}{\sqrt{2}} = 0 \\ 3:\sum{F_y} &= F_3 \cdot \frac{1}{\sqrt{2}} - 100N = 0 \\ &\Rightarrow F_3 \cdot \frac{1}{\sqrt{2}} = 100N \end{align*}

Now this system is simple enough to solve by inspection. Ignoring this though, we can represent this system as a matrix equation C \cdot F = P, where P is a vector of the applied (or known) forces, F is a vector of our unknowns, and C is the coefficient matrix. Thus:

    \begin{equation*} \begin{bmatrix} 0 & 0 & \frac{1}{\sqrt{2}} & 1 & 0 & 0 \\[0.3em] -1 & 0 & -\frac{1}{\sqrt{2}} & 0 & 1 & 0 \\[0.3em] 0 & 1 & 0 & 0 & 0 & 1 \\[0.3em] 1 & 0 & 0 & 0 & 0 & 0 \\[0.3em] 0 & -1 & -\frac{1}{\sqrt{2}} & 0 & 0 & 0 \\[0.3em] 0 & 0 & \frac{1}{\sqrt{2}} & 0 & 0 & 0 \end{bmatrix} \cdot \begin{bmatrix} F_1 \\[0.3em] F_2 \\[0.3em] F_3 \\[0.3em] R_{1x} \\[0.3em] R_{1y} \\[0.3em] R_{2x} \end{bmatrix} = \begin{bmatrix} 0 \\[0.3em] 0 \\[0.3em] 0 \\[0.3em] 0 \\[0.3em] 0 \\[0.3em] 100 \end{bmatrix} \end{equation*}

As long as the matrix C is non-singular, we can find it’s inverse and recover the solution F = C^{-1} \cdot P. In this case:

    \begin{equation*} \begin{bmatrix} F_1 \\[0.3em] F_2 \\[0.3em] F_3 \\[0.3em] R_{1x} \\[0.3em] R_{1y} \\[0.3em] R_{2x} \end{bmatrix} &= \begin{bmatrix} 0 & 0 & \frac{1}{\sqrt{2}} & 1 & 0 & 0 \\[0.3em] -1 & 0 & -\frac{1}{\sqrt{2}} & 0 & 1 & 0 \\[0.3em] 0 & 1 & 0 & 0 & 0 & 1 \\[0.3em] 1 & 0 & 0 & 0 & 0 & 0 \\[0.3em] 0 & -1 & -\frac{1}{\sqrt{2}} & 0 & 0 & 0 \\[0.3em] 0 & 0 & \frac{1}{\sqrt{2}} & 0 & 0 & 0 \end{bmatrix}^{-1} \cdot \begin{bmatrix} 0 \\[0.3em] 0 \\[0.3em] 0 \\[0.3em] 0 \\[0.3em] 0 \\[0.3em] 100 \end{bmatrix} = \begin{bmatrix} 0 \\[0.3em] -100 \\[0.3em] 141.4 \\[0.3em] -100 \\[0.3em] 100 \\[0.3em] 100 \end{bmatrix} \end{equation*}

That was easy! Now comes the question of somehow constructing the matrix for an arbitrary truss, without actually writing the equations by hand.

First, note that for a simple truss (constructed only of triangles) with n joints, there are 2n-3 members connecting them. If the truss is supported by three support forces, for example R_{1x}, R_{1y}, R_{2x} above, then the system is statically determinate: there are 2n equations describing the system and 2n unknowns to solve for.

An arbitrary member

Now consider an arbitrary member k in a truss, connected between two joints i and j. The equations

    \begin{equation*} i:\sum{F_x}, \quad i:\sum{F_y}, \quad j:\sum{F_x}, \quad j:\sum{F_y} \end{equation*}

are the only equations with any contribution from member k (as a member can only connect to two joints), and we can number these equations as 2i-1, 2i, 2j-1, and 2j respectively. The member k will appear in exactly these four equations, although the coefficient may be zero as with F_1 in the example above.

Looking at the x-component of forces at joint i, (equation number 2i-1):

    \begin{equation*} \dots + cos \theta \cdot F_k + \dots + P_{ix} = 0. \end{equation*}

We can simplify this a little by using ratios of the member’s dimensions rather than it’s angle. By defining

    \begin{align*} l_{ij} = cos \theta = \frac{\Delta x}{L_k} \\ m_{ij} = sin \theta = \frac{\Delta y}{L_k} \end{align*}

and by moving the known/applied force P_ix to the right hand side of the equation, we get the following:

    \begin{alignat*}{4} & \dots + l_{ij} \cdot F_k + \dots && = && -P_{ix} \quad && (eqn. \; 2i-1) \\ & \dots + m_{ij} \cdot F_k + \dots && = && -P_{iy} \quad && (eqn. \; 2i) \\ & \dots - l_{ij} \cdot F_k + \dots && = && -P_{jx} \quad && (eqn. \; 2j-1) \\ & \dots - m_{ij} \cdot F_k + \dots && = && -P_{jy} \quad && (eqn. \; 2j) \end{alignat*}

In the matrix C, the entire column k will be zero except for the rows corresponding to these equations. So the contributions to C from member k are:

    \begin{alignat*}{3} & C_{2i-1,k} \quad && = \quad && l_{ij} \\ & C_{2i,k} \quad && = \quad && m_{ij} \\ & C_{2j-1,k} \quad && = \quad && -l_{ij} \\ & C_{2j,k} \quad && = \quad && -m_{ij} \end{alignat*}

With C complete, we just construct the vector F as above, with F_k in order, and the three support reaction forces at the end. The vector of applied forces P is simple enough: for a load L applied at node i we just set

    \begin{alignat*}{3} & P_{2i-1} && = && -L_x \\ & P_{2i} && = && -L_y \end{alignat*}

And now we just find C^{-1}, solve F = C^{-1} \cdot P, and we’re done!