Package: better-rtplot
Real Time Plotting with pyqtgraph and ZMQThe point of this module is to be able to plot remotely over socket protocols using theZMQ library. The use cases that I have in mind is plotting information from the raspberry pi to a host computer so that they can plot the data. This is very useful for setting up real time plots given pyqtgraph's performance. This can also be used to plot local information in real time by using the localhost as the address to publish/subscribe data from.The main highlight in this plotter are the following:Fast Performance. Can do 500+ fps on one trace using an i7-9750H processorRemote Plot Customizability. The plot configuration is defined by the provider of the data. E.g. if you are using a pi to collect data, the plot configuration is also stored on the pi so you only have to change code in one locationSave data. Once you plot the data, you can save it locally by clicking one button.Installpip install better-rtplotIf you are using WSL, you need to install an xorg server such asvcXsrvHow to useStep 1: On the computer that will be used to plot run:For local plots:python3 server.py -lTo plot from a raspberry pi:python3 server.py -p xxx.xxx.xxx.xxxWhere the xxx.xxx.xxx.xxx represents the pi's ip address (note, you must be on the same network)Step 2: On the script that provides data, add the plotting libraryIn order to use this library, you must have installed the library and import the rtplot.client module into your code. The two main steps are to initialize the plotter and then to send data as seen in the following example:#Load plotting libraryfromrtplotimportclient#Common importimportnumpyasnp#Initialize one plot with 5 tracesclient.initialize_plots(5)#Send 1000 datapointsforiinrange(1000):#Send random dataclient.send_array(np.random.randn(5,1))In the simplest plot configuration, you only need to indicate the amount of traces that you want (if you leave it empty, it will default to 1 trace). Then you need to send the data. The data must be either a list or a numpy array. More examples can be seen in theexample code:cherry_blossom: Make pretty plots :cherry_blossom:You can control the following things when callingclient.initialize_plots()by passing in a dictionary:'names' - This defines the names of the traces. The plot will have as many traces as names.'colors' - Defines the colors for each trace.Follow documentation on how to specify color. Should have at least the same length as the number of traces.'line_style' - Defines wheter or not a trace is dashed or not.'-' - represents dashed line"" - emptry string (or any other string) represents a normal line'line_width' - Defines how thick the plot lines are. Expects an integer'title' - Sets the title to the plot'ylabel' - Sets the y label of the plot'xlabel' - Sets the x label of the plot'yrange' - Sets the range of values of y. This provides a performance boost to the plotterExpects values as a iterable in the order [min, max]. Example: [-2,2]'xrange' - Sets the number of datapoints that will be in the real time plotter at a given time.Expects values as a integer that describes how many datapoints are in the subplot. Default is 200 datapointsYou only need to specify the things that you want, if the dictionary element is left out then the default value is used.How to send dataOnce the plot has been configured, the data is sent as a numpy array. The order of the data in the array is very important and it MUST be sent where the rows have data that corresponds to the same order that the trace names were defined in.#Load plotting libraryfromrtplotimportclient#Common importimportnumpyasnp#Let's create two subplots#First, define a dictionary of items for each plot#First plot will have three traces: phase, phase_dot, stride_lengthplot_1_config={'names':['phase','phase_dot','stride_length'],'title':"Phase, Phase Dot, Stride Length",'ylabel':"reading (unitless)",'xlabel':'test 1'}#Second plot will have five traces: gf1, gf2, gf3, gf4, gf5plot_2_config={'names':["gf1","gf2","gf3","gf4","gf5"],'colors':["r","b","g","r","b"],'line_style':['-','','-','','-'],'title':"Phase, Phase Dot, Stride Length",'ylabel':"reading (unitless)",'xlabel':'test 2'}#Aggregate into listplot_config=[plot_1_config,plot_2_config]#Tell the server to initialize the plotclient.initialize_plots(plot_config)#Create plotter array with random dataplot_data_array=[np.random.randn(),#phasenp.random.randn(),#phase_dotnp.random.randn(),#stride_lengthnp.random.randn(),#gf1np.random.randn(),#gf2np.random.randn(),#gf3np.random.randn(),#gf4np.random.randn()#gf5]#Send data to server to plotclient.send_array(plot_data_array)Additional networking configurationsThere are two main ways that you can connect to transmit information. Either the computer that runs the server knows what IP the client is (server connects to client), or the client knows which IP the server has (server connects to client). For either option, follow the steps bellow:Server connects to clientThis is accomplished by running the server with the IP address of the pi as an argument.python3 server.py -p xxx.xxx.xxx.xxxWhere xxx.xxx.xxx.xxx is the IP address of the pi. No additional configuration is needed on the client side.Client connects to serverThis is done by setting up the server with the static ip flag.python3 server.py -sThe client then needs to specify the ip of the plotter by running:client.configure_ip("xxx.xxx.xxx.xxx")Where xxx.xxx.xxx.xxx is the IP address of the server.Plotter too slow?The performance of the plotter is mostly impacted by the amount of traces that you want to plot (this is due to multiple calls to the line drawing algorithms in pyqtgraph). However, sending over multiple datapoints to plot at once is not expensive since there is essentially only the added overhead of receiving the data (the plotter redraws the entire line anyways). Therefore to get the most performance you should have as little traces as possible and send over as much datapoints at once before the plot updates look ugly.Fix y rangeTo get the most performance out of the system, you want to set the 'yrange' configuration of the plot. This has resulted in a 100FPS increase for my testing.Example CodeExample Image