Archive for the ‘Arduino’ Category

鸡腿遥控器

这里说的鸡腿,是指Wii经典的游戏手柄nunchuck。
Wii Nunchuck鸡腿遥控器

Wii Nunchuck鸡腿遥控器

这个单词到底是什么意思其实我也不知道,也不知道该怎么发音。不过大家一般都亲切的把它称为“小鸡腿”,看上去还真有点儿神似。
毕竟是大公司的经典工业设计产品,手感非常好!

Step 1: 功能区设计

鸡腿用于遥控电滑板

鸡腿用于遥控电滑板

原来的遥控器,只有一个JoySticker摇杆,自带一个按键。

而这个鸡腿有一个摇杆和两个按键,实际上它里面还有一个陀螺仪,不过我暂时没有想好用陀螺仪来做什么。

Step 2: 各种组装

剪掉壳里的支撑片

各种飞线和裸奔的模块

各种飞线和裸奔的模块

由于鸡腿里面的空间比较小,所以原来的洞洞板或者PCB板全都放不进去,只能裸奔着用飞线连接,然后一大坨塞进去。

另外,鸡腿里面的隔板还得剪掉,好在这个鸡腿不承力,强度还是够用的。

Step 3: 充电口和开关

一股脑塞进鸡腿

一股脑塞进鸡腿

开关和充电口

开关和充电口

原来的鸡腿是带线的,做遥控器当然要把线剪掉。

尾巴那里正好就可以用来安装充电口和开关。电路图你们就别找我要啦,这么简单个操作,跟连个灯泡是一样的 :)

Step 4: 蓝牙指示灯

连接指示灯

连接指示灯

把电路塞进鸡腿之后,有个比较大的麻烦就是指示灯。原来可以通过指示灯的闪烁,来判断蓝牙是否连接成功,现在全塞在里面了。

为了解决这个问题,我用小电钻在鸡腿上打了个小孔,然后在里面嵌了一个LED灯。
实践证明,虽然蓝色灯看上去比较漂亮,但是白天几乎看不清,还是用红色灯比较醒目。
鸡腿里面的空间太小,这个灯装的非常牵强,几乎是硬卡在那里的。有兴趣的同学以后可以改用贴片的那种小LED。

Step 5: nunchuck使用的Arduino库

在网上能找到很多针对numchuck的Arduino代码库,但是因为Arduino版本升级的原因,大部分都已经不能用了,编译错误。

我做了一些修改,目前Arduino的版本1.6.5已经可以顺利跑通。代码如下:
static uint8_t nunchuck_buf[6]; // array to store nunchuck data,

// Uses port C (analog in) pins as power & ground for Nunchuck
static void nunchuck_setpowerpins()
{
#define pwrpin PC3
#define gndpin PC2
DDRC |= _BV(pwrpin) | _BV(gndpin);
PORTC &=~ _BV(gndpin);
PORTC |= _BV(pwrpin);
delay(100); // wait for things to stabilize
}

// initialize the I2C system, join the I2C bus,
// and tell the nunchuck we're talking to it
void nunchuck_init()
{
Wire.begin();	// join i2c bus as master
Wire.beginTransmission(0x52);	// transmit to device 0x52
Wire.write(0x40);	// sends memory address
Wire.write(0x00);	// sends sent a zero.
Wire.endTransmission();	// stop transmitting
}

// Send a request for data to the nunchuck
// was "send_zero()"
void nunchuck_send_request()
{
Wire.beginTransmission(0x52);	// transmit to device 0x52
Wire.write(0x00);	// sends one byte
Wire.endTransmission();	// stop transmitting
}

// Receive data back from the nunchuck,
// returns 1 on successful read. returns 0 on failure
int nunchuck_get_data()
{
int cnt=0;
Wire.requestFrom (0x52, 6);	// request data from nunchuck
while (Wire.available ()) {
// receive byte as an integer
nunchuck_buf[cnt] = nunchuk_decode_byte(Wire.read());
cnt++;
}
nunchuck_send_request(); // send request for next data payload
// If we recieved the 6 bytes, then go print them
if (cnt >= 5) {
return 1; // success
}
return 0; //failure
}

