Go Down

Topic: self-balancing unicycle problem (Read 10400 times) previous topic - next topic

tgfeminella

Mar 11, 2013, 02:44 am Last Edit: Mar 21, 2013, 11:54 am by tgfeminella Reason: 1
Edit: problem solved! I've begun riding the unicycle although its very difficult with the cheap child's bicycle seat I'm using. The PID controller also needs some more tuning. To make it all worse, I have no unicycle experience.


http://www.youtube.com/watch?v=mEee1iI6emE






I've built a self-balancing unicycle using a sparkfun 6dof IMU,a 250w motor,a SyRen 25 motor controller from Dimension Engineering, with an Arduino Uno. For balance control, I have a PID control system. My problem is that after a little while the directions of the output reverses for no apparent reason. I'm new to Arduino and coding so I can't figure out what is causing the problem.

Here is my code:
Code: [Select]
//code for homemade self balancing unicycle:
//sparkfun 6dof IMU
//PID output control
//Dimension Engineering SyRen 25 motor driver
//Arduino IDE 1.0

#include <FreeSixIMU.h> //fusion filtering library

#include <FIMU_ADXL345.h> //accelerometer library

#include <FIMU_ITG3200.h> //gyro library

#include <PID_v1.h> //PID control library

#include <Wire.h>

//timing
int STD_LOOP_TIME = 9;
int lastLoopTime = STD_LOOP_TIME;
int lastLoopUsefulTime = STD_LOOP_TIME;
unsigned long loopStartTime = 0;

//for IMU:
float angles[3]; //yaw pitch roll (only pitch is necessary for one-axis balance in this application)
FreeSixIMU sixDOF = FreeSixIMU(); //set the FreeSixIMU object

//for PID controller:

double BalancePoint; //angle that unicycle attempts to balance at
double PID_Output;
double Pitch;
PID myPID(&Pitch, &PID_Output, &BalancePoint,1.5,0.5,0.2, DIRECT); // numbers are (respectively): Kp,Ki,Kd

//for SyRen
int SyRen_Output; //final motor output value
int Int_Output; //used to convert pid output to int
int Direction;

void setup()
{
 Serial.begin(2400); //the SyRen is completely non-responsive if a higher baudrate is chosen
 Wire.begin();
 
 delay(5);
 sixDOF.init(); //begin the IMU
 delay(5);
 
 //initialize PID variables
 Pitch = angles[1];
 BalancePoint = 2; //this should be set to what pitch angle reads when unicyle is balanced
 myPID.SetMode(AUTOMATIC);
 myPID.SetOutputLimits(-127,127);
 Serial.write(170);
}

void loop()
{
 
 //reads angles from IMU then plugs pitch into the PID controller
 sixDOF.getEuler(angles);
 Pitch = (double) angles[1];
 myPID.Compute();
 Int_Output = (int) PID_Output; //converts to int for SyRen controller
 
 
 //when output is negative, this code reverses the direction of the motor
 if (Int_Output < 0) {
   SyRen_Output = abs(Int_Output);
   Direction = 0;
 }
 else
 {
   SyRen_Output = Int_Output;
   Direction = 1;
 }
 //packetized serial control:
 Serial.write(128);
 Serial.write(Direction);
 Serial.write(SyRen_Output);
 Serial.write((128+Direction+SyRen_Output) & 0b01111111);
 
 //Serial.print(SyRen_Output); //for debugging


//loop timing control
 
 
lastLoopUsefulTime = millis()-loopStartTime;
 if(lastLoopUsefulTime<STD_LOOP_TIME)         delay(STD_LOOP_TIME-lastLoopUsefulTime);
 lastLoopTime = millis() - loopStartTime;
 loopStartTime = millis();
}


