一、基本功能描述:
1、基本循迹功能。对于直道、S弯道和钝角弯道,小车可沿黑线轨迹自动进行调整使黑线在小车正中间。如若出现小车全脱离黑线,小车进行逆时针旋转直至再次检测到黑线进行循迹。
2、特殊赛道循迹功能。对于直角、锐角赛道,小车进行较大幅度转向不偏离轨道。对于十字路口赛道,即小车检测模块全压在黑线,小车直行跨越黑线。
3、防碰撞功能:在碰撞障碍时可以识别障碍的存在并进行避障。(本次课程未实现)
二、硬件结构:
2.1:小车整体分为5大模块:
(1)控制模块:搭载单片机MSP432的Launch Pad主板
(2)驱动模块:电池组、N20直流电机
(3)循迹模块:红外LED传感器
(4)防撞模块:防撞开关
(5)运动模块:底盘和车轮等
模块作用:
(1)控制模块即使用单片机编程,实现代码控制小车按一定路线行进
(2)驱动模块即是提供能源动力的部分
(3)循迹模块是使用红外传感器,通过反射的不同来识别路面,以获得轨道信息
(4)防撞模块是通过防撞开关进行传感,以使小车识别障碍,进而实现避障功能
(5)运动模块是小车主体部分,是进行运动的主体
2.2:这里主要介绍本次课程重点的3个模块
(1)控制模块:MSP432P401R Launch Pad包含48MHz ARM CortexM4F内核80μA/MHz工作功耗和660nA RTC操作,14位1Msps差动SAR ADC和AES256加速器。
处理器特性:低功耗、高性能的MSP432P401R MCU
带浮点单元和DSP加速功能的48MHz 32位ARM CortexM4F
功耗:80μA/MHz工作功耗和660nA RTC待机操作功耗
模拟:24通道14位1Msps差动SAR ADC两个比较器
数字:高级加密标准(AES256)加速器、CRC、DMA、32位硬件乘法器
存储器:256KB闪存、64KB RAM
计时器:4个16位、2个32位
通信:多达4个I2C、8个SPI、4个UART
40引脚BoosterPack连接器,支持20引脚BoosterPack采用EnergyTrace+技术的板载XDS-110ET仿真器 2个按钮和2个LCD,便于用户交互反向通道UART通过USB连接到PC
MSP432处理器可以使用CCS、IAR和Keil μVision IDE来开发。
整块电路板的框图如下:
以电路板上虚线为分界,上半部分是XDS-110ET仿真器,S101开关来选择板载XDS-110ET仿真器还是用外部仿真器
MSP432P401R与XDS-110ET的连接可以断开,MSP432P401R与XDS-110ET除仿真信号外没有其他通讯连接,仿真信号包括XDS-110ET串行调试信号、应用UART信号和3.3V、5V电源
(2)驱动模块:两节3.7V可充电锂电池
N20直流减速电机:
产品型号: GM12-N20VA
主要用途:标签打印机、标签剥离机、条码打印机、机器人、电子门锁、打印机、电子监控器、美容仪器、展示架、柜台、自动售货机等
电压:3V 4.5V 6V 12V
转速:3rpm--1000rpm
力矩: 最大 7kg.cm
外形尺寸:12*10*25mm
出轴外径: 3mm, (出轴长短可根据客户的要求设计)
输出轴可以加工成M3螺纹, M4螺纹
12*10mm微型精密减速器.
减速比:1/3、1/5、1/10、1/30、1/50、1/63、1/100、1/150、1/210、1/250、1/298、1/370、1/380、1/1000
(3)循迹模块:红外传感器原理:利用8路红外传感器感知地面颜色信息,控制机器人沿白线行进。不同的材料表面对红外线的反射能力不同,红外接收器电压变化也就不同,如下图:
可以看出,白色表面比黑色表面下降更快,由此我们记录高电平时间,通过这个时间的长短即可以判断地面是黑色或白色。
控制:对于这种车型,小车的控制当然是差速控制。8路开关,可以把差速分为4个等级,不同等级差速大小不一样,这几个参数影响小车循迹的稳定性,要根据实际情况调整。电机配有编码器,使用定时器输入捕获功能进行正交解码即可得知电机转速信息,这样就能实现电机闭环控制,让小车运行效果更好。
三、控制算法及软件实现
主要内容:
3.1 软件平台及安装
CCS,Keil uVision5,pack驱动包
3.2 软件系统总体结构及各模块的功能定义
(1)Keil uVision5用于寻迹小车的代码学习和书写,并将程序下入32单片机对小车进行驱动。
(2)Keil.STM32F4xx_DFP.1.0.8.pack为小车配置编程环境
(3)电机使用例程,对单片机对电机的驱动方式方法有了初步的了解,并实现电机的各种功能。
(4)循迹旧版例程,通过红外传感器对黑线识别来控制小车行走,其中涉及二进制,十六进制,单片机IO口等
3.3 关键算法和软件实现
if((0xFF==flag)||(0xFE==flag)||(0x7F==flag))//十字路口代码
{
// if(0==TrackingControlCnt)
// {
// TrackingControlCnt=1;
// return ;
// }
Timer_A_setCompareValue(TIMER_A0_BASE,
TIMER_A_CAPTURECOMPARE_REGISTER_3,
0);
Timer_A_setCompareValue(TIMER_A0_BASE,
TIMER_A_CAPTURECOMPARE_REGISTER_1,
0);
// while(!Turn(TurnForward,120,200));
while(!Turn(TurnForward,90,500));
// TrackingControlCnt=0;
LEDPreError=0;
LEDLastError=0;
TrackingControlFlag=0;
L_Wheel_Speed=L_Wheel_Init_Speed;
R_Wheel_Speed=R_Wheel_Init_Speed;
L_Wheel_Positive();
R_Wheel_Positive();
Timer_A_setCompareValue(TIMER_A0_BASE,
TIMER_A_CAPTURECOMPARE_REGISTER_1,
L_Wheel_Init_Speed);
Timer_A_setCompareValue(TIMER_A0_BASE,
TIMER_A_CAPTURECOMPARE_REGISTER_3,
R_Wheel_Init_Speed);
return ;
}
else if((0xB8==flag)||(0x98==flag)||(0x9C==flag)||(0xB0==flag))//左锐角代码
{
Timer_A_setCompareValue(TIMER_A0_BASE,
TIMER_A_CAPTURECOMPARE_REGISTER_3,
0);
Timer_A_setCompareValue(TIMER_A0_BASE,
TIMER_A_CAPTURECOMPARE_REGISTER_1,
0);
while(!Turn(TurnForward,100,600));
while(!Turn(TurnLeft,80,900));
TrackingControlCnt=0;
LEDPreError=0;
LEDLastError=0;
TrackingControlFlag=0;
L_Wheel_Speed=L_Wheel_Init_Speed;
R_Wheel_Speed=R_Wheel_Init_Speed;
L_Wheel_Positive();
R_Wheel_Positive();
Timer_A_setCompareValue(TIMER_A0_BASE,
TIMER_A_CAPTURECOMPARE_REGISTER_1,
L_Wheel_Init_Speed);
Timer_A_setCompareValue(TIMER_A0_BASE,
TIMER_A_CAPTURECOMPARE_REGISTER_3,
R_Wheel_Init_Speed);
return ;
}
这里给出2个较为关键的识别代码
3.4功能模块的具体实现
//前进后退转弯代码
#include "MOTOR.h"
//在MSP432参考指南找到对应章节有关TimerA的介绍
//定时器A向上计数模式配置结构体
Timer_A_UpModeConfig upConfig =
{
TIMER_A_CLOCKSOURCE_SMCLK, // SMCLK Clock Source
TIMER_A_CLOCKSOURCE_DIVIDER_48, // SMCLK/48 = 1MHz
1000, // 5000 tick period
TIMER_A_TAIE_INTERRUPT_DISABLE, // Disable Timer interrupt
TIMER_A_CCIE_CCR0_INTERRUPT_ENABLE , // Enable CCR0 interrupt
TIMER_A_DO_CLEAR // Clear value
};//SMCLK是系统子时钟,MCLK是系统主时钟
//定时标志
volatile uint8_t TimingFlag=0;
//溢出次数LoopCnt,余数TimingVal 溢出次数=定时时间/0xFFFF 余数=定时时间%0xFFFF
volatile uint32_t LoopCnt=0,TimingVal=0;
//电机初始化
void MotorInit(void)
{
Timer_A_PWMConfig pwm1Config =
{
TIMER_A_CLOCKSOURCE_SMCLK,//时钟源选择SMCK
TIMER_A_CLOCKSOURCE_DIVIDER_1,//1分频
2000,//周期为2000个SMCK时钟周期
L_CAPTURECOMPARE_REGISTER,//选择左轮电机PWM
TIMER_A_OUTPUTMODE_RESET_SET,
0//初始占空比为0
};
Timer_A_PWMConfig pwm2Config =
{
TIMER_A_CLOCKSOURCE_SMCLK,//时钟源选择SMCK
TIMER_A_CLOCKSOURCE_DIVIDER_1,//1分频
2000,//周期为2000个SMCK时钟周期
R_CAPTURECOMPARE_REGISTER,//选择右轮电机PWM
TIMER_A_OUTPUTMODE_RESET_SET,
0//初始占空比为0
};
//GPIO复用配置
MAP_GPIO_setAsPeripheralModuleFunctionOutputPin(MotorPWMPort,
L_PWM_Pin|R_PWM_Pin,
GPIO_PRIMARY_MODULE_FUNCTION);//设置引脚L_PWM_Pin和R_PWM_Pin为复用功能
MAP_GPIO_setAsOutputPin(MotorDirectionPort,L_Direction_Pin|R_Direction_Pin);//设置引脚L_Direction_Pin和R_Direction_Pin为普通输出
//PWM配置
MAP_Timer_A_generatePWM(TIMER_A0_BASE, &pwm1Config);//产生PWM到左轮电机
MAP_Timer_A_generatePWM(TIMER_A0_BASE, &pwm2Config);//产生PWM到右轮电机
//转向定时配置器
MAP_Timer_A_configureUpMode(TIMER_A1_BASE, &upConfig);//配置定时器向上计数模式
MAP_Interrupt_enableInterrupt(INT_TA1_0);//打开定时器中断
}
//原地转向函数
//direction:方向,可选参数TurnLeft,TurnRight,TurnForward,TurnBackward
//angle:角度 范围:任意度数(前进和后退时此参数为运动时间)
//speed:转向速度,范围:0~2000
uint8_t Turn(uint8_t direction,float angle,uint32_t speed)
{
uint32_t time;
if(0==TimingFlag)
{
time=angle*TimingPerAngle;
if(time<=0xFFFF)
{
LoopCnt=0;
TimingVal=time;
}
else
{
LoopCnt=time/0xFFFF;
TimingVal=time%0xFFFF;
}
upConfig.timerPeriod=TimingVal;
MAP_Timer_A_configureUpMode(TIMER_A1_BASE, &upConfig);
//转弯代码--开始转弯
switch(direction)
{
case 1:
Timer_A_setCompareValue(TIMER_A0_BASE,
L_CAPTURECOMPARE_REGISTER,
speed);
Timer_A_setCompareValue(TIMER_A0_BASE,
R_CAPTURECOMPARE_REGISTER,
speed);
L_Wheel_Positive();
R_Wheel_Negative();
break;//右转
case 2:
Timer_A_setCompareValue(TIMER_A0_BASE,
L_CAPTURECOMPARE_REGISTER,
speed);
Timer_A_setCompareValue(TIMER_A0_BASE,
R_CAPTURECOMPARE_REGISTER,
speed);
L_Wheel_Negative();
R_Wheel_Positive();
break;//左转
case 3:
Timer_A_setCompareValue(TIMER_A0_BASE,
L_CAPTURECOMPARE_REGISTER,
speed);
Timer_A_setCompareValue(TIMER_A0_BASE,
R_CAPTURECOMPARE_REGISTER,
speed);
L_Wheel_Negative();
R_Wheel_Negative();
break;//后退
case 4:
Timer_A_setCompareValue(TIMER_A0_BASE,
L_CAPTURECOMPARE_REGISTER,
speed+65);
//(若左右轮转速不同可增加一个常数调节)
Timer_A_setCompareValue(TIMER_A0_BASE,
R_CAPTURECOMPARE_REGISTER,
speed);
L_Wheel_Positive();
R_Wheel_Positive();
break;//前进
default:break;
}
MAP_Timer_A_startCounter(TIMER_A1_BASE,TIMER_A_CONTINUOUS_MODE);
TimingFlag=1;
}
else if(2==TimingFlag)
{
TimingFlag=0;
return 1;
}
return 0;
}
void TA1_0_IRQHandler(void)
{
MAP_Timer_A_clearCaptureCompareInterrupt(TIMER_A1_BASE,
TIMER_A_CAPTURECOMPARE_REGISTER_0);
if(0==LoopCnt)
{
Timer_A_stopTimer(TIMER_A1_BASE);
//转弯代码--停止转弯
Timer_A_setCompareValue(TIMER_A0_BASE,
L_CAPTURECOMPARE_REGISTER,
0);
Timer_A_setCompareValue(TIMER_A0_BASE,
R_CAPTURECOMPARE_REGISTER,
0);
MAP_GPIO_toggleOutputOnPin(GPIO_PORT_P1, GPIO_PIN0);
TimingFlag=2;
}
else
{
LoopCnt--;
upConfig.timerPeriod=0xFFFF;
MAP_Timer_A_configureUpMode(TIMER_A1_BASE, &upConfig);
MAP_Timer_A_startCounter(TIMER_A1_BASE,TIMER_A_CONTINUOUS_MODE);
}
}
//红外传感模块代码
#include "ReflectSensor.h"
//void ReflectSensorInit(void)
//{
//}
uint8_t ReadReflectSensor(void)
{
uint8_t val=0;
//对管端口设置为输出方式
MAP_GPIO_setAsOutputPin(ReflectSensorPort,ReflectSensor_Pin0);
MAP_GPIO_setAsOutputPin(ReflectSensorPort,ReflectSensor_Pin1);
MAP_GPIO_setAsOutputPin(ReflectSensorPort,ReflectSensor_Pin2);
MAP_GPIO_setAsOutputPin(ReflectSensorPort,ReflectSensor_Pin3);
MAP_GPIO_setAsOutputPin(ReflectSensorPort,ReflectSensor_Pin4);
MAP_GPIO_setAsOutputPin(ReflectSensorPort,ReflectSensor_Pin5);
MAP_GPIO_setAsOutputPin(ReflectSensorPort,ReflectSensor_Pin6);
MAP_GPIO_setAsOutputPin(ReflectSensorPort,ReflectSensor_Pin7);
//对管LED设置为输出方式
MAP_GPIO_setAsOutputPin(ReflectSensorLED_Port,ReflectSensorLED_Pin);
//对管端口输出为高电平
MAP_GPIO_setOutputHighOnPin(ReflectSensorPort,ReflectSensor_Pin0);
MAP_GPIO_setOutputHighOnPin(ReflectSensorPort,ReflectSensor_Pin1);
MAP_GPIO_setOutputHighOnPin(ReflectSensorPort,ReflectSensor_Pin2);
MAP_GPIO_setOutputHighOnPin(ReflectSensorPort,ReflectSensor_Pin3);
MAP_GPIO_setOutputHighOnPin(ReflectSensorPort,ReflectSensor_Pin4);
MAP_GPIO_setOutputHighOnPin(ReflectSensorPort,ReflectSensor_Pin5);
MAP_GPIO_setOutputHighOnPin(ReflectSensorPort,ReflectSensor_Pin6);
MAP_GPIO_setOutputHighOnPin(ReflectSensorPort,ReflectSensor_Pin7);
//打开LED,对电容充电
MAP_GPIO_setOutputHighOnPin(ReflectSensorLED_Port,ReflectSensorLED_Pin);
//延迟10us
DelayUs(10);
//设置对管端口为输入方式
MAP_GPIO_setAsInputPin(ReflectSensorPort,ReflectSensor_Pin0);
MAP_GPIO_setAsInputPin(ReflectSensorPort,ReflectSensor_Pin1);
MAP_GPIO_setAsInputPin(ReflectSensorPort,ReflectSensor_Pin2);
MAP_GPIO_setAsInputPin(ReflectSensorPort,ReflectSensor_Pin3);
MAP_GPIO_setAsInputPin(ReflectSensorPort,ReflectSensor_Pin4);
MAP_GPIO_setAsInputPin(ReflectSensorPort,ReflectSensor_Pin5);
MAP_GPIO_setAsInputPin(ReflectSensorPort,ReflectSensor_Pin6);
MAP_GPIO_setAsInputPin(ReflectSensorPort,ReflectSensor_Pin7);
//延时1.7ms(建议在1~2之间,可用来调节对轨道识别灵敏度)
DelayUs(1700);
//读对管引脚
val|=(MAP_GPIO_getInputPinValue(ReflectSensorPort,ReflectSensor_Pin0)<<7);
val|=(MAP_GPIO_getInputPinValue(ReflectSensorPort,ReflectSensor_Pin1)<<6);
val|=(MAP_GPIO_getInputPinValue(ReflectSensorPort,ReflectSensor_Pin2)<<5);
val|=(MAP_GPIO_getInputPinValue(ReflectSensorPort,ReflectSensor_Pin3)<<4);
val|=(MAP_GPIO_getInputPinValue(ReflectSensorPort,ReflectSensor_Pin4)<<3);
val|=(MAP_GPIO_getInputPinValue(ReflectSensorPort,ReflectSensor_Pin5)<<2);
val|=(MAP_GPIO_getInputPinValue(ReflectSensorPort,ReflectSensor_Pin6)<<1);
val|=(MAP_GPIO_getInputPinValue(ReflectSensorPort,ReflectSensor_Pin7)<<0);
//关闭管LED
MAP_GPIO_setOutputLowOnPin(ReflectSensorLED_Port,ReflectSensorLED_Pin);
//返回测量值
return val;
}
//避障模块代码
#include "CrashSensor.h"
#include "Motor.h"
#include "Delay.h"
volatile uint8_t DetectionVal=0;
uint8_t AvoidFlag=0;
extern uint8_t TimingFlag;
const uint8_t SensorAngleTab[]={Sensor1Angle,Sensor2Angle,Sensor3Angle,\
Sensor4Angle,Sensor5Angle,Sensor6Angle};
void CrashSensorInit(void)
{
MAP_GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P6,
GPIO_PIN0|GPIO_PIN1|\
GPIO_PIN2|GPIO_PIN3|\
GPIO_PIN4|GPIO_PIN5);
MAP_GPIO_interruptEdgeSelect(GPIO_PORT_P6,
GPIO_PIN0|GPIO_PIN1|\
GPIO_PIN2|GPIO_PIN3|\
GPIO_PIN4|GPIO_PIN5,
GPIO_HIGH_TO_LOW_TRANSITION);
MAP_Interrupt_setPriority(INT_PORT6,0xE8);
MAP_GPIO_clearInterruptFlag(GPIO_PORT_P6,
GPIO_PIN0|GPIO_PIN1|\
GPIO_PIN2|GPIO_PIN3|\
GPIO_PIN4|GPIO_PIN5);
MAP_GPIO_enableInterrupt(GPIO_PORT_P6,
GPIO_PIN0|GPIO_PIN1|\
GPIO_PIN2|GPIO_PIN3|\
GPIO_PIN4|GPIO_PIN5);
MAP_Interrupt_enableInterrupt(INT_PORT6);
}
void PORT6_IRQHandler(void)
{
uint32_t status,cnt,i;
status = MAP_GPIO_getEnabledInterruptStatus(GPIO_PORT_P6);
MAP_GPIO_clearInterruptFlag(GPIO_PORT_P6, status);
if(status & GPIO_PIN0)
{
cnt=0;
for(i=0;iSampleThreshold)
return ;
DetectionVal|=0x01;
}
if(status & GPIO_PIN1)
{
cnt=0;
for(i=0;iSampleThreshold)
return ;
DetectionVal|=0x02;
}
if(status & GPIO_PIN2)
{
cnt=0;
for(i=0;iSampleThreshold)
return ;
DetectionVal|=0x04;
}
if(status & GPIO_PIN3)
{
cnt=0;
for(i=0;iSampleThreshold)
return ;
DetectionVal|=0x08;
}
if(status & GPIO_PIN4)
{
cnt=0;
for(i=0;iSampleThreshold)
return ;
DetectionVal|=0x10;
}
if(status & GPIO_PIN5)
{
cnt=0;
for(i=0;iSampleThreshold)
return ;
DetectionVal|=0x20;
}
}
void CrashAvoid(void)
{
uint8_t i;
uint32_t sum=0,cnt=0;
//停止状态
if(0==AvoidFlag)
{
AvoidFlag=1;
}
//直线行驶状态
else if(1==AvoidFlag)
{
L_Wheel_Positive();
R_Wheel_Positive();
Timer_A_setCompareValue(TIMER_A0_BASE,
TIMER_A_CAPTURECOMPARE_REGISTER_1,
500);
Timer_A_setCompareValue(TIMER_A0_BASE,
TIMER_A_CAPTURECOMPARE_REGISTER_3,
500);
AvoidFlag=2;
}
//障碍检测状态
else if(2==AvoidFlag)
{
if(DetectionVal!=0)
{
DelayUs(1000);
//停止前进
Timer_A_setCompareValue(TIMER_A0_BASE,
TIMER_A_CAPTURECOMPARE_REGISTER_3,
0);
Timer_A_setCompareValue(TIMER_A0_BASE,
TIMER_A_CAPTURECOMPARE_REGISTER_1,
0);
for(i=0;i>i)&0x01)
{
sum+=SensorAngleTab[i];
cnt++;
}
}
sum=sum/cnt;
//后退
while(!Turn(3,50,1000));
if(sum>90)
{
sum=180-sum;
//退后几步再右拐
//补充退后代码
Turn(1,sum,2000);
}
else if(sum<90)
{
//退后几步再左拐
//补充退后代码
Turn(2,sum,2000);
}
else
{
//退后几步再左拐
//补充退后代码
Turn(2,180,2000);
}
AvoidFlag=3;
}
}
//避障完成
else if(3==AvoidFlag)
{
if(2==TimingFlag)
{
TimingFlag=0;
AvoidFlag=0;
DetectionVal=0;
}
}
}
//红外循迹模块代码
#include "Track.h"
float L_Wheel_Speed=L_Wheel_Init_Speed,R_Wheel_Speed=R_Wheel_Init_Speed;
float LEDError=0,LEDLastError=0,LEDPreError=0,PIDVal=0,ErrorSum=0;
float Kp=150,Ki=0.01,Kd=30;
uint16_t TrackingControlFlag=0,TrackingControlCnt=0;
void TrackLine(void)
{
uint8_t flag=0;
float temp;
flag=ReadReflectSensor();
if((0x18==flag)||(0x08==flag)||(0x10==flag))
{
LEDError=0;
}
else if((0xFF==flag)||(0xFE==flag)||(0x7F==flag))
{
if(TrackingControlFlag0)&&(0==flag))
LEDError=6;
else if((LEDError<0)&&(0==flag))
LEDError=-6;//ÍÑÀëºÚ½º´ø
}
//PID调整量计算
// PIDVal=Kp*(LEDError-LEDLastError)+Kd*(LEDError-2*LEDLastError+LEDPreError);
// PIDVal=Kp*(LEDError-LEDLastError)+Ki*LEDError;
PIDVal=Kp*(LEDError-LEDLastError);
// PIDVal=Kp*(LEDError-LEDLastError)+Ki*LEDError+Kd*(LEDError-2*LEDLastError+LEDPreError);
LEDPreError=LEDLastError;
LEDLastError=LEDError;
//速度调整
//速度限幅
temp=R_Wheel_Speed;
temp+=PIDVal;
if(tempPIDHighThreshold)
R_Wheel_Speed=PIDHighThreshold;
else
R_Wheel_Speed=temp;
temp=L_Wheel_Speed;
temp-=PIDVal;
if(tempPIDHighThreshold)
L_Wheel_Speed=PIDHighThreshold;
else
L_Wheel_Speed=temp;
USARTSendStr("flag:",5);
USARTSendNum(flag);
USARTSendStr(",",1);
USARTSendStr("L_Speed:",8);
USARTSendNum(L_Wheel_Speed);
USARTSendStr(",",1);
USARTSendStr("R_Speed:",8);
USARTSendNum(R_Wheel_Speed);
USARTSendStr(", ",4);
L_Wheel_Positive();
R_Wheel_Positive();
Timer_A_setCompareValue(TIMER_A0_BASE,
L_CAPTURECOMPARE_REGISTER,
L_Wheel_Speed);
Timer_A_setCompareValue(TIMER_A0_BASE,
R_CAPTURECOMPARE_REGISTER,
R_Wheel_Speed);
}
四、测试调试
项目一:小车电机驱动装置是否正常,能否进行前进、倒退和左右转弯。
1方法:我们小组将小车组装完成后,导入相应的代码,并多次调整电机速度观察是否出现相应运动。
2调试结果:当我们将左右电机的运转速度调成一致,小车前进时向右偏。左右转时并不能转动设定的角度。调整后能顺利直行,左右转弯。
3结果分析与结论:我们小组认为两个电机转动时速有些许差异,导致直行与转弯不顺。于是,我们将小车右轮速度调高,多次调整后,小车能准确进行直行运动,转弯也没有问题。
项目二:小车是否具有基本循迹功能,即黑线进行直行,钝角转弯等。
1方法:我们小组首先将循迹功能导入小车,通过人为按键去扫描小车压在黑线上的数据,在此基础上对程序进行提高。同时驱动小车,观察小车能否按预期进行运动,且保持稳定。
2测试仪器设备、环境:小车,XCOM软件,附有黑线的直道、S型弯道和钝角弯道。
3调试结果:当我们把代码导入小车就马上将小车压在黑线上,并打开XCOM软件进行数据采集,采集结果与预期相对应,小车扫描模块没有问题。于是,我们将小车分别压在S弯道和钝角弯道上,根据代码,小车能顺利行走,但是有些许的摇摆,不能很准确地行走。经过完善后能完成基本循迹功能。
4结果分析与结论:小车扫描模块没有任何问题,小车行走时有些许不稳定,我们判断可能是代码中的对小车偏离轨道的分析调整有些过头,于是我们适当地增加调整了小车对于偏离的调整数据,经过多次的测试,小车终于能够稳定地在黑线上行走。
项目三:小车是否具有特殊赛道循迹功能,即直角、锐角和十字路口。
1方法:我们用小车和XCOM软件多次在直角、锐角和十字路口上进行数据扫描,并根据结果对使小车进行相应动作,同时需要多次调整转动角度及电机速度。
2测试仪器设备、环境:小车,XCOM软件,附有黑线的直角、锐角和十字路口。
3调试结果:小车对于直角赛道的判断是较为容易的,只需一边的扫描器压在黑线,另一边压在地板上,仅有几次小车无法顺利判断。而锐角赛道和直角赛道就困难许多。对于锐角赛道,判定条件较多,且扫描速率对小车能否顺利通过有极大的影响,同时小车的转动角度也是一个难点。测试结果不是很理想,我们花费了许多时间去进行调试。而十字路口,起初我们的代码能很顺利的进行判断,并做出相应行动。但是在多次测试后,小车有挺大几率会判断成锐角转弯。
4结果分析与结论:对于直角赛道,我们小组认为是小车对于直角赛道的判断条件少了,通过小车多次扫描,我们认为不能小车一半压在一边一半压在另一边就可,在小车行走过程中,小车些许的晃动会使小车稍微偏离赛道,此时一边就只有3或5个扫描点,于是,我们增加了小车判断条件,更改后,小车能很顺利地通过直角赛道。对于锐角赛道,在多次的测试及数据分析后,我们总结了多个判断条件,更改代码后小车能顺利判断锐角弯道,在此基础上我们对转弯角度进行多次调试,使小车能够很好的转过锐角赛道。对于十字路口,我们采取小车遇十字路口直行方案。导入代码后,起初几次的判断结果都很好,能够顺利通过,但是多次测试后,对于十字路口的判别经常发生错误,很容易误判为锐角,通过串口记录小车行驶数据分析,由于传感器采样速率较快,在到达黑线边界时,这个值其实是模糊的,类似于锐角的值。所以,当检测到锐角时,让车前进一段时间再采样,越过黑线边界,多次调试后,我们找到了较为精确的前进时间数值使小车能顺利通过。
项目四:小车是否能稳定地重复在轨道上跑圈。
1方法:我们将初步成型的代码导入小车中,让小车不加干预的在赛道上跑圈。根据结果对小车具体参数进行更改。
2测试仪器设备、环境:小车,XCOM软件,附有黑线的普通、特殊赛道。
3调试结果:小车第一圈刚开始能够顺利地通过十字路口,在直角赛道有一些偏移,最后锐角也能顺利通过。但是第二圈时,小车通过第一个十字路口后,在第二个十字路口判断成了锐角进行了左转。在后面多次的测试中,小车还出现了无法判断直角赛道的情况,直接直行,锐角也有相应的状况发生,小车很难无失误多次跑圈。
4结果分析与结论:起初我们对于小车为什么出现这种情况真的是很头疼,不知道怎么去更改。但是在小组成员多天的测试后,我们认为轨道旁同学的影子,小车扫描的延迟,直行的稳定性,还有判定条件都是很粗糙的会对小车产生很大的影响。需要进一步去完善。在小车测试时,我们会让旁边的同学稍稍让开点,特别是在锐角这个地方。关于小车扫描的时间,我们发现时间长短会对小车判断锐角和十字路口产生不同的影响,所以,我们进行了多次的测试,终于找到了一个较为合适的时间延迟。使小车能顺利通过锐角,对于十字路口,能采集的确定数据进行直行。同时,对于特殊赛道,我们也完善了判定条件,使在弯道能做出正确的动作。更改代码后,小车能够破除一圈魔咒,进行多次跑圈,为了更完善,我们在接下来的几天再次进行了更多次的数据分析,使小车能够更加稳定。为了能在顺利跑圈的同时增加速度,我们也是不断去磨合了这个速度,使之达到更好的状态。最终小车能在较高速下跑完六七圈的成绩。