// Print the input data we have recieved
// accel data is 10 bits long
// so we read 8 bits, then we have to add
// on the last 2 bits. That is why I
// multiply them by 2 * 2
void nunchuck_print_data()
{
static int i=0;
int joy_x_axis = nunchuck_buf[0];
int joy_y_axis = nunchuck_buf[1];
int accel_x_axis = nunchuck_buf[2]; // * 2 * 2;
int accel_y_axis = nunchuck_buf[3]; // * 2 * 2;
int accel_z_axis = nunchuck_buf[4]; // * 2 * 2;

int z_button = 0;
int c_button = 0;

// byte nunchuck_buf[5] contains bits for z and c buttons
// it also contains the least significant bits for the accelerometer data
// so we have to check each bit of byte outbuf[5]
if ((nunchuck_buf[5] >> 0) & 1)
z_button = 1;
if ((nunchuck_buf[5] >> 1) & 1)
c_button = 1;

if ((nunchuck_buf[5] >> 2) & 1)
accel_x_axis += 2;
if ((nunchuck_buf[5] >> 3) & 1)
accel_x_axis += 1;

if ((nunchuck_buf[5] >> 4) & 1)
accel_y_axis += 2;
if ((nunchuck_buf[5] >> 5) & 1)
accel_y_axis += 1;

if ((nunchuck_buf[5] >> 6) & 1)
accel_z_axis += 2;
if ((nunchuck_buf[5] >> 7) & 1)
accel_z_axis += 1;

Serial.print(i,DEC);
Serial.print("\t");

Serial.print("joy:");
Serial.print(joy_x_axis,DEC);
Serial.print(",");
Serial.print(joy_y_axis, DEC);
Serial.print(" \t");

Serial.print("acc:");
Serial.print(accel_x_axis, DEC);
Serial.print(",");
Serial.print(accel_y_axis, DEC);
Serial.print(",");
Serial.print(accel_z_axis, DEC);
Serial.print("\t");

Serial.print("but:");
Serial.print(z_button, DEC);
Serial.print(",");
Serial.print(c_button, DEC);

Serial.print("\r\n"); // newline
i++;
}

// Encode data to format that most wiimote drivers except
// only needed if you use one of the regular wiimote drivers
char nunchuk_decode_byte (char x)
{
x = (x ^ 0x17) + 0x17;
return x;
}

// returns zbutton state: 1=pressed, 0=notpressed
int nunchuck_zbutton()
{
return ((nunchuck_buf[5] >> 0) & 1) ? 0 : 1; // voodoo
}

// returns zbutton state: 1=pressed, 0=notpressed
int nunchuck_cbutton()
{
return ((nunchuck_buf[5] >> 1) & 1) ? 0 : 1; // voodoo
}

// returns value of x-axis joystick
int nunchuck_joyx()
{
return nunchuck_buf[0];
}

// returns value of y-axis joystick
int nunchuck_joyy()
{
return nunchuck_buf[1];
}

// returns value of x-axis accelerometer
int nunchuck_accelx()
{
return nunchuck_buf[2]; // FIXME: this leaves out 2-bits of the data
}

// returns value of y-axis accelerometer
int nunchuck_accely()
{
return nunchuck_buf[3]; // FIXME: this leaves out 2-bits of the data
}

// returns value of z-axis accelerometer
int nunchuck_accelz()
{
return nunchuck_buf[4]; // FIXME: this leaves out 2-bits of the data
}

用一块Arduino输出多路PWM

【经wwwtiger同学提醒,关于Arduino原生PWM的频率,我之前的理解是错的,应该是490Hz左右,特此更正】

周末有朋友来家里玩,看到了放在角落里的那个大的四轴飞行器。拿下来看了看,发现上面已经落满了灰尘。应该有几个月没动了,当时写的攻略目录也好久没更新了。这段时间打算抽空陆陆续续把这个页面里的目录内容都补齐,免得成为烂尾工程。可惜实验还没有成功,所以不能算是经验,各位兄弟权当教训看吧 :)

四轴飞行器最终的控制手段,就是分别调节4个电机的转速。对于常见的无刷电机,一般通过高电平在0.9ms~2.1ms左右的PWM来控制转速。这就涉及到如何用arduino输出多路PWM的问题,在Arduino中,一般可以通过下面这些方式来输出PWM方波:

1. 用原生的PWM输出功能
把pinMode定义为输出,然后用analogWrite就可以输出PWM。以Arduino Mega 1280为例,它有14路PWM输出。analogWrite的参数范围是0~255,对应的是0~2ms的高电平。
一般情况下,原生的PWM频率固定为490Hz,通过一些参数的修改,可以提高PWM频率,但是参数范围只能是0~255。

int pin = 8; //0~13

void setup()
{
    pinMode(pin, OUTPUT);
}   

