极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 92645|回复: 33

Arduino上拉磁悬浮

[复制链接]
发表于 2012-5-20 21:08:39 | 显示全部楼层 |阅读模式
本帖最后由 johnsonzzd 于 2012-12-9 16:59 编辑

折腾了几天终于把这个东西搞定了。看似简单,还是有一些小技巧的。
主要材料:
  • Arduino UNO主控
  • SS495线性霍尔采集位置信息
  • L298N驱动
  • 直径12.7mm强磁球
  • 电磁铁12V,线圈电阻10欧

主要参数:PID控制,采样周期1ms,PWM频率 3921Hz,悬浮距离30mm,电流300mA
筷子版的,下面是一个磁体,上面是一个小铁块,中间是一截筷子。悬浮距离比较小,但是方便进行建模和控制理论分析。

磁球版,悬浮距离很大,不容易进行理论分析。

  1. const int SS495_PIN = A0;  // Analog input pin that the SS495 is attached to
  2. const int PWM_PIN=3;  // Pins 3 and 11 are connected to Timer 2.

  3. const int SampleTime=600;
  4. const int PWM_BIAS=128;

  5. int Setpoint=850;
  6. double Kp=1, Ki=0.001, Kd=50;

  7. void setupCoilPWM()
  8. {  
  9.    // Setup the timer 2 as Phase Correct PWM, 3921 Hz.
  10.    pinMode(3, OUTPUT);
  11.    // Timer 2 register: WGM20 sets PWM phase correct mode, COM2x1 sets the PWM out to channels A and B.
  12.    TCCR2A = 0;
  13.    TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM20);
  14.    // Set the prescaler to 8, the PWM freq is 16MHz/255/2/<prescaler>
  15.    TCCR2B = 0;
  16.    TCCR2B = _BV(CS21);
  17. }

  18. void writeCoilPWM(uint8_t pin, int val)
  19. {
  20.       OCR2B = val;
  21. }

  22. void PID_Compute( )
  23. {
  24.   static int lastError=0;
  25.   static unsigned long int lastTime=0;
  26.   static double iiterm=0;

  27.   unsigned long now,timeChange;
  28.   int input,output,error,piterm,diterm;

  29.   now = micros();
  30.   timeChange = (now - lastTime);
  31.   if(timeChange>=SampleTime)
  32.   {
  33.     lastTime = now;

  34.     input=read_input();

  35.     error = -(Setpoint - input);

  36.     iiterm+= (Ki * error);
  37.     iiterm=constrain(iiterm,-255,255);

  38.     piterm=Kp * error;
  39.     diterm=Kd * (error - lastError)*1000/timeChange;
  40.     output = PWM_BIAS+(piterm + diterm);
  41.     output=constrain(output,0,255);
  42.     writeCoilPWM(PWM_PIN, output);

  43.     // print the results to the serial monitor:
  44.     //sprintf(str,"%04d,%+04d,%+04d,%+04d,%04d",error,piterm,diterm,(int)iiterm,output);
  45.     //Serial.println(str);   

  46.     /*Remember some variables for next time*/
  47.     lastError = error;
  48.    }
  49. }

  50. int read_input()
  51. {
  52.   static int last=0;
  53.   int r;
  54.   r=analogRead(SS495_PIN);
  55.   if(abs(r-last)<4)
  56.     r=last;
  57.   else
  58.     last=r;
  59.   return r;
  60. }

  61. void setup() {
  62.   setupCoilPWM();
  63. }

  64. void loop() {
  65.   // read the analog in value:
  66.   PID_Compute();
  67. }
复制代码
下一步准备加上串口通讯,用matlab做上位机界面。已经做好了Arduino的自平衡车和两旋翼模型,有空也放上来。

自平衡车就在这里:
http://www.geek-workshop.com/thread-2398-1-1.html

单旋翼实验装置,已经发到amobbs了,有详细资料:
http://www.amobbs.com/thread-5505843-1-1.html

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

评分

参与人数 1 +5 收起 理由
幻生幻灭 + 5 很给力!

查看全部评分

回复

使用道具 举报

发表于 2012-5-20 22:44:13 | 显示全部楼层
非常有趣的东西 , 代码先收下了,有空慢慢看
回复 支持 反对

使用道具 举报

发表于 2012-5-21 00:54:01 | 显示全部楼层
很久以来一直在泡一个blog,跟大家分享下  动力老男孩的博客 (为免广告嫌疑,请自行百度。)
他用了多个电磁线圈实现了 陀螺(自制磁铁陀螺)的悬浮自稳(当然,Arduino的PID平衡算法和霍尔元件也功不可没)
蛮有意思。

一直以来也学了不少。
回复 支持 反对

使用道具 举报

发表于 2012-5-21 00:56:45 | 显示全部楼层
第一幅图用皮筋捆着 上面亮灯的板子能透露细节吗?谢谢!
回复 支持 反对

