10/27/2018 HW-04_Programming_a_robotic_sheepdog-STUDENT
http://localhost:8888/notebooks/HW-04_Programming_a_robotic_sheepdog-STUDENT.ipynb 1/16
CMSE 201 - Fall 2018
Homework 4: Programming a Robotic
Sheepdog
In this homework you will continue to learn about agent-based modeling, by developing and
implementing a set of rules for how agents (sheep, sheepdog) interacts with each other. We will
measure its effectiveness and compare different sets of rules to each other. Make sure to use Slack
and help room hours if you run into issues!
Goals
By the end of the homework assignment you will have practiced:
1. Modeling a real world scenario.
2. Building and manipulating agent-based models.
3. Assessing model outcomes.
4. Defining functions to check the state of a model
Assignment instructions
Work through the following assignment, making sure to follow all of the directions and answer all of
the questions.
This assignment is due at 11:59pm on Friday, October 26th. It should be uploaded into the
"Homework Assignments" submission folder for Homework #4. Submission instructions can be
found at the end of the notebook.
Grading
Question 1: Scattering sheep (3 points)
Question 2: These sheep were made for walkin' (8 points)
Question 3: All bark and no bite (6 points)
Question 4: Go dog go (6 points)
Question 5: Test for success (6 points)
Question 6: Better dog path (4 points)
Question 7: Your turn (5 points)
Total points possible: 38
10/27/2018 HW-04_Programming_a_robotic_sheepdog-STUDENT
http://localhost:8888/notebooks/HW-04_Programming_a_robotic_sheepdog-STUDENT.ipynb 2/16
Introduction
An agent-based model is a model where a set of agents interact according to a set of rules. This is
a general framework that can model many different phenomena. Here we will use a 2D grid, where
the cells are given numbers to represent one of the following:
0 = empty space
1 = fence
2 = sheep
3 = dog
The fence forms a pen where the sheep need to sleep every night. However, the sheep don't
always like to go to the pen, and would rather stay up past their bedtime.
One particular farmer (you) has come up with a 21st century solution: a robotic sheepdog. This
dog will help round up the sheep and get them into their pen at night. One problem is that we're not
sure the best way to program this robot in order to get the sheep into the pen as fast as possible.
Let's get started by defining our grid:
In [ ]:
Now lets define a function to plot our state, using symbols for the fence, the dog and the sheep.
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
nx = 40
ny = 40
fenceline = 10
states = ['empty','fence','sheep','dog']
state_color = np.linspace(0,1,len(states))
system_state = np.zeros([ny,nx]) # intialize everything to empty
dog_start_pos = [int(ny-1),int(nx/2)]
system_state[dog_start_pos[0],dog_start_pos[1]] = 3 # put a dog at the top, in th
system_state[fenceline,:] = 1 # put a fence along y = 10
system_state[fenceline,17:24] = 0 # put a hole in the fence
10/27/2018 HW-04_Programming_a_robotic_sheepdog-STUDENT
http://localhost:8888/notebooks/HW-04_Programming_a_robotic_sheepdog-STUDENT.ipynb 3/16
In [ ]:
In [ ]:
Doesn't really look like a dog, but let's use our imaginations. We're almost ready, but we're missing
something.... oh yes. The sheep.
Question 1: Scattering sheep (3 points)
Write a function that randomly places the sheep in the field above the fence. It should take as an
argument the system_state, the number of sheep to place (N), and a fenceline parameter that
describes the y-position of the fence. It should return a new state with the sheep.
Importantly, the sheep cannot overlap with anything that is not empty space (i.e.
state[try_y,try_x] must equal zero), and the sheep must be placed above the fenceline!
def plotstate(state,time=0):
# first create two vectors based on the x and y sizes of the grid
x_range = np.linspace(0, state.shape[0], state.shape[0])
y_range = np.linspace(0, state.shape[1], state.shape[1])
# use the numpy meshgrid function to create two matrices
# of the same size as myarray with x and y indexes
x_indexes, y_indexes = np.meshgrid(x_range, y_range)
# make a list of all the x and y indexes that are not empty.
fence_x = x_indexes[state == 1];
fence_y = y_indexes[state == 1];
sheep_x = x_indexes[state == 2];
sheep_y = y_indexes[state == 2];
dog_x = x_indexes[state == 3];
dog_y = y_indexes[state == 3];
# plot the squares and triangles. make the size of the polygons
# larger than the default so they're easy to see!
plt.plot(fence_x,fence_y, 's',markersize=20)
plt.plot(sheep_x,sheep_y, 'o',markersize=20)
plt.plot(dog_x,dog_y, '1',markersize=20)
# Set the x and y limits to include half a space overlap so we don't cut off
plt.ylim([-0.5,state.shape[0] + 0.5])
plt.xlim([-0.5,state.shape[0] + 0.5])
# display the current time on the plot
plt.text(0,0,"time={0}".format(time),fontsize=18)
# Turn the axes off
plt.tick_params(axis='both', which='both', bottom=False, top=False, labelbotto
plt.gca().set_aspect('equal', adjustable='box')
plt.figure(figsize=(10,10))
plotstate(system_state)
10/27/2018 HW-04_Programming_a_robotic_sheepdog-STUDENT
http://localhost:8888/notebooks/HW-04_Programming_a_robotic_sheepdog-STUDENT.ipynb 4/16
In [ ]:
Let's call this state initial_state since we'll use it to start our agent-based model simulations.
The following should plot your new system. Run it a few times and make sure it always gives you
something appropriate.
In [ ]:
Question 2: These sheep were made for walkin' (8
points)
Of course these sheep aren't going to just stand there. They will move, randomly over time. For
each time step (say, 1 second), let's say they will move according to the following probabilities and
rules:
- choose to move with probability p_move
- if moving
- choose direction (up, down, left, right) with equal probability
- do not execute the move if it goes beyond the borders
- only execute the move if the new space is empty
- do not execute an upward move if sheep is at the fenceline
Where the latter condition assumes that once the sheep are in the pen, they remember how good it
feels to be home.
Here is a function that takes in: 1) a system state, 2) a sheep's x and y position, 3) p_move and 4)
the fenceline. This function will attempt to move the specified sheep according to the rules above.
Provide comments and insert code where requested below
from copy import copy
def place_sheep(state,N,fenceline):
newstate = copy(state)
sheep_placed = 0
while sheep_placed < N:
# suggest somewhere for a sheep to be placed
try_y = # hint: use np.random.randint
try_x = #
# check if this is appropriate.
# if yes, set good_place to True. if no, set good_place to False.
# if good, place the sheep, if not try again
if good_place is True:
sheep_placed += 1
newstate[try_y,try_x] = 2
return newstate
initial_state = place_sheep(system_state,20,fenceline)
plt.figure(figsize=(10,10))
plotstate(initial_state)
10/27/2018 HW-04_Programming_a_robotic_sheepdog-STUDENT
http://localhost:8888/notebooks/HW-04_Programming_a_robotic_sheepdog-STUDENT.ipynb 5/16
In [ ]:
Great! Now let's write a function to move the whole system forward in time. This will use the
move_sheep function. First we need to find all the sheep. This can be done easily with the
np.where function. You might not have seen this before, so read the doc page before you use it.
In [ ]:
What is sheep_loc? What do the first and second axes represent?
def move_sheep(state,sheep_x,sheep_y,pmove,fenceline):
# what are the next two lines doing?
# (answer here)
r1 = np.random.random()
if r1 < pmove:
# now decide to move up, down, left or right
# store the new location in the variables try_x and try_y
r2 = np.random.random()
if r2 < 0.25: # move left
try_x = sheep_x - 1
try_y = sheep_y
elif r2 < 0.5: # move up
if sheep_y != fenceline:
try_x = sheep_x
try_y = sheep_y + 1
else:
try_x = sheep_x
try_y = sheep_y
elif r2 < 0.75: # move right
try_x = sheep_x + 1
try_y = sheep_y
else: # move down
try_x = sheep_x
try_y = sheep_y - 1
# check and see if the move is good
good_move = True
# set good_move to False if conditions are violated
if try_x < 0 or try_x >= state.shape[1]:
good_move = False
elif try_y < 0 or try_y >= state.shape[0]:
good_move = False
elif state[try_y,try_x] != 0:
good_move = False
# check if the move is good, if so make the move
# INSERT CODE HERE
# return the state
return state
sheep_loc = np.array(np.where(initial_state==2))
10/27/2018 HW-04_Programming_a_robotic_sheepdog-STUDENT
http://localhost:8888/notebooks/HW-04_Programming_a_robotic_sheepdog-STUDENT.ipynb 6/16
// Answer here
Use sheep_loc below to get the x and y position of the first sheep:
In [ ]:
Use sheep_loc to get the total number of sheep:
In [ ]:
Now use sheep_loc to finish the evolve_system function below, which will (at least try to)
move all of the sheep.
In [ ]:
OK, now we'll test out our code:
# code here
x = #?
y = #?
print("There is a sheep at x, y = ({0},{1})".format(x,y))
# code here
nsheep = #?
print("There are {0} sheep in total".format(nsheep))
def evolve_system(state,pmove,fenceline):
sheep_loc = np.array(np.where(state==2))
# use sheep_loc to determine how many sheep there are
nsheep = #?
# write a loop to iterate over each sheep
for i in range(nsheep):
sheep_x = #?
sheep_y = #?
# call move_sheep for each sheep_x and sheep_y position
state = move_sheep(state,sheep_x,sheep_y,pmove,fenceline)
return state
10/27/2018 HW-04_Programming_a_robotic_sheepdog-STUDENT
http://localhost:8888/notebooks/HW-04_Programming_a_robotic_sheepdog-STUDENT.ipynb 7/16
In [ ]:
Wow! Just... wow.
Question 3: All bark and no bite (6 points)
Now we need some sheep-sheepdog interaction. Time to make some changes to our rule set:
- choose to move with probability p_move
- if moving
- choose x dir or y dir with equal probability
- choose toward dog or away from dog as follows
* p = 0.5 - delta (towards dog)
* p = 0.5 + delta (away from dog)
- do not execute the move if it goes beyond the borders
- only execute the move if the new space is empty
- do not execute an upward move if sheep is at the fenceline
where we've introduced another parameter delta which can take on values between 0 and 0.5.
Here is a new move_sheep_bias function that accounts for the dog's position:
from copy import copy
from IPython.display import display, clear_output
import time
nsteps = 20
pmove = 0.5
fenceline = 10
state = copy(initial_state)
fig = plt.figure(figsize=(8,8))
for i in range(nsteps):
state = evolve_system(state,pmove,fenceline)
plotstate(state,time=i)
time.sleep(0.01) # Sleep for 0.01 s to slow down the animation
clear_output(wait=True) # Clear output for dynamic display
display(fig) # Reset display
fig.clear() # Prevent overlapping and layered plots
plt.close() # Close dynamic display
10/27/2018 HW-04_Programming_a_robotic_sheepdog-STUDENT
http://localhost:8888/notebooks/HW-04_Programming_a_robotic_sheepdog-STUDENT.ipynb 8/16
In [ ]: def move_sheep_bias(state,sheep_x,sheep_y,pmove,fenceline,delta):
# set default "try" position at current position
try_x = sheep_x
try_y = sheep_y
# get dog's position
dog_y = np.where(state==3)[0][0]
dog_x = np.where(state==3)[1][0]
# choose whether to move
r1 = np.random.random()
if r1 < pmove:
# first decide x dir or y dir
r2 = np.random.random()
if r2 < 0.5:
x_or_y = 'x'
else:
x_or_y = 'y'
# choose toward dog or away from dog
r3 = np.random.random()
if r3 < 0.5 + delta:
# move away from dog
if x_or_y == 'x':
if sheep_x < dog_x:
try_x = sheep_x - 1
else:
try_x = sheep_x + 1
elif x_or_y == 'y':
if sheep_y < dog_y:
try_y = sheep_y - 1
else:
# moving up, check if you are at the fenceline
if sheep_y != fenceline:
try_y = sheep_y + 1
else:
# move towards dog
if x_or_y == 'x':
if sheep_x < dog_x:
try_x = sheep_x + 1
else:
try_x = sheep_x - 1
elif x_or_y == 'y':
if sheep_y < dog_y:
# moving up, check if you are at the fenceline
if sheep_y != fenceline:
try_y = sheep_y + 1
else:
try_y = sheep_y - 1
# check and see if the move is good
good_move = True
if try_x < 0 or try_x >= state.shape[1]:
good_move = False
elif try_y < 0 or try_y >= state.shape[0]:
10/27/2018 HW-04_Programming_a_robotic_sheepdog-STUDENT
http://localhost:8888/notebooks/HW-04_Programming_a_robotic_sheepdog-STUDENT.ipynb 9/16
Below create an evolve_system_bias function to call this new function. (Don't forget to
include the delta parameter):
In [ ]:
Then test our code again, this time only plotting every 10 steps:
In [ ]:
Maybe a little too intense. I don't think our dog should be that scary.
Add a cutoff distance (d_cut), where if sheep are closer to the dog than d_cut then they use
move_sheep_bias, but if they are farther then they move normally with move_sheep.
Note: calculate distance as , where is the x-position of the sheep,
and is the x-position of the dog, etc.
d = (xs xd)2 + (ys yd)2√ xs
xd
good_move = False
elif state[try_y,try_x] != 0:
good_move = False
if good_move is True:
# make the move
state[sheep_y,sheep_x] = 0
state[try_y,try_x] = 2
return state
def evolve_system_bias(state,pmove,fenceline,delta):
# insert code here
return state
from IPython.display import display, clear_output
import time
nsteps = 500
plot_every = 10
pmove = 0.5
fenceline = 10
delta = 0.25
state = copy(initial_state)
fig = plt.figure(figsize=(8,8))
for i in range(nsteps):
state = evolve_system_bias(state,pmove,fenceline,delta)
if i % plot_every == 0:
plotstate(state,time=i)
time.sleep(0.01) # Sleep for 0.01 s to slow down the animation
clear_output(wait=True) # Clear output for dynamic display
display(fig) # Reset display
fig.clear() # Prevent overlapping and layered plots
plt.close() # Close dynamic display
10/27/2018 HW-04_Programming_a_robotic_sheepdog-STUDENT
http://localhost:8888/notebooks/HW-04_Programming_a_robotic_sheepdog-STUDENT.ipynb 10/16
In [ ]:
In [ ]:
That's better.
Question 4: Go dog go (6 points)
Here's the chance we've been waiting for! Time to make our dog move. But how? We'll assume our
robot can move much faster than the sheep, so we'll allow it to move one square every timestep.
But how should it move? To allow us to try out a set of different strategies, we'll assume we have
a path that the dog follows, which is a list of states that are no more than one square apart.
def evolve_system_bias_cutoff(state,pmove,fenceline,delta,dcut):
# find the dog
dog_loc = np.array(np.where(state==3))
dog_x = dog_loc[1][0]
dog_y = dog_loc[0][0]
sheep_loc = np.array(np.where(state==2))
# determine how many sheep there are
nsheep = #?
# write a loop to iterate over each sheep
for i in range(nsheep):
sheep_x = #?
sheep_y = #?
# use distance to dog to decide which move_sheep function to use
# INSERT CODE HERE
return state
nsteps = 500
plot_every = 10
pmove = 0.5
fenceline = 10
delta = 0.25
dcut = 15
state = copy(initial_state)
fig = plt.figure(figsize=(8,8))
for i in range(nsteps):
state = evolve_system_bias_cutoff(state,pmove,fenceline,delta,dcut)
if i % plot_every == 0:
plotstate(state,time=i)
time.sleep(0.01) # Sleep for 0.01 s to slow down the animation
clear_output(wait=True) # Clear output for dynamic display
display(fig) # Reset display
fig.clear() # Prevent overlapping and layered plots
plt.close() # Close dynamic display
10/27/2018 HW-04_Programming_a_robotic_sheepdog-STUDENT
http://localhost:8888/notebooks/HW-04_Programming_a_robotic_sheepdog-STUDENT.ipynb 11/16
In [ ]:
How many states are in this path? What does this path describe?
// Answer here
Make a path where the dog runs all the way to the left wall, then all the way to the right wall,
and then back to its starting position. Do this by filling in the missing lines below:
In [ ]:
Remember the dog can't move more than one square per loop, and since the dog will be running
this loop over and over again (until the sheep are in the pen), we need to check the endpoints as
well. Run this function below to see if your path is a good one:
In [ ]:
y0,x0 = dog_start_pos
short_dog_path = [(y0,x0),(y0,x0-1),(y0,x0-2),(y0,x0-1)]
dog_path = [(y0,x0)]
x = x0
y = y0
# first run to the left wall
while x > 0:
# update x
# append (x,y) tuple to dog_path
# then run to the right wall
while x < nx - 1:
# update x
# append (x,y) tuple to dog_path
# then run back to the middle
while x > x0+1:
# update x
# append (x,y) tuple to dog_path
def pathcheck(path):
# checks to see if the dog moves more than one square per timestep
oldp = path[-1]
goodpath = True
for p in path:
dy = p[0]-oldp[0]
dx = p[1]-oldp[1]
if abs(dx) + abs(dy) > 1:
goodpath = False
oldp = p
return goodpath
10/27/2018 HW-04_Programming_a_robotic_sheepdog-STUDENT
http://localhost:8888/notebooks/HW-04_Programming_a_robotic_sheepdog-STUDENT.ipynb 12/16
In [ ]:
OK, now we'll make a move_dog function that uses this path:
In [ ]:
Now let's add this move_dog command to a new evolve_system function:
In [ ]:
Let's test it out!
pathcheck(dog_path)
def move_dog(state,path,current_pos):
# check if the next position is occupied
next_pos = current_pos + 1
if next_pos >= len(path):
next_pos = 0
next_y,next_x = path[next_pos]
if state[next_y,next_x] == 0 or state[next_y,next_x] == 3:
# empty! or the dog is already there.
# Either way, its ok to move the dog
# find the dog
dog_loc = np.array(np.where(state==3))
dog_x = dog_loc[1][0]
dog_y = dog_loc[0][0]
# delete dog from current position
state[dog_y,dog_x] = 0
# add dog to next position
state[next_y,next_x] = 3
# update current_pos
current_pos = next_pos
return current_pos, state
def evolve_system_dog(state,pmove,fenceline,delta,dcut,path,current_pos):
state = evolve_system_bias_cutoff(state,pmove,fenceline,delta,dcut)
current_pos, state = move_dog(state,path,current_pos)
return current_pos, state
10/27/2018 HW-04_Programming_a_robotic_sheepdog-STUDENT
http://localhost:8888/notebooks/HW-04_Programming_a_robotic_sheepdog-STUDENT.ipynb 13/16
In [ ]:
Run this code for 5000 steps, with plot_every = 100, and for 20000 steps, with plot_every =
400
In [ ]:
What do you observe?
// Answer here
Question 5: Test for success (6 points)
OK our sheepdog is not very effective right now. Let's quantify exactly how bad it is. Write a function
that will check and see how many sheep are out of the pen (return an integer):
In [ ]:
Test this function to see how many are out of the pen in the initial and final states from
above:
nsteps = 500
plot_every = 10
pmove = 0.5
fenceline = 10
delta = 0.25
dcut = 15
path = dog_path
dog_pos = 0
state = copy(initial_state)
fig = plt.figure(figsize=(8,8))
for i in range(nsteps):
dog_pos, state = evolve_system_dog(state,pmove,fenceline,delta,dcut,path,dog_
if i % plot_every == 0:
plotstate(state,time=i)
time.sleep(0.01) # Sleep for 0.01 s to slow down the animation
clear_output(wait=True) # Clear output for dynamic display
display(fig) # Reset display
fig.clear() # Prevent overlapping and layered plots
plt.close() # Close dynamic display
# code here
def sheep_still_out(state,fenceline):
# return the number of sheep that are above the fenceline
# in the current state
# code here
return sheep_still_out
10/27/2018 HW-04_Programming_a_robotic_sheepdog-STUDENT
http://localhost:8888/notebooks/HW-04_Programming_a_robotic_sheepdog-STUDENT.ipynb 14/16
In [ ]:
Now let's use this function in a while loop to see how long it will take:
In [ ]:
Put the above code in a for loop, and run this simulation 10 times. Save the times in a list
called times.
In [ ]:
Run the cell below to get statistics of your sheepdog.
In [ ]:
What quantity is being reported after the +- signs?
// Answer here
Question 6: Better dog path (4 points)
Now that we have a well-defined measure of success, we can test out different dog_path lists to
see which is better. Let's try running in a rectangle, going down to y = 30, which is 20 above the
fenceline.
initial = #?
final = #?
print("Sheep out, initially: {0}".format(initial))
print("Sheep out, finally: {0}".format(final))
pmove = 0.5
fenceline = 10
delta = 0.25
dcut = 15
path = dog_path
dog_pos = 0
report_every = 1000
state = copy(initial_state)
i = 0
while (sheep_still_out(state,fenceline) > 0):
i += 1
dog_pos, state = evolve_system_dog(state,pmove,fenceline,delta,dcut,path,dog_
if i % report_every == 0:
print("At time = {0} there are {1} sheep still out".format(i,sheep_still_o
print("All sheep in the pen at time = {0}".format(i))
# code here
from math import sqrt
a = np.array(times)
print("It takes an average of {0} +- {1} s for this sheepdog to work".format(a.mea
10/27/2018 HW-04_Programming_a_robotic_sheepdog-STUDENT
http://localhost:8888/notebooks/HW-04_Programming_a_robotic_sheepdog-STUDENT.ipynb 15/16
In [ ]:
First we'll check if this is a good path:
In [ ]:
Run a single 1000-step simulation with this dog path while visualizing every 10 steps
In [ ]:
Now, use dog_path2 to run 10 simulations, and report the results
In [ ]:
Which worked better, and why?
// Answer here
Question 7: Your turn (5 points)
Make a new dog path of your choosing, test it using pathcheck, and then run it 10 times. Compare
the average time and standard error measurements with the two paths above.
In [ ]:
Do you have any tips for an even better sheepdog herding strategy?
// Answer here
y0,x0 = dog_start_pos
dog_path2 = [(y0,x0)]
x = x0
y = y0
# first run to the left wall
# then run to y = 30
# then run to the right wall
# then run back to the top
# then run back to the middle
pathcheck(dog_path2)
# code here
# code here
# code here
10/27/2018 HW-04_Programming_a_robotic_sheepdog-STUDENT
http://localhost:8888/notebooks/HW-04_Programming_a_robotic_sheepdog-STUDENT.ipynb 16/16
Would this require any changes to the code above?
// Answer here
Assignment Wrap-up
Fill out the following Google form before you submit your assignment.
In [ ]:
Congratulations, you're done!
Submit this assignment by uploading it to the course Desire2Learn web page. Go to the "Homework
Assignments" section, find the submission folder link for Homework #3, and upload it there. Make
sure to upload the pictures of your compartmental models as well!
Copyright 2018, Michigan State University Board of Trustees
版权所有:编程辅导网 2021 All Rights Reserved 联系方式:QQ:99515681 微信:codinghelp 电子信箱:99515681@qq.com
免责声明:本站部分内容从网络整理而来,只供参考!如有版权问题可联系本站删除。