草履车

  草履虫只能进行简单且本能的生命活动,没有智慧。草履车也是如此,它只能进行极其简单的机械运动。作为个人第一个项目,用于练手。

开源地址:https://github.com/wearexc/startup/tree/main/草履车

前言

这车按规划,应该是上学期完成。当初的目标是个遥控小车,看看能不能装上能装的外设。

白驹过隙。

还是在这个学期,差不多期中的时候才正式开始。好在平时闲的时候进行规划,做什么样的车,心中有个大致的规划。有实验室位置的时候,则可以快马加鞭。实验室效率太高了,一天顶我一周。

虽然车不是啥稀奇的玩意,网上代码的项目多如牛毛,但我还是决定自己重头写一个。自己布置线路,分配资源,写代码。

开始挺顺利,组装车,调试。遥控车辆,也不费啥劲。

然后,然后就开始遇到了奇奇怪怪的问题。有次把程序跑飞了,即使是看寄存器,也定位不到错误代码,然后又莫名好了。可能因为我今天左脚迈进电梯吧。

然后就开始碰壁,前期想当然的规划,实行后才发现有很多bug以及不切实际。很多时候需要推倒重来,这部分主要是数据处理。先前的数据格式用着用着发现有更好的,优化一下吧。然后就大改啊大改。如果只是蓝牙遥控小车或者避障,实在乏味。所以按照自己的设想,有至少6种模式可以进行切换。每种模式尽量设计的全面些,最后整理好代码,记录好项目,归档,最后彻底摆烂。

这些要在一个月之内完成,必须。

☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆分割线☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆

11月23日:

  总算完成了个大概,完成后发现和预想差的还是蛮多的。项目越到尾声,反而越糟心。它并没有给我带来什么成就感,很多时候我都自我怀疑,我怎么花了这么久的时间,做了一坨屎一样的玩意。不过作为练手来说,还算彳亍吧。给代码上了注释,传了github,拍照并写博客记录一下,就该让它吃灰了。后续也不会再整了,最多调整一下参数。

成品

太乱而且太随意了。

概述

草履车使用STM32,NRF24L01蓝牙模块,电机驱动,舵机。电机驱动电源由两块18650串联,芯片则是用STlink配合PDD9.9包邮小充电宝供电。

主机端也是使用了STM32,还有摇杆,四个按钮以及一块OLED屏。

原计划用W25Q64丢到小车,用它来记录操作,后续改用芯片内部闪存。

运行情况参考状态机:

模式

数据结构

  这个点踩坑最多,数据原来计划是一个8位数。前四位就是摇杆状态等等,后四位是时间。即000(状态)0(标志位) 0000(时间)。读取数据的标志位,如果置1了,则切换到下一个状态。后来发现了丢包问题,想要每次传输的数据对方都成功接收,是不可能的。标志位不可取。

  然后为000(摇杆)000(标志位)00(速度),时间存到另外的数组。三个数,三个标志位,表示不同的模式。后来发现不够用,干脆改为真值表吧。

摇杆真值表

状态 输入
000 无法读取摇杆,error
001 y+
110 y-
010 x+,y+
101 x-,y-
011 x+,y-
100 x-,y+
111 摇杆其它状态

模式真值表

状态 输入
000 实时控制
001 观察模式
010 跟随模式
011 避障模式
100 睡眠模式
101 启动记录
110 启动回溯
111 记录操作

速度真值表

状态 输入
00 无速度
01 1档
10 2档
11 3档

  速度的改变是依靠按下的摇杆。但它的中断一言难尽。我原计划是 0 , 40 , 70 , 100。每按一次,切换一次档位,但摇杆的中断会迅速触发两次,在中断的代码上我无法规避。除非在中断里进行计数,根据计数来切换档位。感觉太麻烦了,所以实际情况是舍弃了0。以40为基础状态,每次按+15(因为它会触发两次,则是+30)。

  最后让我纠结很久的是记录操作的时候,如何记录操作和时间,记录和存储的格式是怎样的。这个点卡了我很久,也让我多次推倒重来。

初始化

  这个原计划是小车对蓝牙模块和W25Q64进行写入读取,确保设备正常。然后发送数据给主机,主机收到后进行判断,数据没问题,则显示初始化成功,并向小车回传数据。

  废弃原因:太麻烦了(懒得写两蓝牙的收发切换互传),并且的确没啥设备需要检测。

实时遥控

  读取摇杆状态并通过蓝牙发送,小车只需要傻傻接收数据,丢给驱动函数执行就行了。值得一提的是我给它设置了警告位,当它有一段时间没有收到蓝牙信号,则自动停下,并发出警告。只有复位后才能继续运行。算是防跑丢吧。

观察模式

  舵机配合超声波,对各方向进行探测,数据传给主机。每n次探测,小车将切换为接收模式,看看有没有收到数据,如果收到,表示主机退出探测,小车也应该退出。退出后将超声波传感器复位。

  其实一开始打算整个雷达图,然后实在实在是懒得画懒得调试。最后主机的界面感觉还不错。

跟随模式

  根据超声波传感器的数据,加个if判断。距离小了就像前,距离大了就退后。值得注意的是需要设置一个阈值,否则会因为误差的原因,即使距离达到要求,小车也会鬼畜前后的运动。(目前我设的阈值好像低了。)

避障模式

  赶鸭子上架,参数还需调试,有点摆烂,不想调了。而且因为超声波传感器的位置比较高,它的避障效果并不好,除非是墙。这没啥办法,传感器位置低了,没法进行观察模式(因为会扫到两侧轮子)。

