DaddyOh's Blog

Making, Cooking, Living

Live Charting From Mbed Devices

| Comments

PID Control Learning System Part 1

I’ve not had much experience with PID controllers, so I thought it might be useful to build a test platform with these components:

  • A microcontoller platform (mbed, Arduino)
  • DC brushed motor
  • A way to measure motor rotational speed in RPM (Photo-Interrupt transistor or Hall Effect transistor)
  • Microcontroller software to measure, control and report speed
  • Ability to collect data in realtime and live chart the data

To provide visual feedback and the ability to iterate quickly through a bunch of manual settings in the software, I wanted the ability to collect data from the microcontroller platform in realtime and chart that data as it happens.

This post serves as a proof-of-concept and focuses on getting fake data from the microcontroller to my Mac OS X system via the USB Serial port and charting the data in Python in realtime.

Microcontroller Platform

I’ve been working with mbed devices and Arduinos lately and decided it would be fun to start with the mBuino mbed controller. It is inexpensive ($10) and has the pinouts needed to meet the requirements outlined above. The code should be easy to port to other platforms like the Arduinos or other mbed devices like my favorite, the NXP LPC1768.

The mBuino data generation program is simple and only generates fake data. It does not measure motor rotation. That way you can focus on just the fundamentals of data transmission, receiving data and charting.

main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/*
   generate sample data and send to the serial port

   loop runs a set number of times and then stops
   so data acquisition system end-point
   can write the data to a file if desired

   License: Apache License, Version 2.0
   http://www.apache.org/licenses/LICENSE-2.0.html
   December 20, 2014
   Copyright: Eric F Palmer, 2014
   Email: eric [at] ericfpalmer [dot] [com]
 */

#include "mbed.h"
#include "USBSerial.h"

USBSerial pc;  // defaults to 9,600 baud

float pi=3.14159265359;
float sValue; // sin value
float cValue; // cosine
int counter = 0;
// don't write so fast to the serial port that it can't keep up
// 30 ms is about 33.3 RPS [Hz] or 2000 RPM
// 13 ms is about 4,500 RPM
int delayMs = 13;

// put some randomness in the cosine value
float randValue(float cValue)
{
    int rValue = 500 - rand() % 1001;
    return cValue + rValue / 1000.0;
}

int main()
{
    wait_ms(3000); // wait so that you can start up a listener on the mac os x system
    pc.printf("#Start\r\n");
    // 72 points for each 0 to 360 degree output loop
    float degree = 0.0;
    while (counter<10*72) {
        sValue = sin(degree*pi/180);
        cValue = cos(degree*pi/180);
        // CSV like output, 5 values, 2 can serve as X axis and 3 can server as Y axis
        // gives you lots of choices
        pc.printf("%i,%f,%f,%f,%f\r\n", counter, degree, sValue, cValue, randValue(cValue));
        degree = degree + 5;
        wait_ms(delayMs);
        counter = counter + 1;
    }
    // write marker out so the other end-point can flush and close any files it might have open
    pc.printf("#Stop\r\n");
}

Testing the mbed Program You want to start a terminal window right after the mBuino is started. You can use the mac app terminal and issue this command:

screen /dev/tty.usbmodem1421 9600

This connects to the same serial port used by the mBuino. Note that your serial port may be different. I use Iterm2 instead of terminal and have a profile set up so I can start up this terminal window, run the screen command all with a simple hot key.

After a couple of seconds you should see output start to scroll past like:

#Start
0,0.000000,0.000000,1.000000,1.334000
1,5.000000,0.087156,0.996195,0.801195
2,10.000000,0.173648,0.984808,1.407808
3,15.000000,0.258819,0.965926,0.857926
4,20.000000,0.342020,0.939693,1.180693
...
716,3580.000000,-0.342016,0.939694,1.270694
717,3585.000000,-0.258816,0.965927,1.015927
718,3590.000000,-0.173643,0.984809,0.588809
719,3595.000000,-0.087152,0.996195,0.622195
#Stop

Charting Software

Charting is done using matplotlib version 1.4.2 under Python version 2.7.5 on Mac OS X.

The liveCharting Phtho program:

