This Lesson finishes the work that was begun in Python with Arduino LESSON 4. In that lesson we built the circuit and programmed the arduino to measure the distance to a target and the color of the target. The program then output that data to the serial port. In today’s lesson we will use python to read that data stream, and use the data to dynamically update a virtual world we create.
You will need to start with the work in LESSON 4 to get your circuit working, and your arduino programmed up. Once you have done that, you are ready to use Python to program up your virtual world. Remember you will need to have the pyserial and the vPython libraries loaded. We showed how to install the software in Python with Arduino LESSON 2.
In the video we will go through the process step-by-step to create a virtual world. The code we end up with is posted below. You should not copy and paste the code, but just glance at it if you get stuck. In the end, you should develop your own virtual world and just use mine as a guide if you need more help.
Python
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
## This program reads data over the serial port
## from that arduino. You have to read an entire line of data
## and then you have to parse it into different number values
## Then those R, G, B numbers are used to make the color of
## a visual object in python change.
importserial#import serial library
fromvisual import*#import vPython library
MyScene=display(title='My Virtual World')#Create your scene and give it a title.
MyScene.width=800#We can set the dimension of your visual box. 800X800 pixels works well on my screen
MyScene.height=800
MyScene.autoscale=False#We want to set the range of the scene manually for better control. Turn autoscale off
MyScene.range=(12,12,12)#Set range of your scene to be 12 inches by 12 inches by 12 inches.
target=box(length=.1,width=10,height=5,pos=(-6,0,0))#Create the object that will represent your target (which is a colored card for our project)
myBoxEnd=box(length=.1,width=10,height=5,pos=(-8.5,0,0))#This object is the little square that is the back of the ultrasonic sensor
myTube2=cylinder(color=color.blue,pos=(-8.5,0,-2.5),radius=1.5,length=2.5)#One of the 'tubes' in the front of the ultrasonic sensor
sensorData=serial.Serial('com11',115200)# Create senorData object to read serial port data coming from arduino
whileTrue:#This is a while loop that will loop forever, since True is always True.
rate(25)#We need to tell Vpython how fast to go through the loop. 25 times a second works pretty well
while(sensorData.inWaiting()==0):# Wait here untill there is data on the Serial Port
pass# Do nothing, just loop until data arrives
textline=sensorData.readline()# read the entire line of text
dataNums=textline.split(',')#Remember to split the line of text into an array at the commas
red=float(dataNums[0])# Make variables for Red, Blue, Green. Remember
green=float(dataNums[1])# the array was read as text, so must be converted
blue=float(dataNums[2])# to numbers with float command
dist=float(dataNums[3])# last number in the list is the distance
blue=blue*.7#On my sensor, blue is always a little too strong, so I tone it down a little
if(dist>=1.5anddist<=2.25):#only change color or target if target is between 1.5 and 2.25 inches from sensor
target.color=(red/255.,green/255.,blue/255.)#This keeps color from flickering.
target.pos=vector(-6+dist,0,0)#Adjust the position of the target object to match the distance of the real target from the real sensor
The video explains each line of the code. Play around and tweak the values and see the effect on your virtual scene. Now your assignment is to take what you have learned here, and continue to expand your virtual world. Add objects to your virtual scene. Perhaps build an object for the breadboard, color sensor and arduino. I will give you several days to do this, and then when I come around for a project grade, I will want to see who has built the most impressive virtual scene. You should go well beyond the simple demonstration I have done here.
In this tutorial we will show how you can use Python with the Vpython library to begin to create some pretty cool graphics for presenting sensor data from the Arduino. For this tutorial, we will be using the HC-SR04 ultrasonic sensor, which you learned about in Arduino LESSON 17,Arduino LESSON 18, and Arduino LESSON 20. Please review those lessons if you are not familiar with the HC-SR04.
The circuit is very simple and can be connected from this schematic:
Since the object of this lesson is to show what you can do using Python and Vpython, I will not go through the arduino code step-by-step. You can go back to the earlier lessons for more info on the HC-SR04 sensor. The code that will allow the sensor to make distance measurements is presented again here. The one thing to note is that since we are going to be using Python to graphically present the data coming from the sensor, we want to simply send the distance measurement over the serial port, and no words or anything else, just the raw distance measurement. This will simplify things on the Python side. Remember that when we do a Serial.println() command in arduino, we can read whatever is printed into Python, as we learned in Arduino and Python LESSON 2. The code below is what you need on the Arduino side.
Arduino
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
inttrigPin=13;//Sensor Trig pin connected to Arduino pin 13
intechoPin=11;//Sensor Echo pin connected to Arduino pin 11
floatpingTime;//time for ping to travel from sensor to target and return
floattargetDistance;//Distance to Target in inches
floatspeedOfSound=776.5;//Speed of sound in miles per hour when temp is 77 degrees.
voidsetup(){
// put your setup code here, to run once:
Serial.begin(9600);
pinMode(trigPin,OUTPUT);
pinMode(echoPin,INPUT);
}
voidloop(){
// put your main code here, to run repeatedly:
digitalWrite(trigPin,LOW);//Set trigger pin low
delayMicroseconds(2000);//Let signal settle
digitalWrite(trigPin,HIGH);//Set trigPin high
delayMicroseconds(15);//Delay in high state
digitalWrite(trigPin,LOW);//ping has now been sent
delayMicroseconds(10);//Delay in low state
pingTime=pulseIn(echoPin,HIGH);//pingTime is presented in microceconds
pingTime=pingTime/1000000;//convert pingTime to seconds by dividing by 1000000 (microseconds in a second)
pingTime=pingTime/3600;//convert pingtime to hourse by dividing by 3600 (seconds in an hour)
targetDistance=speedOfSound*pingTime;//This will be in miles, since speed of sound was miles per hour
targetDistance=targetDistance/2;//Remember ping travels to target and back from target, so you must divide by 2 for actual target distance.
targetDistance=targetDistance*63360;//Convert miles to inches by multipling by 63360 (inches per mile)
Serial.println(targetDistance);
delay(100);//delay tenth of a second to slow things down a little.
}
This code will be constantly sending the distance to the target out over the Serial port. On the Python side, our first task is to read that data in over the serial port. To do this, you must import the serial library. (Instructions on installing Pyserial are in Arduino with Python LESSON2.) You then create a serial object which will be used to read the data. In the sample below, we call the object ‘arduinoSerialData’. We then create a While loop that loops continuously. Inside that loop we check to see if there is any data available on the serial port, and if there is, we read it into the variable myData, and print it.
Python
1
2
3
4
5
6
7
8
9
importserial#Import Serial Library
arduinoSerialData=serial.Serial('com11',9600)#Create Serial port object called arduinoSerialData
while(1==1):
if(arduinoSerialData.inWaiting()>0):
myData=arduinoSerialData.readline()
printmyData
Remember than in the line that creates the adruinoSerialData object, you need to change the com port to whatever com port your arduino is sending on. You can see this by looking under tools- port in your arduino IDE window. Also, this format is for windows machines. You would have to adjust for apple computers.
It is important to remember that the command .readline() in Python will read a string, so we need to remember that myData is a string, and if we want to use it as a number we will need to convert it to a float, with something like distance = float (myData). Then distance will be a normal number, not a string.
As a first demonstration lets create an object using the vPython library that is a cylinder. We will need to import the vPython library, and we will create the object before the while loop, and then inside the while loop, we will adjust the length parameter of the cylinder. We will make the cylinder with a length of six inches, we will make it yellow in color, and with a radius of 1/2 inch. Also, when we are using Vpython and dynamically updating a graphic object, inside the loop that is adjusting the graphic, we have to issue a rate() command. The rate command tells vPython how many times a second you want to go through the loop. You need to play with this command, so that it gives smooth graphics for the rate at which the Arduino is sending data. rate(20) is sometimes a good starting point. So, our code now looks like this:
Python
1
2
3
4
5
6
7
8
9
10
11
12
importserial#Import Serial Library
fromvisual import*#Import all the vPython library
arduinoSerialData=serial.Serial('com11',9600)#Create an object for the Serial port. Adjust 'com11' to whatever port your arduino is sending to.
while(1==1):#Create a loop that continues to read and display the data
rate(20)#Tell vpython to run this loop 20 times a second
if(arduinoSerialData.inWaiting()>0):#Check to see if a data point is available on the serial port
myData=arduinoSerialData.readline()#Read the distance measure as a string
printmyData#Print the measurement to confirm things are working
distance=float(myData)#convert reading to a floating point number
measuringRod.length=distance#Change the length of your measuring rod to your last measurement
So, this is pretty cool! It creates a little rod, and the length of the little rod dynamically changes in response to how far your target is from your sensor. This gives a very nice qualitative visual to what is happening in the real world with your sensor. Often times you also want quantitative indication of the data. We can do that by adding a label. Before the while loop, we will create a label object called lengthLabel. We will position it up just a little bit, so it is not on top of the measuringRod. We will set the label initially to ‘Target Distance is: ‘. We will also want to set box=false, since we do not want a box around our text. Then, a good height for the label is about 30 pixels. You can play around with all these settings. We create the lengthLabel before the while loop, but then inside the while loop we dynamically update the lengthLabel.text parameter. We set it to our myLabel string, which is dynamically being updated to be the concatination of the string ‘Target Distance is: ‘ and the string myData. Remember, myData is read as a string, and so we use that variable, and not distance, which is a number, not a string. Pulling this together leads to this code:
Arduino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
importserial#Import Serial Library
fromvisualimport*#Import all the vPython library
arduinoSerialData=serial.Serial('com11',9600)#Create an object for the Serial port. Adjust 'com11' to whatever port your arduino is sending to.
lengthLabel=label(pos=(0,1,0),text='Target Distance is: ',box=false,height=30)
while(1==1):#Create a loop that continues to read and display the data
rate(20)#Tell vpython to run this loop 20 times a second
if(arduinoSerialData.inWaiting()>0):#Check to see if a data point is available on the serial port
myData=arduinoSerialData.readline()#Read the distance measure as a string
printmyData#Print the measurement to confirm things are working
distance=float(myData)#convert reading to a floating point number
measuringRod.length=distance#Change the length of your measuring rod to your last measurement
myLabel='Target Distance is: '+myData#Create label by appending string myData to string
lengthLabel.text=myLabel#display updated myLabel on your graphic
Lets keep playing around with this. The graphic will begin to look more like a’Virtual World’ if we create a box to represent the target we are using in the real world. I will make the box the dimensions of the real target, which is .2 X 3 X 3. Also, I will make it green, just like the target in the real world. In order to better fill our viewing window, I will move the cylinder down by about 2 inches, so its new position will be (-3,-2,0). For box type objects position is measured from the center, so I will need to also move the target box down by about .5, so it will coincide with the measuring rod. this is because 1/2 the target would be 1.5, and moving down by an additional .5 will make it coincide with the measuringRod.
Finally, I need to dynamically update the position of the target box in the while loop. I will need it to be placed at target.pos=(-3+distance,-.5,0). Along the x-axis this will put it right at the end of the rod, and will keep it properly positioned in y, as before. Bringing all this code together we get:
Arduino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
importserial#Import Serial Library
fromvisualimport*#Import all the vPython library
arduinoSerialData=serial.Serial('com11',9600)#Create an object for the Serial port. Adjust 'com11' to whatever port your arduino is sending to.
while(1==1):#Create a loop that continues to read and display the data
rate(20)#Tell vpython to run this loop 20 times a second
if(arduinoSerialData.inWaiting()>0):#Check to see if a data point is available on the serial port
myData=arduinoSerialData.readline()#Read the distance measure as a string
printmyData#Print the measurement to confirm things are working
distance=float(myData)#convert reading to a floating point number
measuringRod.length=distance#Change the length of your measuring rod to your last measurement
target.pos=(-3+distance,-.5,0)
myLabel='Target Distance is: '+myData#Create label by appending string myData to string
lengthLabel.text=myLabel#display updated myLabel on your graphic
Play around with the parameters until you get something you are happy with. Also, you can change your view of the visual once the program is running. Right mouse click and you can change your positional view of the virtual world you have created. Press and scroll the mouse wheel to change the zoom.
OK, you need to start exploring and creating your own virtual world. Go ahead and see if you can create additional objects to make your virtual world more realistic. Try creating a graphic for the sensor itself, and maybe even your PC board and arduino! You can refer to the Vpython site for more details on the 3D objects you can create, and their parameters at:
http://vpython.org/contents/docs/cylinder.html
Also, if the discription in this lesson is confusing at all please just watch the video, and I will take you through things one step at a time.
Making The World a Better Place One High Tech Project at a Time. Enjoy!