2011年02月 文档列表

关于磁悬浮飞轮电池的讨论帖

前几天有位叫老顽童的网友留了言,提到了磁悬浮飞轮电池。一开始我还以为又是磁电机的支持者,后来才知道这位前辈说的是飞轮蓄能电池。这是个非常有意思的话题,我还没有深入研究过,若干年前在宿舍的夜聊会上曾经讨论过这个东西,在此记录一下,有空的时候研究研究 :)

飞轮电池简单的说就是用一个巨大的陀螺来储存能量,充电的时候用电磁感应让它的转速提高,放电的时候它带动发电机工作。

以下是老顽童同学的留言,该同学今年59岁,内功非常深厚,可能已经达到江湖传闻的“肉眼调板,意念编程”之境界:

点此查看原帖
老顽童 说:

你误会了.
飞轮蓄机械能,众所周知,不会有异义吧: )

所谓的“飞轮电池”就是把电机和飞轮合并(当然不只是简单的组合).
充电时电能通过电机转换成机械能给飞轮蓄起来.
放电时飞轮上的机械能通过电机再转成电能供负载使用……(需要真空,但不难).

十年前清华实验室的飞轮电池就达到了铅酸电池的比能量.样机工作了三年. (当时还没有真正用磁悬浮). 24小时总损耗好象是12%. 样机容量约600Wh左右,飞轮直径200mm.

就以上指标,其实己经很够实用了. 只是至今未见商业化.

当前的电池技术和电池应用技术,还无法满足电动车和电动汽车的要求.
飞轮电池是完全有希望争得一席之地的.

比如,再烂的飞轮技术(6小时歇搁的那种),用在公交车上,都比任何最先进的电容和电池要好! 用在电动自行车上,也至少可少用一半电池.
如果飞轮电池能做到24小时总损耗不大于20%,大多数的城区电动汽车和电动自行车都可以不用电池了.

飞轮电池用于UPS上,也是大有用武之地的.
智能电网,用大飞轮调峰填谷,也是现实的.

老美航母上投掷飞机,就是用飞轮电池,取其比功率大,比能量也比超级电容大得多.

磁悬浮是飞轮电池的最重要技术之一.

记得当时我们讨论的是飞轮式公交车,我的观点是比较消极的,原因如下:

1. 安全性:用动能的形式保持能量,势必需要高密度,高转速的转子,这种巨大能量的东西一旦飞出来,比小李飞刀还厉害(当然汽油也可能会燃烧爆炸,但是现在的油箱保护技术已经很少见到爆炸了)。

2. 自然损耗:汽油或者电动车,不开的时候放在那里,一星期也不会有什么损耗,而转子一星期早停了,就是说它不能稳定的储存能量。估计这个就是你最关注的“24小时总损耗”,对每天都运营的公交系统倒还算可行。

3. 能量输出:这个我没有研究过,直观上感觉给飞轮电池“充电”时比较方便,用电磁感应之类的方式让它转起来就好;但是放电怎么控制呢,应该还要考虑不同的输出功率吧?我想像的是不同档位的齿轮组带动发电机,感觉效率会很低,这个是我乱说的,不知道怎么样

4.实用性:巨大的转子如果采用固定轴的安装方式,那么会有很大的陀螺力矩,汽车转弯和上下坡都会比较费劲;如果用万向轴安装,那么充放电就会复杂很多。另外,如果用磁悬浮的方式减少摩擦阻力,公交车上带这么一大坨强磁铁,恐怕乘客们的银行卡都该报销了。

5. 体积:看到老顽童说飞轮直径200mm,我还是有点诧异的,当时感觉至少应该是一个巨大的铁坨子吧?不知道600Wh是个什么概念,等有空了查查汽车行驶100公里需要多少能量,估算下 :)

以上是针对汽车的,如果是针对智能电网或者UPS,貌似还有点意思。但是既然这个东西十多年都没有商业化,我觉得一定有它不能商业化的道理,请老顽童同学向这方面想想,如果有业内人士看到此贴,请帮忙解释一下!

