I've been working with stepper motors quite a bit recently for work, which has inspired me to return to a long-lost project: creating a 2-axis movement system.
Specifically, I have a long-term project idea to create a computer-playable, physical version of the board game Diplomacy. My version would have a physical board with pieces moved around via an automated electromagnet underneath the board. A system computer would take user input moves, adjudicate turn outcomes, and move the pieces to their proper locations.
I have already done a good amount of work on writing the adjudication engine from scratch, but I have yet to delve into the physical system, apart from some brief tinkering with stepper motors in the past.
For my physical system, I'm starting with an extremely common design, relying on two stepper motors and a pair of linear rails to move a toolhead around in two directions, enabling my toolhead to reach anywhere underneath the board to pick up a piece.
These sorts of 2-axis and 3-axis systems are very common in all sorts of machinery, including CNC mills, 3D printers, plotters, and more. My application is a bit more niche, but the building blocks of my system are the same, so there is plenty of existing design to draw inspiration from.
For the scope of this phase of the project, I want to focus in on the movement system. So, for now, I'll be ignoring the electromagnet itself, the board top, and the game engine. I just want to be able to move the system around well.
The basic design of the system is fairly simple, and can be broken down in mostly identical axes. Each axis consists of a linear rail, providing a guide for the motion of the system. On one end of the rail is a stepper motor, and on the other is a simple pulley.
The stepper motor drives a belt, which is looped around the pulley. The belt is also connected to a block which rides on the linear rail. In this way, turning the stepper motor one way or the other will pull the block left and right along the rail.
To complete the 2-axis system, we will basically stack another axis on top of the first one, which does introduce some complexity. But let's worry about that a bit later.
Two important considerations for the system are friction and tension. Higher belt tension will increase the accuracy that stepper motor motion is transferred to the output block, but it will also increase the shear loading and required torque from the motor.
Too much tension, and the system will bind up completely, potentially damaging the motor. Of course, with too little tension, the belt will fall off and fail to drive anything.
Tensioned belts will also stretch over time, so it's doubly important that we design in a method for easily adjusting the belt tension.
One common way of doing this, which I've implemented here, is to use a screw to adjust the position of the pulley. The pulley rides on a small rail of it's own within a mounting block, but it can be moved forward and backwards via turning a screw.
So, that's the design. You can see the output here. I modelled and 3D printed my mounting blocks, cut a big piece of plywood to use as my base, and got some cheapo linear rails from Amazon. The pulley tensioning block can be seen on the left.
To test the first axis, I got the system wiring up and running. I use an Arduino Nano as the microcontroller, with a tiny A4988 stepper motor driver receiving step and direction signals.
I tried to keep all the wiring as quick and clean as possible, and made a couple additional housings for the 9V power input and joystick control board mount.
I did some initial testing in full-step output mode, which was quite jerky. Using a microstep output from the A4988 driver smoothed everything out, though, so it was time to jump into the more difficult part of the project: adding a second axis.
The second axis (let's call it the y-axis) presents a couple complications beyond the scope of the original x-axis.
First, the first axis itself has to be made more robust. The moving part of the first axis no longer carries just a single block, but the entire second axis (linear rail, stepper motor, and opposite pulley).
Second, the second axis must be designed to allow for proper functioning of the first, allowing the x-axis belt to pass through cleanly during movement.
Finally, the wiring is also slightly more complex. Whereas the x-axis motor is fixed in space, the y-axis motor moves around on the x-axis, so wires connecting this motor to the control system have to account for this.
The wiring issue can be solved by adding a cable "drag chain," seen below, which protects the cables from the movement of the system and confines their movement.
(It also serves to protect the cables from curious cats, also seen below).
Drag chains are tried-and-true elements of many moving mechatronic systems; I didn't have to look very hard to find something pre-designed online I could easily print out.
To allow the x-axis belt to pass through the y-axis pieces, I had to design the y-axis motor mount somewhat carefully.
There is a recess to allow the x-axis belt to pass above the block, and the y-axis motor sits behind that, well out of the way. The y-belt then rides above the x-belt to avoid interference.
To handle the weight and complexity of the y-axis itself, the x-axis rail is doubled, so that both ends of the y-axis ride on its own rail.
In this way, the y-axis motor mount (connected directly to the x-belt) moves up and down the x-axis, pulling the y-axis pulley along with it (on the opposite x-axis rail). All this talk of axes makes it sound more complicated than it really is, I think.
One important thing to note, however, is that the "far-side" rail, holding the y-axis pulley, is undriven. That is, it only moves because it is pulled along by the opposite side of the rail. So, it's very important to keep friction here to a minimum, keeping both x-rails perfectly parallel, and the y-rail mounted securely and perpendicularly.
Total non-sequitur, but my god do I love using heat-set inserts with 3D-printed parts. These little brass nuts can very easily be melted into place on a PLA part, where they provide a very secure and reuseable threaded mounting point. I've never really used them in the past, but I'm never going back to a world where I don't use them - they're so handy.
Anyway, that's about all there is to say about the build. Finally, it was time to write some quick stepper control code and test the dang thing!
Well, I say quick code, but it actually took some thinking to accomplish. The trick was to write non-blocking code to move both steppers at the same time.
For the first axis, I wrote my stepper commands manually (using delay and digitalWrite functions). However, this method wouldn't work for two steppers running in unison. In order for one stepper to move, the other would have to stop. I fiddled with manually controlling both in a non-blocking way, using "micros" timing to update each motor simultaneously. However, it was becoming fiddly, so I settled on a simpler solution: using a library.
The handy accelStepper Arduino library contains non-blocking stepper movement functions -- I just dropped these into my control program. In the future, I can write my own functions, but for, I want to test the mechanics of the system.
And, well, you can see the results in the video below (warning: the audio is VERY LOUD).
The system does accomplish the milestone I wanted to reach with this phase: using a joystick control, you can move the "toolhead" to any position on the x-y space. Axes are moved independently and can be moved simultaneously, and the speed of the movement can be adjusted by the joystick angle.
However, the system is very noisy, and I had trouble with it occasionally binding up. In the next phase, I want to dive into solving these issues, as well as introducing a more sophisticated control scheme.
I might have to just lubricate the system or adjust alignments to solve the noise/binding issue, but I may also need to explore using more powerful steppers/drivers, or introducing a system to drive the right side of the x-axis in parallel with the left.
For now, I'm calling this a milestone and leaving the project for now. Hopefully, it doesn't take me another three years to return to it again!