1760浏览
查看: 1760|回复: 6

[教程] Raspberry Pico尝鲜之GPS轨迹记录仪

[复制链接]
本帖最后由 沧海笑 于 2021-2-13 12:16 编辑

【项目故事】
Raspberry基金会在2021年新年伊始就推出了树莓派第一款单片机产品:Raspberry Pico。主控芯片是英国树莓派设计的RP2040,这是一款双核ARM Cortex M0+ 处理器,时钟高达 133 MHz,载有264KB 的 SRAM 和 2MB 的板载闪存。IO方面提供26 个多功能 GPIO 引脚。2×SPI, 2×I2C, 2×UART, 3×12-bit ADC以及16个可控制 PWM 通道。PICO的亮点还有8 路可编程 I/O (即:PIO) 状态机,用于自定义外设支持。美中不足是 PICO的USB接口仍然为USB 1.1而非Type-c,有网友戏称,这是树莓派有多少库存配件需要消化啊。
Raspberry-Pi-Pico-development-board.jpg
我在第一时间关注到了这款新板子,出于对树莓派长久的喜爱,虽然PICO从硬件配置上并不比esp8266/esp32占优,也没有wifi和ble,但我对树莓派社区非常有信心,也迫不及待地想尝鲜、做一个新玩具。
由于PICO推荐用micropython以及c++开发,我对前者的IDE以及一些入门库比较熟悉,所以我决定用micropython做一个gps轨迹记录仪。
这个小玩具的知识点不超过五个,上手难度属于较易上手,自认为难度★★(如果设定★★★★★为高难),是供树莓派以及嵌入式玩家尝鲜以及全面了解认识PICO的一个不错的项目。
装配图之三_ab.jpg
该项目从构思、搭建原型到代码编写、调试,装配外壳以及路测验证,大致经历了一周多时间,这也是一个完成度较好的小玩具。下面我们一起来做吧。

【硬件准备】

  
  
内容
数量(型号)
备注
1
Raspberry Pico
1

2
GSP模块
1
大疆悟1拆机模块
3
OLED屏幕
10.96寸)
IIC接口
4
电源管理模块
1
Type-c接口输入
5
5-3.3V转换器

6
锂聚合物电池
1800mah)

7
按钮1
1
控制存储/停止存储
8
按钮2
1
接电源模块Key-Gnd,单击供电,双击关机
硬件零件图.jpg

部分零件:
1800mAh锂电池
2、电源管理模块(充电、放电、电量显示、开关)
35.5~3.3v电压转换模块
4、微触按钮

【软件准备】
  
  
内容
型号及描述
备注
  
1
  
Raspberry Pico micropython固件
2021-1-25 v1.13

  
2
  
gps解析库
Micropygps

  
3
  
Ssd1306
官方

  
4
  
图形文件转换字节数组程序
第三方

  
5
  
Button陷阱库
第三方

  
6
  
Thonny ide
v3.3
适配pico必须要求在v3.3及以上

【制作过程】
(一)整体设计思路
使用Gps模块采集坐标以及时间数据,通过uart传送至pcio,Pico负责解析为经纬度以及时间变量,根据按钮的控制来进行gpx文件的存储。存储结束后,将记录文件导出,利用在线解析工具进行轨迹解析和展示。
人机对话方面:本装置设一块oled(128*64)用于监测gps数据以及存储情况。按钮1用于用户控制存储、停止流程。按钮2用于用户控制装置电源的开、关。

(二)硬件接线图
gps_rec1_bb_1_1b.jpg
图中:
1、  OLED 128*64
2、  大疆悟1GPS模块
3、  AMS1117-3V3,5.0~3.3V转换模块
4、  存储控制按钮
5、  电源管理模块
6、  锂电池(本项目实际使用800mAh)
7、  Raspberry Pico开发板
由于fritzing从元件库中提取,所以带有个别商家logo。