盗梦陀螺攻略5- PID平衡算法

刚过完长假,继续发攻略来克服长假综合症。看到galiu同学带着女儿玩乐高,我突然非常高兴,哈哈,也许这就是若干年后的我啊! :)

进入正题,介绍提了多次的PID平衡算法。先从网上摘抄一段:

当今的自动控制技术都是基于反馈的概念。反馈理论的要素包括三个部分:测量、比较和执行。测量关心的变量,与期望值相比较,用这个误差纠正调节控制系统的响应。

这个理论和应用自动控制的关键是,做出正确的测量和比较后,如何才能更好地纠正系统。

在工程实际中,应用最为广泛的调节器控制规律为比例、积分、微分控制,简称PID控制,又称PID调节。它以其结构简单、稳定性好、工作可靠、调整方便而成为工业控制的主要技术之一。当被控对象的结构和参数不能完全掌握,或得不到精确的数学模型时,控制理论的其它技术难以采用时,系统控制器的结构和参数必须依靠经验和现场调试来确定,这时应用PID控制技术最为方便。即当我们不完全了解一个系统和被控对象,或不能通过有效的测量手段来获得系统参数时,最适合用PID控制技术。PID控制,实际中也有PI和PD控制。PID控制器就是根据系统的误差,利用比例、积分、微分计算出控制量进行控制的。

比例(P)控制
比例控制是一种最简单的控制方式。其控制器的输出与输入误差信号成比例关系。当仅有比例控制时系统输出存在稳态误差(Steady-state error)。

积分(I)控制
在积分控制中,控制器的输出与输入误差信号的积分成正比关系。对一个自动控制系统,如果在进入稳态后存在稳态误差,则称这个控制系统是有稳态误差的或简称有差系统(System with Steady-state Error)。为了消除稳态误差,在控制器中必须引入“积分项”。积分项对误差取决于时间的积分,随着时间的增加,积分项会增大。这样,即便误差很小,积分项也会随着时间的增加而加大,它推动控制器的输出增大使稳态误差进一步减小,直到等于零。因此,比例+积分(PI)控制器,可以使系统在进入稳态后无稳态误差。

微分(D)控制
在微分控制中,控制器的输出与输入误差信号的微分(即误差的变化率)成正比关系。自动控制系统在克服误差的调节过程中可能会出现振荡甚至失稳。其原因是由于存在有较大惯性组件(环节)或有滞后(delay)组件,具有抑制误差的作用,其变化总是落后于误差的变化。解决的办法是使抑制误差的作用的变化“超前”,即在误差接近零时,抑制误差的作用就应该是零。这就是说,在控制器中仅引入 “比例”项往往是不够的,比例项的作用仅是放大误差的幅值,而目前需要增加的是“微分项”,它能预测误差变化的趋势,这样,具有比例+微分的控制器,就能够提前使抑制误差的控制作用等于零,甚至为负值,从而避免了被控量的严重超调。所以对有较大惯性或滞后的被控对象,比例+微分(PD)控制器能改善系统在调节过程中的动态特性。

看概念可能有点晕,举个小小的例子也许能帮助理解。看下面的图:

假设我们想把一个小球稳定在一个光滑的坡顶,这显然是一个不平衡的系统,稍有扰动小球就会滚下来。假设恰好平衡的位置坐标是L,我们可以测量到小球的位置是x,那么怎么给小球施加f(x)的力反馈,让它能够平衡呢?

最直观的想法就是f(x) = Kp*(L-x),简单的说就是你在左边我就向右推,你在右边我就向左推,这就是比例因子P;

现在考虑两种情况,同样是在x位置,小球静止和小球具有速度V这两种情况。很明显,如果V>0,我们只需要施加更小的力,因为小球自身的惯性会让它运动向平衡位置。所以可以修正f(x) = Kp*(L-x) – Kd*V。因为速度一般不容易测量,我们常常用位置的变化Δx除以测量的时间差Δt来计算速度,所以这就是微分因子D;

