Contents

Introduction

In this IoT project, we use a Raspberry Pi, a web cam and ThingSpeak to analyze traffic on a busy highway. We deploy a traffic monitoring algorithm to the Raspberry Pi device, and we analyze and visualize the traffic patterns with ThingSpeak, a cloud data aggregator. This project stores data in channel 38629 on ThingSpeak.

Analytics are everywhere in the Internet of Things (IoT) occurring at:

  1. The edge node
  2. Offline on the desktop
  3. In the cloud

For this project, we show how to develop analytics for the edge device and how to perform exploratory analysis on data collected on the cloud. We also illustrate a simple example of how to perform automated online analysis in the cloud. The example uses ThingSpeak and MATLAB to perform the analyses.

Hardware Setup

We constructed the traffic monitor using a Raspberry Pi 2, and a USB webcam. The webcam, a Logitech HD Pro C920, was mounted on a Monoprice® flexible mini tripod. We placed the camera near a window on the 4th floor of our office building that overlooks Route 9 in Natick, MA. We angled the camera to have a clear view of both sides of the highway. The camera was connected to one USB port of the Raspberry Pi, and we placed a Wi-Pi™ WLAN USB dongle on another USB port. We then connected the Raspberry Pi to the wireless network in the building. The complete parts list is shown below.

Because we did not want to send high-bandwidth video images to the cloud, we chose to detect the vehicles at the edge using the processor on the Raspberry Pi 2. We then send the count value to the data aggregator at an update rate of once every 15 seconds, the maximum data rate allowed by ThingSpeak.

Developing the Traffic Monitoring Algorithm

To develop the traffic monitoring algorithm, we used Simulink, Image Processing Toolbox, Computer Vision System Toolbox and the Simulink Support Package for Raspberry Pi Hardware. Simulink is a modeling environment that can to automatically generate code that can run on an embedded controller. In this example, Simulink generates code that runs on the Raspberry Pi 2. The Simulink model for the traffic monitoring algorithm is shown below.

To develop the algorithm, we used the external mode capability of Simulink. In this mode, Simulink gathers the video stream from the Raspberry Pi, and the user can view the video on an external monitor using the SDL Video Display block while the algorithm is running, as shown here.

Moving from left to right in the model, we can see the USB webcam connected to the Raspberry Pi captures video with a region of interest selected. Next the vision.ForegroundDetector estimates the foreground pixels of the video sequence captured from the stationary webcam. The vision.ForegroundDetector estimates the background using Gaussian mixture models and produces a foreground mask highlighting foreground objects-in this case, moving cars.

The foreground mask is then post-processed using Median Filter to remove unwanted noise. It is then analyzed using the Blob Analysis block, which computes centroids of the blob containing the cars.

Finally, the Car Density Block determines the number of cars traveling in each direction in that video frame. This block breaks up the region of interest into two sub sections along the highway median. It then counts the cars above and below that line. Lastly the data is sent to the data aggregator using the ThingSpeak Write block. In this block, we send a value for eastbound vehicle count and westbound vehicle count. We send both values to ThingSpeak (channel 38629), and store the eastbound value in Field 2 and the westbound value in Field 1.

Limitations of the Traffic Monitoring Algorithm

The algorithm is an estimate because it does not track individual cars. Instead, it uses one video frame every 15 seconds to estimate the traffic level. This might misrepresent traffic levels as one video frame might have more or less cars than actually drove by in the 15 second period. Here are some other limitations of the algorithm:

  1. Because we are not using a night-vision camera, the algorithm may not give an accurate result at nighttime.
  2. If there is traffic jam on the road segment, cars at or near a standstill become part of background and may not be properly counted.
  3. If there is abrupt change in lighting condition, the algorithm takes couple of seconds to readjust itself.
  4. The algorithm may not count partially occluded cars.

Deploying the Algorithm to the Hardware

Once the algorithm is developed, we can deploy the algorithm to the hardware, and it will run without being connecting to Simulink. This can be accomplished using the Simulink and the Simulink Support Package for Raspberry Pi hardware. For more information on programming the Raspberry Pi with Simulink, see Raspberry Pi Programming Using Simulink (video).

Analyzing Data on ThingSpeak

With the traffic monitoring algorithm deployed on the Raspberry Pi device, we can start to analyze the raw data sent by the Raspberry Pi to ThingSpeak by fetching it from thingspeak.com. To simplify the retrieval of the data from ThingSpeak, we use the functions from the ThingSpeak Support Toolbox, available on MATLAB Central File Exchange.

Reading One Week of Data into MATLAB