(三)关于大疆悟1GPS模块的接线分析
我从网上淘到一只大疆悟1的gps模块。因为在以往接触gps过程中,饱受搜星慢、用户体验差之苦。这块大疆悟1的gps模块用于无人机导航,天线口径足够大(直径46mm),根据其用途定位而言,该模块应该在定位、寻星、启动方面有不错的表现,事实证明这个猜测是正确的。模块到手后,看到了模块分为两部分:模块本体以及屏蔽底壳,屏蔽底壳对于无线信号系统的正常工作非常重要。大疆gps模块的线材也是屏蔽多股铜线,用料扎实。美中不足就是这款模块用于diy的资料非常有限,一是模块输出不清楚:一共六根输出线,且全部是黑色,无法从颜色和排序上获得参考。二是供电电压不了解,m8n的供电是3.3v没问题,但是我不知道这款模块是否自带电压转换电路。首先在背板上发现了gnd铜焊盘,而且两个焊盘是开窗的(没有用阻焊油封闭),所以很容易通过对线找到了两根(第2、6)是GND线,或者我们理解是和GND接通的。有了这个GND定义就好办了,至少不会在测试中烧模块。实际上我们只需要三根线(VCC,GND以及TX),经过反复测试,顺利找出了这三根线(如图)。后来在谷歌上也找到了两张国外玩家的拆解图,证明了我的分析。关于供电电压,我先用3.3V进行测试,工作正常。我就没有再尝试提高电压。后来在国外玩家的拆解图(不一定是悟1的)也看到了供电电压是3.3V~3.6V,进一步印证了我的分析。至此,这两个问题都得到了解决。由于是拆机件,没有带原配螺丝。我用M2的螺丝+螺母以及一个M3的螺母做垫片,基本上解决了屏蔽底壳的固定问题。我把vcc,gnd以及tx用排针引出,并且在接头处做了热缩处理,就可以开始后续的gps部分测试了。
gps模块背板接线图A.jpg gps模块正面图.jpg
(四)软件框图及代码
使用Thonny ide过程中,需要安装pico板解释器,类似的教程网上很多,不在本文讨论范围内。本文末尾附上了相关的配置文件。请注意:pico需要Thonny的版本在3.3.3及以上。
软件框图.png
  1. #基于raspberry pico的GPS轨迹记录仪
  2. # 主要功能:于gps数据轨迹记录,包括按钮控制以及oled屏幕显示
  3. # 2021-01~02
  4. #作者:沧海
  5. #micropyGPS解析库 https://github.com/inmcm/micropyGPS
  6. #ssd1306库 https://github.com/micropython/micropython/tree/master/drivers/display
  7. #button陷阱库https://github.com/researchnix
  8. from machine import Pin,I2C,PWM
  9. from machine import UART
  10. import time
  11. from ssd1306 import SSD1306_I2C
  12. from micropyGPS import MicropyGPS
  13. import Button
  14. import framebuf
  15. #定义oled显示部分
  16. sda=machine.Pin(4)
  17. scl=machine.Pin(5)
  18. i2c=machine.I2C(0,sda=sda, scl=scl, freq=400000)
  19. oled = SSD1306_I2C(128, 64, i2c)
  20. #定义uart串口部分
  21. com = UART(0,9600) #定义uart0
  22. #定义gps解析部分
  23. my_gps = MicropyGPS(8)#东八区的修正
  24. my_gps.local_offset
  25. #定义按钮以及按钮触发
  26. triggerActive = False
  27. but = Pin(3, Pin.IN, Pin.PULL_UP)
  28. lat=''
  29. lng=''
  30. s_time=''
  31. rtc=''
  32. #关于存储的控制标志字
  33. ifsave= False  #是否存储标志
  34. ifhead= False #文件头标志
  35. ifend = False #文件尾标志
  36. b_Trigger = False #触发标志字,默认未触发
  37. #按钮触发例程
  38. def toggleLed():
  39.     global ifsave,ifhead,ifend, b_Trigger
  40.     if b_Trigger == False:
  41.         print("button press")
  42.         b_Trigger = True #状态反转
  43.         ifsave= True
  44.         ifhead = True
  45.         ifend = False
  46.     else:
  47.         print("button unpress")
  48.         ifsave= False
  49.         ifhead = False
  50.         ifend = True
  51.         b_Trigger = False #状态反转
  52.       
  53. #获取并且解析gps数据例程
  54. def get_GPS_values():
  55.     global lat,lng,s_time,rtc
  56.     time.sleep_ms(200)
  57.     cc = com.readline()
  58.     cc1=cc.decode()
  59.     print(cc1)
  60.     for x in cc:
  61.         my_gps.update(chr(x))
  62.         
  63.     lat=str(my_gps.latitude[0] + (my_gps.latitude[1] / 60))
  64.     lng=str(my_gps.longitude[0] + (my_gps.longitude[1] / 60))
  65.     date = "20"+str(my_gps.date[2])+"-"+str(my_gps.date[1])+"-"+str(my_gps.date[0])+"T"
  66.     timestamp = my_gps.timestamp
  67.     rtc = str(int(timestamp[0]))+":"+str(int(timestamp[1]))+":"+str(int(timestamp[2]))
  68.     s_time= date+rtc+"Z"
  69.     print(lat,lng,s_time,rtc)
  70.     return lat,lng,s_time,rtc
  71. while 1:
  72.     time.sleep_ms(2000)
  73.     #绘制UI的表头
  74.     oled.contrast(50)
  75.     oled.fill(0)
  76.     oled.text("Time:", 42, 5)
  77.     oled.text("Lat:", 42, 23)
  78.     oled.text("lng:", 42, 41)
  79.     oled.text(" ", 2, 55)
  80.     oled.text(rtc, 80, 5)
  81.     oled.text(lat, 80, 23)
  82.     oled.text(lng, 80, 41)
  83.     #树莓派logo
  84.     buffer = bytearray(b'\x00\x00\x00\x00\x00\x00\xf0\xf8\x18XX\xd8\x980\xf0\xe0\xf0\x98\x98\xd8X\x18\x18\xf0\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xf3\xff\xee\xec|?o\xe7\xe7\xef?<|\xec\xde\xfb\xe3\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x7f\xf8??8\xf0\xf8\x18\x0f\x0f\x0e\x98\xf8x8\x1f\xfc\xf1?\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x03\x06\x0e\x0e\x1f?323\x1f\x0f\x0e\x06\x06\x03\x00\x00\x00\x00\x00\x00\x00')
  85.     fb = framebuf.FrameBuffer(buffer, 30, 32, framebuf.MVLSB)
  86.     oled.blit(fb, 5, 0)
  87.     oled.show()
  88.     get_GPS_values()
  89.    
  90.     if ifend == True  : # 是文件尾部
  91.         my_gps.start_logging('log.txt') #制定存储的文件,是以“追加”的方式打开,请参考库
  92.         endstring="</trkseg></trk> </gpx>"+"\n"
  93.         my_gps.write_log(endstring)  #写一个文件尾部标记
  94.         my_gps.stop_logging() #停止本次记录
  95.         ifsave = False
  96.         ifhead = False
  97.         ifend= False
  98.         oled.text("End", 2, 55) #屏幕显示文件结束
  99.         oled.show()
  100.         print("end====")
  101.     else :        
  102.         if ifhead == True : #是文件头
  103.             my_gps.start_logging('log.txt') #制定存储的文件,是以“追加”的方式打开,请参考库
  104.             headstring="""<?xml version='1.0' encoding='UTF-8'?><gpx version='1.0'> <name>Example gpx</name>
  105.     <trk><name>Example gpx</name><number>1</number><trkseg>"""+"\n"
  106.             logstring="<trkpt lat='"+lat+"' lon='"+lng+"'><time>"+s_time+"</time></trkpt>"+"\n"
  107.             my_gps.write_log(headstring+logstring)  #写一个文件头+一个内容串
  108.             ifhead = False #头只写一次就关闭
  109.             ifsave = True
  110.             ifend= False
  111.             my_gps.stop_logging() #停止本次记录
  112.             oled.text("New REC", 2, 55) #屏幕显示新记录
  113.             oled.show()
  114.             print("start====")
  115.         else:
  116.             if ifsave == True : #是正常写入串
  117.                 my_gps.start_logging('log.txt') #制定存储的文件,是以“追加”的方式打开,请参考库
  118.                
  119.                 oled.text("Saving", 2, 55) #屏幕显示文件正在记录中
  120.                 logstring="<trkpt lat='"+lat+"' lon='"+lng+"'><time>"+s_time+"</time></trkpt>"+"\n"
  121.                 my_gps.write_log(logstring)
  122.                 time.sleep_ms(100)
  123.                 my_gps.stop_logging() #停止记录
  124.                 oled.text("New REC", 2, 55) #屏幕显示新记录
  125.                 oled.show()
  126.                 print("nomal====")
  127.                 ifsave = True
  128.                 ifhead = False
  129.                 ifend= False
  130.    
  131.     trig = Button.ButtonTrigger(but)
  132.     trig.appendListener(toggleLed, "short")
  133.     but.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler= trig.buttonCall)