使用道具 举报

发表于 2012-5-21 07:13:08 | 显示全部楼层
darkorigin 发表于 2012-5-21 00:56
第一幅图用皮筋捆着 上面亮灯的板子能透露细节吗?谢谢!

那应该是L298N电极驱动板吧~~看楼主的电磁铁是12V的,Arduino提供不了那么大的电压。Arduino先连L298N,外部供电给L298N,再连电磁铁。Arduino通过PWM控制L298N,间接控制电磁铁。
以上纯属猜测,望指点。
回复 支持 反对

使用道具 举报

发表于 2012-5-21 08:34:05 | 显示全部楼层
调参数的话悬浮高度范围有多大?
回复 支持 反对

使用道具 举报

发表于 2012-5-21 16:10:27 | 显示全部楼层
我刚要说,是不是仿照动力老男孩做的,就看到他的名字了……
话说他的磁悬浮鸡蛋还在我们这里
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-5-21 23:25:59 | 显示全部楼层
那个带个皮筋的板子是L298驱动板,在这里有点浪费,用个功率三极管或者MOS管就可以了。
这个东西的高度调整范围主要受限于霍尔元件。SS495从0到饱和,磁球的运动范围只有2、3毫米。所以有用图像识别的手段测量高度的,调整范围就很大了。
我看过动力老男孩的东西,受到启发,确实不错,但是有一个缺点是理论分析不够。磁悬浮不是什么创新的东西,国内的项目和国外的比起来还有很大差距。有兴趣的可以参考下面这个,从实践到理论非常详细。
http://www.coilgun.info/levitation/home.htm
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-5-21 23:45:36 | 显示全部楼层
完整版,用串口向PC机发送信息:
  1. const int SS495_PIN = A0;  // Analog input pin that the SS495 is attached to
  2. const int PWM_PIN=3;  // Pins 3 and 11 are connected to Timer 2.

  3. const double SampleTime=0.5;  //ms
  4. const int PWM_BIAS=128;

  5. int Setpoint=850;
  6. double Kp=1, Ki=0.01, Kd=90;
  7. double piterm,diterm, iiterm=0;
  8. int input,output,error;
  9. unsigned long now,timeChange;

  10. void setupCoilPWM()
  11. {  
  12.   // Setup the timer 2 as Phase Correct PWM, 3921 Hz.
  13.   pinMode(3, OUTPUT);
  14.   // Timer 2 register: WGM20 sets PWM phase correct mode, COM2x1 sets the PWM out to channels A and B.
  15.   TCCR2A = 0;
  16.   TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM20);
  17.   // Set the prescaler to 8, the PWM freq is 16MHz/255/2/<prescaler>
  18.   TCCR2B = 0;
  19.   TCCR2B = _BV(CS21);
  20. }

  21. void setupControlTimer()
  22. {  
  23.   //set timer1 in CTC mode.
  24.   //"ATmega4_88_168_328_DataSheet" , section 16.9.2 Clear Timer on Compare Match (CTC) Mode
  25.   cli();          // disable global interrupts
  26.   TCCR1A = 0;     // set entire TCCR1A register to 0
  27.   TCCR1B = 0;     // same for TCCR1B

  28.   // turn on CTC mode:
  29.   TCCR1B |= (1 << WGM12);
  30.   // Set CS12 bits for 256 prescaler:
  31.   TCCR1B |= (1 << CS12);
  32.   // set compare match register to desired timer count:
  33.   OCR1A = (SampleTime/1000)*(16000000/256);
  34.   // enable timer compare interrupt:
  35.   TIMSK1 |= (1 << OCIE1A);
  36.   sei();          // enable global interrupts
  37. }

  38. void writeCoilPWM(uint8_t pin, int val)
  39. {
  40.     OCR2B = val;
  41. }

  42. ISR(TIMER1_COMPA_vect)
  43. {
  44.   PID_Compute();
  45. }

  46. void PID_Compute( )
  47. {
  48.   static int lastError=0;
  49.   static unsigned long int lastTime=0;

  50.   now = micros();
  51.   timeChange = (now - lastTime);
  52.   if(timeChange>=SampleTime)
  53.   {
  54.     lastTime = now;

  55.     input=read_input();

  56.     error = -(Setpoint - input);

  57.     iiterm+= (Ki * error);
  58.     iiterm=constrain(iiterm,-255,255);

  59.     piterm=Kp * error;
  60.     diterm=Kd * (error - lastError)/SampleTime;
  61.     output = PWM_BIAS+(piterm + diterm);
  62.     output=constrain(output,0,255);
  63.     writeCoilPWM(PWM_PIN, output);

  64.      /*Remember some variables for next time*/
  65.     lastError = error;
  66.   }
  67. }

  68. int read_input()
  69. {
  70.   static int last=0;
  71.   int r;
  72.   r=analogRead(SS495_PIN);
  73.   if(abs(r-last)<4)
  74.     r=last;
  75.   else
  76.     last=r;
  77.   return r;
  78. }

  79. void setup() {
  80.   setupCoilPWM();
  81.   setupControlTimer();
  82.   // initialize serial communications at 9600 bps:
  83.   Serial.begin(115200);
  84. }

  85. void loop() {
  86.   // read the analog in value:
  87.   // User commands.
  88.   //   if( Serial.available() )
  89.   //   {
  90.   //      processCommand( Serial.read() );
  91.   //   }
  92.      // print the results to the serial monitor:
  93.   char str[50];
  94.   sprintf(str,"%ld, %03d, %04d, %+04d, %+04d, %+04d, %04d",now/1000, input,error,piterm,diterm,(int)iiterm,output);
  95.   Serial.println(str);   

  96.   delay(20);
  97. }