情况继续发生变化,上面考虑的是斜坡静止的情况,如果这个变态的斜坡是移动的怎么办呢?(例如两轮平衡机器人实际上是可以运动的,对于静止的磁悬浮来说,不需要考虑这个参数)这时候我们需要不断的累加并平均x值,来计算平衡位置的L,这个就是积分因子I;

以上就是PID的简要介绍。说起来容易,真正调试的时候,最恼火的就是这几个参数到底是多少,办法只有一个:试,不断的试!

当然,试验也不要当老黄牛,累死都没人知道。我曾经试其中某个参数,从0.1开始,每次加0.01,差点试到崩溃。后来想了个办法,用串口把Arduino的读数发送到电脑,然后用软件分析结果,看到数据明显发现这个值偏小,发狠改到20,就这样成功了…..

当时的数据找不到了,发一段成功悬浮时的log吧,其中两种颜色分别代表两个方向的传感器读数(相当于x):

PID的测量值

从图上可以看出,平衡的位置具体在哪里,我们可能不一定能精确知道,但是通过合适的反馈系统,陀螺能够自动稳定到相应的位置上。参数不正确的情况下,这些点会越振越远,直到失控。

最后附上源代码,没有时间整理,可能有不少问题,有兴趣的同学凑合看吧 :)

//PINs setting
int adjust1Pin = 1;    //用来调节A的电位器
int adjust2Pin = 2;    //用来调节B的电位器
int read1Pin = 4;      //用来连接输入A传感器
int read2Pin = 3;      //用来连接输入B传感器
int i1Pin = 36;        //连接电机驱动板的I1接口
int i2Pin = 37;        //连接电机驱动板的I2接口
int i3Pin = 39;        //连接电机驱动板的I3接口
int i4Pin = 38;        //连接电机驱动板的I4接口
int power1Pin = 5;     //连接电机驱动板的EA接口
int power2Pin = 6;     //连接电机驱动板的EB接口
int rotatePin = 3;     //用来控制磁场旋转的PMW接口

boolean debug = false;
boolean writeLog = false;
double setKd1 = 0.55;
double setKd2 = 0.55;
double setKp = 22;
int offset = 70;
int delayMs = 1;
int tick = 0;
int myLog[3500];

//PID structure
typedef struct {
  double target;
  double aver;
  double Kp;
  double Kd;
  int preError;
  int power;
  boolean flag;
  double v;
} PID;

PID Pid1, Pid2;

void setup()
{
  pinMode(i1Pin, OUTPUT);     //I1和I2都是数字信号
  pinMode(i2Pin, OUTPUT);     //通过设置I1和I2来控制电流方向
  pinMode(i3Pin, OUTPUT);     //I1和I2都是数字信号
  pinMode(i4Pin, OUTPUT);     //通过设置I1和I2来控制电流方向
  pinMode(power1Pin, OUTPUT);  //按占空比方式输出的模拟信号
  pinMode(power2Pin, OUTPUT);  //按占空比方式输出的模拟信号
  pinMode(rotatePin, OUTPUT);  //按占空比方式输出的模拟信号

  //analogWrite(rotatePin, 128);

  Serial.begin(9600);          //设置波特率
  TCCR0B = 0x01;   // Timer 0: PWM 5 &  6 @ 16 kHz
  TCCR1B = 0x01;   // Timer 1: PWM 9 & 10 @ 32 kHz
  TCCR2B = 0x01;   // Timer 2: PWM 3 & 11 @ 32 kHz
  Pid1.Kp = setKp;
  Pid1.preError = 0;
  Pid1.Kd = setKd1;
  Pid1.power = 0;
  Pid1.flag = true;
  Pid1.target = 300;
  Pid1.aver = 0;
  Pid1.v = 0;
  Pid2.Kp = setKp;
  Pid2.preError = 0;
  Pid2.Kd = setKd2;
  Pid2.power = 0;
  Pid2.flag = true;
  Pid2.target = 300;
  Pid2.aver = 0;
  Pid2.v = 0;
  tick = 0;
}