The serial mode used by the motor driver needs the address(128-135, direction(0 or 1), and a speed (0-127) to work. The PID is configured to give an output of -127 to 127. When it drops below zero, I set it to invert so that it runs 0-127and I set direction to reverse. I am guessing that this is the part where my problem lies, am I right? what can I do to fix this?

Thanks in advance.

michinyon

How are you stopping the unicycle falling over sideways ?

Try building a device with two wheels, first.

tgfeminella

I should have specified that this a is a ride on machine that relies on the user for left/right balance

tgfeminella

I managed to fix the reversing issue by changing this changing this section of the code from this:

Code: [Select]
if (Int_Output < 0) {
    SyRen_Output = abs(Int_Output);
    Direction = 0;
  }
  else
  {
    SyRen_Output = Int_Output;
    Direction = 1;


to this:

Code: [Select]
SyRen_Output = abs(Int_Output);
    if (angles[1] > BalancePoint && Direction == 1) {
      Direction = 0;
    }
    if (angles[1] < BalancePoint && Direction == 0) {
     Direction = 1;
    }
   


I have no idea why that worked, but for now, that seems to have done the trick.

tgfeminella

scratch that, the motor reversing problem is back, and it seems to be at least partially linked to enabling my 9v external power supply as opposed to the USB power from the computer

tgfeminella

I have confirmed that my Arduino is acting up because of power issues. If I use a 9v battery, it is most profound, but will only occur on USB power if the connection is disturbed. I just need a more powerful external supply. My unicycle is now balancing great! With a little more tuning, and some finishing touches, I should be ready to take a spin!

michinyon

You can power an actual ride-on device with a 9V battery ?   How do you accomplish that ?

tgfeminella


You can power an actual ride-on device with a 9V battery ?   How do you accomplish that ?


The motor driver is powered by two 10amp hour 12v scooter batteries in series, I just used the 9v to power the Arduino. I switched to a 7.2v 1200mah battery though. I have tuned the unicycle somewhat and it will now balance and accelerate with me riding it. I believe I just solved my reversing issue: For packetized serial commands, you must transmit a checksum after each packet to confirm that the data is correct. To serial write 0, you must specify that it is a byte. In the packet, I did remember to do this, but I forgot in the checksum. The code would work, but any momentary power interruptions (not perceivable) would somehow cause the numbers to switch. Before if you wiggled the USB cable or unplugged it for a moment, the command would reverse and the unicycle would fall and quickly go to max power (injuring me twice in the process). I added that checksum and so far momentarily unplugging the power will no longer upset the machine. Its too soon to say for sure, but I believe my problem might be over.

tgfeminella

I'm so angry! The problem has returned with a vengeance! I am completely out of ideas. My unicycle is done now and I could be riding if it weren't for this! I feel that this is most likely a code issue. If anyone can help me, please! I am desperate, I have tried everything I can think of. It could be a simple issue that an adept coder could easily spot, I have no clue. Here is the latest form of my code:

Code: [Select]
//code for homemade self balancing unicycle:
//sparkfun 6dof IMU
//PID output control
//Dimension Engineering SyRen 25 motor driver
//Arduino IDE 1.0

#include <FreeSixIMU.h> //fusion filtering library

#include <FIMU_ADXL345.h> //accelerometer library

#include <FIMU_ITG3200.h> //gyro library

#include <Wire.h>

#define GUARD_GAIN 20.0

//timing
int STD_LOOP_TIME = 9;             
int lastLoopTime = STD_LOOP_TIME;
int lastLoopUsefulTime = STD_LOOP_TIME;
unsigned long loopStartTime = 0;


//for IMU:
float angles[3]; //yaw pitch roll (only pitch is necessary for one-axis balance in this application)
FreeSixIMU sixDOF = FreeSixIMU(); //set the FreeSixIMU object

//for PID controller:

float K = 0.1;
int   Kp = 95;                     
int   Ki = 2;                   
int   Kd = 1; 
int last_error = 0;
int integrated_error = 0;
int pTerm = 0, iTerm = 0, dTerm = 0;
int error;
int BalancePoint = 0;
int Pitch;

//for SyRen
int SyRen_Output; //final motor output value
int Int_Output; //used to convert pid output to int


void setup()
{
  Serial.begin(9600); //set baud rate
  Wire.begin();
 
  delay(5);
  sixDOF.init(); //begin the IMU
  delay(5);
 
 
 
 
  Serial.write(170);
}

int updatePid(int BalancePoint, int Pitch)   {
  sixDOF.getEuler(angles);
  Pitch = angles[1] * 2;
  error = BalancePoint - Pitch;
  pTerm = Kp * error;
  integrated_error += error;                                       
  iTerm = Ki * constrain(integrated_error, -GUARD_GAIN, GUARD_GAIN);
  dTerm = Kd * (error - last_error);                           
  last_error = error;
  return -constrain(K*(pTerm + iTerm + dTerm), -127, 127);
}

void loop()
{
 
  Int_Output = updatePid(BalancePoint, Pitch);
 
    SyRen_Output = abs(Int_Output);
    if (angles[1]*2 > BalancePoint) {
    Serial.write(128);
  Serial.write((byte)0);
  Serial.write(SyRen_Output);
  Serial.write((128+(byte)0+SyRen_Output) & 0b01111111); 
    }
    if (angles[1]*2 < BalancePoint) {
     Serial.write(128);
  Serial.write(1);
  Serial.write(SyRen_Output);
  Serial.write((128+1+SyRen_Output) & 0b01111111);
    }
   
 
   
   
 

 
 
  //Serial.print(SyRen_Output); //for debugging


//loop timing control
  lastLoopUsefulTime = millis()-loopStartTime;
  if(lastLoopUsefulTime<STD_LOOP_TIME)         delay(STD_LOOP_TIME-lastLoopUsefulTime);
  lastLoopTime = millis() - loopStartTime;
  loopStartTime = millis();
 

}

tgfeminella

If anyone cares, I finally discovered the real problem: The function getEulerAngles in the IMU library for my digital IMU would invert the pitch measurement if the IMU had spun around. I don't know why, but that was an intentional part of the library. Because this IMU lacks a magnetometer, it cannot accurately measure yaw, only change in yaw. As the yaw drifted around, it would finally pass 90 degrees, which told the library to invert the pitch.  I discovered that using getYawPitchRoll instead will eliminate the pitch inversion which completely solved my problem. In an earlier attempt to fix it, I completely revamped my PID controller which turned out to be worth it anyway as it greatly improved balancing performance. I will have a bit more tuning to do, and my seat mount suffered damage in an accident due to the reversing problem, but I will soon be rolling one-wheeled!

LarryXz

It would be really interesting to see a picture on your unicycle :)

tgfeminella


It would be really interesting to see a picture on your unicycle :)