睡眠模式

  按照我的想法,它应该是关闭所有时钟,内核等等,只有蓝牙模块工作,收到信号后进行唤醒。

  后来了解到STM32的低功耗模式,最后发现,只有睡眠合适比较合适。但睡前还是需要手动关闭时钟。其它模式“睡的太死”了。这个模式没有深究。仅是调用了一个睡眠函数。

记录操作

  这玩意我整了老久了。

设想情境:

启动该模式,将记录用户操作,数据存储置W25Q64。记录完毕后,将回溯用户操作,比如前进的小车,回溯后将倒行。

数据格式为8位,高3位存储摇杆的状态,低5位存储记录时间。

方案1:

设想车辆控制端同步时序,在同一个200ms时间片内,控制发送命令数据,车辆存储,车辆状态200ms更新一次。因为无法同步时间片,而且担心传输丢包,卒。

方案2:

发送端同时发送命令和时间,车辆只需要处理,记录即可,命令更新时,车辆状态也随之更新。因为无法同时处理车辆状态,接收数据而卒。个人想法是边接收,边处理并放置缓存区。但因为某种原因,无法完成。本来不是啥复杂的玩意,但是遇到了奇怪的Bug,定义的全局变量会因为蓝牙模块的一个操作函数改变。摸不着头脑。

方案3:

发送端发送已处理的数据,车辆仅做傻瓜式接收,这总行了吧。原先不这样处理,是担心操作端的中断会打乱时序。并且操作端200ms才发送一次数据,这段时间足够车辆接收并处理数据了。

好吧好吧,我还是太理想了。

我现在两难了。

车辆接收数据,不能使用DMA。因为DMA太快了。会导致很多重复的数据被一直搬运,并且DMA只负责搬运,后续的处理会很麻烦。那咱退一步,每次接收数据后触发一次搬运不就行了?那不好意思,硬件触发DMA搬运,虽然每次可以很迅速的搬运,但是搬运过去的地址需要增加。而DMA似乎只能在初始化的时候配置次数,由此进行连续搬运自增地址。

这导致了如果想使用这种方法,则需要不断用软件更新DMA地址。这会导致数据无法迅速搬运,进而丢包。

那么就是有DMA却无法用吗?。。。

丢包率达到10%~20%,太夸张了。。。。。。

方案4:

下调发送端发送时间,改为500ms发送一次,测试丢包率。不行啊,即使调到1s发送一次,丢包仍不可避免。应该思考如何处理数据并进行优化了。似乎又要推倒重来。

当时:

大概是遇到了踩内存,全局变量只要涉及到蓝牙,就会被改。所以我的处理方法是定义一个局部变量,并使用while循环来事变量计次。记录完成后将使用DMA转运到另一个数组。作为缓存器数组还算够用,倒也用不到W25Q64,先实现功能,再完善吧。

目前是小车接收数据存至数组,操作记录完毕后使用DMA转运到另一个数组存起来。数据格式为原生数据,回溯时将数据的摇杆位取反。并使用定时器,每200ms将读取一次数据进行更新。

回溯勉强算是实现了。遇到踩内存BUG太难受了,所以代码很烂,用代码弥补BUG。除非能解决BUG,不然一优化就无法运行。

最后:

主机每200ms发送一次数据,小车接收后存放至临时数据,结束记录后,将用DMA转运至FLASH数组,并记录数据个数,放到FLASH数组首位。FLASH数组将数据记录到内部闪存。每次上电初始化,将读取FLASH至FLASH数组。播放和回溯操作将根据FLASH数组的数据进行。

启动记录

通过TIM2,每200ms读取一次FLASH数组数据,丢到驱动函数。然后自增,下一个200ms就能读取数组的下一个数据。至于读取次数,参考FLASH数组第一个数据,它记录了数据个数。

回溯模式

也是通过TIM2,操作过程和启动记录差不多。只是执行顺序是从后往前。

总结

大体上还是完成了,花了大致两周。有空就钻实验室捣鼓,花的时间还是蛮多的。增加了不少调试经验,吸取的教训就是数据格式这类了。项目没规划好,导致后续需要返工,为了更合理的代码。

  草履车就像这鸟,当初出现踩内存的BUG,但还是硬着头皮写了下去。倒也能跑。后续发现是蓝牙接受数据的代码,数组越界了。后续修了BUG,大改了很多地方,一度觉得代码烂就烂吧,BUG能跑起来就行了,非常想摆烂。只是后续进度比我想象的快了很多。在写出了整体框架,规划并确定好了资源。后续也只需要随便往里面填些简单的代码。随便测试一下,然后觉得也就这样吧。感觉自己花了这么长时间,就整了个这么个没啥用的玩意。多少有点失望。

  作为第一个练手的项目,选的还是比较经典的小车,尽管网上有很多实例可以参考,但还是自己一点点规划资源,写了出来。整个过程,第一个比较满意的就是数据规划了。一个8位,规划好了摇杆,模式和速度,个人觉得还是蛮合理。摇杆的真值表,相反的状况还特意取反,就是为了回溯做准备。第二个满意的就是观察模式。主机的界面还行。

  尽管能记录操作并且执行,但我总觉得会有些BUG。我并没有对时间,数组这类进行严格的规划,当记录的操作过久或许会出现问题。自己也没有给它规划好上限。

它还有很多可以优化的地方,但就先这样吧。