Live waveform with python-can

335 Views Asked by At

I'm trying to plot can-data using the python-can and matplotlib library. I have a basic example on how to plot live data with matplotlib:

import numpy as np
import matplotlib.pyplot as plt

plt.axis([0, 10, 0, 1])


for i in range(10):
    y = np.random.random()
    plt.scatter(i, y)
    # plt.pause(0.05)
    plt.draw()

plt.show()

And I used a basic python-can example and added the matplotlib:

import sys
import argparse
import socket
from datetime import datetime

import can
from can import Bus, BusState, Logger
import numpy as np
import matplotlib.pyplot as plt



def main():

    plt.axis([0, 10, 0, 1])

    bus = Bus(bustype ='pcan')
    i = 0

    print(f"Connected to {bus.__class__.__name__}: {bus.channel_info}")
    print(f"Can Logger (Started on {datetime.now()})")

    plt.show()

    try:
        while True:
            msg = bus.recv(1)
            if msg is not None:
                y = np.random.random()
                plt.scatter(i, y)
                plt.draw()
                i += 1
                print('Message id {id}'.format(id=msg.arbitration_id))

    except KeyboardInterrupt:
        pass
    finally:
        plt.show()
        bus.shutdown()


if __name__ == "__main__":
    main()

However as soon as I add the plt commands the script stops working, it just doesn't output the print statement anymore. When I Debug into it and step manually it works. Anything I'm doing wrong? Is there a better way to solve this? I'm not set on matplotlib, just the fastest way to plot something :)

1

There are 1 best solutions below

1
On BEST ANSWER

I found a working solution using the blit function, its not perfect yet but its a good starting point:

from datetime import datetime
import can
from can import Bus, BusState, Logger
import numpy as np
import matplotlib.pyplot as plt
import struct



def main():
    bus = Bus(bustype ='pcan')
    i = 0

    print(f"Connected to {bus.__class__.__name__}: {bus.channel_info}")
    print(f"Can Logger (Started on {datetime.now()})")

    x = np.linspace(0, 1000, num=1000)
    y = np.zeros(1000)

    fig = plt.figure()
    ax1 = fig.add_subplot(1, 1, 1)
    line, = ax1.plot([], lw=3)
    text = ax1.text(0.8, 0.5, "")

    ax1.set_xlim(x.min(), x.max())
    ax1.set_ylim([-10, 10])

    fig.canvas.draw()  # note that the first draw comes before setting data

    ax2background = fig.canvas.copy_from_bbox(ax1.bbox)

    plt.show(block=False)
    k = 0


    try:
        while True:
            msg = bus.recv(1)
            if msg is not None:
                y = np.roll(y, 1)
                value = struct.unpack('f', msg.data)
                y[0] = np.int8(value)
                line.set_data(x, y)
                fig.canvas.restore_region(ax2background)
                ax1.draw_artist(line)
                ax1.draw_artist(text)
                fig.canvas.blit(ax1.bbox)
                fig.canvas.flush_events()
                # print('Message data {data}, value {value}, counter {counter}'.format(data=msg.data, value=value, counter=k))
                k += 1
                if k > 100:
                    k = 0

    except KeyboardInterrupt:
        pass
    finally:
        plt.show()
        bus.shutdown()


if __name__ == "__main__":
    main()

The script assumes that the can message carries one single float over the first four uint8 and just plots it.