I’ve uploaded some videos of my latest progress on the pendulum. It can be found on my YouTube channel here:
Posts
Meeting Notes 5/15/15
I will add to this website:
YouTube videos of the pendulum operating
A picture of myself with the device
Goals of this project
What sections are on this website
Introduction on sections and what they contain
I will also create:
An outline of the presentation
A rough draft of presentation
Fully functioning prototype bring in for the next (last) meeting
Build update 5/20/15
Since adding the potentiometer, I have gotten the pendulum fully functional. It took several hours of manual PID tuning, but I was able to make the device stabilize the pendulum with minimal movement. I also took some videos of the progress tuning the machine which I will add below. All that is left is a bit of electrical and code cleanup and the final presentation.
Build update 5/13/15
Since my last post I have:
- Replaced the encoder with a potentiometer in hopes of better accuracy.
- Sanded and finished track spacers.
- Modified the pendulum arm to work with the potentiometer.
- Revised the code to be as close to working as possible with the encoder alone.
The encoder is able to balance the pendulum to a certain extent within the bounds of the track in the sense that is is able to keep the pendulum mostly balanced and oscillates slowly back and forth along the bounds of the track.
Meeting notes (5/8/15)
Today we met and talked about:
- Problems I’ve had in the last two weeks and what I did to fix them. (Such as track slop due to extra space on the cart, and the guide rail moving in the slot)
- One large problem I am having right now is the small size of the track relative to the pendulum. I am unable to travel fast and far enough to fix the pendulum after a certain angle has been reached. Because of this, I will have to manually “start” the pendulum upright and balance from there.
- We also discussed any issues I expect to become future problems. This includes problems with the encoder accuracy and tuning the code just right to avoid oscillation.
Meeting Notes 5/1/15
The meeting today was canceled.
Build update 5/6/15
Since last week, I’ve solved most of the problems that I discussed yesterday.
- I solved the cart shifting on the track with some plastic spacers. I still need to sand the spacers just right to reduce friction but not leave any wobble left.
- I added brackets to both ends of the shaft so that the springs hold the shaft still.
- I made another arm out of sheet metal because the old one flexed too much.
- I added a small weight to the end of the arm to keep it unstable, and make the machine actually useful
- I added an adjustable spring screw to the mount plate with the encoder
- I added a spring behind the arm connector to remove the excess wobble from the shaft encoder.
- I added a three pin connector to the encoder, to make servicing it easier.
- I added an LED to the control board, for seeing where I am in the program

