직렬 포트에서 데이터를 수집하고 도착 시간에 대해 수집 된 데이터의 그래프를 그리는 Python으로 응용 프로그램을 만들고 있습니다. 데이터 도착 시간은 불확실합니다. 데이터가 수신되면 플롯을 업데이트하고 싶습니다. 이 작업을 수행하는 방법을 검색하고 두 가지 방법을 찾았습니다.
- 플롯을 지우고 모든 포인트로 플롯을 다시 그립니다.
- 특정 간격 후에 플롯을 변경하여 애니메이션화합니다.
프로그램이 장시간 (예 : 하루) 데이터를 실행하고 수집하므로 첫 번째 것을 선호하지 않으며 플롯을 다시 그리는 속도가 매우 느립니다. 두 번째 것은 데이터 도착 시간이 불확실하고 데이터가 수신 될 때만 플롯이 업데이트되기를 원하기 때문에 바람직하지 않습니다.
데이터를받을 때만 더 많은 포인트를 추가하여 플롯을 업데이트 할 수있는 방법이 있습니까?
답변
더 많은 포인트를 추가하여 플롯을 업데이트 할 수있는 방법이 있습니까?
사용중인 버전에 따라 matplotlib에서 데이터를 애니메이션하는 방법에는 여러 가지가 있습니다. matplotlib 요리 책 예제를 보셨습니까 ? 또한 matplotlib 문서에서 보다 현대적인 애니메이션 예제 를 확인하십시오 . 마지막으로 애니메이션 API 는 FuncAnimation 함수를 정의합니다. 시간에 따라 함수를 애니메이션하는 함수 을 . 이 함수는 데이터를 수집하는 데 사용하는 함수일 수 있습니다.
각 방법은 기본적으로 data
그려지는 객체 의 속성을 설정 하므로 화면이나 그림을 지울 필요가 없습니다. 그만큼data
이전 포인트를 유지하고 당신의 라인 (또는 이미지 또는 당신이 무엇을 그리는)에 계속 추가 할 수 있도록 속성을 간단하게 확장 할 수 있습니다.
데이터 도착 시간이 확실하지 않다고한다면 다음과 같이하는 것이 가장 좋습니다.
import matplotlib.pyplot as plt
import numpy
hl, = plt.plot([], [])
def update_line(hl, new_data):
hl.set_xdata(numpy.append(hl.get_xdata(), new_data))
hl.set_ydata(numpy.append(hl.get_ydata(), new_data))
plt.draw()
그런 다음 직렬 포트에서 데이터를 수신하면 update_line
.
답변
FuncAnimation없이이 작업을 수행하려면 (예 : 플롯이 생성되는 동안 코드의 다른 부분을 실행하거나 동시에 여러 플롯을 업데이트하려는 경우) draw
단독으로 호출 해도 플롯이 생성되지 않습니다 (적어도 qt 백엔드).
다음은 나를 위해 작동합니다.
import matplotlib.pyplot as plt
plt.ion()
class DynamicUpdate():
#Suppose we know the x range
min_x = 0
max_x = 10
def on_launch(self):
#Set up plot
self.figure, self.ax = plt.subplots()
self.lines, = self.ax.plot([],[], 'o')
#Autoscale on unknown axis and known lims on the other
self.ax.set_autoscaley_on(True)
self.ax.set_xlim(self.min_x, self.max_x)
#Other stuff
self.ax.grid()
...
def on_running(self, xdata, ydata):
#Update data (with the new _and_ the old points)
self.lines.set_xdata(xdata)
self.lines.set_ydata(ydata)
#Need both of these in order to rescale
self.ax.relim()
self.ax.autoscale_view()
#We need to draw *and* flush
self.figure.canvas.draw()
self.figure.canvas.flush_events()
#Example
def __call__(self):
import numpy as np
import time
self.on_launch()
xdata = []
ydata = []
for x in np.arange(0,10,0.5):
xdata.append(x)
ydata.append(np.exp(-x**2)+10*np.exp(-(x-7)**2))
self.on_running(xdata, ydata)
time.sleep(1)
return xdata, ydata
d = DynamicUpdate()
d()
답변
다음은 특정 개수의 포인트가 표시된 후 포인트를 제거 할 수있는 방법입니다.
import matplotlib.pyplot as plt
# generate axes object
ax = plt.axes()
# set limits
plt.xlim(0,10)
plt.ylim(0,10)
for i in range(10):
# add something to axes
ax.scatter([i], [i])
ax.plot([i], [i+1], 'rx')
# draw the plot
plt.draw()
plt.pause(0.01) #is necessary for the plot to update for some reason
# start removing points if you don't want all shown
if i>2:
ax.lines[0].remove()
ax.collections[0].remove()
답변
이 질문에 늦었 음을 알고 있지만 문제에 대해서는 “joystick”패키지를 살펴볼 수 있습니다. 직렬 포트에서 데이터 스트림을 플로팅하도록 설계했지만 모든 스트림에서 작동합니다. 또한 대화 형 텍스트 로깅 또는 이미지 플로팅 (그래프 플로팅 추가)을 허용합니다. 별도의 스레드에서 자체 루프를 수행 할 필요가 없습니다. 패키지가 처리하므로 원하는 업데이트 빈도 만 제공하면됩니다. 또한 플로팅하는 동안 명령을 모니터링하는 데 터미널을 사용할 수 있습니다. http://www.github.com/ceyzeriat/joystick/ 또는 https://pypi.python.org/pypi/joystick을 참조 하십시오 (설치하려면 pip 설치 조이스틱 사용)
np.random.random ()을 아래 코드의 직렬 포트에서 읽은 실제 데이터 포인트로 바꾸십시오.
import joystick as jk
import numpy as np
import time
class test(jk.Joystick):
# initialize the infinite loop decorator
_infinite_loop = jk.deco_infinite_loop()
def _init(self, *args, **kwargs):
"""
Function called at initialization, see the doc
"""
self._t0 = time.time() # initialize time
self.xdata = np.array([self._t0]) # time x-axis
self.ydata = np.array([0.0]) # fake data y-axis
# create a graph frame
self.mygraph = self.add_frame(jk.Graph(name="test", size=(500, 500), pos=(50, 50), fmt="go-", xnpts=10000, xnptsmax=10000, xylim=(None, None, 0, 1)))
@_infinite_loop(wait_time=0.2)
def _generate_data(self): # function looped every 0.2 second to read or produce data
"""
Loop starting with the simulation start, getting data and
pushing it to the graph every 0.2 seconds
"""
# concatenate data on the time x-axis
self.xdata = jk.core.add_datapoint(self.xdata, time.time(), xnptsmax=self.mygraph.xnptsmax)
# concatenate data on the fake data y-axis
self.ydata = jk.core.add_datapoint(self.ydata, np.random.random(), xnptsmax=self.mygraph.xnptsmax)
self.mygraph.set_xydata(t, self.ydata)
t = test()
t.start()
t.stop()