Here it is:



I have just started riding it, and it is very difficult. The cheap child's bicycle seat is far too narrow which makes left and right balancing very hard. I should have bought a bigger one, but I didn't feel like paying $20 as opposed to $3. Also, my PID controller stills needs some tuning. To make all of this much worse, I have never ridden a Unicycle before.

Here is a YouTube clip of some of my first attempts:

http://www.youtube.com/watch?v=mEee1iI6emE

alvin2014

so did you end up fixing the problem completely?

JihoPark

#13
Nov 19, 2016, 08:32 pm Last Edit: Dec 02, 2016, 01:39 pm by JihoPark
I am really amazed that you built your own self balancing unicycle from scratch. I hope everything ended up perfectly for you. I was planning to something like you but at this point my knowledge is the biggest problem. Anyway I'm inspired by your work and a little bit more motivated. So hopefully I'm gonna move forward very soon.

Edit: I've made some arrangements and I'm finally starting this January ;).

Bana_haw

#14
Jul 07, 2017, 05:29 am Last Edit: Jul 07, 2017, 05:36 am by Bana_haw
It's a good thing that you were able to figure out your problem with the self-balancing electric unicycle. Congrats! I'm also having a problem because I couldn't make it work. I'm trying to make it look like this one here but I always mess up with the codes! I don't have unicycle experience as well so it's kinda hard to do this stuff. Anyway, I'm hoping for the best and hopefully, I'll be able to make this work.

Go Up