magnet levitation algorithm help plz

Ok so I finally got around to levitating a magnet that I've always wanted to do since I saw it online
my current methodology I have my ratiometric hall sensor on the coil and I just write down the analog reading of where its desired to be and I adjust the pwm to hopefully keep it there
the coil is about 35 minus the offset (512) at full power
I know for the magnet I have that if the pwm is <50 it will let it drop assuming its stuck to it
the range I think I want it in is around 570 (about 3/4 inch away) so I have it when its less than 570 I raise pwm to 200, and when its more than 580 I drop the pwm to 50
this actually works but only if the magnet is very steady at first and at most will levitate 1 second before it oscillates out too far out
I think I need some sort of pid algorithm but I don't understand it that well
I know that it needs 3 variables, and input, output and error but I don't know how to implement that or how to get those numbers
has anyone done something like this that I can look at there code or anyone feel like explaining pid in a way I can implement it? Thanks in advance

Ok so I got it running pretty well, sometimes it'll go for 5 or more seconds, usually as I move it around it'll hold better otherwise about 1 second
my code kinda just approximates some function, mainly figured out with trial and error, I had only 4 if statements to adjust power level and when I doubled that with smaller steps its much smoother obviously I guess
it would be awesome to figure out a formula to give me much more variability( maybe utilizing the full 255 of pwm atleast)
here's my code so far, pretty simplistic I think

 #include "LiquidCrystal.h"

LiquidCrystal lcd(8,9,4,5,6,7);
unsigned long lcdt;
int power;
int key;
int reading = 0;
//coil max 33un
//mag hold min power 50/255
//max power pick up 1 inch
//desired rand 570/640 big mag 


void setup(){
  Serial.begin(57600);
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print("hello");
  pinMode(A0,0);
  pinMode(A1,0);
  pinMode(3,1);
}

void loop(){
  if(millis() - lcdt > 300){
  lcdt = millis();
  lcd.clear();
  lcd.print(reading);
  lcd.setCursor(0,1);
  lcd.print(power);
}
reading = analogRead(A1);
if(reading > 620) power = 0;
if(reading > 610) power = 10;
if(reading > 600) power = 30;
if(reading < 595) power = 75;
if(reading < 590) power = 125;
if(reading < 580) power = 175;
if(reading < 575) power = 225;
if(reading < 560) power = 255;
key = buttons();
if(key == 0) power = 0;
analogWrite(3,power);









}
    
int buttons(){
  int _reading = analogRead(A0);
  if(_reading - 10 < 0 && _reading + 10 > 0) return 1;
  if(_reading - 10 < 143 && _reading + 10 > 143) return 2;
  if(_reading - 10 < 328 && _reading + 10 > 328) return 3;
  if(_reading - 10 < 505 && _reading + 10 > 505) return 4;
  if(_reading - 10 < 742 && _reading + 10 > 742) return 5;
  if(_reading - 10 < 1023 && _reading + 10 > 1023) return 0;
}

any help, comments are much appreciated

think it is a timing problem, and there are several programming flaws especially > and <

  • removed the buttons part as that needs refactoring - why not just use an digitalRead if you only want a stop function? Keep It Simple!
  • removed the LCD part to speed things up
  • created an in then else ladder (does less comparing)

not tested, give it a try,

//coil max 33un
//mag hold min power 50/255
//max power pick up 1 inch
//desired rand 570/640 big mag 


void setup()
{
  Serial.begin(115200);

  pinMode(3, OUTPUT);
}

void loop()
{
  int power = 0;

  int reading = analogRead(A1);
  Serial.println(reading);

  if (reading > 620) power = 0;
  else if(reading > 610) power = 10;
  else if(reading > 600) power = 30;
  else if(reading > 595) power = 75;
  else if(reading > 590) power = 125;
  else if(reading > 580) power = 175;
  else if(reading > 575) power = 225;
  else if(reading > 560) power = 255;
  else power = 255;

  analogWrite(3, power);
}