void loop()
{
    analogWrite(pin, 128);
    delay(500);
}

2. 通过普通的输出管脚手动控制PWM

PWM归根结底无非就是输出0,1序列的方波,所以我们还可以简单的通过digitalWrite和delay来生成PWM方波。
这种方式的特点是可以非常精确的控制高电平时间和PWM周期,缺点是CPU时间不好充分利用,多路时编程有点复杂。

void loop()
{
  long t0 = micros(); //记录进入loop的时间
  double T = 20000; //一个周期的微秒值
  double len = 900;  //高电平时间
  for(int i = 0; i < 4; i++) digitalWrite(pins[i], HIGH);
  delayMicroseconds(len);
  for(int i = 0; i < 4; i++) digitalWrite(pins[i], LOW);
  /*
   *在这里干别的事情
   */
  int leftMs = (int) (t0 + T - micros()); //剩余时间
  if(leftMs < 0 ) leftMs = 0; //万一超出周期,只能立刻中止
  delayMicroseconds(leftMs);
}

3. 用servo库
Servo库是Arduino用来控制伺服电机的,印象中是使用了内部中断来触发的,所以主程序只管干自己的事情,时间到了PWM就会自动触发。
它默认的频率是50Hz,但是可以在头文件中修改。文件位置是arduino-0023\libraries\Servo.h,修改下面这行可以把PWM频率升到400Hz:
#define REFRESH_INTERVAL 2500

具体的细节在之前的一篇小结里提到了:Arduino自带的Servo库小实验

先记录这些,希望在把全部的“教训”写完之前,能有空把四轴飞起来 :)

微型四轴加电机的简单尝试

这段时间比较萎靡,工作忙,身体状态也不太好,基本上处于停滞状态。上次大家提了很多好建议,比如换模块、换电机或自制PCB等等。zxzxy1988同学还给我发了制板的软件和教程,另外还有焊接的技巧,真是非常感谢。

因为时间有限,我暂时还没有尝试这些建议。当时答应darkorigin做一个简单的加电机的实验,就是把4个电机换成8个,今天算是有个回应。其实就是简单的并联,把另外4个电机直接用胶棒粘到原来的4个电机上,大头朝下,转向相反。

加上去的电机

加上去的电机

拖拖拉拉两个星期,今天终于下决心把这些电机们焊上了(主要是到了晚上就迷迷糊糊的:D ),看上去非常非常非常的山寨……

“8桨”飞行器

“8桨”飞行器

加上4个电机后,飞行器重量从46克变成了58克。刚才试飞了一把,这次居然真的离地了,但是需要把马力开到最大。
因为上下两个电机互相干扰可能有些损耗,所以估算每个空心杯电机的升力大概是10克左右。

目前我还没有加上读传感器和调节转速的代码,所以只是简单的试了试能否起飞。把马力调到最大后,小四轴摇摇晃晃的升起来。比较神奇的是,虽然没有平衡的程序,但是它基本是水平的,没有像大四轴那样翻滚直接坠毁。不过依然会侧飞,然后撞上了墙壁,最后掉进橱柜下面一阵乱撞。从照片里可以看到打坏了一个小叶片,不过说实话,小四轴比我想象的结实多了,这样乱撞居然只坏了一个小叶片。

总体来说,如果最大马力才能够勉强升空的话,基本上靠它调节平衡是不行的,因为转速没有调节的范围了。另外一个可能的结论是,小四轴的平衡性会比大四轴好一点,如果能顺利升空的话,也许调节起来会比大四轴容易些。

这次小四轴的尝试基本算是失败了,接下来可以考虑给它减肥。比如某位朋友说的,把arduino nano换成mini版;另外蓝牙可以不需要那个降压模块;3.7V到5V的升压模块应该用一块芯片就可以搞定;那个低电量报警的小喇叭改用一个闪烁的led;传感器导线可以改用漆包线;背后那个支架PCB如果是自制板的话,应该也可以减少部分重量。如果体重能够减到30克以内,估计升空应该就没问题了。
另外的想法是考虑再大一点的空心杯电机和螺旋桨,一个顶现在两个的。假设升力能到60克,然后把体重控制在40克左右,这样将来还能挂载点其他配件。接下来准备买几个其他型号的小电机测量下升力,另外学习下画PCB。哎呀,又将是一个大周期……

又一次悲剧的试飞

在闲置了一个多月之后,今天又进行了一次试飞。首先说说最近的修改:

