Introduction: Road Monitoring

Today we’re gonna show you how we set up a road anomalies monitoring system based on accelerometers, LoRaWAN, Amazon Web Services and Google Cloud API.

Step 1: Requirements

  • DISCO-L072CZ-LRWAN1 board
  • X-NUCLEO-IKS01A2 expansion module (for the accellerometer)
  • X-NUCLEO-GNSS1A1 (for localization)
  • An AWS account
  • A Google Cloud Platform account

Step 2: Building and Flashing the Sensor’s Firmware

Connect the IKS01A2 and GNSS1A1 on top of the board through the GPIO pins. Download the firmware's code from GitHub. Create (if you don’t already have one) an account on ARM Mbed and import the code in the repo on the online compiler. Set the target platform to DISCO-L072CZ-LRWAN1 and save the project.
Now head to The Things Network and create an account if you don’t have it already. Create an application, create a new device inside the application and set the connection mode to OTAA. Grab the appropriate parameters to fill the following fields in the mbed_app.json file: "lora.appskey", "lora.nwkskey", "lora.device-address".

The sensor will periodically record accelerometer and GNSS data and send them through the LoRa connection to the nearest gateway, which will forward them to our application on The Things Network. The next step is to set up a cloud server, and an HTTP integration on TTN.

Step 3: Setting Up the Cloud

Now we are ready to set up the cloud infrastructure that will collect and aggregate the data from all the deployed boards. This infrastructure is shown in the figure below and it is composed by:

  • Kinesis, to handle the incoming data stream;
  • Lambda, to filter and preprocess the data before storing it;
  • S3, to store all the data;
  • EC2, to analyze data and to host our front-end.

Step 4: Set Up AWS Lambda

We will illustrate the steps necessary to set up this infrastructure, starting with Lambda.

  1. Login with your AWS account, and from the console main page and go to Lambda
  2. Click on Create Function
  3. In the upper part of the page it should be selected Author from Scratch. Then complete the other fields as in picture and then click Create Function
  4. Now that you have created a AWS Lambda function, go to https://github.com/roadteam/data-server and copy the content of the aws_lambda.py file into the editor that you find the second half of the page. Your Lambda function is now ready :)

Step 5: Set Up AWS Kinesis Firehose Data Stream

  1. Return now to AWS console main page, and into Services go to Kinesis
  2. Now you are in the main page of Kinesis. At the right of the page, under ‘Kinesis Firehose delivery streams’, select ‘Create new delivery stream’
  3. In ‘Delivery stream name’ write ‘road-monitoring-stream’. Leave the other fields to default and click next
  4. Now under ‘Transform source records with AWS Lambda’ select Enabled, and as Lambda function click on the newly created ‘road-monitoring-lambda’. Do not worry if a warning about the function timeout pops up, since the operation that we are doing are not computationally expensive. Leave the other fields to default and click next
  5. As destination select Amazon S3, and as S3 destination select Create New. As bucket name enter ‘road-monitoring-bucket’ and then go. Now leave the other fields to default and click Next
  6. You may want to set Buffer size to 1MB and Buffer interval to 60 seconds. The buffer will be flushed to S3 whenever one of the two condition is satisfied. Do not leave the page, see next step

Step 6: Set Up IAM Role for Kinesis

Now we set up the security permissions for Kinesis, since it must call the Lambda function for preprocessing and then it will write on S3

  1. At the bottom of the page that you are in ‘IAM role’ select ‘Create new of choose’, create a new IAM role as in picture and click Allow
  2. Now you are back to the previous page, click next. Now you may want to double-check all the parameters. When finished click on ‘Create Delivery Stream’

The Kinesis-Lambda-S3 pipeline is up and running!

Step 7: Set Up AWS EC2

Now we will set up an EC2 instance with some APIs that will permit us to push and pull data from the AWS cloud and also the server where to host our application frontend. In production environment, you may want to publish API using the more scalable AWS API Gateway.

  1. From the AWS console main page, go to EC2 service
  2. Click on Launch Instance
  3. In the upper search bar paste this code: ‘ami-08935252a36e25f85’, that is the identificative code of the preconfigured virtual machine to use. Click Select on the right
  4. Select t2.micro from the ‘Type’ column and click ‘Review and launch’. Do not launch the instance yet, go to the next step

Step 8: Set Up IAM Security Role for EC2

  1. Before launching we want to modify the security group of our instance. To do this, at the far right of section ‘Security Groups’ click ‘Edit security groups’
    Set up a new security group as follows. This basically configures the firewall of your instance exposing port 22 for SSH connection and port 80 for http services
  2. Click again ‘Review and Launch’. Now check that all the parameters are set. When finished click Launch
  3. On click a new window will pop up to set up a key pair for ssh connection to the instance. Select ‘Create new key pair’ and as name enter ‘ec2-road-monitoring’. Click Download key pair. It is extremely important that this file gets lost or (worse) is insecurely stored: you won’t be able to download the key again. Once downloaded the .pem key the instance is ready to be launched

Step 9: Access Your EC2 Instance