int tick2 = 0;
//boolean rotateFlag = true;
void loop()
{
  //digitalWrite(rotatePin, rotateFlag);
  //rotateFlag = ! rotateFlag;
  //delay(16000);
  //return;

  if(debug) tick = 0;
  tick++;
  if(tick==500)
  {
    tick2++;
    if(tick2<50) {tick = 0;return;}
    tick2 = 0;
    if(writeLog)
    {
      for(int i=0;i<500;i++)
      {
        Serial.print(myLog[i*7 + 0]);
        Serial.print("  ");
        Serial.print(myLog[i*7 + 1]);
        Serial.print("  ");
        Serial.print(myLog[i*7 + 2]);
        Serial.print("  ");
        Serial.print(myLog[i*7 + 3]);
        Serial.print("  ");
        Serial.print(myLog[i*7 + 4]);
        Serial.print("  ");
        Serial.print(myLog[i*7 + 5]);
        Serial.print("  ");
        Serial.print(myLog[i*7 + 6]);
        Serial.println("  ");
      }
      Serial.println(Pid1.target);
      Serial.println(Pid1.preError);
      Serial.println(Pid2.target);
      Serial.println(Pid2.preError);
    }
    return;
  }
  else if(tick>500)
  {
    tick = 0;
    //delay(990000);
    return;
  };

  //=======第一组电位器和传感器========
  int readValue1 = 0;
  for(int i = 0; i < 4; i++) readValue1 += analogRead(read1Pin);
  readValue1 >>= 2;
  //readValue1 += (Pid1.flag ? 1 : -1) * Pid1.power / 17;
  int adjustValue1 = analogRead(adjust1Pin); //410 analogRead(adjust1Pin);
  Pid1.aver = Pid1.aver * 0.9995 + readValue1 * 0.0005;
  Pid1.target = Pid1.target + (Pid1.target - Pid1.aver) / 100.0;
  Pid1.target = max(0, max(adjustValue1 - offset, Pid1.target));
  Pid1.target = min(755, min(adjustValue1 + offset, Pid1.target));

  //=======第二组电位器和传感器=======
  int readValue2 = 0;
  for(int i = 0; i < 4; i++) readValue2 += analogRead(read2Pin);
  readValue2 >>= 2;
  //readValue2 += (Pid2.flag ? 1 : -1) * Pid2.power / 6;
  int adjustValue2 = analogRead(adjust2Pin); //240 analogRead(adjust2Pin);
  Pid2.aver = Pid2.aver * 0.9995 + readValue2 * 0.0005;
  Pid2.target = Pid2.target + (Pid2.target - Pid2.aver) / 1000.0;
  Pid2.target = max(0, max(adjustValue2 - offset, Pid2.target));
  Pid2.target = min(755, min(adjustValue2 + offset, Pid2.target));

  if(debug)
  {
    Serial.println(adjustValue1);
    Serial.println(adjustValue2);
    Serial.println(readValue1);
    Serial.println(readValue2);
    Pid1.flag = adjustValue1 > 512;
    Pid1.power = abs(adjustValue1 - 512) / 2;
    if(Pid1.power > 255) Pid1.power = 255;
    digitalWrite(i1Pin, Pid1.flag);
    digitalWrite(i2Pin, !Pid1.flag);
    analogWrite(power1Pin, Pid1.power);
    Pid2.flag = adjustValue2 > 512;
    Pid2.power = abs(adjustValue2 - 512) / 2;
    if(Pid2.power > 255) Pid2.power = 255;
    digitalWrite(i3Pin, Pid2.flag);
    digitalWrite(i4Pin, !Pid2.flag);
    analogWrite(power2Pin, Pid2.power);
    delay(32000);
    return;
  }

  //Calculate power values
  double v, error;
  error = readValue1 - Pid1.target;
  v = error - Pid1.preError;
  Pid1.v = (Pid1.v * 6 + v) / 7;
  Pid1.power = (int)error * Pid1.Kd + Pid1.v * Pid1.Kp;
  Pid1.flag = Pid1.power > 0;
  Pid1.power = abs(Pid1.power);
  if(Pid1.power>255) Pid1.power = 255;
  Pid1.preError = error;

  error = readValue2 - Pid2.target;
  v = error - Pid2.preError;
  Pid2.v = (Pid2.v * 6 + v) / 7;
  Pid2.power = (int)error * Pid2.Kd + Pid2.v * Pid2.Kp;
  Pid2.flag = Pid2.power < 0;
  Pid2.power = abs(Pid2.power);
  if(Pid2.power>255) Pid2.power = 255;
  Pid2.preError = error;

  //Write PMW to control the floa
  digitalWrite(i1Pin, Pid1.flag);
  digitalWrite(i2Pin, !Pid1.flag);
  analogWrite(power1Pin, Pid1.power);

  digitalWrite(i3Pin, Pid2.flag);
  digitalWrite(i4Pin, !Pid2.flag);
  analogWrite(power2Pin, Pid2.power);

  myLog[tick * 7 + 0] = tick;
  myLog[tick * 7 + 1] = (int)Pid1.target;
  myLog[tick * 7 + 2] = readValue1;
  myLog[tick * 7 + 3] = Pid1.power;
  myLog[tick * 7 + 4] = (int)Pid2.target;
  myLog[tick * 7 + 5] = readValue2;
  myLog[tick * 7 + 6] = Pid2.power;

  /*
  for(int i=0;i<8;i++)
  {
    digitalWrite(rotatePins[i] , 0);
    digitalWrite(rotatePins[(i + 1) % 8] ,1);
    delay(1);
  }
  digitalWrite(rotatePins[0] , 0);
  */

  delay(delayMs);
}

