迄今为止用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的四叶飞行器控制板,感觉也有能做的可能。不过那货是直流转交流……
最终展示下主文件的内容,其他的库看看方法名应该就明白。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
#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; } } |