in stead of the "if then else" ladder you should interpolate between these hard coded points, e.g 605 should get a power of ~20 , not 30 not 10,

for such interpolation I wrote multiMap() - Arduino Playground - MultiMap -

Give it a try..

//coil max 33un
//mag hold min power 50/255
//max power pick up 1 inch
//desired rand 570/640 big mag 

// The multiMap array's
// these can be tuned manually to better fit your function.
int in[] =   {560, 575, 580, 590, 595, 600, 610, 620 };
int out[] = {255, 225, 175, 125,   75,   30,  10,    0 };

void setup()
{
  Serial.begin(115200);
  pinMode(3, OUTPUT);
}

void loop()
{
  int reading = analogRead(A1);
  Serial.println(reading);

  int power = multiMap(reading, in, out, 8); 
  analogWrite(3, power);
}

//
// http://arduino.cc/playground/Main/MultiMap
//
int multiMap(int val, int* _in, int* _out, uint8_t size)
{
  // take care the value is within range
  // val = constrain(val, _in[0], _in[size-1]); 
  if (val <= _in[0]) return _out[0];
  if (val >= _in[size-1]) return _out[size-1];

  // search right interval
  uint8_t pos = 1;  // _in[0] allready tested
  while(val > _in[pos]) pos++;

  // this will handle all exact "points" in the _in array
  if (val == _in[pos]) return _out[pos];

  // interpolate in the right segment for the rest
  return map(val, _in[pos-1], _in[pos], _out[pos-1], _out[pos]);
}

There is a working PID algorithm for an electromagnetic levitation project here: Arduino Forum

-Patrick

I hate verizon btw had to turn my data off because I was 3 mb away from 10$ fee, anyway this is what I figured out yesterday and it'll hold it about 5 seconds before oscillating out
and I realized that thw lcd was doing that so I made it possible to not do that, and I mapped the whole function
an interesting thing I didn't expect was when I added a smoothing factor it helped
im gonna try your code today if I have time, hopefully it works well
here's what I did yesterday

 #include "LiquidCrystal.h"

LiquidCrystal lcd(8,9,4,5,6,7);
unsigned long lcdt;
unsigned long buttondelay;
int power = 0;
int key = 0;
int reading = 0;
int target = 558;
int error = 0;
int smooth = map(target,0,1024,0,255) + 10;
int thigh = 15;
int tlow = -10;
int maxpower = 245;
//coil max 33un
//mag hold min power 50/255
//max power pick up 1 inch
//desired rand 570/640 big mag 


void setup(){
  Serial.begin(57600);
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print("hello");
  pinMode(A0,0);
  pinMode(A1,0);
  pinMode(3,1);
  
}

void loop(){
  if(millis() - lcdt > 300 && key == 5){
  lcdt = millis();
  lcd.clear();
  lcd.print("r");
  lcd.print(reading);
  lcd.print(" R");
  lcd.print(analogRead(A1));
  lcd.print(" t");
  lcd.print(target);
  lcd.setCursor(0,1);
  lcd.print("P");
  lcd.print(power);
  lcd.print(" E");
  lcd.print(error);
  lcd.print(" s");
  lcd.print(smooth);
}

reading = analogRead(A1);

reading = reading - map(power,0,255,0,26);
error = target - reading;

power = map(reading,target + thigh,target + tlow,0,maxpower);
if(reading > target + tlow && reading < target + thigh)power = (power + smooth) / 2;
if(reading > target + thigh) power=0;
if(reading < target + tlow) power = maxpower;



 if(millis() - buttondelay > 100){
    buttondelay = millis();
    key = buttons();
 }
 
if(key == 0) power = 0;
analogWrite(3,power);


}
    