liveChart.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# Inspired from  http://www.lebsanft.org/?p=48
# License: Apache License, Version 2.0
# http://www.apache.org/licenses/LICENSE-2.0.html
# December 20, 2014
# Copyright: Eric F Palmer, 2014
# Derived from http://www.lebsanft.org/?p=48
# Email: eric [at] ericfpalmer [dot] [com]
# xaxis on plot is just arbitrary number of the point ploted
# data is saved to a file for more robust analysis in other tools (e.g., excel, R Studio, etc.)

import serial
import numpy as np
from matplotlib import pyplot as plt
import sys
import time


def usage():
    print ""
    print "Arguments supplied ", str(sys.argv[1:])
    print "Usage: python liveChart.py [mbuino|lpc1768|arduino] dataFileName"
    print "       Note: arduino not yet implemented"
    print "       program will save all data lines to dataFileName"
    print "Example:"
    print "       python LiveChart.py mbuino data.csv"


if not len(sys.argv) == 3:
    print "2 arguments expected", len(sys.argv), "found."
    usage()
    exit(1)

#open serial port
if str(sys.argv[1]) == "mbuino":
    ser = serial.Serial('/dev/tty.usbmodem1421', 9600, timeout=1, bytesize=8, parity='N', stopbits=1)
elif str(sys.argv[1]) == "lpc1768":
    ser = serial.Serial('/dev/tty.usbmodem1422', 57600, timeout=1, bytesize=8, parity='N', stopbits=1)
elif str(sys.argv[1]) == "arduino":
    print "Arduino devices not yet implemented"
    usage()
    exit(1)
else:
    print "Microcontroller Device", str(sys.argv[1]), "not supported"
    usage()

#open output datafile
f = open(str(sys.argv[2]), 'w')
f.write('Data capture\n')

# set plot to be interactive
plt.ion()

print "Setting up matplotlib for chart"
plt.figure(figsize=(10, 5))
plt.ylabel('Fake Data')
plt.title("Plot sin() and some random number\nnear the cos() value")
# 72 pts for 360 degrees so lets display 2 cycles
y1data = [0] * 72 * 2
y2data = [0] * 72 * 2
ax1 = plt.axes()
line1, = plt.plot(y1data)
line2, = plt.plot(y2data)
# limit the range of values for Y
plt.ylim([-1.5, 1.5])

#start data collection
plotData = True
while plotData:
    data = ser.readline().rstrip()
    #todo remove the next line
    # print data # just to check
    values = data.split(",")
    if len(values) == 5: # the number of data values expected in CSV string
        y1data.append(values[2]) # sin value
        y2data.append(values[4]) # some random values
        del y1data[0]
        del y2data[0]
        # TODO what does this do
        line1.set_xdata(np.arange(len(y1data)))
        line1.set_ydata(y1data)  # update the data
        line2.set_xdata(np.arange(len(y2data)))
        line2.set_ydata(y2data)  # update the data
        plt.draw()
    elif (data == "#Stop"):
        f.flush()
        f.close
        plotData = False
    else:
        # bad data received so just ignore that line
        pass
    f.write(data + "\n")
print "#Stop Encountered, ending live chart\nEnter ctrl-C to end program"

response = raw_input('Do you want to save the chart as chart.png? [Y/N]: ').strip()
if response.capitalize() == 'Y':
    print "Saving the chart to \"plot.png\" in the current working directory"
    plt.savefig("plot.png", format='png')

Run this code from the command line:

python liveChart.py mbuino data.csv

Once the mBuino sends this line:

#Stop

liveChart.py will flush the data to the file you supplied on the command line and will prompt you for saving the final chart to a file. If you respond with “Y” then the plot will be saved in the current working directory.

Example saved plot file


Sample plot.png

What About MS Windows?

I focus 99% of my hobby time on MAC OS X and a variety of microcontrollers. It should be possible, and maybe easy, to port this to MS Windows Systems. This article on Reading Arduino Serial using Python refers to the use of Windows. If you get this working on windows please post a comment and let us know about your experiences.

What is Next?

This live charting proof-of-concept gives us a mbed model for writing data to a live charting system and provides sample code in Python for live charting. Now this is working I have the ability to measure motor RPM and control it using a PID controller. My hope is I will be able to learn a lot about PID controllers. So it is time to finish my motor control mbed code and see what I can learn.

I hope you find this useful, if you do please leave a comment.

Now Go Make Something!

Comments