Matplotlib绘制动态实时曲线的方法改进

本篇博客搬运自个人简书链接戳我,欢迎大家关注。

  • 很多时候,我们需要实时的绘制曲线,如实时的绘制串口接收到的数据。最先想到的解决策略是类似于Matlab种的drawnow函数。
  • 在python中Matplotlib库有着和Matlan绘图库相似的功能,但是并没有drawnow这样的函数。
已有的解决方案
存在的问题
  • 通过上述方法实时绘图,存在一个严重的问题:随着时间推移,CPU消耗越大,费时越多,最终导致程序卡顿。这显然无法满足我们实时绘图的要求。
  • 以下通过time模块计算每一步的耗时,直观地表现这一现象。
    • 完整代码
def Method(point):
    es_time = np.zeros([point]) 
    fig=plt.figure()
    ax=fig.add_subplot(1,1,1)
    ax.axis("equal") #设置图像显示的时候XY轴比例
    ax.set_xlabel('Horizontal Position')
    ax.set_ylabel('Vertical Position')
    ax.set_title('Vessel trajectory')
    plt.grid(True) #添加网格
    plt.ion()  #interactive mode on
    IniObsX=0000
    IniObsY=4000
    IniObsAngle=135
    IniObsSpeed=10*math.sqrt(2)   #米/秒
    print('开始仿真')
    for t in range(point):
        t0 = time.time()
        #障碍物船只轨迹
        obsX=IniObsX+IniObsSpeed*math.sin(IniObsAngle/180*math.pi)*t
        obsY=IniObsY+IniObsSpeed*math.cos(IniObsAngle/180*math.pi)*t
        ax.scatter(obsX,obsY,c='b',marker='.')  #散点图
        #下面的图,两船的距离
        plt.pause(0.001)
        es_time[t] = 1000*(time.time() - t0)
    return es_time
  • 耗时结果
    Method
    • 很显然每步绘图时间与绘图点数呈线性相关的趋势,且随着点数增加,时间消耗越多。可以想象,当绘图的点数到达上万乃至亿的时候,那电脑就卡住了。
分析原因
  • 个人猜测出现上述这种现象的原因,是由代码ax.scatter(obsX,obsY,c='b',marker='.')造成的。这段代码每一循环一次就新画一条曲线,而不清除之前的曲线,这就必然导致越往后循环所花费的CPU资源内存资源越多,最终机器卡死。
改进方法
  • 既然原因是因为不断重复画图所致,导致机器资源的累积消耗,所以想到的第一个解决方法,那就是每次画图前,清除之前的曲线
  • 根据上述思想,在每一次的画图代码ax.scatter(obsX,obsY,c='b',marker='.')前加上清除代码plt.cla()。即:
        plt.cla()
        ax.plot(obsX,obsY,'-g',marker='*')  #散点图
  • 可是这样做之后就会存在新的问题:之前定义的坐标轴,标题,图例等等信息就都被清除了。解决方法则,需要在每一步的循环中,重新定义这些信息。
  • 完整代码
def Method_Improve(point):
    def initial(ax):
        ax.axis("equal") #设置图像显示的时候XY轴比例
        ax.set_xlabel('Horizontal Position')
        ax.set_ylabel('Vertical Position')
        ax.set_title('Vessel trajectory')
        plt.grid(True) #添加网格
        return ax

    es_time = np.zeros([point]) 
    fig=plt.figure()
    ax=fig.add_subplot(1,1,1)
    ax = initial(ax)
    plt.ion()  #interactive mode on
    IniObsX=0000
    IniObsY=4000
    IniObsAngle=135
    IniObsSpeed=10*math.sqrt(2)   #米/秒
    print('开始仿真')
    obsX = [0,]
    obsY = [4000,]
    for t in range(point):
        t0 = time.time()
        #障碍物船只轨迹
        obsX.append(IniObsX+IniObsSpeed*math.sin(IniObsAngle/180*math.pi)*t)
        obsY.append(IniObsY+IniObsSpeed*math.cos(IniObsAngle/180*math.pi)*t)
        plt.cla()
        ax = initial(ax)
        ax.plot(obsX,obsY,'-g',marker='*')  #散点图
        #下面的图,两船的距离
        plt.pause(0.001)
        es_time[t] = 1000*(time.time() - t0)
    return es_time
  • 耗时结果
    Method_Improve
  • 显然循环次数与耗时不再呈正相关趋势,可以说是在一定误差范围内,耗时保持稳定。