年终总结

今天是兔年兔月兔日,又正好是我结婚兔的兔次方纪念日。在过去的一年里,工作很忙碌,养娃很辛苦,但是干劲十足,心情很好。特此跟风发个年终总结,辞旧迎新 :)

1. 去年最大的变化是当爹了,从此进入上有老下有小的阶段,感觉肩头的责任更重大了;

2. 最震撼的事情是进产房陪产,那叫一个惊心动魄啊,有幸成为全家第一个见到圈圈的人;

3. 最失败的煽情是结婚三周年纪念日,写了封情书,圈妈正准备挤点眼泪配合的时候,发现日期写的是两周年,于是被痛扁;

4. 最意外的事情是换了份工作,本来不想在圈圈太小的时候变动工作,结果很巧合的找到一家很不错的公司,而且是一天之内决定,高效!

5. 最无厘头的事情是给原公司发了封无厘头的搞笑版离职信,同事们算是记住我了; :)

6. 最神奇的事情是博客居然有了40万的IP访问量,非常感谢关注这个小博客的朋友们;

7. 最欣慰的事情是今年工作虽然忙,但是好歹做了点新东西,一个磁悬浮的陀螺;

8. 最衰的事情是去年发烧两次,好吧,很多年没生病了,加热一下杀杀菌;

9. 最遗憾的事情是一个十多年的老朋友离开北京了,走的那天我还加班,都没能去送送,祝所有的朋友们都前程似锦!

10. 最拖沓的事情是把车头蹭了,半年了还没去修,当个爹真不容易啊;

明年的愿望:

1. 所有的家人们健健康康,牙好胃口更好;

2. 在新公司做点有价值的事情,公司发大财,我跟着发点小财;

3. 小爱和机器人的爱好不要丢掉,至少做一些新东西出来;

4. 恢复锻炼计划,身体是革命的本钱;

5. 多更新博客,感谢大家的支持!

今天本来是打算去看个电影庆祝纪念日的,还计划买个新手机送给圈妈。结果前天圈圈发烧了,电影肯定是看不成了,常去的手机店也放假了,圈妈每年必看的晚会也泡汤鸟。圈圈还算是给力,知道我上个月一直加班,所以坚持到放假才生病。保佑圈圈早日康复,一切顺利!