Your fresh EC2 instance is up in the AWS cloud. You can connect to it with the key file downloaded before (for this tutorial we assume that you know the basics of ssh). You can retrieve the instance’s IP by selecting it in the dashboard in the ‘Description’ section as follows:
You can use both your public IP or your public DNS it’s the same. With a ssh client now enter the command:

ssh -i ec2-road-monitoring.pem ec2-user@YOUR-IP-ADDR-OR-DNS<br>

where ec2-road-monitoring.pem is your key generated before.

Now proceed to pull the server-side code through

git clone --recursive  https://github.com/roadteam/data-server

Step 10: Retrieve Google Maps API

We’re almost done. Now we have to set up the google maps API in our html page in order to display the map with the waypoints to the user:

  1. Login to your Google account and go to https://cloud.google.com/maps-platform/
  2. Click to ‘Get Started’ on the left of the page
  3. Select ‘Maps’ in the menu and then click continue
  4. As project name enter ‘road-monitoring’ and click Next
  5. Enter your billing details and click Continue
  6. Now your project is ready and we are going to get the API key by clicking to APIs & Services -> Credentials

Step 11: Launch Server

And there it is your API key. The last thing you have to do is to go to data_visualization/anomalies_map.html and copy your key to the end of the file like so, substituting ‘YOUR-KEY-HERE’

Now all is set and ready to go! To make it start execute in the EC2 instance: ‘cd data-server’ ‘python flask_app.py’

Enter in your browser the ip or dns address of your EC2 instance, you should see the anomalies map with some dummy data

Step 12: Perform HTTP Integration on the Things Network

Now that we have all the backend infrastructure up and running we can process to perform the HTTP integration.

  1. Create a new application and register your device. We assume basic knowledge of TTN, if not refer to the quick start guide
  2. In your application menu select ‘Integrations’ and then ‘add integration’
  3. Select HTTP integration
  4. Enter the fields following the image and replacing with your EC2 ip or public DNS

Step 13: Data Processing

For each tuple of data T collected from the sensor you must perform the following steps:

  1. Get the set of tuples that have GPS coordinates falling in the local area of T. The local area is all the tuples that are 100 meters around T.
  2. For each nearest tuple N compute the square mean of the accelerometer Z-axis in N. In pseudocode: mean = sum ( [ x.Z_accel ** 2 for x in near ] ) / near.size
  3. Compute the squared standard deviation. In pseudocode: std = sum ( [ ( x.Z_accel ** 2 - mean ) ** 2 for x in near ] )
  4. Output in this format: lat, long, Z_accel **2 , mean, std

To compute the local area use the GPS distance in meters. In C++:

#define D2R (M_PI / 180.0)
#define EARTH_RAY 6371

double distance(double lat1, double long1, double lat2, double long2) {
    double dlong = (long2 - long1) * D2R;
    double dlat = (lat2 - lat1) * D2R;
    double a = pow(sin(dlat/2.0), 2) + cos(lat1*D2R)
        * cos(lat2*D2R) * pow(sin(dlong/2.0), 2);
    double c = 2 * atan2(sqrt(a), sqrt(1-a));

Now, using the intermediate data generated in the previous step, detect the anomalies and do a naive classification using this snipped applied to each line:

line = map(float, line.split(",")) 
v = line[2] 
mean = line[3] 
std = line[4] 
if v < (mean + std*3) or v > (mean + std*3):
    if v < (mean + std*2) or v > (mean + std*2):
        if v < (mean - std) or v > (mean + std):
            o.append([1, line[0], line[1]])
        else:
            o.append([2, line[0], line[1]])
    else:
        o.append([3, line[0], line[1]])

The anomalies are classified using the 68–95–99.7 rule https://en.wikipedia.org/wiki/68%E2%80%9395%E2%80...

Now you have a collection with this format [type, lat, long].

The type meaning is the following:

  1. Slight anomaly, probably irrelevant
  2. Medium anomaly
  3. Critical anomaly

Step 14: Visualization

To understand and maybe change the visualization part we must learn how to use custom markers, a feature of Google Maps API https://developers.google.com/maps/documentation/...


Firstly, the map must be initilizated in a callback:

function initMap() {
  data = queryData();
  map = new google.maps.Map(document.getElementById('map'), {
    zoom: 15,
    center: {lat: data[0][1], lng: data[0][2]}
  });

  remark();
}

Specify the name of this callback in the url (we inserted here before our API key) in an HTML tag:

script async defer src="https://maps.googleapis.com/maps/api/js?key=[KEY]&callback=initMap"

A marker can be inserted in the map when the object is created:

new google.maps.Marker({
position: {lat: LATITUDE, lng: LONGITUDE}, map: map, icon: “/path/to/icon.png” })

You can see in the code that for each data in the dataset of the anomalies a marker is inserted (see the remark() function) and the icon is based on the class of the anomaly.
When running this into the browser we can explore a map in which anomalies can be filtered using checkboxes, as seen in picture.

Step 15: Credits and External Links

This project was made by Giovanni De Luca, Andrea Fioraldi and Pietro Spadaccino, first year MSc in Engineering in Computer Science students at Sapienza University of Rome.