复制代码


(五)软件知识点以及解决过程
1、GPS的NMEA-0183库的解析、记录在pico上的移植和实现
大名鼎鼎的micropyGps库是Micropython在github.com上评分最高的一款解析库,借鉴了著名的Arduino下的解析库tinygps++。最初是为pyb板子设计的,我在esp32上移植过,没有问题。这次在pico上也比较顺利。固件采用1-25发布的1.13版,在后续1.14版上有些不稳定,只能待后续micropyGps升级了,毕竟这个库已经几年没有更新了。
2、Ssd1306 I2Coled屏幕在pico上的实现,以及图片显示功能实现
使用了官方的ssd1306驱动库,在认真阅读库并且尝试解决了i2c的表述后,也顺利点亮。主要使用到了亮度调整、字符显示等功能。目前默认只有一种字体及大小,如果自定义字体需要进行字模的设计,这个话题留待以后再讲。本项目解决了pico显示图形的问题,我用了一个树莓派黑白LOGO图标,转换为32*16的黑白png文件,然后利用Micropython.org上网友提供的转换工具(在文末一并分享),将png文件转换为字节数组,直接在程序中调用即可,由此,本项目中显示了一个可爱的树莓派32*16的logo。玩家也可以根据自己的需要进行图形展示,甚至一些汉字(图形方式)的显示。
树莓派logo.jpg png2byte.png
3、button陷阱单击以及长按功能在pico上的移植和实现。
这个button陷阱库很棒,可以单独开一个线程侦测按钮的触发,我只做了一处修改,将原示例当中的led.duty修改为led.duty_u16,即可通过。
我用此库作为GPS坐标点(路径)的存储、停止控制,所以我设计了三个状态控制字(新建、存储以及停止),采用button陷阱的单击进行控制和切换,非常方便。在软件框图中有详细解释。
4、gps轨迹的记录(开始、终止)为gpx格式,并且在适当的工具上进行解析和展示。
gpx文件的实质是一个拥有gps坐标(经纬度、海拔(也可以不要这个参数)、时间戳)等内容的xml文件。找到了标准的gpx文件格式后,将文件头、文件尾进行拆离。在存储过程中将文件头、文件体的正常存储、文件尾装配在一起,就形成了完整的gpx轨迹文件了。解析展示方式有很多种,有离线工具(如微软商店里面的GPX查看器)、在线工具等。都可以方便地对所记录的轨迹进行展示。下图是一个gxp文件示例:
gxp文件格式.png