1. 把电机控制的频率改到了400Hz,之前是50Hz;
2. 把螺丝固定的Arduino板子改成4个弹簧支撑,用来减少电机高频振动对传感器的影响;
3. 顺便把45度安装的X型改成了垂直安装的十型,作用不大,节省一点代码和减少可能出错的环节;
4. 尝试增加一个自动调节平衡位置的代码;(好几个参数需要调节,都调准了才能飞稳)
5. 加了一个安全的控制,如果飞行器倾角超过45度,则所有电机同时停止;

传感器改用弹簧连接

传感器改用弹簧连接

用手握的方式测试看来,目前四轴飞行器的姿态判断还算可以。基本上晃动它的时候,能感觉到电机速度的变化以及带来的反向阻力。
所以目前的调试主要在PID参数的调节和XY方向的偏移量调节。这个偏移量,是因为电机和飞行器不对称带来的误差。简单的说,假如飞行器已经处于平稳飞行的状态,这时候计算得到的左右两侧的螺旋桨转速应该相同,但是由于飞机本身的不对称,这时候反而会发生倾斜,所以需要给它设置一个平衡的偏移量。

这几天跟wnq同学请教了一下,据他介绍kk飞控仅用陀螺仪就可以上天,至于陀螺仪长期积分带来的漂移误差,可以由“飞行员”通过遥控器手动调节。我之前一直调试的目标是让飞行器实现平衡自稳,这样说来可能有点儿冒进了。wnq提醒我适当的斜飞是可以通过手动调节修正的,这样可以不用太纠结在完美的水平悬停上

另外最近的新朋友darkorigin也提醒了“近地效应”,因为贴近地面的时候,各种气流互相干扰,反而不如飞上天以后稳定。

下一阶段的一些想法和目标:
1. 暂时放弃手机控制,改用遥控器和接收器进行控制,主要优势在于控制距离、手感和便利性;
2. 增加运动的控制,就是在倾斜状态下手动调节平衡;
3. 进行电子指南针的一组小实验,因为我的遥控经验不足,争取先实现“无头模式”避免炸鸡;
4. 组装一台wnq同学的飞控练练手;
5. 自动寻常平衡点的算法需要再多测试和验证。

最后附上今天悲催的试飞视频。今天有点小风,犹豫了一下还是去阳台试飞了。前40秒使用的PID参数看上去还不算太离谱,之后试着调了下参数,结果振动失控了,摔碎若干小配件……

Bug无处不在……

上周做了个关于Servo库控制电机的小实验,经验证可以把PWM的频率提高到400Hz左右。于是一阵高兴,迅速改好代码,准备周末试飞。

本来计划先做分解实验,拴上绳子一步步测试之后再去试飞。结果星期六刮大风耽误一天,星期天头脑一热,没测试直接去飞了。结果飞机变成炮弹,眼皮还没眨完一下,就已经撞毁在墙壁上了。 :(

回家检查了下程序,果然发现巨大的bug一只。真是心急吃不了热豆腐啊,这个周末需要上班,然后是外地度假7天,再试飞只能是半个月后了。这几天干些乱七八糟的活,新买的4对儿电机电调都焊好了。下次试飞再不成功的话,就准备用wnq朋友的ACM飞控再做一个四轴啦。

另外,如果有电机轴摔弯的朋友,也可以参考wnq推荐的自己换轴的攻略。我试了下,确实比较方便,一根轴才几块钱,相当划算!

无刷电机换轴

无刷电机换轴

另外,还出手了一个JR XG8 2.4G的遥控器,准备下一步把手机遥控改成射频遥控。啧啧,这个东东真是不便宜啊,按照wnq同学的说法,恭喜我上贼船了,不过这个遥控器就我这种级别的玩家,恐怕一辈子都够用了 :)

JR_XG8

JR_XG8

接下来的计划是修改bug等待试飞,闲暇时间学习下ACM飞控和遥控器的使用。

Arduino自带的Servo库小实验

之前有很多朋友问过我,为什么不用Arduino自带的Servo库来控制电机。我在动手做四轴之前,其实也大概看了下Servo库的说明,当时看到

servo.write(angle) 这个函数,其中angle(int)是角度,取值范围是0 到 180

我想当然的认为,这个servo库最多也就是180级的梯度来设置PWM,这样说来精度完全不够啊;另外也没有看到有地方能修改PWM频率。

总结了下我当时不用Servo库的原因:
1. 控制的级数太少,不够精确;
2. 不可修改PWM频率;
3. 需要使用特殊的几个脚;(不知道在什么地方看错了,留下的错误印象)

