使用Arduino开发板制作数字出租车计价器
现今,数字仪表正在逐步取代各个部门的模拟仪表,无论是电表还是出租车费用表。主要原因是模拟仪表的机械部件在长时间使用后容易磨损,并且不如数字仪表那么精确。
一个很好的例子是模拟速度计和里程表,用于旧摩托车测量速度和行驶距离。它们有一个称为小齿轮和齿条布置的特殊部件,其中当车轮旋转时,使用电缆旋转速度表的引脚。这在长时间使用时会磨损,也需要更换和维护。
在数字仪表中,不使用机械部件,而是使用光学中断器或霍尔传感器等传感器来计算速度和距离。这比模拟仪表更精确,并且不需要长时间的任何维护。在本篇文章中,我们将使用Arduino制作数字出租车计价器的原型。该项目计算出租车车轮行驶的速度和距离,并连续显示在1602 LCD显示屏上。根据行驶距离,按下按钮时会产生票价。
下图显示了数字出租车仪表项目的完整设置
该原型车使用RC汽车底盘,带有速度传感器模块和连接到电机的编码器轮。一旦测量到速度,我们就可以通过按下按钮来测量行进距离并得到票价量值。我们可以使用电位器设置车轮的速度。我们来看一下速度传感器模块的简短介绍。
红外线槽式光学LM-393速度传感器模块
该原型车使用RC汽车底盘,带有速度传感器模块和连接到电机的编码器轮。一旦测量到速度,我们就可以通过按下按钮来测量行进距离并得到票价量值。我们可以使用电位器设置车轮的速度。我们来看一下速度传感器模块的简短介绍。
这是一种槽型模块,可用于测量编码器轮的旋转速度。该速度传感器模块基于槽型光学中断器工作,也称为光源传感器。该模块需要3.3V至5V的电压并产生数字输出。因此它可以与任何微控制器连接。
红外光传感器由光源(IR-LED)和光电晶体管传感器组成。两者之间都有一个小间隙。当物体放置在IR LED和光电晶体管的间隙之间时,它将中断光束,导致光电晶体管停止通过电流。
因此,使用该传感器,使用可以连接到电动机的开槽盘(编码器轮),并且当轮与电动机一起旋转时,它中断IR LED和光电晶体管之间的光束,使得输出接通和断开(产生脉冲)。
这样,当源和传感器之间存在中断时(当任何物体置于其间时),它会产生高电平输出,并且当没有物体放置时产生低电平输出。在模块中,我们有一个LED指示光学中断引起的。
该模块配有LM393比较器IC,用于在OUTPUT产生精确的HIGH和LOW信号。因此,该模块有时称为LM393速度传感器。
测量速度和行驶距离来计算票价
要测量旋转速度,我们需要知道编码器轮中存在的槽数。我有一个带20个插槽的编码器轮。当它们旋转一个完整的旋转时,我们在输出端有20个脉冲。因此,为了计算速度,我们需要每秒产生的脉冲数。
例如,如果一秒内有40个脉冲,那么
Speed = Noo. Of pulses / No. of slots = 40/20 = 2RPS (Revolution per second)
以RPM(每分钟转数)计算速度乘以60
Speed in RPM = 2 X 60 = 120 RPM (Revolutions per Minute)
测量距离:测量车轮行驶的距离非常简单。在计算距离之前,应该知道车轮的周长。
Circumference of the wheel = π*d
其中d是车轮的直径。
π的值是3.14。
我有一个直径6.60厘米的轮子(RC车轮),所以周长是(20.7厘米)。
因此,要计算行进距离,只需将检测到的脉冲数乘以圆周即可。
Distance Travelled = Circumference of Wheel x (No. of Pulses / No. of slots)
因此当周长20.7cm的轮子需要20个脉冲,即编码器轮的一次旋转时,轮子行进的距离由
Distance travelled = 20.7 x (20/20) = 20.7cm
为了计算距离,以米为单位将距离以cm值除以100。
注意:这是一个小型的RC车轮,实时车比这个车轮更大。所以我假设本教程中轮子的周长为230cm。
基于行驶距离计算票价:
要得到总票价金额,请将行驶距离乘以票价(金额/米)。
假设每米收费5卢比。因此,如果车轮行驶20米,那么票价将是20 * 5 = 100卢比。
所以现在让我们得到组件并构建电路。
需要的组件
● Arduino UNO开发板
● LCD(16×2)
● 按钮
● 电位计-10k
● ULN2003电机驱动器IC
● LM393速度传感器模块(FC-03)
● RC智能车底盘带速度编码器
● 电池9V
● 面包板
● 连接线
电路原理图
RC车载底盘安装速度传感器
速度传感器安装时,编码器轮位于传感器间隙之间。在我的机箱中,有一个用于放置传感器的特殊孔。见下图
速度传感器模块和Arduino之间的连接
Arduino和ULN2003之间的连接
ULN2003、直流电机和9v电池之间的连接
使用9V电池和ULN2003为电机提供外部电源。
按钮和电位器的连接
按钮通过一只下拉电阻连接到Arduino的引脚3,用于在按下按钮时生成票价。
电位器用于向Arduino的引脚A0提供模拟输入电压,以改变车轮的速度。
数字出租车计价器的Arduino编程
在本文末尾处给出了完整的代码。在这里,我们介绍代码的一些重要部分。
在开始介绍代码之前,我们需要了解中断和Timer One库,因为它们会在代码中使用。
这里使用中断是因为我们需要不断检查速度传感器模块上检测到的输出是否为高优先级。所以在代码中使用了ISR。 ISR是中断服务程序,在中断引脚2和3发生中断时调用。
Arduino UNO有两个中断引脚2和3。
● 在引脚2处,连接速度传感器的D0的输出。
● 在引脚3处,连接带有下拉电阻的按钮。
代码中使用TimerOne库来检查一秒钟内检测到的旋转次数(多少个脉冲),然后我们可以计算每秒的速度并在输出端显示它们。此ISR函数每秒执行一次。
让我们详细看看代码:
1. 首先,包含函数的库将用于程序。
#include "TimerOne.h"
#include <LiquidCrystal.h>
2. 接下来声明全局变量,因为它们将在整个程序中使用。
volatile unsigned int counter=0;
volatile unsigned int rotation=0;
float rotationinm=0;
unsigned int speed=0;
3. 接下来定义并初始化连接到Arduino的LCD引脚。
const int rs = 12, en = 13, d4 = 8, d5 = 9, d6 = 10, d7 = 11;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
4. 接下来在void setup()函数中,
定义引脚模式,此处PIN A0用于从电位器获取模拟输入,引脚5用于写入连接到ULN2003 IC的IN1引脚的模拟输出。
pinMode(A0,INPUT);
pinMode(5,OUTPUT);
接下来显示一些欢迎消息,然后清除。
lcd.begin(16,2); //Sets LCD as 16x2 type
lcd.setCursor(0,0);
lcd.print("CIRCUIT DIGEST");
lcd.setCursor(0,1);
lcd.print("WELCOME TO TAXI");
delay(3000);
lcd.clear();
lcd.print("LETS START :)");
delay(1000);
lcd.clear();
接下来重要的部分是设置中断引脚和中断发生时要调用的ISR。
首先,我们需要将timer1设置为1秒,然后为timer1附加一个ISR,以便每秒调用一次ISR。 ISR名称是timerIsr
Timer1.initialize(1000000);
Timer1.attachInterrupt( timerIsr );
接下来关联两个外部中断。第一次中断使Arduino引脚2成为中断引脚,当引脚2检测到RISING(低电平到高电平)时,调用ISR(计数)。该引脚2连接到速度传感器模块的D0输出。
第二个将Arduino引脚3作为中断引脚,并在引脚3检测到高电平时调用ISR(生成)。该引脚通过下拉电阻连接到按钮。
attachInterrupt(digitalPinToInterrupt(2), count, RISING);
attachInterrupt(digitalPinToInterrupt(3), generatefare, HIGH);
5. 接下来让我们看看我们在这里使用的ISR:
当引脚2(连接到速度传感器)发生上升沿中断(低到高)时,调用ISR1- count() ISR函数。
void count() // ISR for counts from the speed sensor
{
counter++; // increase the counter value by one
rotation++; //Increase the rotation value by one
delay(10);
}
ISR2- timerIsr() ISR每秒调用一次,并执行ISR中存在的那些代码。
void timerIsr()
{
detachInterrupt(digitalPinToInterrupt(2));
Timer1.detachInterrupt();
lcd.clear();
float speed = (counter / 20.0)* 60.0;
float rotations = 230*( rotation / 20);
rotationinm = rotations/100;
lcd.setCursor(0,0);
lcd.print("Dist(m):");
lcd.print(rotationinm);
lcd.setCursor(0,1);
lcd.print("Speed(RPM):");
lcd.print(speed);
counter=0;
int analogip = analogRead(A0);
int motorspeed = map(analogip,0,1023,0,255);
analogWrite(5,motorspeed);
Timer1.attachInterrupt( timerIsr );
attachInterrupt(digitalPinToInterrupt(2), count, RISING);
}
该函数包含实际首先分离Timer1和中断pin2的行,因为我们在ISR中有LCD打印语句。
为了以RPM计算SPEED,我们使用下面的代码,其中20.0是编码器轮中预设的槽数。
float speed = (counter / 20.0) * 60.0;
并且为了计算距离,使用以下代码:
float rotations = 230*( rotation / 20);
这里车轮的周长假定为230厘米(这对于实时车来说是正常的)
接下来,通过将距离除以100来转换距离(m)
rotationinm = rotations/100;
之后,我们在LCD显示屏上显示SPEED和DISTANCE.
lcd.setCursor(0,0);
lcd.print("Dist(m):");
lcd.print(rotationinm);
lcd.setCursor(0,1);
lcd.print("Speed(RPM):");
lcd.print(speed);
重要提示:我们必须将计数器重置为0,因为我们需要获得每秒检测到的脉冲数,因此我们使用此代码
counter=0;
接下来读取模拟引脚A0并将其转换为数字值(0到1023)并进一步将这些值映射到0-255以进行PWM输出(设置电机速度),最后使用连接到ULN2003的analogWrite函数写入这些PWM值电机IC。
int analogip = analogRead(A0);
int motorspeed = map(analogip,0,1023,0,255);
analogWrite(5,motorspeed);
ISR3:generatefare() ISR用于根据行进距离生成票价金额。当检测到中断引脚3为高电平时(按下按钮时),将调用此ISR。该函数分离引脚2的中断和定时器中断,然后清除LCD。
void generatefare()
{
detachInterrupt(digitalPinToInterrupt(2)); pin at 2
Timer1.detachInterrupt();
lcd.clear();
lcd.setCursor(0,0);
lcd.print("FARE Rs: ");
float rupees = rotationinm*5;
lcd.print(rupees);
lcd.setCursor(0,1);
lcd.print("Rs 5 per metre");
}
之后行驶的距离乘以5。你可以根据自己的意愿改变。
float rupees = rotationinm*5;
计算出数值后,将其显示在连接到Arduino的LCD显示屏上。
lcd.setCursor(0,0);
lcd.print("FARE Rs: ");
lcd.print(rupees);
lcd.setCursor(0,1);
lcd.print("Rs 5 per metre");
您可以通过提高准确性、稳健性并添加更多功能(如Android应用程序、数字支付等)并将其开发为产品来进一步改进此原型。
代码
完整的代码如下:
#include "TimerOne.h" //Include Timer1 library for using Timer1 functions
#include <LiquidCrystal.h> //Include LCD library for using LCD display functions
const int rs = 12, en = 13, d4 = 8, d5 = 9, d6 = 10, d7 = 11; //Define the LCD pins
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
volatile unsigned int counter=0;
volatile unsigned int rotation=0;
float rotationinm=0;
unsigned int speed=0;
void count() // ISR for counts from the speed sensor
{
counter++; //increase the counter value by one
rotation++; //Increase the rotation value by one
delay(10);
}
void timerIsr()
{
detachInterrupt(digitalPinToInterrupt(2)); //Stops the interrupt pin 2
Timer1.detachInterrupt(); //Stops the timer1 interrupt
lcd.clear();
float speed = (counter / 20.0)* 60.0; //Calcukate speed in minute (20-No of slots in Encoder Wheel)
float rotations = 230*( rotation / 20); //Calculate distance in cm (230-Circumference of the wheel assumed 20- No of slots)
rotationinm = rotations/100;
lcd.setCursor(0,0);
lcd.print("Dist(m):");
lcd.print(rotationinm); //Display rotationinm at LCD
lcd.setCursor(0,1);
lcd.print("Speed(RPM):");
lcd.print(speed); //Dsiplay speed in RPM
counter=0; //Reset counter to 0
int analogip = analogRead(A0); // Analog read from pin A0
int motorspeed = map(analogip,0,1023,0,255); //convert digital vales 0-1023 to 0-255
analogWrite(5,motorspeed); //Sets PWM value at pin 5
Timer1.attachInterrupt( timerIsr ); //Starts timer1 again
attachInterrupt(digitalPinToInterrupt(2), count, RISING); //Attaches interrupt at pin2 again
}
void generatefare() //ISR to generate the fareamount
{
detachInterrupt(digitalPinToInterrupt(2)); //Disables the Interrupt pin at 2
Timer1.detachInterrupt(); //Disables the Timer1 interrupt
float rupees = rotationinm*5; //Muliply 5 with distance travelled (Rs 5 per meter )
lcd.clear(); //Clears LCD
lcd.setCursor(0,0);
lcd.print("FARE Rs: ");
lcd.print(rupees); //Display fare amount
lcd.setCursor(0,1);
lcd.print("Rs 5 per metre");
}
void setup()
{
pinMode(A0,INPUT); //Sets pin A0 as INPUT
pinMode(5,OUTPUT); //Sets pin 5 as OUTPUT
lcd.begin(16,2); //Sets LCD as 16x2 type
lcd.setCursor(0,0); //The following code displays welcome messages
lcd.print("CIRCUIT DIGEST");
lcd.setCursor(0,1);
lcd.print("WELCOME TO TAXI");
delay(3000);
lcd.clear();
lcd.print("LETS START :)");
delay(1000);
lcd.clear();
Timer1.initialize(1000000); //Initilize timer1 for 1 second
Timer1.attachInterrupt( timerIsr ); //ISR routine to be called for every one second
attachInterrupt(digitalPinToInterrupt(2), count, RISING); // Pin 2 as Interrupt pin with count ISR is called when LOW to RIGH happens.
attachInterrupt(digitalPinToInterrupt(3), generatefare, HIGH); //Pin 3 as Interrupt pin with generatefare ISR is called when HIGH is detected.
}
void loop()
{
}