1. ADC采集值绘制曲线#

1.1. 主要完以以下功能:#

  • 从串口接收MCU发送的ADC采集数据

  • 将ADC数据以图表的形式显示出来

1.2. MCU串口上传数据协议#

  • 发送ADC值:

    • "/*"开头

    • "*/\r\n"结尾

    • 中间ADC数据可以是ADC的16进制数据或ADC值转换后的ascii码 ascii码发送时以","做为数据分隔符

  • 发送MCU计算后的数据

    • "$ "开头

    • " $\r\n"结尾

1.3. 源代码#

import serial
from serial.tools.list_ports import comports
import time
from matplotlib import pyplot as plt
import matplotlib
import numpy as np

matplotlib.rc("font",family='MicroSoft YaHei',weight="bold")

port = 'COM12'
ports = [i.name for i in comports()]
adc_data_prefix = '/*'.encode()
adc_data_suffix = '*/\r\n'.encode()
mcu_calc_prefix = adc_data_suffix + '$ '.encode()
mcu_calc_suffix = ' $\r\n'.encode()
adc_send_by_buffer = 1

def get_bytes_with_prefix_suffix(data:bytes,prefix:bytes=adc_data_prefix,suffix:bytes=adc_data_suffix) -> bytes:
    """
    从字节数据中截取指定开头和结尾的字节数据 -> bytes

    prefix: 开始字节数据
    suffix: 结尾字节数据

    """
    start = data.find(prefix)
    result = data[start+len(prefix):]
    end = result.find(suffix)
    # print("start:",start,"end:",end)
    if start < 0 or end < 0:
        raise Exception("\r\n{0}\r\n中没找到以{1}开头且{2}结尾的数据".format(data,prefix,suffix))
    else:
        return result[:end]

def draw_curves(data:list):

    x = np.linspace(1,len(data),len(data))
    y = np.array(data)
    print("ADC max:{0} min:{1}\r\n{0}-{1}={2}".format(max(y),min(y),(max(y)-min(y))))
    # print(f'DC:{np.average(y)}')
    # print(f'AC:{np.sqrt(np.sum([(x-np.average(y)) **2 for x in y])/len(y))}')
    # print(f'方差:{np.var(y)}')
    # print(f'标准差:{np.std(y)}')

    y1 = np.convolve(y,np.ones(4)/4,'same')
    # for i in range(4):
    #     print(f'{i}:y[{i}]{y[i]},y1[{i}]{y1[i]}')
    # #     y1[i] = y[i]
    # for i in range(len(y)-4,len(y),1):
    #     print(f'{i}:y[{i}]{y[i]},y1[{i}]{y1[i]}')
    #     y1[i] = y[i]
    # y1 = y1[2:-1]

    plt.figure(figsize=(20,10))
    # plt.axes((0,65,1800,3000))
    # plt.ylim(1500,2500)
    plt.xlabel('采样点')
    plt.ylabel('ADC值')
    plt.title("MCU ADC采集值绘制的曲线")

    plt.plot(x,y)
    plt.plot(x,y1)
    plt.show()

def adc_print_process(data:bytes) -> None:

    if (data != b''): 
        adc_list = data.decode().split(',')
        # 末尾可能有空字符,先去掉
        adc_list = filter(lambda x: not x== '',adc_list)
        # 字符串列表转换成数值列表
        adc_list = list(map(int,adc_list))
        draw_curves(adc_list)

def adc_buffer_process(data:bytes) -> None:
    if(data != b''):
        adc_list = np.frombuffer(data,np.uint32)
        # print(adc_list)
        draw_curves(adc_list)
 

if port not in ports:
    raise Exception(f"seril port[{port}] error, Please select port in {ports}")

ser = serial.Serial()
ser.port = port
ser.baudrate = 115200
# 如果使用readlines来读取所有串口数据,需要设置一个超时时间这里是1ms,不设将一直接收
ser.timeout = 0.001

