Python项目实践:串口字符串数据的读取、分割与保存到csv文件

Python项目实践:串口字符串数据的读取、分割与保存到csv文件

    • 程序功能
    • 运行条件
    • 准备工作与说明
    • 代码解读
      • import 部分
      • 从串口读取数据(函数)
      • 对csv文件的处理(函数)
      • 将串口数据读取并保存到csv(函数,主进程)
      • 键盘中断(函数,第二进程)
      • 程序主体
      • 全部代码
    • 帮助、参考资料(文章)
          • 进程间通信(multiprocessing库):
          • 键盘监听:
          • python串口的使用:
          • csv文件操作:
          • os库的使用(系统文件操作):

程序功能

	1、从串口读取循环读取如同$4567.0,12.3,12.3#的字符串,字符串开始符、分隔符、结束符是规定好的
	2、将字符串分割为如同4567.0、12.3、12.3的浮点型数据
	3、将数据保存到csv文件中
	4、按esc结束运行

运行条件

Python 3.8.2

Python库:
multiprocessing, csv, serial, os(python自带)
keyboard 0.13.5
numpy 1.19.2

准备工作与说明

此程序原本开发的目的是将航模机载数据通过数传(串口)传到电脑上,供后期处理分析。

为了使串口调试方便,可以通过使用arduino或者其他单片机向串口按固定时间间隔输入数据,来模仿真实数传环境。如果有其他模拟方式也可,这不是重点。

为了便于终止程序、保存文档(强制终止可能导致csv文件损坏),采用双线程的方式同时保存数据和监听键盘输入。(才疏学浅,没有找到python中断的方法,并且目前了解到的键盘监听不是很麻烦就是拓展性不强,因此选用双线程通信的方法解决退出的问题。)

现在期末烤漆太紧张,有空再回来完善。

代码解读

import 部分

from multiprocessing import Process, Queue
import keyboard, csv, serial, os
import numpy as np

从串口读取数据(函数)

def get_numbers_from_serial(target_com, begin='\r\n', end='$', cut=','):
    '''
    从串口获得float类型数据列表
    如果两个分隔符cut相邻或分隔符中间为无效字符串(无法转换为float的字符串),在中间位置补-32768
    若起始符begin不符则返回空列表
    '''
    
    data = target_com.read_until(bytes(end, encoding='utf-8'))  #一直读取直到遇到截止符end
    data = data.decode('utf-8','ignore')
    #print('gotten:', data)

    begin_len=len(begin)
    if (data[0:begin_len]!=begin):    #检测begin是否与字符串开头相符
        return []

    cut_len = len(cut)
    pos = begin_len
    num_list = []
    label = True
    while (data[pos:] != end):
        num_str = ''

        #确认目标位及其后方不为cut和end
        while (data[pos:] != end and data[pos:pos+cut_len] != cut):
            label = True    #至少采集到了一个有效字符
            num_str = num_str+data[pos]
            pos = pos+1
        
        #根据label和num_str向num_list中赋值
        if label:
            try:
                num_list.append(float(num_str))
            except Exception:
                num_list.append(-32768) #num_str为无效字符串
        else:
            num_list.append(-32768) #label为负,两个cut相邻
        
        if data[pos:] != end:
            pos = pos+cut_len
        label = False
    
    return num_list

对csv文件的处理(函数)

def deal_with_csv_file(file_path='save_csv.csv', csv_head=[], mode='new'):
    '''
    csv文件预处理
    file_path:文件保存的路径
    mode两种模式:
    'new'删除原同名文件(如果有)并新建文件
    'add'在原文件后续写
    csv_head是以列表形式给出的csv文件每列标题,只有在'new'模式才有用
    '''

    def create_csv(csv_head=[], path='save_csv.csv'):
        '''
        新建csv文件
        '''
        with open(path, 'w', newline='') as f:
            csv_write = csv.writer(f)
            if csv_head!=[]:
                csv_write.writerow(csv_head)

    if mode=='new': #新建模式
        if os.path.isfile(file_path):
            os.remove(file_path)
        create_csv(csv_head, file_path)
    elif mode=='add':   #续写模式
        if os.path.isfile(file_path)==False:
            print('源文件缺失,自动新建')
            create_csv(csv_head, file_path)

将串口数据读取并保存到csv(函数,主进程)

def serial_to_csv(q):
    '''
    第一进程,从串口保存数据到csv文件
    '''

    #初始设置
    file_path = 'save_csv.csv'  #保存文件名称
    csv_head = ['速度A', '速度B', '速度C'] #数据含义
    mode = 'new'    #新建文件还是在原有文件上续写
    # mode = 'add'
    ser = serial.Serial('COM4', baudrate=115200)    #串口信息(名称,波特率)
    ser_msg = ['\r\n$', '#', ',']   #串口符号规定,若有换行则需在原有开始符前加\r\n
    show = True #是否实时展示数据

    #csv文件预处理
    deal_with_csv_file(file_path, csv_head, mode)
    
    #开始读数并保存
    with open(file_path,'a+', newline='') as f:
        csv_write = csv.writer(f)
        n = 0
        while q.empty():
            num_list=get_numbers_from_serial(ser, begin=ser_msg[0], end=ser_msg[1], cut=ser_msg[2])
            csv_write.writerow(num_list)
            if show:
                print('\r', num_list, end='')
            n = n+1
        print('\n总共保存了', n-1, '个数据')