(六)基于蓝牙HC-05的面包板验证原型、调试程序
由于我的工作台离窗边比较远,为了方便调试,我用了一组HC-05蓝牙模块,主从方式设置后形成了蓝牙串口的透传,将gps数据通过这组HC-05模块传送到工作台的pico这一端。方便了调试工作,示意图如下:
hc05示意图.png
面包板原型图.jpg
蓝牙模块是串口透传,设置好主从后就没问题,所幸大疆悟1GPS模块的输出也是9600波特率,节省了非常多的调试时间。

(七)成品制作过程
1、外壳准备
在搭建完面包板原型后,春节临近,物流快递陆续不发货了。所以就暂时没有考虑3D打印外壳。手里有一块亚克力面板,是上次其他小制作时激光切割的剩余存货,可惜没有找到合适的铜柱。只好放弃。但是找到了吃灰已久的充电宝DIY套件外壳,尺寸非常合适,面板上有一个开孔适合OLED的引出,两个侧面的开孔可以方便电源模块的type-c以及gps模块的引线进入外壳。于是就选定了这个外壳作为本记录仪的外壳。美中不足(也是特点吧)就是外壳一旦扣紧实在咬合太紧,所以把一些咬合部分做了打磨处理,把外壳内一些影响安装的部分也做了拆除。只是对两组按钮进行了打孔,整个外壳就可以用了。
外壳全图.jpg 外壳全图之打开.jpg
2、电源管理模块的安装
本次使用的电源管理模块是集充放电管理、type-c与电池连接、5V输出以及key键唤醒一体的模块。体积也比较小,标称输出电流2A,还有电量显示功能,是一块性价比很好的电源管理模块。由于本项目中oled以及gps都是3.3v供电,实际上PICO本身有3.3v外供电端子,但是为了不增加pico的外供电负担,我还是选择了一块1117电源转换IC作为5-3.3V的转换,这样在本项目中将提供两个电压,5V给树莓派PICO供电(采用VSYS端子),3.3V给OLED以及GPS供电,稳定的供电是非常重要的,这样考虑也是不希望给PICO过大负担。将电源管理模块的KEY端子与5V输出的负极相连接,短击可唤醒电量检测以及供电,双击则关断电源输出。将一个微触开关引出在外壳侧面,用于电源控制。
3、Pico、按钮以及OLED屏的安放
Pico我焊接了排针,所以将两列排母焊接在洞洞板上,就可以很好固定pico。按钮在外壳上开孔(用游标卡尺实测尺寸)后,就可以稳定安装在外壳上,oled屏幕也是用了排母,将排母用热熔胶固定在外壳顶板即可。这样oled模块可以方便装卸。
装配图之一.jpg 装配图之二.jpg
4、整体安装以及路测结果
整体的安装和焊接工作比较简单,由于接线比较简单,所以也没有绘制PCB板,用一块洞洞板作为底板,在板子的端部做了5.0V/3.3V/GND等三组母线,便于各元器件引用。元件之间的飞线在接头处都用了热缩处理,按钮、OLED屏幕以及洞洞板与外壳固定用了热熔胶。当然在全部调试后再固定,否则会很麻烦。我就经历了这样一次麻烦。
整体安装后,在窗台进行了测试,开机-存储-停止-再存储等功能均正常,记录到的gpx文件,也可以正确在解析工具上解析,至此就可以进行路测了。
开车路测很顺利,大疆的GPS模块名不虚传,开机后在几十秒内就可以定位,然后进行存储,开车大约半小时,将路径解析后,表明整个装置工作正常。

