This notebook uses ipywidgets. Run Jupyter with: jupyter nbextension enable --py --sys-prefix widgetsnbextension
The following exercises demonstrate various ways of programming the robot.
#Wake up the kernel
print('Hello World!')
Hello World!
%matplotlib inline
#This cell may take some time to run
# because the matplotlib requirement is quite large...
from ev3dev.auto import *
import time
from __future__ import division
from math import pi
from ipywidgets import widgets, interact
def wait(time_in_ms):
time.sleep( time_in_ms / 1000)
Familiarisation with driving:
#Drive demo based on https://github.com/rhempel/ev3dev-lang-python/blob/master/demo/auto-drive.py
#Originally Copyright (c) 2015 Denis Demidov <dennis.demidov@gmail.com>
# In this demo an Explor3r robot with touch sensor attachement drives
# autonomously. It drives forward until an obstacle is bumped (determined by
# the touch sensor), then turns in a random direction and continues. The robot
# slows down when it senses obstacle ahead (with the infrared sensor).
#
# The program may be stopped by pressing any button on the brick.
#
# This demonstrates usage of motors, sound, sensors, buttons, and leds.
from time import sleep
from random import choice, randint
from ev3dev.auto import *
# Connect two large motors on output ports B and C:
motors = [LargeMotor(address) for address in (OUTPUT_B, OUTPUT_C)]
# Every device in ev3dev has `connected` property. Use it to check that the
# device has actually been connected.
assert all([m.connected for m in motors]), \
"Two large motors should be connected to ports B and C"
def demo_start_motors(duty_cycle_sp=None):
"""
Start both motors. `run-direct` command will allow to vary motor
performance on the fly by adjusting `duty_cycle_sp` attribute.
"""
sp_set=False
for m in motors:
m.run_direct() if duty_cycle_sp is None else m.run_direct(duty_cycle_sp=duty_cycle_sp)
if m.duty_cycle_sp!=0: sp_set=True
if not sp_set:
print('**WARNING**: duty_cycle_sp is zero for both motors')
demo_start_motors()
demo_start_motors(75)
# Stop the motors before exiting.
def demo_stop_motors():
for m in motors:
m.stop()
demo_stop_motors()
def demo_turn():
"""
Turn the robot in random direction.
At the end of the turn, the motors will be switched off.
"""
# Turn the robot wheels in opposite directions from 1/4 to 3/4 of a second.
# Use `random.choice()` to decide which wheel will turn which way.
power = choice([(1, -1), (-1, 1)])
t = randint(250, 750)
for m, p in zip(motors, power):
m.run_timed(duty_cycle_sp=p*75, time_sp=t)
# Wait until both motors are stopped:
while any(m.state for m in motors):
sleep(0.1)
demo_start_motors()
demo_turn()
def demo_backup():
"""
Back away from an obstacle.
"""
# Sound backup alarm.
Sound.tone([(1000, 500, 500)] * 3).wait()
# Turn backup lights on:
for light in (Leds.LEFT, Leds.RIGHT):
Leds.set_color(light, Leds.RED)
# Stop both motors and reverse for 1.5 seconds.
# `run-timed` command will return immediately, so we will have to wait
# until both motors are stopped before continuing.
for m in motors:
m.stop(stop_command='brake')
m.run_timed(duty_cycle_sp=-50, time_sp=1500)
# When motor is stopped, its `state` attribute returns empty list.
# Wait until both motors are stopped:
while any(m.state for m in motors):
sleep(0.1)
# Turn backup lights off:
for light in (Leds.LEFT, Leds.RIGHT):
Leds.set_color(light, Leds.GREEN)
demo_start_motors()
demo_backup()
# Connect infrared and touch sensors.
ir = InfraredSensor(); assert ir.connected
ts = TouchSensor(); assert ts.connected
# Put the infrared sensor into proximity mode.
ir.mode = 'IR-PROX'
ir.modes
[u'IR-PROX', u'IR-SEEK', u'IR-REMOTE', u'IR-REM-A', u'IR-S-ALT', u'IR-CAL']
# Simple get out clause - stop on brick button press
btn = Button()
# Run the robot until a button is pressed.
start()
while not btn.any():
if ts.value():
# We bumped an obstacle.
# Back away, turn and go in other direction.
backup()
turn()
start()
# Infrared sensor in proximity mode will measure distance to the closest
# object in front of it.
distance = ir.value()
if distance > 60:
# Path is clear, run at full speed.
dc = 90
else:
# Obstacle ahead, slow down.
dc = 40
for m in motors:
m.duty_cycle_sp = dc
sleep(0.1)
Simple example of a widget to control / report on current duty cycle (crude speed control).
m = LargeMotor(OUTPUT_B)
def run_m(dc):
m.duty_cycle_sp=dc
print('Duty cycle: {}'.format(dc))
interact(run_m, dc=(-100,100,10))
m.run_direct()
m.stop()
How to handle the Lego remote control...
import threading
def checkThreads():
print(', '.join(threading.enumerate()))
#Parameter t is here is just a placeholder in case we want to pass in a specific thread to kill?
#- Timers get cancelled
#- Threads get joined?
# Can we detect type and clear all the ones we started?
#Start of notebook get a list of all threads, then clear back to that list?
def killThreads(n=None,t=None):
if n is None:
n=['Repeated Timer','Data logger']
for t in threading.enumerate():
if t.getName() in n:
t.cancel()
#We can then kill any threads we know we've created if we have to...
killThreads()
#http://stackoverflow.com/a/13151299/454773down vote
from threading import Timer
class RepeatedTimer(object):
def __init__(self, interval, function, name='Repeated Timer', *args, **kwargs):
self._timer = None
self.interval = interval
self.function = function
self.name=name
self.args = args
self.kwargs = kwargs
self.is_running = False
self.start()
def _run(self):
self.is_running = False
self.start()
self.function(*self.args, **self.kwargs)
def start(self):
if not self.is_running:
self._timer = Timer(self.interval, self._run)
#name the timer so we can spot it and kill it
self._timer.setName(self.name)
self._timer.start()
self.is_running = True
def stop(self):
self._timer.cancel()
self.is_running = False
To start with, let's see if we can use the remote control to control the lights on the EV3 brick.
R=RemoteControl()
r_output = widgets.Text()
L=Leds
L.all_off()
def test_remote_reading():
''' Indicate the last button pressed '''
txt= 'No button pressed' if not R.any() else 'Buttons pressed: {}'.format(','.join(R.buttons_pressed))
r_output.value='Remote actions: {}'.format(txt)
if 'red_up' in txt:
L.set_color(L.RIGHT, L.RED)
elif 'red_down' in txt:
L.set_color(L.LEFT, L.RED)
elif 'blue_up' in txt:
L.set_color(L.RIGHT, L.GREEN)
elif 'blue_down' in txt:
L.set_color(L.LEFT, L.GREEN)
#The output units / value look wrong to me? Decimeters, maybe?
r_output
t_r=RepeatedTimer(0.1,test_remote_reading)
Exception in thread Repeated Timer: Traceback (most recent call last): File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner self.run() File "/usr/lib/python2.7/threading.py", line 1082, in run self.function(*self.args, **self.kwargs) File "<ipython-input-6-a379ec017516>", line 38, in _run self.function(*self.args, **self.kwargs) File "<ipython-input-119-05cc41968287>", line 11, in test_remote_reading L.set_color(L.RIGHT, L.RED) File "/usr/lib/python2.7/dist-packages/ev3dev/ev3.py", line 76, in set_color l.brightness_pct = v * pct File "/usr/lib/python2.7/dist-packages/ev3dev/core.py", line 1777, in brightness_pct self.brightness = value * self.max_brightness File "/usr/lib/python2.7/dist-packages/ev3dev/core.py", line 1695, in max_brightness return self.get_attr_int('max_brightness') File "/usr/lib/python2.7/dist-packages/ev3dev/core.py", line 207, in get_attr_int return int(self._get_attribute(attribute)) ValueError: invalid literal for int() with base 10: '' Exception in thread Repeated Timer: Traceback (most recent call last): File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner self.run() File "/usr/lib/python2.7/threading.py", line 1082, in run self.function(*self.args, **self.kwargs) File "<ipython-input-6-a379ec017516>", line 38, in _run self.function(*self.args, **self.kwargs) File "<ipython-input-119-05cc41968287>", line 17, in test_remote_reading L.set_color(L.LEFT, L.GREEN) File "/usr/lib/python2.7/dist-packages/ev3dev/ev3.py", line 76, in set_color l.brightness_pct = v * pct File "/usr/lib/python2.7/dist-packages/ev3dev/core.py", line 1777, in brightness_pct self.brightness = value * self.max_brightness File "/usr/lib/python2.7/dist-packages/ev3dev/core.py", line 1695, in max_brightness return self.get_attr_int('max_brightness') File "/usr/lib/python2.7/dist-packages/ev3dev/core.py", line 207, in get_attr_int return int(self._get_attribute(attribute)) ValueError: invalid literal for int() with base 10: ''
t_r.stop()
killThreads()
The basic tyre supplied with the Lego EV3 has a circumference of 56mm (it's written on the tyre).
This means that we can programme the robot to travel a particular distance.
First, let's define some constants and do some simple sums to work out the values of certain derived quantities.
left_motor=LargeMotor(OUTPUT_A)
right_motor=LargeMotor(OUTPUT_D)
#Assume that the left motor and right motor have similar properties...
COUNT_PER_ROTATION =left_motor.count_per_rot
#Degrees per tacho count
DEGREES_PER_COUNT = COUNT_PER_ROTATION / 360
#Wheel size in mm
WHEEL_CIRCUMFERENCE = 56
#Distance in millimeters per tacho count
MM_PER_COUNT = WHEEL_CIRCUMFERENCE / COUNT_PER_ROTATION
#Distance per degree
MM_PER_DEGREE = WHEEL_CIRCUMFERENCE / 360
#Distance per tacho count
COUNT_PER_MM = COUNT_PER_ROTATION / WHEEL_CIRCUMFERENCE
ERROR: Internal Python error in the inspect module. Below is the traceback from this internal error.
Traceback (most recent call last): File "/usr/local/lib/python2.7/dist-packages/IPython/core/ultratb.py", line 1120, in get_records return _fixed_getinnerframes(etb, number_of_lines_of_context, tb_offset) File "/usr/local/lib/python2.7/dist-packages/IPython/core/ultratb.py", line 301, in wrapped return f(*args, **kwargs) File "/usr/local/lib/python2.7/dist-packages/IPython/core/ultratb.py", line 346, in _fixed_getinnerframes records = fix_frame_records_filenames(inspect.getinnerframes(etb, context)) File "/usr/lib/python2.7/inspect.py", line 1044, in getinnerframes framelist.append((tb.tb_frame,) + getframeinfo(tb, context)) File "/usr/lib/python2.7/inspect.py", line 1004, in getframeinfo filename = getsourcefile(frame) or getfile(frame) File "/usr/lib/python2.7/inspect.py", line 454, in getsourcefile if hasattr(getmodule(object, filename), '__loader__'): File "/usr/lib/python2.7/inspect.py", line 491, in getmodule if ismodule(module) and hasattr(module, '__file__'): KeyboardInterrupt
Unfortunately, your original traceback can not be constructed.
--------------------------------------------------------------------------- IndexError Traceback (most recent call last) /usr/local/lib/python2.7/dist-packages/IPython/core/interactiveshell.pyc in run_code(self, code_obj, result) 2900 if result is not None: 2901 result.error_in_exec = sys.exc_info()[1] -> 2902 self.showtraceback() 2903 else: 2904 outflag = 0 /usr/local/lib/python2.7/dist-packages/IPython/core/interactiveshell.pyc in showtraceback(self, exc_tuple, filename, tb_offset, exception_only) 1828 except Exception: 1829 stb = self.InteractiveTB.structured_traceback(etype, -> 1830 value, tb, tb_offset=tb_offset) 1831 1832 self._showtraceback(etype, value, stb) /usr/local/lib/python2.7/dist-packages/IPython/core/ultratb.pyc in structured_traceback(self, etype, value, tb, tb_offset, number_of_lines_of_context) 1392 self.tb = tb 1393 return FormattedTB.structured_traceback( -> 1394 self, etype, value, tb, tb_offset, number_of_lines_of_context) 1395 1396 /usr/local/lib/python2.7/dist-packages/IPython/core/ultratb.pyc in structured_traceback(self, etype, value, tb, tb_offset, number_of_lines_of_context) 1300 # Verbose modes need a full traceback 1301 return VerboseTB.structured_traceback( -> 1302 self, etype, value, tb, tb_offset, number_of_lines_of_context 1303 ) 1304 else: /usr/local/lib/python2.7/dist-packages/IPython/core/ultratb.pyc in structured_traceback(self, etype, evalue, etb, tb_offset, number_of_lines_of_context) 1184 structured_traceback_parts += formatted_exception 1185 else: -> 1186 structured_traceback_parts += formatted_exception[0] 1187 1188 return structured_traceback_parts IndexError: string index out of range
Specify how far we want to travel.
#Set the distance to travel in centimeters
distance_to_travel_cm = 10
Create a simple function to convert from centimeters to millimeters.
#Define a function to convert centimeters to millimeters
def mm_from_cm(cm):
return 10 * cm
Do the conversion...
distance_to_travel_mm = mm_from_cm( distance_to_travel_cm )
If we know the distance, and we know the distance per tach count, we can calculate the tacho count required to go a particular distance:
#Calculate tacho count required to travel that distance
count_for_distance = distance_to_travel_mm * COUNT_PER_MM
Use that calculated tacho count value to turn on the motors for that particular count.
#Drive for a specified tacho count
#Set the motor speed
dutycycle=40
#Turn on the motors for the required tacho count
left_motor.run_to_rel_pos(position_sp=count_for_distance, duty_cycle_sp=dutycycle )
right_motor.run_to_rel_pos(position_sp=count_for_distance, duty_cycle_sp=dutycycle )
Having tested the code, we might then create a function:
def travel_N_cm( distance_to_travel_cm , dutycycle= 45):
distance_to_travel_mm = mm_from_cm( distance_to_travel_cm )
count_for_distance = distance_to_travel_mm * COUNT_PER_MM
left_motor.run_to_rel_pos(position_sp=count_for_distance, duty_cycle_sp=dutycycle )
right_motor.run_to_rel_pos(position_sp=count_for_distance, duty_cycle_sp=dutycycle )
Test the function - forward 5cm, then back 5cm:
travel_N_cm(5)
travel_N_cm(-5)
Note that if we call the forward and back functions together, the second command is issued immediately after the first, which means that the first is unlikely to be able to take effect.
What this means is that we need to check that the commands issued by the travel_N_cm
function the first time we call it have completed before we call it again.
##NOT TESTED##
travel_N_cm(5)
while 'running' in left_motor.state or 'running' in right_motor.state :
time.sleep(0.1)
travel_N_cm(-5)
def actually_travel_N_cm():
#add some logic to text that we've actually travelled the distance
pass
To turn through 90 degrees, for example, we can either turn on the spot or turn about a particular angle.
To turn about one wheel, turn one wheel on and leave the other one switched off.
To run through a particular again, we could either calculate how far the activated wheel need to turn to cover one quarter of the circumference of a circle with a radius equal to the width of the robot's track (that is, distance between the mid-points of its wheels), or we could turn the motor on for a set period of time, which requires that we calibrate the robot in order to get an estimate of the time needed to turn through 360 degrees, for example, and divide that by 4.
To turn on the spot, turn one motor forward and the other one backward at the same speed.
Again, there are at least the following two ways of calculating this. One is to calculate the distance turned by each wheel (a quarter of the circumference of a circle with diameter equal to the track of the robot) or a quarter of the time take to turn the robot through one complete turn on the spot.
I'm not sure what the best way of getting the ev3dev to finish driving a distance before it starts driving another distance is?
#drive one edge
def one_edge(length=20):
pass
#Turn ninety degrees
def turn(typ='onspot',angle=90):
pass
def drive_square(side=20):
done = False
for i in range(4):
while not done:
one_edge(side)
while not done:
turn(90)
For the color sensor inspector, we'll introduce a timer that lets us repeatedly check the sensor values with a specified sample rate.
The timer actually works by repeatedly defining another timer every time it is called, until we tell it to stop.
#UNTESTED
#Based on https://github.com/rhempel/ev3dev-lang-python/blob/1c9265db6dd5439884a4764f3a84f8ec6bc85220/tests/motor/ev3dev_port_logger.py
#Another approach is just to run a parallel thread
#This is more akin to the RCX task
#Use this as a model for student's own task?
class myTaskThread(threading.Thread):
def __init__(self, name='Task thread'):
threading.Thread.__init__(self)
self.name = name
self.done = threading.Event()
def run(self):
#to do
while not self.done.isSet():
pass
def stop(self, timeout=None):
self.done.set()
self.join(timeout)
Let's define a colour sensor and check the mode:
cs = ColorSensor()
cs.mode
Now define a simple text widget to display the colour sensor value (the widget will remain empty until you call the timer to poll the sensor and run the widget updating function test_color_sensor()
).
cs.mode='COL-REFLECT'
cs_output = widgets.Text()
colorMap={0:'No color', 1:'Black',2: 'Blue',3: 'Green',
4: 'Yellow', 5: 'Red', 6: 'White', 7: 'Brown'}
def test_color_sensor():
if cs.mode=='COL-REFLECT':
cs_output.value='Colour sensor output: {}'.format(cs.value())
elif cs.mode=='COL-COLOR':
cs_output.value='Colour sensor output: {} ({})'.format(cs.value(), colorMap[cs.value()])
#The output units / value look wrong to me? Decimeters, maybe?
cs_output
#Call the timer that polls the sensor
t_cs=RepeatedTimer(0.1,test_color_sensor)
#Stop polling the sensor
t_cs.stop()
Now change the color mode and start the timer again to see the difference.
cs.mode='COL-COLOR'
#EMERGENCY STOP BUTTON
killThreads()
Continue driving forward until you see a black line on a lighter background.
Report on the colour of various colour lines using the colour sensor.
Continue driving forward until you see over the edge of a cliff. (Light sensor? Or proximity sensor?)
We can create a simple test programme to illustrate whether the touch sensor is being pressed that will switch the colour of the left and right LEDs depending on whether the touch sensor switch is closed or not.
#Set colour according to switch
ts = TouchSensor()
#Simple timoeut routine from http://stackoverflow.com/a/13293316/454773
timeout = time.time() + 10 # 10 seconds from now
while True:
#Set Leds depending on whether switch is pressed or not
L.set_color(L.RIGHT, L.RED if ts.value() else L.GREEN)
L.set_color(L.LEFT, L.GREEN if ts.value() else L.RED)
#Break out of the loop after a certain amount of time
if time.time() > timeout:
break
Modify the program to say "ouch" when the robot bumps into something
Test activities to use the touch sensor:
Add an additional indicator that the robot has bumped by saying "ouch" in the reverse phase.
Provide an audio alert as something gets closer to the infra-red sensor.
ir=InfraredSensor()
ir.mode='IR-PROX'
To provide an audible signal correpsonding to proximity, set a frequency that is inversely proportional to the distance and then use the Sound.tone(frequency,duration).wait()
construction to generate the sound.
#Set the frequency inversely proportional to the distance
#The frequency should go up as we get closer
#Infra-red range is 0..100, map onto range 20,000..~200
def frequency_by_dist_ir(dist):
if dist!='' and dist>=0:
return 20000 / (dist+1)
return -1
ir_output = widgets.Text()
def infrared_range():
try:
d=ir.value()
except:
d=''
ir_output.value='Infra-red range: {}'.format(d)
def infrared_beeper():
try:
d=ir.value()
except:
d=''
f=frequency_by_dist_ir(d)
if f>0:
Sound.tone(f,10)
def infrared_response():
infrared_range()
infrared_beeper()
ir_output
irt = RepeatedTimer(0.2, infrared_response)
irt.stop()
Try to pre-empt the bumper bumping by using the proximity sensor. See how close you can get to the obstacle before taking avoiding action and before actually bumping.
The robot should find its way back to the sensor beacon.
Start by trying to report on the likely direction of the homing beacon.
ir=InfraredSensor()
ir.mode='IR-SEEK'
beacon_output = widgets.Text()
def ir_beacon_distDir(channel=1):
beacon_output.value='Beacon heading: {}, and range: {}'.format(ir.value(2*(channel-1)),
ir.value(2*(channel-1)+1))
beacon_output
Widget Javascript not detected. It may not be installed properly. Did you enable the widgetsnbextension? If not, then run "jupyter nbextension enable --py --sys-prefix widgetsnbextension"
t_beacon=RepeatedTimer(0.1, ir_beacon_distDir)
t_beacon.stop()
Simple experiment to see how the gyro works
Is there likely to be confusion around the ultrasound sensor when working with multiple robots / ultrasound sensors?
us=UltrasonicSensor()
us_output = widgets.Text()
US_UNITS=us.units
def ultrasound_range():
us_output.value='Ultrasound range: {} {}'.format(us.value(), US_UNITS)
#The output units / value look wrong to me? Decimeters, maybe?
us_output
The installed widget Javascript is the wrong version.
rt = RepeatedTimer(0.1, ultrasound_range)
#Stop the timer
rt.stop()
Provide an audio alert as something gets closer to the ultrasonic sensor.
#Set the frequency inversely proportional to the distance
#The frequency should go up as we get closer
def frequency_by_dist_us(dist):
return 100000 / dist
def ultrasound_beeper():
f=frequency_by_dist_us(us.value())
if f>0:
Sound.tone(f,10)
urt = RepeatedTimer(0.2, ultrasound_beeper)
urt.stop()
Use the gyro to turn the right angles.
def demo_forward(tsp=1000,dc=20):
for m in motors:
m.run_timed(time_sp=tsp, duty_cycle_sp=dc)
def demo_turn(direction=(-1,1),dc=25):
for m, p in zip(motors, direction):
m.run_timed(duty_cycle_sp=p*dc, time_sp=100)
# Wait until both motors are stopped:
while any(m.state for m in motors):
sleep(0.1)
gs=GyroSensor()
gs.mode='GYRO-ANG'
#If we're within ish degrees that's close enough
ish=2
def gyro_turn_till_relative(bearing):
#What heading does the gyro currently give, in degrees?
current_heading=gs.value() % 360
#The target heading is the current heading plus the desired bearing
target_heading=(360+current_heading+bearing) % 360
#Keep turning while the current heading is more than ish degrees from the target heading
#Should really optimise the turn so we turn the shortest angle
while abs(( gs.value() % 360)-target_heading) >ish:
if gs.value() % 360 -target_heading <0:
demo_turn((-1,1))
else:
demo_turn((1,-1))
demo_forward()
gyro_turn_till_relative(90)
While driving over coloured tapes, use the colour sensor to log the colours seen, then interprest the chart:
from threading import Timer
from datetime import datetime
class SensorDataLogger(object):
def __init__(self, sensor, interval=0.1, maxSamples=20000, name='Data logger'):
self.sensor = sensor
self.interval = interval #period between samples in seconds
self.name= name
self.results=[]
#self.basetime =time.time()
self.is_running=False
def getsample(self):
#now = time.time()
#who have a FIFO queue to drop early samples and retain more recent ones?
#Or something more intelligent? eg drop every othersample? (so resample, essentially?)
if len(self.results)<=maxSamples:
self.results.append((datetime.now(), self.sensor.value()))
def _run(self):
self.is_running = False
self.start()
self.getsample()
def start(self):
if not self.is_running:
self._timer = Timer(self.interval, self._run)
self._timer.setName(self.name)
self._timer.start()
self.is_running = True
def stop(self):
self._timer.cancel()
self.is_running = False
killThreads(self.name)
#return self.results
def log(self):
return self.results
#Define a colour sensor
cs=ColorSensor()
#Log the colour sensor
dd=SensorDataLogger(cs)
#Start the logger
dd.start()
#Stop the logger
dd.stop()
#Show the first few data log values
dd.log()[:10]
[(datetime.datetime(2016, 5, 16, 15, 57, 23, 739515), 0), (datetime.datetime(2016, 5, 16, 15, 57, 23, 888700), 0), (datetime.datetime(2016, 5, 16, 15, 57, 24, 20172), 0), (datetime.datetime(2016, 5, 16, 15, 57, 24, 138923), 0), (datetime.datetime(2016, 5, 16, 15, 57, 24, 260983), 0), (datetime.datetime(2016, 5, 16, 15, 57, 24, 380033), 0), (datetime.datetime(2016, 5, 16, 15, 57, 24, 496049), 0), (datetime.datetime(2016, 5, 16, 15, 57, 24, 612732), 0), (datetime.datetime(2016, 5, 16, 15, 57, 24, 731662), 0), (datetime.datetime(2016, 5, 16, 15, 57, 24, 880443), 0)]
We can cast the data into a pandas dataframe on the brick, although it may be more resource friendly for the brick to use something like the csv
library to save the data to a CSV file on the brick, copy the data over to the host machine, and then process the data using pandas and / or whatever analysis or charting packages we want using a kernel based on the host?
from pandas import DataFrame
df=DataFrame.from_records(dd.log())
df.columns=['datetime','value']
df.set_index('datetime',inplace=True)
df[:10]
value | |
---|---|
datetime | |
2016-05-16 15:57:23.739515 | 0 |
2016-05-16 15:57:23.888700 | 0 |
2016-05-16 15:57:24.020172 | 0 |
2016-05-16 15:57:24.138923 | 0 |
2016-05-16 15:57:24.260983 | 0 |
2016-05-16 15:57:24.380033 | 0 |
2016-05-16 15:57:24.496049 | 0 |
2016-05-16 15:57:24.612732 | 0 |
2016-05-16 15:57:24.731662 | 0 |
2016-05-16 15:57:24.880443 | 0 |
df.plot()
<matplotlib.axes._subplots.AxesSubplot at 0xae7e59d0>
df.to_csv('text.csv')
import os
print(os.getcwd(),os.listdir(os.getcwd()))
#If we then run a kernel on host, we can use it to grab the file over via scp:
#!scp robot@192.168.1.106:/home/robot/text.csv ./
#and then do heavier weight charting on the host using a host based kernel
('/home/robot', ['.pip', 'rik_kernel.json', '.profile', '.bashrc', '.ipython', '.idlerc', '.bash_logout', '.ssh', '.cache', '.bash_history', 'text.csv', '.config'])
#Free up memory
dd=None
df=None
What is the final challenge?