键盘中断(函数,第二进程)

def key_board_listen(q):
    '''
    第二进程,键盘监听充当中断函数
    '''

    keyboard.wait('esc')
    q.put(1)

程序主体

if __name__ == "__main__":
    '''
    从串口保存数据到csv文件,具体参数设置在serial_to_csv中。
    按下esc停止保存,程序退出
    '''

    q = Queue()
    p1 = Process(target=serial_to_csv, args=(q,))
    p2 = Process(target=key_board_listen, args=(q,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()

全部代码

from multiprocessing import Process, Queue
import keyboard, csv, serial, os
import numpy as np


def get_numbers_from_serial(target_com, begin='\r\n', end='$', cut=','):
    '''
    从串口获得float类型数据列表
    如果两个分隔符cut相邻或分隔符中间为无效字符串(无法转换为float的字符串),在中间位置补-32768
    若起始符begin不符则返回空列表
    '''
    
    data = target_com.read_until(bytes(end, encoding='utf-8'))  #一直读取直到遇到截止符end
    data = data.decode('utf-8','ignore')
    #print('gotten:', data)

    begin_len=len(begin)
    if (data[0:begin_len]!=begin):    #检测begin是否与字符串开头相符
        return []

    cut_len = len(cut)
    pos = begin_len
    num_list = []
    label = True
    while (data[pos:] != end):
        num_str = ''

        #确认目标位及其后方不为cut和end
        while (data[pos:] != end and data[pos:pos+cut_len] != cut):
            label = True    #至少采集到了一个有效字符
            num_str = num_str+data[pos]
            pos = pos+1
        
        #根据label和num_str向num_list中赋值
        if label:
            try:
                num_list.append(float(num_str))
            except Exception:
                num_list.append(-32768) #num_str为无效字符串
        else:
            num_list.append(-32768) #label为负,两个cut相邻
        
        if data[pos:] != end:
            pos = pos+cut_len
        label = False
    
    return num_list

def deal_with_csv_file(file_path='save_csv.csv', csv_head=[], mode='new'):
    '''
    csv文件预处理
    file_path:文件保存的路径
    mode两种模式:
    'new'删除原同名文件(如果有)并新建文件
    'add'在原文件后续写
    csv_head是以列表形式给出的csv文件每列标题,只有在'new'模式才有用
    '''

    def create_csv(csv_head=[], path='save_csv.csv'):
        '''
        新建csv文件
        '''
        with open(path, 'w', newline='') as f:
            csv_write = csv.writer(f)
            if csv_head!=[]:
                csv_write.writerow(csv_head)

    if mode=='new': #新建模式
        if os.path.isfile(file_path):
            os.remove(file_path)
        create_csv(csv_head, file_path)
    elif mode=='add':   #续写模式
        if os.path.isfile(file_path)==False:
            print('源文件缺失,自动新建')
            create_csv(csv_head, file_path)

def serial_to_csv(q):
    '''
    第一进程,从串口保存数据到csv文件
    '''

    #初始设置
    file_path = 'save_csv.csv'  #保存文件名称
    csv_head = ['速度A', '速度B', '速度C'] #数据含义
    mode = 'new'    #新建文件还是在原有文件上续写
    # mode = 'add'
    ser = serial.Serial('COM4', baudrate=115200)    #串口信息(名称,波特率)
    ser_msg = ['\r\n$', '#', ',']   #串口符号规定,若有换行则需在原有开始符前加\r\n
    show = True #是否实时展示数据

    #csv文件预处理
    deal_with_csv_file(file_path, csv_head, mode)
    
    #开始读数并保存
    with open(file_path,'a+', newline='') as f:
        csv_write = csv.writer(f)
        n = 0
        while q.empty():
            num_list=get_numbers_from_serial(ser, begin=ser_msg[0], end=ser_msg[1], cut=ser_msg[2])
            csv_write.writerow(num_list)
            if show:
                print('\r', num_list, end='')
            n = n+1
        print('\n总共保存了', n-1, '个数据')

def key_board_listen(q):
    '''
    第二进程,键盘监听充当中断函数
    '''

    keyboard.wait('esc')
    q.put(1)


if __name__ == "__main__":
    '''
    从串口保存数据到csv文件,具体参数设置在serial_to_csv中。
    按下esc停止保存,程序退出
    '''

    q = Queue()
    p1 = Process(target=serial_to_csv, args=(q,))
    p2 = Process(target=key_board_listen, args=(q,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()

帮助、参考资料(文章)

进程间通信(multiprocessing库):

进程间的通信:multiprocessing库下的Pipe类与Queue类
python多进程的理解 multiprocessing Process join run
【python】详解multiprocessing多进程-process模块(一)

键盘监听:

Python 键盘/鼠标监听及控制

python串口的使用:

Python 之 Serial串口通信 (可能不全)

csv文件操作:

python 读写csv文件(创建,追加,覆盖)

os库的使用(系统文件操作):

python读写、创建文件、文件夹等等

你可能感兴趣的:(程序组学习,python,串口通信,csv)