We begin by specifying a start date and an end date using a datetime object. Because we are sending data to ThingSpeak once every 15 seconds, we have approximately 40,000 data points to retrieve. ThingSpeak allows only 8000 points in a single read operation, so we create a for loop to gather the data in batches. We then append the traffic and time data from each iteration into two vectors, called alltrafficData and timestamp.

endDate = datetime('12/23/2017', 'InputFormat', 'MM/dd/yyyy');
startDate = datetime('12/16/2017', 'InputFormat', 'MM/dd/yyyy');
% Create date vector
dateVector = startDate: endDate;
% check to see that the last dateVector value is the same as endDate, if
% not append it
if (dateVector(end) ~= endDate)
   dateVector = [dateVector, endDate];
end
alltrafficData = [];
timestamp = [];
% Read data in chunks because ThingSpeak has a limit of 8000 pts per read
for dayCount = 1:length(dateVector)-1
   dateRange = [dateVector(dayCount), dateVector(dayCount+1)];
   [channelData, t] = thingSpeakRead(38629, 'DateRange', dateRange);
   [alltrafficData] = [alltrafficData; channelData ];
   [timestamp] = [timestamp; t];
end

Plotting Raw Traffic Data

Next, we plot the data and label the graph. The daily fluctuations in traffic are clearly visible from the raw data. Although harder to see from the raw data, you can observe a different pattern on the weekend days (12/16 and 12/17) compared to the weekdays (12/18-12/22). The weekdays show stronger morning and afternoon peaks due to commuter traffic.

figure
plot(timestamp, alltrafficData)
datetick
xlabel('Date')
ylabel('Traffic Density')
grid on
title('Traffic Density for the week starting December 16')
legend('West Bound Traffic','East Bound Traffic')

Looking at Daily Mean Density for Eastbound and Westbound Traffic and Plotting

To visualize traffic density each day, we can average the traffic density observations and use a bar chart to visualize the data. We note that both the eastbound and westbound traffic on the weekend are lighter than the Monday traffic that follows the weekend.

eastTraffic = alltrafficData(:,2);
westTraffic = alltrafficData(:,1);
for i=1:4
    eastTraffic(i)=[];% remove a few  points to make divisible by 7
    westTraffic(i)=[];
end
% end
% Divide data into 7 chunks (approximates a 24 hour day)

dailymeaneast = mean(reshape(eastTraffic, floor(length(alltrafficData)/7),[]));
dailymeanwest = mean(reshape(westTraffic, floor(length(alltrafficData)/7),[]));
dates = dateVector; % convert to serial date for bar plot
dates(8) = [];
dates = datenum(dates);
figure
bar(dates,dailymeaneast)
grid on
xlabel('Date')
ylabel('Mean Traffic Density per observation')
title('East Bound Traffic Density for the week of December 16')
datetick
figure
bar(dates, dailymeanwest)
grid on
xlabel('Date')
ylabel('Mean Traffic Density per observation')
title('West Bound Traffic Density for the week of December 16')
datetick

Taking a Deeper Look at Individual Days during the Week and on the Weekend

To gain more insight into our traffic data, we look at traffic on the individual days. For each day, we specify a start time and a stop time.

clear all
startTime{1} = 'December 16, 2017 00:00:00';% weekend day
stopTime{1} = 'December 16, 2017 23:59:59';
startTime{2} = 'December 17, 2017 00:00:00';% weekend day
stopTime{2}= 'December 17, 2017 23:59:59';
startTime{3} = 'December 18, 2017 00:00:00';
stopTime{3} = 'December 18, 2017 23:59:59';
startTime{4} = 'December 19, 2017 00:00:00';
stopTime{4} = 'December 19, 2017 23:59:59';
startTime{5} = 'December 20, 2017 00:00:00';
stopTime{5} = 'December 20, 2017 23:59:59';
startTime{6 }= 'December 21, 2017 00:00:00';
stopTime{6} = 'December 21, 2017 23:59:59';
startTime{7}= 'December 22, 2017 00:00:00';
stopTime{7} = 'December 22, 2017 23:59:59';

Fetch Each Individual Day in a Loop

For each of the 7 days, we retrieve the data from ThingSpeak, and we downsample the data to remove short-term fluctuations. We then use the findpeaks function to plot and find the times where the traffic density is highest, and we make some observations for each day. For simplicity, we look at the eastbound data only.

for jj = 1:7
    % This loop executes once for each day of the week