Meeting notes (4/24/15)
First of all I would like to thank Stuart Brand for the seedstudio motor shield. This will allow me to drive the DC motor on the track instead of the slower stepper motor.
I met with Professor Parkhurst and discussed the problems I have been having with the speed of the stepper motor on the pendulum. The stepper simply does not go fast enough to balance the current pendulum. I plan to replace the stepper with a much faster DC motor at the cost of positional accuracy. I will also have to use some external drive circuitry to operate the DC motor. I will be using the motor shield that Mr. Brand provided.
Code update #1
Here is my first code update:
Currently, the code is setup to zero the pendulum using the limit switches and to try to keep the pendulum balanced once you balance it by hand. The code operates a motor control Arduino Shield connected to a 12V DC motor.
const int in1 = 8; // H bridge Pin 1
const int in2 = 11; // H bridge Pin 2
const int ME = 9; // Motor Enable / Speed Control
const int lim = 4; // Limit Switches
const int led = A1; // LED indicator
volatile int encoderValue = 0;
int lastEnc = 0;
int a = 0;
void setup() {
pinMode(2, INPUT_PULLUP); //Pin Setup
pinMode(3, INPUT_PULLUP);
pinMode(led, OUTPUT);
attachInterrupt(0, encoder, CHANGE);
attachInterrupt(1, encoder, CHANGE);
Serial.begin(9600);
Serial.println(“Begin”);
pinMode(ME, OUTPUT);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
digitalWrite(led, LOW);
pinMode(A0, INPUT_PULLUP);
zero(); // Find Home on the Axes
delay(500);
movePos(12, 200); //Move to center
// flipUp(); // Command to right the pendulum from resting, still in progress.
//wait for balance ready
boolean ledState = false;
long ledDelay = 0;
while (abs(analogRead(A0) – 14) > 2) { //Flash the light while the processor waits for you to right the pendulum and push the red button.
if (millis() – ledDelay > 500) {
ledState = !ledState;
digitalWrite(led, ledState);
ledDelay = millis();
}
}
encoderValue = 0;
Serial.println(“Beginning”);
}
boolean r = true;
int lastEncoderValue = 0;
float position = 0;
long loopDelay = 0;
void loop() {
/*
if (encoderValue != lastEncoderValue) {
Serial.println(encoderValue);
lastEncoderValue = encoderValue;
}
*/
if (millis() – loopDelay > 2000) {
loopDelay = millis();
Serial.println(“Looping”);
}
a = analogRead(A0); //Read for button inputs
if (a < 1020 && a > 1000) {
r = true;
}
else {
if (abs(a – 14) < 2 && r) {
r = false;
Serial.println(“Red Button”);
// flipUp();
}
}
PID();
}
/*************** Basic Functions ******************/
void encoder() { //ISR to read the encoder value
int A = digitalRead(2);
int B = digitalRead(3);
int enc = (A << 1) | B;
int sum = (enc << 2) | lastEnc;
if (sum == B0001 || sum == B0111 || sum == B1110 || sum == B1000) encoderValue++;
if (sum == B1101 || sum == B0100 || sum == B0010 || sum == B1011) encoderValue–;
lastEnc = enc;
}
void move(boolean mdir, int mspeed) { //Sets the pins for moving in a certain direction and speed
if (mdir) {
digitalWrite(in1, LOW);
digitalWrite(in2, HIGH);
}
else {
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW);
}
analogWrite(ME, mspeed);
}
void movePos(float pos, int speed) { // Move to a certain coordinate at a certain speed
if (speed < 0 || speed > 255) {
while (1) {
move(0, 0);
Serial.print(“Invalid Speed: “);
Serial.println(speed);
delay(2000);
}
}
float del = abs(position – pos) * 18;
// Serial.println(“Move Position Called:”);
// Serial.print(“Delay: “);
// Serial.println(del);
long posTime = millis();
if (position < pos) {
// Serial.println(“Moving Right”);
move(1, speed);
while (millis() – posTime < del) {
if (digitalRead(lim) == HIGH) {
while (1) { //stop
move(0, 0);
Serial.println(“Error: Right Limit Hit”);
digitalWrite(led, HIGH);
delay(1000);
digitalWrite(led, LOW);
delay(1000);
}
}
if (abs(encoderValue – lastEncoderValue) > 2) { //Encoder Value Printing may cause inaccuracy in movement
Serial.println(encoderValue);
lastEncoderValue = encoderValue;
}
}
move(0, 0);
}
else {
// Serial.println(“Moving Left”);
move(0, speed);
while (millis() – posTime < del) {
if (digitalRead(lim) == HIGH) {
while (1) { //stop
Serial.println(“Error: Left Limit Hit”);
move(0, 0);
digitalWrite(led, HIGH);
delay(1000);
digitalWrite(led, LOW);
delay(1000);
}
}
if (abs(encoderValue – lastEncoderValue) > 2) { //Encoder Value Printing may cause inaccuracy in movement
Serial.println(encoderValue);
lastEncoderValue = encoderValue;
}
}
move(0, 0);
}
position = pos;
// Serial.print(“New position: “);
// Serial.println(position);
// Serial.println(“Move position done”);
}
void movePosNoLim(int pos) { // Same as movePos, but ignoring the limit switches (for moving off of zero)
int del = abs(position – pos) * 18;
Serial.println(“Move Position, no limits:”);
long posTime = millis();
if (position < pos) {
Serial.println(“Moving Right”);
move(1, 150);
while (millis() – posTime < del) {
}
move(0, 0);
}
else {
Serial.println(“Moving Left”);
move(0, 200);
while (millis() – posTime < del) {
}
move(0, 0);
}
position = pos;
Serial.print(“New position: “);
Serial.println(position);
}
void moveRel(float rel, int rspeed) {
Serial.print(“Move Relative Command: “);
Serial.println(rel);
movePos(position + rel, rspeed);
}
/************* pendulum commands *******************/
void zero() { //Move to the left until a limit is hit, then move slightly off from that side
Serial.println(“Zeroing”);
while (digitalRead(lim) == LOW) {
move(0, 150);
// Serial.println(“Moving towards zero”);
}
// Serial.println(“Zero Hit”);
move(0, 0);
position = 0;
movePosNoLim(1);
position = 0;
delay(1000);
Serial.println(“Zeroed”);
}
void flipUp() { //WIP, will eventually right the pendulum from resting (fully down)
int fLastEnc = 0;
int fDelay = 100;
delay(2000);
//pos is clockwise
int minAngle = 0;
int fSpeed = 200;
int fDist = 16;
for (int i = 0; i < 6; i++) {
digitalWrite(led, HIGH);
moveRel(-fDist, fSpeed);
fLastEnc = encoderValue ;
while (encoderValue > fLastEnc) {
if (encoderValue < minAngle) {
minAngle = encoderValue;
}
fLastEnc = encoderValue;
delay(fDelay);
}
digitalWrite(led, LOW);
delay(500);
moveRel(fDist, fSpeed);
while (encoderValue < fLastEnc) {
if (encoderValue < minAngle) {
minAngle = encoderValue;
}
fLastEnc = encoderValue;
delay(fDelay);
}
if (minAngle < -9) {
fSpeed = 230;
fDist = 10;
Serial.println(“Speeding Up”);
}
Serial.print(“i = “);
Serial.println(i);
}
fLastEnc = encoderValue;
}
void PID() { //Currently just the “P”, I intend to make the P affect the distance it moves as well as the speed at which it moves there.
int dead = 0;
int P = encoderValue;
float Pk = 3.5`;
if (abs(P) > dead) {
moveRel(Pk * P, 250);
if (P > 0) {
Serial.print(“Correcting to the right: “);
}
else{
Serial.print(“Correcting to the left: “);
}
Serial.println(encoderValue);
}
else {
Serial.println(“Within Limits”);
}
}
Build update (4/29/15)
This week, I worked mostly on the programming. In testing the initial code, the pendulum proves too difficult to balance properly with its length. I solved this by cutting a larger version of the arm. This has in turn caused more problems, because the mount that the pendulum attaches to is now falling off from the weight. I plan to remount the sheet metal plate to the cart more securely and take up a bit of the slack in the encoder shaft with a lock washer.
Here are my test results from this week:
4/25/15 3:06 PM
I reviewed the library for the seedstudio motor shield and was able to load the
example sketch onto the the Arduino to test the operation of the new DC motor. I was
unable to find much documentation on the library itself however, so I looked through
the library source code. The library does not give me fine enough control over the
motor to use it for my pendulum, so I will have to write my own. The board designer
made available online the Eagle files for the board, as well as linked to the
datasheet for the driver IC. With these pieces of information, as well as the library
and example sketch, I gathered the following information:
**Turn on the external motor supply before turning on Arduino, otherwise the driver
will attempt to drive from the Arduino supply line**
analogwrite to enable pin to set speed
digitalwirte to the in1, 2, 3, & 4 pins to set direction
cw = in1 low, in2 high
ccw = in1 high, in2 low
Pinout:
enable A – 9
enable B – 10
in1 – 8
in2 – 11
in3 – 12
in4 – 13
stop by setting speed pin to zero
3:51 PM
I’ve written and tested some code to confirm that I can properly control the motor and
it worked perfectly. The next step is to connect it to the limit switches. There’s
also the added bonus that the regulator on the motor control board powers the
Arduino through the 5V line. The downside of this is that there is slightly more risk of damaging the
arduino due to overvoltage.
6:54 PM
I just tested the new motor with limit switches. It runs much faster than the other
motor and reacts instantly to the limit switches. I am going to add encoder interrupts
to the program now. Hopefully the interrupt routines won’t interfere with the limit
switches.
4/26/15
I just received the DuPont crimp connectors that I ordered a month ago for this project, so I created a custom connector for the Arduino human interface. I also added an LED to the control board for diagnostics. I also learned that if you try to bend DuPont crimp connectors 90 degrees in a socket, they snap off immediately and get stuck in whatever you plugged them into and it takes a very long time with very small tweezers to get them out.