try:
    ser.open()
    # 如果用readall来读所有串口数据,在打开串后需等待一段时间用于接收数据。不然返回的都是空表
    time.sleep(0.1)
    seril_data = ser.read_all()
    # seril_data = ser.readlines()
    # print(seril_data)
    seril_data = adc_data_prefix + get_bytes_with_prefix_suffix(seril_data,adc_data_prefix,mcu_calc_suffix) + mcu_calc_suffix
    print(seril_data)
    adc_data = get_bytes_with_prefix_suffix(seril_data,adc_data_prefix,adc_data_suffix)
    # print(adc_data)
    if adc_send_by_buffer:
        # MCU 直接发送ADC内存区
        adc_buffer_process(adc_data)
    else:
        # MCU 使用printf("%d,",adcValue[i])的形式上传ADC值
        adc_print_process(adc_data)
    mcu_calc = get_bytes_with_prefix_suffix(seril_data,mcu_calc_prefix,mcu_calc_suffix)
    print("MCU calcutale:",mcu_calc.decode())

finally:
    ser.close()
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[1], line 1
----> 1 import serial
      2 from serial.tools.list_ports import comports
      3 import time

ModuleNotFoundError: No module named 'serial'

1.4. 待改进之处#

  1. 🛠️ 做成图形界面

  2. 🛠️ 多线程或异步IO模式

import numpy as np
import matplotlib.pyplot as plt

# plt.figure(figsize=(40,20))
# plt.plot([1, 2, 3, 4], [1, 4, 9, 16], 'ro')
x = np.linspace(0,2*np.pi,64)
y = 5*np.sin(x+np.deg2rad(0))
# y = [0,0,0,0,0,1,0,0,0,6,4,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,2,6,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0]
plt.plot(x,y,label='输入信号')
y1 = [np.average(y) for i in x]
plt.plot(x,y1,label=f'平均值:{y1[0]}')
y2 = [np.std(y) for i in x]
plt.plot(x,y2,label=f'标准差:{y2[0]}')
y3 = [np.var(y) for i in x]
plt.plot(x,y3,label=f'方差:{y3[0]}')
plt.plot(x,np.abs(y),'r')
# plt.text(2, 9, r'$\mu=100,\ \sigma=15$')
# plt.ylabel('some numbers')
plt.legend()
plt.show()

print(np.average(np.abs(y)))
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 10, 1000)
y = 5*np.sin(2*np.pi*5*x) + 3*np.sin(2*np.pi*20*x) + np.random.normal(0, 0.5, 1000)

plt.figure(figsize=(50,10))
plt.plot(x, y)

def rms_smoothing(x, window_size):
    window = np.ones(int(window_size))/float(window_size)
    return np.sqrt(np.convolve(x**2, window, 'same'))

y_rms_5 = rms_smoothing(y, 1)
y_rms_20 = rms_smoothing(y, 20)
y_rms_100= rms_smoothing(y, 100)

plt.plot(x, y_rms_5, label='RMS窗口=5')
plt.plot(x, y_rms_20, label='RMS窗口=20')
plt.plot(x, y_rms_100, label='RMS窗口=100')
plt.title('不同RMS窗口下的平滑信号')
plt.legend()
plt.show()
# 实现数据可视化中的数据平滑
import numpy as np
import matplotlib.pylab as plt
 
'''
其它的一些知识点:
raise:当程序发生错误,python将自动引发异常,也可以通过raise显示的引发异常
一旦执行了raise语句,raise语句后面的语句将不能执行
'''
 
def moving_average(interval, windowsize):
    window = np.ones(int(windowsize)) / float(windowsize)
    re = np.convolve(interval, window, 'same')
    return re
 
def LabberRing():
    t = np.linspace(-4, 4, 100)   # np.linspace 等差数列,从-4到4生成100个数
    # print('t=', t)
    # np.random.randn 标准正态分布的随机数,np.random.rand 随机样本数值
    y = np.sin(t) + np.random.randn(len(t)) * 0.1   # 标准正态分布中返回1个,或者多个样本值
    # print('y=', y)
    
    plt.plot(t, y, 'k')     # plot(横坐标,纵坐标, 颜色)
    
    y_av = moving_average(y, 10)
    plt.plot(t, y_av, 'b')
    plt.xlabel('Time')
    plt.ylabel('Value')
    # plt.grid() #网格线设置
    plt.grid(True)
    plt.show()
    return
 
LabberRing()  # 调用函数

# https://blog.csdn.net/weixin_42782150/article/details/107176500
../../_images/5a8d13fad23c1d4fad9a63c3e8b8451ca587189d33b51e020bb079fcaa3588b0.png