startDate = datetime(startTime{jj}, 'InputFormat', 'MMMM d, yyyy HH:mm:ss ');
endDate = datetime(stopTime{jj}, 'InputFormat', 'MMMM d, yyyy HH:mm:ss ');
datevector = [startDate, endDate];
[Daily, t] = thingSpeakRead(38629, 'DateRange', datevector);
% Look at east bound traffic only
DailyEast = Daily(:, 2);
timestamp = datetime(t,'ConvertFrom','datenum');
dateAnalyzed = startTime{jj};
dateAnalyzed = {dateAnalyzed(1:(end-8))};

Downsample into 48 Bins of Approximately 30 Minute Chunks of Data and Find Peaks

The raw traffic data is very spiky and hard to visualize. If we want to see what time of day has the highest volume of traffic, we need to look at the data on a time scale larger than 15 seconds. To do this we divide the 24-hour day into 30 minute segments. Each segment begins near the top of the hour and ends at approximately 30 minutes later.

downsamplesize = floor(length(DailyEast)/48);
teastper30 = downsample(t, downsamplesize);
DailyEastper30(1:48) = 0; % pre-allocate
for k = 1:48
DailyEastper30(k) = mean(DailyEast(1+downsamplesize*(k-1):downsamplesize*k));
end
teastper30(1) = []; % start first bin at 12:30 am
timestampPer30=datetime(teastper30,'ConvertFrom','datenum');
% Find peaks and their times (locations)
[peaks,location] = findpeaks(DailyEastper30,  'Threshold',0.3, 'MinPeakHeight', 5);
% Plot peaks
figure
findpeaks(DailyEastper30, datenum(timestampPer30),'Threshold',0.3, 'MinPeakHeight', 5)
datetick
xlabel('Time of Day')
ylabel('Mean Traffic Density per 30 minute interval')
title(strcat('Traffic Density on ', {' '}, dateAnalyzed))
dateAnalyzed
peaktimes = timestampPer30(location)
dateAnalyzed =

  1×1 cell array

    {'December 16, 2017 '}


peaktimes = 

  11×1 datetime array

   16-Dec-2017 05:57:30
   16-Dec-2017 06:56:54
   16-Dec-2017 08:25:42
   16-Dec-2017 09:25:04
   16-Dec-2017 10:54:22
   16-Dec-2017 13:52:41
   16-Dec-2017 15:51:51
   16-Dec-2017 17:51:37
   16-Dec-2017 18:51:59
   16-Dec-2017 20:22:25
   16-Dec-2017 21:22:59

dateAnalyzed =

  1×1 cell array

    {'December 17, 2017 '}


peaktimes = 

  7×1 datetime array

   17-Dec-2017 06:56:34
   17-Dec-2017 08:55:31
   17-Dec-2017 11:24:24
   17-Dec-2017 13:52:50
   17-Dec-2017 15:51:07
   17-Dec-2017 17:51:00
   17-Dec-2017 19:21:29

dateAnalyzed =

  1×1 cell array

    {'December 18, 2017 '}


peaktimes = 

  7×1 datetime array

   18-Dec-2017 06:27:25
   18-Dec-2017 08:25:52
   18-Dec-2017 11:53:24
   18-Dec-2017 17:19:59
   18-Dec-2017 18:48:47
   18-Dec-2017 21:18:39
   18-Dec-2017 23:18:34

dateAnalyzed =

  1×1 cell array

    {'December 19, 2017 '}


peaktimes = 

  8×1 datetime array

   19-Dec-2017 07:25:22
   19-Dec-2017 09:53:41
   19-Dec-2017 11:53:26
   19-Dec-2017 13:22:26
   19-Dec-2017 16:49:53
   19-Dec-2017 17:49:00
   19-Dec-2017 18:48:22
   19-Dec-2017 20:17:51

dateAnalyzed =

  1×1 cell array

    {'December 20, 2017 '}


peaktimes = 

  6×1 datetime array

   20-Dec-2017 07:27:09
   20-Dec-2017 09:25:30
   20-Dec-2017 10:54:57
   20-Dec-2017 12:24:24
   20-Dec-2017 13:23:41
   20-Dec-2017 16:51:27

dateAnalyzed =

  1×1 cell array

    {'December 21, 2017 '}


peaktimes = 

  7×1 datetime array

   21-Dec-2017 06:26:26
   21-Dec-2017 07:55:24
   21-Dec-2017 14:21:12
   21-Dec-2017 16:49:10
   21-Dec-2017 17:48:22
   21-Dec-2017 18:47:34
   21-Dec-2017 21:18:04

dateAnalyzed =

  1×1 cell array

    {'December 22, 2017 '}


peaktimes = 

  10×1 datetime array

   22-Dec-2017 06:56:50
   22-Dec-2017 07:55:58
   22-Dec-2017 09:24:41
   22-Dec-2017 12:22:16
   22-Dec-2017 15:20:04
   22-Dec-2017 17:00:29
   22-Dec-2017 18:59:06
   22-Dec-2017 19:59:27
   22-Dec-2017 20:59:44
   22-Dec-2017 21:59:33