复制代码


PC机监控程序。Matlab,能够自动滚屏
  1. s1=serial('COM4','Baudrate',115200);
  2. fopen(s1);
  3. x=0;time=0;input=0;error=0;piterm=0;diterm=0;iiterm=0;pwm=0;
  4. str='';
  5. sen=0;


  6. try                             % use try catch to ensure fclose
  7. figure(1)
  8. hErr=plot(x,error);
  9. %title('Dados de LDR Aquisitados');
  10. hold all
  11. hP=plot(x,piterm);
  12. hD=plot(x,diterm);
  13. hPWM=plot(x,pwm);

  14. j=1;
  15. while(j<2000)
  16.     str=fscanf(s1);
  17.     sen=str2num(str);
  18.     if length(sen)<7
  19.         continue
  20.     end
  21.    
  22.     x(j)=j
  23.    time(j)=sen(1);
  24.    input(j)=sen(2);
  25.    error(j)=sen(3);
  26.    piterm(j)=sen(4);
  27.    diterm(j)=sen(5);
  28.    iiterm(j)=sen(6);
  29.    pwm(j)=sen(7);
  30.    
  31.    low=j-200;
  32.    if(low<1)
  33.        low=1;
  34.    end
  35.    up=j;
  36.    
  37.    x_1=x(low:up);
  38.    error_1=error(low:up);
  39.    piterm_1=piterm(low:up);
  40.    diterm_1=diterm(low:up);
  41.    pwm_1=pwm(low:up);

  42.    set(hErr,'XData',x_1,'YData',error_1)
  43.    set(hPWM,'XData',x_1,'YData',pwm_1)
  44.    if(up<200)
  45.        up=200;
  46.    end
  47.    axis([low up -500 1010]);
  48.    drawnow;
  49.    j=j+1;

  50. end;

  51. figure(2)
  52. plot(time,input);
  53. hold all
  54. plot(time,error);
  55. plot(time,piterm);
  56. plot(time,diterm);
  57. plot(time,pwm);
  58. legend('input','error','pIterm','dIterm','pwm');

  59. fclose(s1);
  60. delete(s1);
  61. clear s1;

  62. catch exception
  63.     fclose(s1);                 % always, always want to close s1
  64.     throw (exception);
  65. end                  
复制代码
回复 支持 反对

使用道具 举报

发表于 2012-7-3 20:36:31 | 显示全部楼层
有动手能力的实践者都是牛人。。。赞
回复 支持 反对

使用道具 举报

发表于 2012-7-27 13:23:31 | 显示全部楼层
johnsonzzd 发表于 2012-5-21 23:45
完整版,用串口向PC机发送信息:const int SS495_PIN = A0;  // Analog input pin that the SS495 is attac ...

用matlab 来图形显示串口数据 这个满好的
楼主能不能    把这部分内容展开一下  
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-16 08:53:34 | 显示全部楼层
ysit1990 发表于 2012-7-27 13:23
用matlab 来图形显示串口数据 这个满好的
楼主能不能    把这部分内容展开一下

大部分是常规内容。关键的显示部分:
   设置数据: set(hErr,'XData',x_1,'YData',error_1)
   强制刷新: drawnow;
回复 支持 反对

使用道具 举报

发表于 2012-8-16 10:02:00 | 显示全部楼层
楼住什么时候放 Arduino的自平衡车和两旋翼模型 上来啊,好其待哦{:soso_e154:}
回复 支持 反对

使用道具 举报

发表于 2012-8-16 13:44:51 | 显示全部楼层
求详细教程~ 好想做啊~
动力老男孩博客里面怎么只要一端做线圈呢?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-16 17:10:28 | 显示全部楼层
详细资料已经上传到百度文库里了,正在审核中。感兴趣的朋友可以搜索:
  杨麟 - 磁悬浮下位机软件毕业设计
  来俊鹏-磁悬浮系统下位机硬件设计

自平衡车和两旋翼模型已经完成,但还不很完美,需要修正。正在整理中。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-4-27 03:00 , Processed in 0.047336 second(s), 30 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表