大家好,我给大家分享我做的Lubancar自平衡小车,Lubancar只用到了野火鲁班猫(Lubancat ZW)作为主控,纯python语言开发的自平衡小车。Lubancar运行的系统是Debian 10,实现的功能主要有读取mpu6050,卡尔曼滤波,控制直流减速电机转向、转速、读取霍尔编码器、OLED显示、输入中断、PID控制、超声波测距、python多线程、tcp网络通信、串口通信、无线手柄控制、蓝牙通信、Flask+opencv+rknn进行远程监控和图像识别、网页控制等。
由于作者平时白天要上班,晚上回来要肝毕设、写论文,还要“指导”同学的毕设,而Lubancar这个项目是从2月初才开始搞的,所以时间十分紧张,目前PID参数调得一般,只能使小车基本保持直立或者受到一定干扰也能保持直立,一些通信手段只实现大致框架,没有进行联调。
(补充说明: 1、由于个人能力极其有限,难免有错误的地方,还请大家指正。
2、作者写本帖的时候还在肝毕设初稿,初稿还没写完,后面还需要按要求修改初稿,所以时间比较紧迫,这个Lubancar项目只作大致说明,详细开源说明和资料发布,待答辩完后再整理发布,可能会是7、8月份。)
一、项目展示
Lubancar:
野火比赛项目——Lubancar自平衡小车_哔哩哔哩_bilibili
解析手柄展示:
野火比赛项目——Lubancar自平衡小车的手柄解析部分展示_哔哩哔哩_bilibili
PCB:
二、硬件设计
原理图:
Lubancar原理图.zip
(43.01 KB, 下载次数: 20)
以上是根据V1版实际调试后调整的V2版原理图。
(1)电源部分:
Lubancar需要12V电源供电,原因在于电机采用的是12V直流减速电机,对比多种电池后选择12V锂电池进行供电最为合适,支持板载供电、充电,上图中DC电源座子为锂电池充电接口,5.08座子是锂电池供电接口。
(2)稳压部分
采用的稳压降压芯片为LM2596,LM2596可以驱动最大3A的负载电流,并且具有良好的输出电压线性和负载调节特性。由于鲁班猫ZW启动电流比较大,最大启动电流达到0.75A左右,正常工作电流为0.18A左右,加上其他外设,整体所需要的电流比较大,设计3A及以上最大输出较为合适。 (3)电机及驱动部分 采用的电机驱动模块为TB6612FNG,其是一种直流电机驱动器,支持驱动两个电机。可以控制PWM口电压从而控制电机转速、控制AIN、BIN端口逻辑电平从而控制电机转速。 电机采用的是JBG37-520直流减速电机,带霍尔编码器,提供较低的转速和较大的力矩,M+ M-是电机电源接口,VCC是霍尔编码器供电接口,OR选择5V\3.3V是因为编码器供电电压影响编码器A、B相输出,接5V则A、B相逻辑电压为5V、0V,接3.3V则A、B相逻辑电压为3.3V、0V,鲁班猫的IO容压可能达不到5V,这里需要跳帽短接到3.3V。 (3)其余模块部分 其余模块部分选择鲁班猫合适引脚对接即可,其余一些设计选择合适IO设计即可。 (4)预留部分 预留2个舵机控制口,后续可以控制云台,预留spi接口可以接spi屏幕或者其他spi模块。
三、软件部分 (1)读取MPU6050 作者一开始参考野火快速使用手册用python读取和写入MPU6050相关寄存器,成功读取三轴加速度和三轴陀螺仪,通过这种方式完全可以对MPU6050进行读取,但不推荐使用这种方法,因为python有第三方库可以十分方便地进行读取。 python之所以简单,一是本身语法相对简单,二是有非常多的第三方库协助开发。
可以在README.rst文件查看相关说明
可以从使用例子看到,加载模块后,使用board.I2C()方法来创建I2C接口的实例对象,然后通过将该对象作为参数传递给adafruit_mpu6050.MPU6050()方法来初始化MPU6050对象,最后访问MPU6050的成员输出相应数据。
Lubancar使用I2C_3读取MPU6050,创建I2C接口的实例对象方式为:
i2c = busio.I2C(board.I2C3_SCL, board.I2C3_SDA)
此处使用了 board和busio模块进行创建,与项目例子稍有差异。
更多python I2c通信说明查看野火教程:https://doc.embedfire.com/linux/rk356x/Python/zh/latest/circuit/i2c_demo.html
(2)卡尔曼滤波
处理MPU6050原始数据,进行姿态解算有几种方式,分别是使用MPU6050的DMP库获取四元数、卡尔曼滤波、互补滤波。DMP库是C库,python下不能直接使用,所以不考虑DMP库,卡尔曼滤波相比互补滤波更加精确,所以采用卡尔曼滤波进行姿态解算,但是卡尔曼滤波相对互补滤波会复杂很多。
卡尔曼滤波概念:卡尔曼滤波(Kalman filtering)一种利用线性系统状态方程,通过系统输入输出观测数据,对系统状态进行最优估计的算法。由于观测数据中包括系统中的噪声和干扰的影响,所以最优估计也可看作是滤波过程。
卡尔曼滤波具体原理本文不作说明,相关文章可以参考:https://www.zhihu.com/search?type=content&q=%E5%8D%A1%E5%B0%94%E6%9B%BC%E6%BB%A4%E6%B3%A2
卡尔曼滤波编程思路笔者参考的是:https://www.bilibili.com/video/BV1sL411F7fu/ ,原视频作者使用的开发平台为arduino,作者参考编程思路和相关代码进行转换,调整。
程序代码中卡尔曼滤波部分有较多注释,结合视频理解起来并不困难,主要是先通过def mpu6050_init():初始化相关变量, def mpu6050_Kalman(): 进行卡尔曼滤波,最终得到最优估计的翻滚角(roll)和俯仰角(Pitch)。
因为,单靠mpu6050不能准确获取偏航角(Yaw),所以只获取了翻滚角(roll)和俯仰角(Pitch),而在自平衡小车系统中只需要用到 k_roll,所以用卡尔曼滤波进行姿态解算完全可行。
不理解的请结合b站视频和一些文档资料进行理解,时间原因不作过多说明。
(3)控制电机转向
控制电机转向执行控制TB6612FNG的AIN、BIN端口逻辑电平即可。由于Lubancat不能使用树莓派的Rpi.GPIO和WiringPi的python库,但野火教程足足提供了3种方式来控制GPIO,而且都十分简单!不存在脱离树莓派的库,就开发不了的情况,只要稍微学习下就能熟练使用。
野火教程链接:https://doc.embedfire.com/linux/rk356x/Python/zh/latest/circuit/digital_io.html#libgpiod
Lubancar程序代码中控制GPIO使用了以上3种方式控制GPIO,证明这三种方式都是可行的,其中控制电机IO部分使用python3-libgpiod。
根据教程文档介绍创建对象chipx,设置使用的引脚,然后通过访问request控制GPIO电平即可。
(4)控制电机转速
控制转速需要控制TB6612FNG的PWM引脚的电平,野火教程文档里面有详细使用PWM的说明和例程,如下:
https://doc.embedfire.com/linux/rk356x/Python/zh/latest/circuit/pwm_output.html
Lubancar程序使用python-periphery库控制PWM。
这部分是初始化PWM,并将PWM占空比为设置为0%,即PWMx.duty_cycle = 1.0,电机默认不转。
这部分代码是根据PID算法输出控制PWM的占空比实现调速,该部分也包括根据PID算法输出确定AIN、BIN端口逻辑电平,使用电机转向控制。
(4)读取霍尔编码器
霍尔编码器每个周期内会产生两个相位差为90度的脉冲,通过读取两个通道的输入捕获值,可以确定当前所在的位置,从而计算出电机角度或位移信息。
在stm32中读取霍尔编码器使用定时器的输入捕获功能,但是linux的定时器并没有这个输入捕获功能,(PWM可能有输入捕获功能,没有测试),所以只能考虑中断或者高速扫描的方式。
采用中断的方式读取相对来说肯定比较靠谱,但是python是应用层的,如果没有驱动层支持是不能实现中断,目前Lubancar中IO中断并没有驱动层支持,但野火教程驱动篇有输入中断的教程,可以看教程自行实现。
作者看过一些能实现“中断”的python第三方库的源码,其原理都是轮询的方式,不是真正的中断!这种假中断的方式是不能读取霍尔编码器输出的高速的电平变化,作者对linux驱动层有一定了解,目前板卡中是开启了内核自带的按键驱动的,但没有相关设备树配置,我们可以添加设备树插件来使用内核自带的中断进行测试。
作者实际测试,用内核自带的这个输入中断驱动读取霍尔编码器发现内存占用率极高,只读取一个电机的霍尔编码器,只是用手转动车轮,cpu占用率高达55%,所以走捷径用内核自带的中断来读取霍尔编码器并不可行。这种高频中断消耗很大,可能需要在驱动层实现,编写特定驱动程序进行处理才行。驱动层开启定时器定时采集、定时上报数据等。作者是很想完成这部分驱动代码编写,但确实没有时间。。。。
既然中断的方式不可行,那只能用高速扫描了,作者测试过必须定时周期要低于0.0003s才能正确读取霍尔编码器,但这样cpu占用率也很高,那怎么办呢?其实不难想到在短时间内加速度是基本不变的,转速变化不大,可以通过短时间高速并间隔读取的方式进行读取,实测这种方式cpu压力可以降到原来的5%以内,但数据比较粗糙,但可以对测速结果进行滤波和离散数据连续化处理。。。
具体实现参考代码的class Measurement_speed(object):
(5)PID控制
PID用的是位置式PID,具体可以参考:https://blog.csdn.net/weixin_44270218/article/details/113665051
程序代码为def lubancar_Vertical_Ring_PD():和def lubancar_Incremental_PI():
PID调参可以参考:
https://www.bilibili.com/video/BV1zo4y1D7bx/?buvid=XY579685D8F6F2D557A979629F098AC5922EC&is_story_h5=false&mid=%2B%2BxVYS3aIkTtlKmt74t7gw%3D%3D&p=1&plat_id=114&share_from=ugc&share_medium=android&share_plat=android&share_session_id=ffb38139-a84f-4d13-a999-0e88986a6b5d&share_source=WEIXIN&share_tag=s_i×tamp=1681327150&unique_k=6CccQE3&up_id=286802729
https://www.bilibili.com/video/BV1oW4y1Y7nv/?buvid=XY579685D8F6F2D557A979629F098AC5922EC&is_story_h5=false&mid=%2B%2BxVYS3aIkTtlKmt74t7gw%3D%3D&p=1&plat_id=114&share_from=ugc&share_medium=android&share_plat=android&share_session_id=c411474f-1471-4b6b-b621-60da83790ca1&share_source=WEIXIN&share_tag=s_i×tamp=1681327165&unique_k=Imm0ev0&up_id=472407878
(6)oled显示
OLED显示部分同样使用Adafruit的库,项目地址:https://github.com/adafruit/Adafruit_CircuitPython_DisplayIO_SSD1306
默认不支持中文,作者添加了中文字库,支持显示中文。
(7)输入中断
使能内核自带的输入中断,编写设备树插件
python中使用evdev模块进行解析
需要作超时处理
(8)超声波测距
超声波模块有两个引脚:触发引脚和回波引脚。给触发引脚一个至少10us的脉冲信号,当模块接收到触发信号后,会立即向外发射一组超声波。如果这些超声波遇到物体并被反弹回来,模块会将其接收到,并通过回波引脚输出一个高电平信号。通过测量高电平信号的持续时间,就可以得到超声波从发射到返回所用的时间。
编程思路和上面描述一致。
(9)python多线程
使用threading模块实现多线程
(10)tcp网络通信
使用socket模块实现网络通信。
(11)串口通信
参考:https://doc.embedfire.com/linux/rk356x/Python/zh/latest/circuit/uart_demo.html
(12)无线手柄控制
板卡可能没有相应驱动,笔者的手柄识别为xbox 360,需自行适配驱动
内核开启现有驱动,编译内核、替换。
手柄解析代码查看test_js.py,运行前需要安装相应库,如果手柄的编码不同程序定义部分需稍作调整
(13)Flask+opencv+rknn
参考:https://doc.embedfire.com/linux/rk356x/Python/zh/latest/example/camera_demo.html
四、代码附件
代码.zip
(30.46 MB, 下载次数: 3158)
|