这几天obK仔给了很多建议,我也看了看源代码。现在看来,上面这几点都是我瞎操心了。

首先,0~180是servo库对应的从min到max的控制,对应于0.9ms到2.1ms的电调来说,angle参数加1对应的时间变化为:
(2.1-0.9)*1000/180 = 6.7微秒,已经是比较精确了。
即使这样如果还觉得不够精确,还可以使用servo.writeMicroseconds,这个函数可以直接用微秒作为参数。

其次,PWM频率是可以修改的,在Servo.h文件中,修改下面这行可以把PWM频率升到400Hz:
#define REFRESH_INTERVAL 2500
不过这只能在编译之前改,不能在程序运行的过程中动态修改。当然修改源代码也可以解决这个问题,把这个参数做成一个变量,用接口进行修改控制即可。

最后,Servo库可以绑定所有digitalWrite的脚,这一点来说,比原生的PWM还方便!

做了一个简单的小程序,测试Servo库同时控制4个电机的情况。实验结果看上去还不错,不过没有测试4个电机转速各不相同的情况。
代码果然看上去简洁多了:

Servo powerServo[4];
void setup()  
{
  motorPins[0] = 38;   //x1
  motorPins[1] = 43;   //x2
  motorPins[2] = 42;   //y1
  motorPins[3] = 39;   //y2
  for(int i = 0; i < 4; i++) powerServo[i].attach(motorPins[i]);
}
void loop()
{
  long t0 = micros(); //记录进入loop的时间
  while(Serial.available()) {
    // 这里是获取控制指令
    pushData(Serial.read());
  }
  double T = 2500; // 一个周期的微秒值
  double len = 900 + ctrlPower * 5;  //(0~200对应总周期0.9ms~1.9ms)
  for(int i = 0; i < 4; i++) powerServo[i].writeMicroseconds(len);

  int leftMs = (int) (t0 + T - micros());
  delayMicroseconds(leftMs);
}

电调的特性实验

最近收到了好多朋友的帮助,在此表示感谢。wnq同学送了很多好东西,并且帮我挑选了一款好用的航模专用遥控器,终于让我下定决定再重新做一个新的四轴。很多快递都还在路上,这几天继续研究老四轴的问题。

今天有位年仅14岁的年轻朋友ENIAC留了言:“对于我开始的商品电调,由于里面自带的 PID 控制器。严重影响了转速的快速反应。这就造成了对于四轴稳定性之一的“自动悬停”基本无法做到了。由于自动悬停的首要要求就是在四轴倾斜时能够在最短时间内自动回到水平位置, 这就要求马达转速在四轴有倾斜时需要加快,而到快回到平衡位置时需要降下来。商品电调里的 PID 恰好阻止了这个的发生。转速上去可以,想要马上降下来?没门!我试验的结果就是一旦转速上去了,想要降下来,等 0.2秒吧。即使这段时间里通知电调降低转速也没效果。带来的后果就是四轴一旦倾斜后就会不可控地晃来晃去,好像抬轿子。

这段文字是ENIAC在无线电上看到的,不得不佩服现在的孩子们,14岁已经开始看这种专业的书籍了。回想我这么大的时候,干的事情基本都是从楼上尿尿,往厕所里扔鞭炮之类的。闲话不多说,我在网上也很容易搜到了相关的讨论。刚看到它的时候,我确实是心头一惊,如果有0.2秒的下降延时,那么我的350Hz的采样,50Hz的控制全白瞎了。但是转念一想,不对啊,现在很多飞的很好(包括悬停动作)的飞控,配套的也是普通的电调啊。

再后来看到一名叫做gale的网友评论:“误区啊。。。 1500元以下的商品电调,根本没有PID。。。连P都没有,何谈ID? 我的经验是商品电调使用挺好的,PPM的信号控制速度也挺快的,目前最低档的KK飞控,也达到了300HZ的控制频率,其所谓的时延问题最主要还是在电机和桨的惯性上。I2C没有看出有明显的必要。” 原文看这里

从这篇讨论贴中的视频看,确实有人用KK飞控+普通电调实现了较为稳定的控制,这样说来我就放心点儿了。另外一个有用的信息是其中提到的300Hz的频率,前几天我刚做了一个实验(企图修改电调PWM频率的实验),当时的结论是电调的频率不可修改。今天突然意识到一个错误的理解:我假定电调是按高电平的占空比来控制电机的,事实上,电调也可能不看占空比,而是只计算高电平的脉宽来控制转速。

加大电调控制频率的方式

加大电调控制频率的方式

上面这幅图表示了从50Hz的控制变成100Hz控制的方式,其中中间那部分是我之前的错误理解,以为高电平的脉宽应该减半,事实上应该是下面那种方式。

为了验证这个想法,特地又做了一个小实验,代码如下:

void loop()
{
  long t0 = micros(); //记录进入loop的时间
  while(Serial.available()) {
    // 这里是获取控制指令
    pushData(Serial.read());
  }
  double frequency = P_Control * 1.5 + 50;  //(0~200对应50到350)
  double T = 1000000 / frequency; // 一个周期的微秒值
  double len = 900 + ctrlPower * 5;  //(0~200对应总周期0.9ms~1.9ms)
  for(int i = 0; i < 4; i++) digitalWrite(motorPins[i], HIGH);
  delayMicroseconds(len);
  for(int i = 0; i < 4; i++) digitalWrite(motorPins[i], LOW);
  int leftMs = (int) (t0 + T - micros());
  if(leftMs < 1 ) leftMs = 1;
  if(leftMs > 2000) {
    int ms = leftMs >> 10 - 1;
    delay(ms);
    delayMicroseconds(leftMs - ms<<10);
  } else {
    delayMicroseconds(leftMs);
  }

其中P_Control和ctrlPower是我从手机遥控器传入的参数,偷了个懒保留了P_Control的变量名,其实它在这里是用来调节PWM周期的。

手机端截图

手机端截图

其中ctrlPower就是图中那个油门值了。这个实验表明,普通商品电调的控制频率确实可以修改的!
我之前的控制频率是50Hz,确实有点少了,反应比较慢,如果推理正确的话,也许可以加到到300Hz左右。

今天有点生病了,做完这个实验,有点头痛欲裂的感觉。把接下来打算做的事情整理一下,然后去睡觉啦:
1. 修改电调的控制频率,看看是否能让飞行器更平稳
2. 四个电机轴都已经有点歪了,买了几个新电机,也买了几根新的电机轴,看看能不能自己修好
3. 给老四轴加上螺旋桨保护罩,前几天听说有人玩航模砸死了过路人,罪过啊,大家也一定要多加小心!
4. 用wnq朋友的飞控和机架组装一套新四轴,改为用遥控器控制
5. SSS_SXS朋友提到了Arduino自带的PID库,这个可以考虑替换掉自己的PID

带好盈程序的电子调速器控制

最近制作四轴飞行器陷入困境,用手抓住它左右摇晃,能够感受到螺旋桨明显的阻力。但是PID参数调节的结果,要么就是飞行器剧烈摇摆失控,要么就是起飞后向一侧斜飞,很难达到一个较为稳定的状态。

应朋友们的建议,先陆陆续续写一些攻略和步骤。一来可以相当于做好笔记,二来也可以请大家帮忙查找原因,出谋划策。

前面几篇提到了目前控制电机的一些问题,正好在这里介绍下我控制电机的方式。

电机

首先,航模的电机一般都是无刷电机,这种高速的无刷电机不像直流电机那样,接上一定电压就可以旋转,一般需要在多个电源线中输入周期性的交流电。例如我买的电机就是下面这样的:

无刷电机

无刷电机

它有三根输入电源线,如果用单片机直接输出交变的电信号给它,因为单片机输出功率太低,是转不动的。
如果把单片机的输出经过电流放大电路,例如L298N,事实上很多步进电机就是这样控制的。它的问题在于一个单片机只能控制几个电机,并且很难再做其他的计算了。

电调

由于上面的原因,我们必须采用专门控制电机转速的设备,就叫做电调(全称电子调速器,英文electronic speed controller ,简称ESC)。它的作用就是接受单片机的PWM指令,并且生成成无刷电机需要的高频交流电。

电调

电调

大家可以看到电调有很多线,其中两根粗线连接的是电源的正负极;三根输出线分别连电机的三根输入线(如果需要电机反转,任意对换两根接线即可);另外还有三根细线,分别是单片机接地,5V输出电源和PWM信号线。

 根据航模标准,PWM信号线的频率应该是50Hz,对应的每个周期总时长是20ms。经过我的实测,基本上超过55Hz以后就不转了。
在这个20ms的周期中,高电平的持续时间应该在0.92ms到2.120ms之间,分别对应电机的最低转速和最高转速。

我曾经在想为什么只给转速控制留了这么小的PWM范围?如果是按Arduino系统PWM输出的话,用0~255来输出20ms之间的PWM值,有效控制转速的值范围大约只有pwm=(20~40)之间。后来大概明白了,这是为了给单片机节约宝贵的CPU资源,这样才可以在剩余的18ms中处理其他事务。

好盈程序

看电调的说明书,它有很多功能。比如设置电池的特性,设置刹车特性,设置最大转速等等。这些功能都可以通过遥控器进行设置,其中大部分功能我们使用默认值就可以,只有初始化一项是必须做的。
具体的设置方法是,把油门推到最大(2.12ms),这时候电调就会滴滴几声提醒你进入设置模式,然后就可以把油门迅速拉到最低(0.92ms)保持一会儿之后,电调就可以初始化成功了。

新手可能会有和我一样的疑问:把油门推到最大的时候,会不会电机突然暴走起来?
所以这里需要强调的是,电调的使用一定要按照说明书操作。单片机通电的时候,需要立刻给电调输出最低油门的PWM值(0.92ms),经过我的实验,0.85或0.95也没有太大关系。再低的话就会滴滴滴的报警,告诉你没有初始化成功;再高的话(0.95到2.1之间)只会有一声警报,但是无法正常转动;再再高到最高油门值(2.12ms)的时候,就会提醒你进入设置模式。

这样做是安全的,如果你不慎把遥控器留到了高位,这时候给电机通电,不至于突然上升到非常高的转速。前几天我企图修改PWM的时候,发生了一次这样的失误,手机控制的参数一下子升到最大,螺旋桨的风力大到手几乎抓不住,真是吓的屁滚尿流……

初始化和调速

之前有提到过用电脑通过串口通信控制电机,这里也是类似,在PC上写一段小程序,可以输出0~200之间的一个整数到Arduino。
在Arduino上,分别把0对应到0.9ms的高电平;200对应到2.1ms的高电平。

void loop()
{
  long t0 = micros(); //记录进入loop的时间
  while(Serial.available()) {
    // 这里是获取控制指令
    pushData(Serial.read());
  }
  //ctrlPower从串口读取,0~200分别对应高电平脉宽0.9ms~2.1ms
  double len = 1000 * (0.9 + ctrlPower * (2.1 - 0.9) / 200.0));
  // 输出高电平
  for(int i = 0; i < 4; i++) digitalWrite(motorPins[i], HIGH);
  // 延时len毫秒
  delayMicroseconds(len);
  // 输出低电平
  for(int i = 0; i < 4; i++) digitalWrite(motorPins[i], LOW);
  // 等待一段时间,形成20ms的周期
  delayMicroseconds(t0 + 20000 - micros());
}

其中ctrlPower就是从PC端读取的那个控制值。新电机和电调买回来,把4个装到一起,然后从这个程序统一进行初始化和控制转速。强烈建议第一次玩电机的朋友,先不要装上螺旋桨,应该等调试胸有成竹之后再安装。 :)

关于四轴飞行器

今天看到SSS_SXS同学的提醒,发现已经有一个多月没更新了。这段时间乱七八糟的事情挺多,期间还开小差又玩了一阵子陀螺,总之进展不多。

目前的状况是:传感器可以正确读数,电机驱动和手机蓝牙控制也已经完成,最复杂的姿态平衡控制正在调试中。虽然还没有完成,现有的东西也可以大概总结一下攻略,这里先列出目录,以后慢慢补充内容。

快过年啦,祝大家新年快乐,万事如意,多拿红包!

基于Arduino的四轴飞行器制作攻略

  • 四轴飞行器教程1:器件介绍(上)
  • 四轴飞行器教程2:器件介绍(下)
  • Arduino串口通信
  • 用一块Arduino输出多路PWM
  • 带好盈程序的电子调速器控制
  • I2C总线和SPI总线
  • 三轴陀螺仪的调试
  • 三轴加速度感应器的调试
  • 三轴电子指南针的调试
  • 蓝牙接收板的使用
  • Android手机开发环境设置
  • Android手机的蓝牙通信
  • 自制Arduino扩展板
  • Arduino中的指令时间
  • 卡尔曼滤波和平衡滤波
  • 空间坐标系变换及调试
  • PID平衡算法
  • 在电脑上记录log并调试
  • 超声波测距传感器的使用

四轴飞行器攻略1:器件介绍(上)