int buttons(){

    int _reading = analogRead(A0);
    if(_reading - 10 < 0 && _reading + 10 > 0) return 1;
    if(_reading - 10 < 143 && _reading + 10 > 143) return 2;
    if(_reading - 10 < 328 && _reading + 10 > 328) return 3;
    if(_reading - 10 < 505 && _reading + 10 > 505) return 4;
    if(_reading - 10 < 742 && _reading + 10 > 742) return 5;
    if(_reading - 10 < 1023 && _reading + 10 > 1023) return 0;
  
}

Wow even seeing the code for a pid algorithm seems complex, especially with those states and all it jumps around a lot
what I did get so far is I put the pwm in phase porportion and it work a litttle beter, but changing the frequency does not and makes it worse, I guess since the inductor im using can't handle that fast response?
Im attempting at making my own simpler pid,
So far I have the difference from the target driving the response, then I smooth it, then I add 30% error back in, then I add in some kickback from the last response to smooth it out some more
also the target and range of oscillation before goin to the extremes are all picked from trial and error
lol
all this basically from trial and error and I got 10 sec hold max

The guy that wrote the PID algorithm for electromagnetic levitation did a nice job and has a simple interface to adjust the P, I, and D values. The "modes" just keep the control from turning the magnet all the way on if no levitated magnet is present and release the field if the levitated magnet gets too close to the electromagnet.

In any case, here is another implementation of an algorithm to levitate a ball if you want to try this one:

I have a similar project and will be using the PID routine and maybe the timing strategy of "Mekonikuv" along with two hall effect sensors. I'll post my code once I get it working.

-Patrick

I actually saw this and found it too confusing as well, ni really need to read up
I think I implemented a crude pid in mine right now, and it works well for maybe 10 seconds until it goes out
Im currently trying tonfind a way to detect that its oscillating and adjust to counter that

winner10920:
I actually saw this and found it too confusing as well, ni really need to read up
I think I implemented a crude pid in mine right now, and it works well for maybe 10 seconds until it goes out
Im currently trying tonfind a way to detect that its oscillating and adjust to counter that

I suggest you use an existing PID algorithm rather than spend time trying to reinvent it.

The generic PID has some tuning parameters that you can use to tweak the characteristics. If it's oscillating, you probably need more gain on the derivative feedback component.

Well I tried that magnetic leviatation pid and it doesn't work at all, maybe its because I have a bigger magnet, idk
so im going back to reinventing the wheel and making my own
Mine works great, I just gotta figure out how to cancel out the oscillations with either a correcting factor or a more accurate power factor
any ideas?

winner10920:
Well I tried that magnetic leviatation pid and it doesn't work at all, maybe its because I have a bigger magnet, idk
so im going back to reinventing the wheel and making my own
Mine works great, I just gotta figure out how to cancel out the oscillations with either a correcting factor or a more accurate power factor
any ideas?

I have no idea what 'I tried' means or in what respect it didn't work. Do you understand what a PID algorithm is and how to tune it? If it isn't tuned correctly, it's not going to work. PID isn't a magic wand, it's just a very widely applicable algorithm.

If you want to roll your own then you need to replace all three components of the PID algorithm. If it's oscillating, that means you probably don't have enough damping in your control algorithm. In PID terms you would address that by increasing the authority of the derivative feedback component (the 'D' part of PID). But how that would relate to your algorithm I have no idea, since I have no idea how your re-invented wheel works.

I tried altering all 3 factors in the sketch that was suggested above, and the best it gets is rapidly shaking up and down,
maybe im using it wrong idk, it didn't seem that hard to work with

Hi, you can go through Arduino Playground - PIDLibrary followed by Arduino Playground - PIDAutotuneLibrary. I am also working on the similar thing just for myself. Let see if we can make it out.

All the best.

wow this is amazing, i'm dioscovering what you can do with such boards. so you can control a levitating magnet ? where can i find pictures, subjects about it and the arduino ? thanks !

Search google to find some good examples, I've only been able to levitate it for a around ten seconds so far, but there are peoples out there that have it working flawlessly