Weekend Observations for Saturday, December 16

On Saturday, we see a traffic pattern with many peaks scattered throughout the day. There appear to be two large peaks in the afternoon at 1:52pm and 3:51pm. This is not surprising as the highway connects many malls and shopping centers which are frequented on the weekends.

Weekend Observations for Sunday, December 17

Sunday looks similar to Saturday in that we have many peaks and two large peaks in the afternoon again at 1:52 and 3:51pm.

Observations for Monday, December 18

On Monday, we see a different pattern characteristic of people commuting to and from work. There is a large peak in the morning rush from 7:55 to 8:25am rush and two peaks in the evening rush at 5:19pm and 6:48pm. We even see a strong peak at 12 noon as people go out for lunch or perhaps do some last-minute holiday shopping.

Observations Tuesday, December 19

The observed pattern on Tuesday looks similar to Monday although we see two peaks in the morning and three in the evening on this day.

Observations for Wednesday, December 20

On Wednesday, the morning traffic density is heaviest from 6:57 to 7:27am, but the density is also strong for the 30 minutes before and after this peak period so there is a 90-minute stretch of moderately heavy traffic on this morning. In the evening, we see two strong peaks although our peak detector misses one of them because the threshold is set too high and it does not stand out enough from its nearest neighbor.

Observations for Thursday, December 21

Thursday looks like a typical weekday with 2 strong peaks in the morning and 2 or 3 strong peaks in the evening commuting rush.

Observations for Friday December 22

On Friday, we see a different pattern more like the weekend with heavy traffic peaks scattered throughout the day. Perhaps people are taking the day off to have an extended holiday weekend.

end

Online Analysis and Visualization: Creating MATLAB Visualizations inside ThingSpeak

So far, we’ve developed a traffic monitoring algorithm that was deployed onto the Raspberry Pi 2 hardware, and we've programmed it to send its results to the ThingSpeak IoT platform. Next, we brought the data back into MATLAB to do some offline analysis of the daily and weekly traffic patterns. We used the ThingSpeak Support Toolbox, available on MATLAB Central File Exchange to retrieve the data for analysis.

But what if we want to create a visualization indicating whether the current is traffic light, moderate or heavy? It’s very simple to do this with MATLAB offline, but ThingSpeak provides a MATLAB integration which enables us to do this online too. We write code to fetch traffic data for the last 15 minutes. We calculate the mean and set a threshold for moderate and heavy traffic based on our observations. We run this code on ThingSpeak using the MATLAB Visualizations App. This code will update whenever the ThingSpeak channel is viewed so we now have a remote means of gauging how heavy the traffic is on this highway.

Create a Dynamic Visualization of Traffic Density

Read Data for last 15 minutes

[data, time] = thingSpeakRead(38629, 'NumPoints', 60);

% Create Color coded rectangle
analyzedData = mean(data,'omitnan');
analyzedDataEast=analyzedData(2);
if analyzedDataEast<15
    colorstring='g'; % green is light traffic
elseif analyzedDataEast>50
        colorstring='r';% red is heavy traffic
else
        colorstring='y'; % yellow is moderate traffic
end
figure
rectangle('Position',[1,2,5,10],'FaceColor',colorstring,'EdgeColor',colorstring,...
    'LineWidth',3)
    ax=gca;
 set(ax, 'Visible','off')
 dim = [0.2 0.5 0.3 0.3];
 annotation('textbox',dim,'String','Current Traffic Condition','FitBoxToText','on');

Viewing the Data Online

We have now created a channel that reports the raw traffic density in each direction and estimates how heavy the traffic is right now. Because this channel is on ThingSpeak and the MATLAB Visualizations App is built in, we can look at this visualization at any time using any web browser on a PC or mobile device. To see the current traffic conditions, go to channel 38629 on ThingSpeak.

Conclusions and Acknowledgements

Analytics are everywhere in IoT! In this article, we demonstrated how to develop a traffic monitoring that can be deployed onto Raspberry Pi edge node and sends data to ThingSpeak. We showed how to use MATLAB for offline analysis by retrieving data from ThingSpeak and analyzing and visualizing daily and weekly traffic patterns. And we also showed how to perform custom online visualizations inside the ThingSpeak web service by using the MATLAB Visualizations App to create a color-coded live traffic indicator that updates continuously as traffic data arrives.

I would like to thank Mohammad Khan and Sandeep Hiremath for their contributions to this article.

Copyright 2018, The MathWorks, Inc.