INSANE Gear Reduction! Making a Hypocycloid Gearbox

Hypocycloidal drives offer incredible gear reduction and increased torque for motors!

Let’s make a DIY version for a super slow machine designed to chop a toothpick in half in 50 years?? YEAH, that sounds good! The obsession with getting more torque and lower speed from motors began in the StrikeMark days (talk about a throwback) but the project itself was inspired by Arthur Ganson’s Machine with Concrete!

Some might think it’s a waste of time BUT it’s about the lessons and takeaways! We’ll cover the modified design, a double-sided custom workholding fixture, and tips and tricks in Fusion 360–including how to reduce annoying linking/z-retract moves, AND a new feature that allows us to see ONLY the cutting toolpath.

5 Stage Gearbox

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*

|| NYCCNC HYPOCYCLOID DRIVE

|| -version 1

|| -author Alex Pinson

|| -8/6/2019

|| -Copyright Saunders Machine Works, LLC 2019

*/


#include <SabertoothSimplified.h>
#include <PID_v1.h>  //PID library by Brett Beauregard

//Source for positional readings: https://github.com/BenTommyE/BenRotaryEncoder/blob/master/BenRotaryEncoder.ino

SabertoothSimplified ST; //Names the sabertooth object ST

volatile float temp, counter = 0; //This variable will increase or decrease depending on the rotation of encoder
int PPR = 400; //Equal to the number of ticks per revolution of your encoder

float pulsemSec = 0; //the amount of ticks the encoder is reading every millisecond
float RPM = 0; //RPM of the motor
float timeToRev = 0; //time in uSec per revolution of encoder
float timeSample1 = 0;
float timeSample2 = 0;

double setPoint; //desired value
float goalRPM = 1500;
double input; //sensor (RPM in my case)
double output; //action to be taken (Altering the PWM frequency in my case)
double kP = .8, kI = 1.1, kD = .0035; //PID tuners

PID rpmPID(&input, &output, &setPoint, kP, kI, kD, DIRECT);

void setup() {

setPoint = map(goalRPM, 0, 6500, 0, 127);

Serial.begin (38400);

rpmPID.SetOutputLimits(0, 80);
rpmPID.SetTunings(kP, kI, kD); //Defines which variables above are tuning variables
rpmPID.SetSampleTime(10);
rpmPID.SetMode(AUTOMATIC); //Turns on PID loop

ST.motor(2, 10);

pinMode(2, INPUT_PULLUP); //sets pin mode for pin 2

pinMode(3, INPUT_PULLUP); //sets pin mode for pin 3
//Setting up interrupt
//A rising pulse from encoder activated ai0(). AttachInterrupt 0 is DigitalPin nr 2 on most Arduinos.
attachInterrupt(0, ai0, RISING);

//B rising pulse from encoder activated ai1(). AttachInterrupt 1 is DigitalPin nr 3 on most Arduinos.
attachInterrupt(1, ai1, RISING);
}

void loop() {

RPM = (1/timeToRev) * 1000 * 1000 * 60; //conversion for uSec/rev to RPMs

input = map(RPM, 0, 6500, 0, 127);
rpmPID.Compute();
Serial.println(output);
ST.motor(2, output);

// Send the value of counter
if ( counter != temp ) { //if change is detected in the encoder, print the positional data values
//Serial.println ("Positional Data: ");
//Serial.println (counter);
temp = counter;
}

if ( counter >= PPR or counter <= -PPR) { //This if statement resets the counter every time the encoder does a full revolution, protecting from reset after memory becomes filled
counter = 0;
timeSample2 = micros();
timeToRev = timeSample2 - timeSample1;
//Serial.println ("timeToRev: ");
//Serial.println (timeToRev);
//Serial.println ("RPM: ");
Serial.println (RPM);
//input = map(RPM, 0, 3840, 0, 80);
timeSample1 = timeSample2;

//rpmPID.Compute();
//Serial.println(output);
//ST.motor(2, output);
}
//}
}

void ai0() {
// ai0 is activated if DigitalPin nr 2 is going from LOW to HIGH
// Check pin 3 to determine the direction
if (digitalRead(3) == LOW) {
counter++;
} else {
counter--;
}
}

void ai1() {
// ai0 is activated if DigitalPin nr 3 is going from LOW to HIGH
// Check with pin 2 to determine the direction
if (digitalRead(2) == LOW) {
counter--;
} else {
counter++;
}
}

The code for this project needed to handle three main tasks –

  1. Powering the motor
    • Powering the motor was originally done using a transistor circuit and PWM control through the Arduino – however the transistor could not handle the current draw from the motor, so a Sabertooth motor controller was used. Instead of PWM output (-255 to 255), this takes serial input (-127  to 127) from the Arduino.
  2. Reading the speed of the motor
    • Instead of building and coding a tachometer, we used a rotary encoder we already had in the shop. This works by reading the position of the motor and counting the time between each position. This is then converted into an RPM value.
  3. Adjusting the speed of the motor as needed
    • Once the motor speed is calculated, we can feed that into a PWM loop. This is similar to the cruise control on your car, the closer you get to the set speed the more slowly you accelerate. The PID loop checks to see if the motor is spinning at the goalRPM value, and if not will adjust the output to the Sabertooth accordingly. The output of the PID loop is also limited to avoid a “runaway” state. Because our encoder has a max RPM of 5000, any higher speed causes the encoder to read incorrect (low) values. If the motor is actually spinning fast but the encoder is reading low, it tries to speed the motor up and this will continue until the motor is spinning at full speed (which is why we limit the PID to an output that provides lower than our limit RPM).

On this rough schematic you can see the basic workings of the hypocycloid circuitry.

Power

  • A 12V DC wall power supply provides the main power for the mechanism

Backup Power

  • Because the system has such a long life, backup power is needed. A 12V rechargeable battery is used that will kick in if the power goes out or the system is unplugged to be moved. This is done by creating a trickle charge circuit with a resistor and a diode – the resistor steps down the voltage to slowly and safely charge the battery while the diode allows current to flow to the SaberTooth at full voltage when powering the motor. Both wall power and backup power run to the SaberTooth, which in turn powers both our Arduino and motor.

Motor Control

  • Motor control is handled by a feedback loop created from the encoder, Arduino, and SaberTooth controller. The motor spins up, the encoder reads the speed in RPM, the Arduino compares that speed to how fast the motor should be spinning and sends that data via serial to the SaberTooth which adjusts the motor speed.

Related Videos & Resources:

Arduino Master Lock Cracker!
6 Ways to control Motors! Including with Arduino & Raspberry Pi!
Automatic Arduino CNC Part Loader!