我使用了两个网站进行解析,第一个网站是基于百度地图的https://www.cyclingroad.cn/gpx/index.html,第二个网站是基于卫星地图https://www.sunearthtools.com/cn/tools/gps-view.php,只需要把GPX文件拖到页面上就可以解析了。
路径截图0_副本.png 路径截图3_副本.png 路径截图2_副本.png 路径截图4_副本.png
【小结】
1、合理善用旧物料。我们在diy多年后,都或多或少积累了一些线材、模块、外壳等,合理善用,一方面减少吃灰,另一方面改造也带来了不同的乐趣。
2、将搜集到的软件库进行适当修改完善,就可以用到新玩具上。micropython发展很快,一些适配于不同板子、不同时期的库,需要进行适当完善,这需要有一定的库代码阅读能力。
3、往往我们能想到的点子,在github上大多已有思路和实践,我们注意搜集这些信息、善用资源并且记得向作者点亮星星并且注明引用位置。
感谢dfrobot.com.cn提供交流平台,感谢raspberry不断创新。感谢giuhub.com这座宝库。
大年初一,牛年已至,祝新春大吉。
沧海抱拳。

本文用到的代码、工具、库均作为附件分享。
upload.zip (24.6 KB, 下载次数: 26)
装配图之三_a.jpg

pATAq  版主

发表于 2021-2-28 20:54:47

赞一个,动手能力很强哇
回复

使用道具 举报

yoyojacky  初级技匠

发表于 2021-3-3 17:59:04

哈哈,很不错! 我刚想用GPS搞点儿事情,没想到你这边东西都做好了。哈哈
回复

使用道具 举报

沧海笑  高级技师
 楼主|

发表于 2021-3-5 19:15:51

yoyojacky 发表于 2021-3-3 17:59
哈哈,很不错! 我刚想用GPS搞点儿事情,没想到你这边东西都做好了。哈哈
...

一起玩起来,gps还有很多好玩的玩法。这算是一个中规中矩的。
回复

使用道具 举报

yoyojacky  初级技匠

发表于 2021-3-12 11:27:13

对,GPS做一个traccer 工具也不错,还有PPM 来进行授时操作~
回复

使用道具 举报

pATAq  版主

发表于 2021-3-28 08:51:20

感谢分享,很棒的文章
回复

使用道具 举报

yoyojacky  初级技匠

发表于 2021-10-7 23:41:27

pATAq 发表于 2021-3-28 08:51
感谢分享,很棒的文章

谢谢夸奖~
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2021 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail