互联网上有大量关于 Arduino 控制的汽车项目的描述。套件包括电机和底盘。其中一些套件是两轮驱动,第三轮作为浮动脚轮。
这种三轮配置本质上是不稳定的,即使在驱动程序中将两个电机设置为相同的速度,也不会直线行驶。车轮直径、滚动摩擦和电机特性的变化会导致偏差。克服这个问题的唯一方法是增加反馈并定期对电机速度进行修正。该项目将解释如何实施以实现直线行驶。
这是一个由 Arduino 控制的汽车示例,由两轮驱动套件构建。主要组成部分是:
两轮驱动套件,包括电机、底盘、各种螺丝和螺母、车轮和车轮编码器。
本文不包括如何制造汽车的描述;互联网上有很多资源可以解释所需的步骤。
成品车如下:
整车电路图如下:
只是配置上需要注意的几点:
HC-020K 编码器模块依靠 LM393 比较器来生成方波脉冲。编码器轮上的每个孔都会在方波中产生上升沿和下降沿,其频率与轮速成正比。对这些方波脉冲进行计数也会得出与行进距离成正比的总数。使用 Arduino 对这些脉冲进行计数的标准方法是将它们连接到中断并让中断服务例程 (ISR) 递增计数器(稍后提供示例代码)。
几项试图将脉冲计数与 rpm 和行进距离相协调的实验表明,脉冲计数不正确的因素约为十倍 - 十倍于许多脉冲。这使得编码器几乎毫无用处。此异常需要进一步调查
以下是 HC-020K 编码器的一些示波器轨迹:
在 5 毫秒的时间尺度上,方波的上升沿和下降沿看起来很干净。然而,如果放大到 2 微秒的时间尺度,方波的下降沿会在 5V 到 0V 的转换期间显示多个向上向下的尖峰。
这些尖峰中的每一个都可能触发中断并导致脉冲计数过多。它们是由 HC-020K 编码器模块上的比较器电路的性质引起的——它在开环模式下运行。
解决这个问题的正确方法是通过在输出和提供参考电压的分压器之间添加一个反馈电阻,在比较器电路中引入迟滞。有几篇文章解释了这个电路——这里是德州仪器的一个例子。
https://www.ti.com/lit/ug/tidu020a/tidu020a.pdf
要针对迟滞修改 HC-020K 编码器模块,需要从输出端到 LM393 的引脚 2 连接一个 50KOhm 电阻。由于使用了表面贴装 IC,实际上很难执行此 mod。如果你能做到——祝你好运!
一些额外的研究显示了一种替代方法。这需要在输出和地之间有一个 100nF 的电容器。这是一个更容易实现的模组,如下所示。
电容器有效滤除高频尖峰并提供平滑过渡。产生的波不是方波,因为它被电容器在前沿和下降沿上的充电和放电所修改。示波器轨迹如下:
放大到5微秒的时间尺度,方波的下降沿显示单次跳变
进行此修改后,脉冲计数与观察到的转速很好地对齐。
既然解开了太多脉冲的谜团,是时候将车轮编码器的反馈控制应用于电机速度了。基本思想是根据测得的脉冲数调整单个电机速度,使轮子覆盖相同的距离并沿直线驱动 Arduino。
反馈控制回路分为许多类别,但最常见的称为 PID(比例、积分和微分)回路。关于这个主题的信息很多,他们的研究是一门完整的学科。简单系统如下图:
将设定点(期望输出)与反馈(实际输出)进行比较,误差用于生成对受控系统的输入。该输入将系统驱动到所需的输出,以便最终反馈等于设定点。
对于 Arduino 汽车,需要控制的输出是两个车轮的脉冲计数之差。如果此差异为零,则车轮将行驶相同的距离(假设车轮直径相等)。
几个定义:
下面是Arduino小车基本控制策略的流程图:
Arduino 上的控制程序使用中断有两个目的:
第一个中断的相关代码片段是
const int encoder1 = 2;
const int encoder2 = 3;
volatile int pulse1;
volatile int pulse2;
void setup(){
pulse1 = 0;
pulse2 = 0;
attachInterrupt(digitalPinToInterrupt(encoder1), count1, FALLING);
attachInterrupt(digitalPinToInterrupt(encoder2), count2, FALLING);
}
void count1(){
// counting the number of pulses for encoder 1
pulse1++;
}
void count2(){
// counting the number of pulses for encoder 2
pulse2++;
引脚 2 和 3 用于编码器的中断输入。pulse1 和 pulse2 是用于保存计数的变量。Count1 和 count2 是中断服务程序,只是增加计数器。中断在从编码器接收到的方波的下降沿触发。
第二个中断使用 ATMega328 内置的 Timer1。定时器以预定的时间间隔触发中断。然后,这会运行一个控制回路,使汽车保持直线行驶。相关代码片段如下:
void setup(){
cli();//stop interrupts
//set timer1 interrupt at 4Hz
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
TCNT1 = 0;//initialize counter value to 0
// set compare match register for 4hz increments
OCR1A = 3905;// = (16*10^6) / (4*1024) - 1 (must be <65536)
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS12 and CS10 bits for 1024 prescaler
TCCR1B |= (1 << CS12) | (1 << CS10);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
sei();//allow interrupts
}
ISR(TIMER1_COMPA_vect){
//Control loop here
}
预分频器 (0CR1A) 的值决定了中断的频率。
第一种直接驾驶汽车的方法使用基于流程图的简单算法。代码如下:
int pError = 0;
pError = pulse1 - pulse2;
// pError is positive speed up motor A and slow down motor B
if(pError > 0){
analogWrite(enA, (motorSpeed + 7));
analogWrite(enB, (motorSpeed - 7));
}
// pError is negative speed up motor B and slow down motor A
else if(pError < 0){
analogWrite(enA, (motorSpeed - 7));
analogWrite(enB, (motorSpeed + 7));
}
else {
analogWrite(enA, motorSpeed);
analogWrite(enB, motorSpeed);
}
digitalWrite(led, toggle);
toggle = !toggle;
一种更复杂的方法是使用 PID 控制器。Arduino 库包括一个名为 FastPID 的库,它实现了 PID 控制器。添加到与 IDE 关联的库。
有关如何在以下链接中使用此库的文档
https://github.com/mike-matera/FastPID
这是代码片段
float Kp=0.6, Ki=0.4, Kd=0, Hz=4;
int output_bits = 8;
bool output_signed = false;
FastPID driveStraight(Kp, Ki, Kd, Hz, output_bits, output_signed);
ISR(TIMER1_COMPA_vect){
static int pError;
pError = 0;
pError = pulse1 - pulse2;
uint8_t output = driveStraight.step(setpoint, pError);
analogWrite(enA, (motorSpeed - output));
analogWrite(enB, (motorSpeed + output));
digitalWrite(led,toggle);
toggle = !toggle;
}
可以通过为 Kp、Kd 和 Ki 分配不同的值来调整回路的灵敏度
希望这有助于直接驾驶!
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
全部0条评论
快来发表一下你的评论吧 !