改进方法的改进
  • 改进方法中仍存在一个问题:由于每次循环都需要清除坐标轴信息,那么每次循环也必须再重新设置坐标轴信息。显然这种做法,导致了额外的算力消耗,那能否有新的方法,规避这种问题呢?答案显然是有的。
  • 但是解决思路还是得从原始问题出发,即重复画图,导致资源的累积消耗。所以令一种新的思路:只画一条(需要数量的)曲线,每次循环更改这些曲线的数据
  • 那么按照上述思路之后,只需程序开头定义好坐标轴信息,而不需要每次循环内清除重设坐标轴信息。
  • 具体做法,就是获取曲线的句柄,进行修改,即有:
        line.set_xdata(obsX)
        line.set_ydata(obsY)

完整代码:

def ImprovedMethod_Improve(point):    
    es_time = np.zeros([point]) 
    fig=plt.figure()
    ax=fig.add_subplot(1,1,1)

    ax.set_xlabel('Horizontal Position')
    ax.set_ylabel('Vertical Position')
    ax.set_title('Vessel trajectory')

    line = ax.plot([0,0],[4000,4000],'-g',marker='*')[0]
    plt.grid(True) #添加网格
    plt.ion()  #interactive mode on
    IniObsX=0000
    IniObsY=4000
    IniObsAngle=135
    IniObsSpeed=10*math.sqrt(2)   #米/秒
    print('开始仿真')
    obsX = [0,]
    obsY = [4000,]
    for t in range(point):
        t0 = time.time()
        #障碍物船只轨迹
        obsX.append(IniObsX+IniObsSpeed*math.sin(IniObsAngle/180*math.pi)*t)
        obsY.append(IniObsY+IniObsSpeed*math.cos(IniObsAngle/180*math.pi)*t)

        line.set_xdata(obsX)
        line.set_ydata(obsY)
        ax.set_xlim([-200,10*point+200])
        ax.set_ylim([3800-10*point,4200])
        #下面的图,两船的距离
        plt.pause(0.001)
        es_time[t] = 1000*(time.time() - t0)
    return es_time
三种方法对比

三种方法对比

  • 12
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
【RT-Thread作品秀】基于 lvgl 的漏电保护装置校验仪 UI 界面设计作者:赵加文 概述低压漏电引起的各种安全事故已经严重影响到生产生活,威胁到生命财产安全。而解决这一现象的设备就是漏电保护开关,漏电保护开关的漏报率、误报率是很关键的参数,因此有必要对漏电保护开关的性能进行测试。因此,漏电保护装置校验仪是很有必要的。 开发环境硬件:ART-Pi 开发板,正点原子 480*272 4.3寸 RGB 屏幕 RT-Thread版本:4.0.3 开发工具及版本: RT-Thread Studio 2.0.0 :编写 编译 调试 下载代码 STM32CubeMX 6.1.0: codeBlocks 20.03:用于在 PC 机上进行 lvgl 模拟 MCU_Font V2.0: 用于转换中文,然后使得中文能够在 lvgl 中显示 RT-Thread使用情况概述在 UI 设计的整个过程中,使用到 RT-Thread 的部分主要有以下几个方面: 内核部分:动态线程,信号量 组件部分:PIN 设备、I2C 设备、TOUCH 设备框架、LCD 设备框架、finsh 组件 软件包部分:littlevgl2rtt、gt9147 硬件框架软件框架说明系统整体流程图: 软件模块说明整个UI 系统设计所遵循的是 lvgl 图形库的一个回调函数的机制,将各个事件与对应的操作所绑定起来,当滑动滑条时对应的滑条的回调函数就会被触发,那么就会执行滑条回调函数的内容,当滑动点击文本框时,文本框对应的回调函数就会被触发,进而创建键盘的控件,通过键盘输入所需要的数据。 演示效果图片展示: 演示视频: 比赛感悟这次参赛,之前还没有使用过 RT-Thread studio 这个集成开发环境,这次在使用 ART-Pi 的时候全程是使用 RT-Thread studio 这个开发环境,在使用的过程中也碰到了很多问题,有时候明明配置了相关组件,但是保存之后,并没有代码添加到工程里。现在也没有弄明白问题出在哪里,虽然存在着这里问题,但是在使用的过程中,还是非常的方便,整个开发过程就如同搭积木一样方便,与 RTT操作系统贴合的非常的紧密。 除此之外,便是在使用 lvgl 的过程中碰到了很多的问题,现在网上的教程基本是 lvgl v6 版本的教程,关于 lvgl v7 版本的教程很少,而且 v6版本与 V7 版本的 API 相差很大,不能按照 V6 版本来使用 V7 ,在这个过程中摸索了好多,同时也感受到了 lvgl 的魅力,使用在嵌入式系统上是非常不错的选择。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值