目录
-
- 简介
- 演示
- BOM
- 接线
- Arduino IDE 代码
- 参考
简介
通过舵机的左右旋转来控制超声波模块的测量区域。障碍物出现时, 即探测到的距离小于10cm时,底盘会朝着远离障碍物的方向转动。
演示
【Arduino玩具】东张西望的避障车
BOM
Arduino UNO x 1
超声波测距模块(HC-SR04) x 1
舵机(SG 90) x 1
红色LED x 1
履带式底盘,含两个减速电机(Pololu Zumo) x 1
马达驱动版(Zumo Shield V1.3) x 1
接线
Arduino IDE 代码
舵机的操控使用了 ATmega328P 的 timer-2, 代码稍微复杂一些。因为马达驱动板的库文件 <ZumoShield.h> 使用的是 timer -1,会与 <Servo.h> 冲突。如果使用其它不需要库文件的马达驱动的话,代码会简单很多,例如 L298N。
#include <Wire.h>
#include <ZumoShield.h> //马达驱动板需要的库文件
ZumoMotors motors;
#define Echo 2 //HC-SR04 Echo pin//超声波测距模块的“回声”引脚接 D2
#define Trig 3 //HC-SR04 Trig pin//超声波测距模块的“激发”引脚接 D3
#define SERVO 11 //Servo pin//舵机接 D11或 D3,因为要使用 timer-2
#define LED 5 //LED pin//LED接 D5
uint16_t volatile servoTime = 0;
uint16_t volatile servoHighTime = 3000; // Pulse width in units of 0.5us//高电平持续时间
boolean volatile servoHigh = false;
float distance; //distance measured by HC-SR04//超声波模块探测到的距离
void setup(){
Serial.begin(9600);
pinMode(Trig, OUTPUT); pinMode(Echo, INPUT); pinMode(LED, OUTPUT);
servoInit();
}
void loop(){
//Drive Forward//前进*******************************************************************************************
motors.setLeftSpeed(100); motors.setRightSpeed(100); //0-25
//Look Right//向右看*********************************************************************************************
servoSetPosition(1000); //Send 1000us pulses to servo//1000us的脉冲,转动舵机
distance = MeasureDistance(); //HC-SR04//超声波模块测距
while (distance <10 ) { //turn left, till the obstacle is more than 10cm away
motors.setLeftSpeed(-100); motors.setRightSpeed( 100); //底盘向左转动,直到障碍物处于10cm开外
digitalWrite(LED, HIGH);
distance = MeasureDistance();}
digitalWrite(LED, LOW);
delay(700); //for stable HC-SR04 readings
//700ms毫秒的延迟貌似会让超声波模块的读数稳定一些
//Look Forward//向前看********************************************************************************************
servoSetPosition(1500);
distance = MeasureDistance();
while (distance <15) {
motors.setLeftSpeed(-100); motors.setRightSpeed( 100);
digitalWrite(LED, HIGH);
distance = MeasureDistance();}
digitalWrite(LED, LOW);
delay(700);
//Look Forward//向右看********************************************************************************************
servoSetPosition(2000);
distance = MeasureDistance();
while(distance <10) {
motors.setLeftSpeed( 100); motors.setRightSpeed(-100);
digitalWrite(LED, HIGH);
distance = MeasureDistance();}
digitalWrite(LED, LOW);
delay(700);
//Look Forward//向前看********************************************************************************************
servoSetPosition(1500);
distance = MeasureDistance();
while (distance <15 ) {
motors.setLeftSpeed( 100); motors.setRightSpeed(-100);
digitalWrite(LED, HIGH);
distance = MeasureDistance();}
digitalWrite(LED, LOW);
delay(700);
}
//Functions//函数****************************************************************************************************
float MeasureDistance(){ //测距
float d;
digitalWrite(Trig, LOW); delayMicroseconds(2);
digitalWrite(Trig, HIGH); delayMicroseconds(10);
digitalWrite(Trig, LOW);
d = (pulseIn(Echo, HIGH) / 2) * 343/10000; //测距公式,"d" 的单位是 "cm"
Serial.println(d);
return d; }
//在Timer 2 达到 OCR2A 并重置后启动ISR
ISR(TIMER2_COMPA_vect){ //Timer/Counter2 Compare Match A//中断向量
servoTime += OCR2A + 1;
static uint16_t highTimeCopy = 3000;
static uint8_t interruptCount = 0;
if(servoHigh){
if(++interruptCount == 2){
OCR2A = 255;}
if(servoTime >= highTimeCopy){ //Servo引脚处在高电平状态
digitalWrite(SERVO, LOW); //做一个下降沿
servoHigh = false;
interruptCount = 0;}
}
else{ //Servo引脚处在低电平状态
if(servoTime >= 40000){
highTimeCopy = servoHighTime;
digitalWrite(SERVO, HIGH); //做一个上升沿
servoHigh = true; servoTime = 0; interruptCount = 0;
OCR2A = ((highTimeCopy % 256) + 256)/2 - 1;}
}
}
void servoInit(){
digitalWrite(SERVO, LOW); pinMode(SERVO, OUTPUT);
//开启CTC模式,Timer 2 会增加到 OCR2A 然后重置,造成中断
TCCR2A = (1 << WGM21);
TCCR2B = (1 << CS21); //0.5us分辨率
TCNT2 = 0; OCR2A = 255;
TIMSK2 |= (1 << OCIE2A); // 开启 timer compare interrupt.
sei(); // 开启中断
}
void servoSetPosition(uint16_t highTimeMicroseconds){
TIMSK2 &= ~(1 << OCIE2A); // 关闭 timer compare interrupt.
servoHighTime = highTimeMicroseconds * 2;
TIMSK2 |= (1 << OCIE2A); // 开启 timer compare interrupt.
}
参考
- 中断处理教程
- ATmega328P 数据手册
- Polulu Zumo 底盘,驱动版手册