新闻动态

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

发布日期:2022-03-16 16:38 | 文章来源:源码之家

很多时候,我们需要实时的绘制曲线,如实时的绘制串口接收到的数据。最先想到的解决策略是类似于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

耗时结果


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


三种方法对比

总结

到此这篇关于Matplotlib绘制动态实时曲线的文章就介绍到这了,更多相关Matplotlib绘制动态实时曲线内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

海外服务器租用

版权声明:本站文章来源标注为YINGSOO的内容版权均为本站所有,欢迎引用、转载,请保持原文完整并注明来源及原文链接。禁止复制或仿造本网站,禁止在非www.yingsoo.com所属的服务器上建立镜像,否则将依法追究法律责任。本站部分内容来源于网友推荐、互联网收集整理而来,仅供学习参考,不代表本站立场,如有内容涉嫌侵权,请联系alex-e#qq.com处理。

相关文章

实时开通

自选配置、实时开通

免备案

全球线路精选!

全天候客户服务

7x24全年不间断在线

专属顾问服务

1对1客户咨询顾问

在线
客服

在线客服:7*24小时在线

客服
热线

400-630-3752
7*24小时客服服务热线

关注
微信

关注官方微信
顶部