迄今为止用Arduino做出来的最复杂的项目。大学生估计会把这个当做毕业设计吧。不过电子技术远不止Arudino和模型车那么简单。之后就考虑直接用单片机了。比如电压显示那个项目。
车侧面
车正面
为了做车,至今为止所有的工具,材料。实际上到今天12月8日为止,3个盒子已经塞不下了,太多零散的东西了。
简单来说,模型车,核心功能是前进,后退,左右转,速度测量,距离探测。这些功能一上,基本上arduino所有IO接口都满了(当时我还不知道Ax可以当输出接口)。哦,距离探测到一定距离会自动停止,并开动蜂鸣器。简单来说就是防撞。
驱动芯片是L298,光耦。两路。车是4WD,左边两个马达一路,右边两个马达一路。
驱动电源是两节3.7V的锂电池。没用那种焊接好的。单独买电池盒和电池。焊接电池盒的接线和接口的时候差点把车给烧了……(好久没焊了)
电源买了好多东西。电池,电池盒,可以用来当充电器的电池盒,充电器等等。不过看到一节3.7V锂电池是手机电池板的2倍时就觉得自己的电池多伟大了……
电源线路是给3.7 x 2给L298供电,同时给Arduino供电。买了个3.5mm电源输出头,用尖嘴钳把另外半边剪了,焊接普通的线。当时不知道热缩管。知道的话,就不用弄那么多焊锡上去了。
线几乎占满了。
线太多了,有些凌乱。
控制方案,仍旧是BC04的蓝牙方案。之前写过上位机,这次相对轻松点。协议上给蓝牙通讯包加上同步头。实际上就是一段冗余数据,下位机在读到正式开始前不会处理。由于代码比较多,后来把下位机通讯这段放到单独的库中去了。
速度显示。实际用的是中断计数。由于测速码盘精度相对高,有20个,两个中断一起上感觉不行。所以后来改成左一秒右一秒。
这个速度测试也弄了一个库。不过由于追加中断需要全局函数地址,所以严格上不算完整封装。
普通的操作,前进,后退,只要调整M1,M2的输出(各两个)即可。如果要处理速度的话,需要控制ENA,ENB。所以实际上要控制6个PIN。为了上位机控制简单。我设置类似-128为后退,PWM为128。0为停止。+255,前进,PWM最大(全速前进)。这段也写成了库,方便操作。
转弯的话,实际上控制两边速度。比如左转,左边速度降低,右边全速。不过实际要调校。这次我手动控制,要一定值后才有左转显示。感觉是车转弯内距什么的问题(我会开车的哈)。
顺便说一句,如果车子开不动,或者满了,赶紧充电吧。我调试了一天不到。这货把6400mAh用完了。我还以为是程序哪里写错了。
超声波测速,很常见的技术。实际使用时注意需要设置超时时间,否则车就像假死一下不接受你的命令。理论上一定时间超声波没返回的话代表前面有好大距离,所以没问题。另外实际中发现车子的响应波有突然的尖刺的感觉。手边没示波器,暂时还是算了。有可能是电路其他地方引起的。
最终实际的项目包含库有1个主文件,4个库,自己做的最大的项目了。不过也基于此次指导Arudino中如何处理多文件。之前看到的MWC的四叶飞行器控制板,感觉也有能做的可能。不过那货是直流转交流……
最终展示下主文件的内容,其他的库看看方法名应该就明白。
#include <hcsr04.h> #include <LiquidCrystal_I2C.h> #include <Wire.h> #include "motor_ctrl.h" #include "serial_ctrl_server.h" #include "speed_tester.h" #define PROTOCOL_MODELCAR_TEST 1 #define PROTOCOL_MODELCAR_BASIC_OP 4 #define PROTOCOL_MODELCAR_SPEED 6 #define PROTOCOL_MODELCAR_FREE 7 #define MCO_ADVANCE 1 #define MCO_STOP 2 #define MCO_BACK 3 #define STATUS_STOP 100 #define STATUS_ADVANCE 101 #define STATUS_BACK 102 #define PIN_BUZZ 11 #define SPEED_TEST_INTERVAL 1000 const float speedK = 3.14 * 6.4 / 4; // pinM11, pinM12, pinM1Pwm, pinM21, pinM22, pinM2Pwm MotorCtrl motorCtrl(7, 8, 6, 9, 10, 5); SerialCtrlServer serialCtrlServer; HCSR04 hcsr04(4, 12); // pin trig, pin echo SpeedTester speedTester; LiquidCrystal_I2C lcd(0x27, 16, 2); int workingStatus; void setup() { motorCtrl.init(); serialCtrlServer.init(); hcsr04.init(); speedTester.init(); attachInterrupt(0, speedTestIncreaseRightCount, CHANGE); lcd.init(); lcd.backlight(); Serial.begin(115200); workingStatus = STATUS_STOP; pinMode(2, INPUT); pinMode(3, INPUT); } void loop() { checkDistance(); serialCtrlServer.read(); processCmd(); speedTest(); } void checkDistance() { float distance = hcsr04.getDistance(2320); if(distance >= 0 && distance < 40) { if(workingStatus == STATUS_ADVANCE) { stopMe(); buzzWarning(); } } } void speedTest() { switch(speedTester.check(SPEED_TEST_INTERVAL)) { case SpeedTester::TEST_SIDE_LEFT: detachInterrupt(0); attachInterrupt(1, speedTestIncreaseLeftCount, CHANGE); lcd.clear(); lcd.print("LSV: "); lcd.setCursor(0, 1); lcd.print(speedTester.getLeftSpeed() * speedK); lcd.print("m/s"); break; case SpeedTester::TEST_SIDE_RIGHT: attachInterrupt(0, speedTestIncreaseRightCount, CHANGE); detachInterrupt(1); lcd.clear(); lcd.print("RSV: "); lcd.setCursor(0, 1); lcd.print(speedTester.getRightSpeed() * speedK); lcd.print("m/s"); break; } } void speedTestIncreaseLeftCount() { speedTester.increaseLeftCount(); } void speedTestIncreaseRightCount() { speedTester.increaseRightCount(); } void processCmd() { if(serialCtrlServer.cmdAvailable()) { switch(serialCtrlServer.getProtocolType()) { case PROTOCOL_MODELCAR_TEST: serialCtrlServer.done(SerialCtrlServer::RETURN_CODE_OK); break; case PROTOCOL_MODELCAR_BASIC_OP: processModelCarBasicOpCmd(); serialCtrlServer.done(SerialCtrlServer::RETURN_CODE_OK); break; case PROTOCOL_MODELCAR_SPEED: processModelCarSpeedCmd(); serialCtrlServer.done(SerialCtrlServer::RETURN_CODE_OK); break; case PROTOCOL_MODELCAR_FREE: processModelCarFreeCmd(); serialCtrlServer.done(SerialCtrlServer::RETURN_CODE_OK); break; default: serialCtrlServer.done(SerialCtrlServer::RETURN_CODE_UNKNOWN_PROTOCOL); break; } } } void processModelCarFreeCmd() { int pwm1 = serialCtrlServer.getPayloadByte(0); int pwm2 = serialCtrlServer.getPayloadByte(1); switch(workingStatus) { case STATUS_ADVANCE: motorCtrl.run(pwm1, pwm2); break; case STATUS_BACK: motorCtrl.run(-pwm1, -pwm2); break; } } void processModelCarSpeedCmd() { int pwm = serialCtrlServer.getPayloadByte(0); switch(workingStatus) { case STATUS_ADVANCE: motorCtrl.run(pwm, pwm); break; case STATUS_BACK: motorCtrl.run(-pwm, -pwm); break; } } void processModelCarBasicOpCmd() { switch(serialCtrlServer.getPayloadByte(0)) { case MCO_ADVANCE: if(workingStatus != STATUS_ADVANCE) { motorCtrl.run(180, 180); workingStatus = STATUS_ADVANCE; } break; case MCO_STOP: stopMe(); break; case MCO_BACK: if(workingStatus != STATUS_BACK) { motorCtrl.run(-180, -180); workingStatus = STATUS_BACK; } break; } } void buzzWarning() { for(int i = 0; i < 3; i++) { delay(250); analogWrite(PIN_BUZZ, 128); delay(250); analogWrite(PIN_BUZZ, LOW); } } void stopMe() { if(workingStatus != STATUS_STOP) { motorCtrl.stop(); workingStatus = STATUS_STOP; } }