每次想做东西的时候,都会经历一个疯狂采购的过程,虽然不一定花很多钱,但是一定是零七八碎的一大箱子。以前圈圈妈很爱逛街,而我每次逛街就像参加一次马拉松似的。现在终于能理解这种购物的乐趣了,唯一的区别是兴趣方向不同。

正如开始计划的,我正在用蚂蚁搬家的方式做这个四轴飞行器,进展比较慢。目前正在做一些实验,电机和陀螺仪的控制和读数已经调试成功。思路和方案也慢慢的成型了,下面介绍一下制作所需的各种材料和器件,供大家参考:

一、电源
所谓“兵马未动,粮草先行”。四轴飞行器想上天,好电池是必须配备的。怎么才能算是一块好电池?除了长相英俊潇洒之外,还得有“吃的多,跑得快,力气大”等优良品质。这里推荐的是格氏11.1V/ 2200mAh/ 25C的锂电池。
选用锂电池的好处是单位重量的电容量大,所以电池比较轻巧,电量比较充足。锂电池的标准电压都是3.7V左右,所以套装的锂电池基本都是3.7的倍数,这个11.1V就是由三块锂电串联而成。2200mAh这个参数大家比较熟悉了,充电的电池一般都有这个参数。它表示电池的电容量,在2.2A的电流下,可以工作1小时。
25C对于初次接触航模的人来说可能有点陌生,某些色狼若有所思但是可能觉得数字不太对 :) 其实这里的xxC表示充放电的速率,可以在1/xx小时内把电用完。也就是说,25C的电池可以在60/25=2.4分钟把电放完。某些同学可能会提问:“这样说来,C值越大的电池用的越快,应该是越不好吧?”为了避免这些同学暴露智商,这里集中提示一下。航模一般都需要比较大的瞬间功率,所以需要很大的电流。一般的电池在这么大的电流条件下早就过热烧掉了,所以必须用C值较大的电池,可以认为C值越大的电池越好。

四轴飞行器专用锂电池

四轴飞行器专用锂电池

另外需要注意的是,好马配好鞍。好电池最好还是给它配一个好一点的充电器,例如B6充电器。其实充电器我也不太懂,但是作为北航的老校友,看到B6立刻冒出了巨大的轰炸机形象,于是果断买下。试用了一下,果然还不错,可以充放各种规格的电池,还可以设置充电速度,并且可以查看三组电池中任一组的状态。有负载均衡的充电器,可以保证电池组的每个电池基本都处于相同的状态。

专用充电器

专用充电器

二、电机和电调
曾经有人问能不能用玩具小车里拆出来的小电机来做四轴飞行器,其实飞行器对电机的要求是非常高的,不仅要转速快,还需要能够精确控制。对于四轴飞行器,推荐新西达或者郎宇的电机。以郎宇A2212为例,它分为KV980和KV1400两种。KV参数的含义是:电压每升高1V,电机转速的增加量。需要注意的是,并不是KV值越高就越好的,因为电机的最大转速是有限的,KV值越大,对控制的精度要求越高。
电机的最大转速我没有查到,有知道的朋友麻烦吱一声。

四轴飞行器专用电机

四轴飞行器专用电机

这几款电机都是使用的三相交流电,所以别指望直接接上电就可以转,我们还需要一种叫电调的东西。电调全称叫“电子调速器”。它的接线很多:两根输入电源线,和电池连接;三根输出电源线,和电机连接;三根控制线,和单片机连接。
其中三根控制线,一根是地线(地线是各种器件的潜规则,都会有),一根是引出的5V(这是个贴心的设计,单片机可以从这里取电,不用再考虑电池的问题),最后一根是控制线,接收PWM信号。关于PWM的介绍,可以看这里这里这里
电调有个重要的参数是最大电流,事实上我们通过简单的程序和电流放大器,也可以做出一个简易的电调。这里推荐使用20A以上的电调,最好是40A的。
购买电调的时候,还会经常看到一个词叫“好盈程序”。一些好点的电调都会带这个容易被念成“好淫”的程序。它不仅可以控制电机的转速,还可以进行更复杂的设置,例如:最大和最小转速,是否支持刹车,过热保护等等。
电调的三根输出电流线和电机的三根线可以随意接,如果发现电机转向不对,随便挑两根反接即可 :)

电子调速器

电子调速器

本来打算一篇介绍完所有器件的,结果罗里巴嗦写了一大堆,需要休息一下,然后继续实验我的电子指南针和重力感应器,改日再继续发攻略。