关于Matplotlib绘制动态实时曲线的方法改进指南

作者:脑荼地 时间:2021-03-17 23:48:10 

很多时候,我们需要实时的绘制曲线,如实时的绘制串口接收到的数据。最先想到的解决策略是类似于Matlab种的drawnow函数。

在python中Matplotlib库有着和Matlan绘图库相似的功能,但是并没有drawnow这样的函数。

已有的解决方案

通过网上现有的资料 基于Python实现matplotlib中动态更新图片(交互式绘图) ,可以通过打开Matplotlib的交互模式来实现实时绘图的目的,此时需要用到函数matplotlib.pyplot.ion

存在的问题

通过上述方法实时绘图,存在一个严重的问题:随着时间推移,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

耗时结果

关于Matplotlib绘制动态实时曲线的方法改进指南
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

耗时结果

关于Matplotlib绘制动态实时曲线的方法改进指南
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

关于Matplotlib绘制动态实时曲线的方法改进指南
三种方法对比

总结

来源:https://www.jianshu.com/p/1a25ce4563ec

标签:matplotlib,动态,曲线
0
投稿

猜你喜欢

  • vue车牌搜索组件使用方法详解

    2024-04-30 10:30:37
  • pycharm远程连接vagrant虚拟机中mariadb数据库

    2024-01-13 19:11:33
  • python 队列基本定义与使用方法【初始化、赋值、判断等】

    2023-08-13 06:34:46
  • python匹配两个短语之间的字符实例

    2022-08-31 04:49:57
  • windows下Idea使用git clone failed. Could not read from remote repository.

    2022-06-17 03:13:05
  • Pandas中DataFrame数据删除详情

    2023-12-18 10:40:02
  • Python中的优先队列(priority queue)和堆(heap)

    2023-11-22 05:40:03
  • Python多重继承的方法解析执行顺序实例分析

    2021-06-08 04:34:51
  • 详解Vue 多级组件透传新方法provide/inject

    2024-06-05 09:21:03
  • 定制FileField中的上传文件名称实例

    2022-06-07 14:21:05
  • 微信跳一跳小游戏python脚本

    2023-07-06 10:15:15
  • golang结构体与json格式串实例代码

    2024-04-27 15:36:44
  • Python实现堆排序的方法详解

    2023-12-02 07:43:20
  • 解决sql server 数据库,sa用户被锁定的问题

    2024-01-18 23:21:08
  • Python使用QRCode模块生成二维码实例详解

    2022-07-04 16:50:44
  • 利用Python实现自动扫雷小脚本

    2023-05-10 01:45:27
  • 赚疯了!转手立赚800+?大佬的python「抢茅台脚本」使用教程 <font color=red>原创</font>

    2022-03-02 04:40:53
  • 选择MySQL数据库进行连接的简单示例

    2024-01-24 02:35:50
  • php for 循环语句使用方法详细说明

    2023-11-17 21:43:21
  • MSSQL中递归SQL查询语句实例说明-

    2024-01-28 19:54:01
  • asp之家 网络编程 m.aspxhome.com