intro2ai/p3_rl/crawler.py

385 lines
13 KiB
Python
Raw Normal View History

2021-11-27 16:16:51 +01:00
# crawler.py
# ----------
# Licensing Information: You are free to use or extend these projects for
# educational purposes provided that (1) you do not distribute or publish
# solutions, (2) you retain this notice, and (3) you provide clear
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
#
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
# The core projects and autograders were primarily created by John DeNero
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
# Student side autograding was added by Brad Miller, Nick Hay, and
# Pieter Abbeel (pabbeel@cs.berkeley.edu).
#!/usr/bin/python
import math
from math import pi as PI
import time
import environment
import random
class CrawlingRobotEnvironment(environment.Environment):
def __init__(self, crawlingRobot):
self.crawlingRobot = crawlingRobot
# The state is of the form (armAngle, handAngle)
# where the angles are bucket numbers, not actual
# degree measurements
self.state = None
self.nArmStates = 9
self.nHandStates = 13
# create a list of arm buckets and hand buckets to
# discretize the state space
minArmAngle,maxArmAngle = self.crawlingRobot.getMinAndMaxArmAngles()
minHandAngle,maxHandAngle = self.crawlingRobot.getMinAndMaxHandAngles()
armIncrement = (maxArmAngle - minArmAngle) / (self.nArmStates-1)
handIncrement = (maxHandAngle - minHandAngle) / (self.nHandStates-1)
self.armBuckets = [minArmAngle+(armIncrement*i) \
for i in range(self.nArmStates)]
self.handBuckets = [minHandAngle+(handIncrement*i) \
for i in range(self.nHandStates)]
# Reset
self.reset()
def getCurrentState(self):
"""
Return the current state
of the crawling robot
"""
return self.state
def getPossibleActions(self, state):
"""
Returns possible actions
for the states in the
current state
"""
actions = list()
currArmBucket,currHandBucket = state
if currArmBucket > 0: actions.append('arm-down')
if currArmBucket < self.nArmStates-1: actions.append('arm-up')
if currHandBucket > 0: actions.append('hand-down')
if currHandBucket < self.nHandStates-1: actions.append('hand-up')
return actions
def doAction(self, action):
"""
Perform the action and update
the current state of the Environment
and return the reward for the
current state, the next state
and the taken action.
Returns:
nextState, reward
"""
nextState, reward = None, None
oldX,oldY = self.crawlingRobot.getRobotPosition()
armBucket,handBucket = self.state
armAngle,handAngle = self.crawlingRobot.getAngles()
if action == 'arm-up':
newArmAngle = self.armBuckets[armBucket+1]
self.crawlingRobot.moveArm(newArmAngle)
nextState = (armBucket+1,handBucket)
if action == 'arm-down':
newArmAngle = self.armBuckets[armBucket-1]
self.crawlingRobot.moveArm(newArmAngle)
nextState = (armBucket-1,handBucket)
if action == 'hand-up':
newHandAngle = self.handBuckets[handBucket+1]
self.crawlingRobot.moveHand(newHandAngle)
nextState = (armBucket,handBucket+1)
if action == 'hand-down':
newHandAngle = self.handBuckets[handBucket-1]
self.crawlingRobot.moveHand(newHandAngle)
nextState = (armBucket,handBucket-1)
newX,newY = self.crawlingRobot.getRobotPosition()
# a simple reward function
reward = newX - oldX
self.state = nextState
return nextState, reward
def reset(self):
"""
Resets the Environment to the initial state
"""
## Initialize the state to be the middle
## value for each parameter e.g. if there are 13 and 19
## buckets for the arm and hand parameters, then the intial
## state should be (6,9)
##
## Also call self.crawlingRobot.setAngles()
## to the initial arm and hand angle
armState = self.nArmStates/2
handState = self.nHandStates/2
self.state = armState,handState
self.crawlingRobot.setAngles(self.armBuckets[armState],self.handBuckets[handState])
self.crawlingRobot.positions = [20,self.crawlingRobot.getRobotPosition()[0]]
class CrawlingRobot:
def setAngles(self, armAngle, handAngle):
"""
set the robot's arm and hand angles
to the passed in values
"""
self.armAngle = armAngle
self.handAngle = handAngle
def getAngles(self):
"""
returns the pair of (armAngle, handAngle)
"""
return self.armAngle, self.handAngle
def getRobotPosition(self):
"""
returns the (x,y) coordinates
of the lower-left point of the
robot
"""
return self.robotPos
def moveArm(self, newArmAngle):
"""
move the robot arm to 'newArmAngle'
"""
oldArmAngle = self.armAngle
if newArmAngle > self.maxArmAngle:
raise 'Crawling Robot: Arm Raised too high. Careful!'
if newArmAngle < self.minArmAngle:
raise 'Crawling Robot: Arm Raised too low. Careful!'
disp = self.displacement(self.armAngle, self.handAngle,
newArmAngle, self.handAngle)
curXPos = self.robotPos[0]
self.robotPos = (curXPos+disp, self.robotPos[1])
self.armAngle = newArmAngle
# Position and Velocity Sign Post
self.positions.append(self.getRobotPosition()[0])
# self.angleSums.append(abs(math.degrees(oldArmAngle)-math.degrees(newArmAngle)))
if len(self.positions) > 100:
self.positions.pop(0)
# self.angleSums.pop(0)
def moveHand(self, newHandAngle):
"""
move the robot hand to 'newArmAngle'
"""
oldHandAngle = self.handAngle
if newHandAngle > self.maxHandAngle:
raise 'Crawling Robot: Hand Raised too high. Careful!'
if newHandAngle < self.minHandAngle:
raise 'Crawling Robot: Hand Raised too low. Careful!'
disp = self.displacement(self.armAngle, self.handAngle, self.armAngle, newHandAngle)
curXPos = self.robotPos[0]
self.robotPos = (curXPos+disp, self.robotPos[1])
self.handAngle = newHandAngle
# Position and Velocity Sign Post
self.positions.append(self.getRobotPosition()[0])
# self.angleSums.append(abs(math.degrees(oldHandAngle)-math.degrees(newHandAngle)))
if len(self.positions) > 100:
self.positions.pop(0)
# self.angleSums.pop(0)
def getMinAndMaxArmAngles(self):
"""
get the lower- and upper- bound
for the arm angles returns (min,max) pair
"""
return self.minArmAngle, self.maxArmAngle
def getMinAndMaxHandAngles(self):
"""
get the lower- and upper- bound
for the hand angles returns (min,max) pair
"""
return self.minHandAngle, self.maxHandAngle
def getRotationAngle(self):
"""
get the current angle the
robot body is rotated off the ground
"""
armCos, armSin = self.__getCosAndSin(self.armAngle)
handCos, handSin = self.__getCosAndSin(self.handAngle)
x = self.armLength * armCos + self.handLength * handCos + self.robotWidth
y = self.armLength * armSin + self.handLength * handSin + self.robotHeight
if y < 0:
return math.atan(-y/x)
return 0.0
## You shouldn't need methods below here
def __getCosAndSin(self, angle):
return math.cos(angle), math.sin(angle)
def displacement(self, oldArmDegree, oldHandDegree, armDegree, handDegree):
oldArmCos, oldArmSin = self.__getCosAndSin(oldArmDegree)
armCos, armSin = self.__getCosAndSin(armDegree)
oldHandCos, oldHandSin = self.__getCosAndSin(oldHandDegree)
handCos, handSin = self.__getCosAndSin(handDegree)
xOld = self.armLength * oldArmCos + self.handLength * oldHandCos + self.robotWidth
yOld = self.armLength * oldArmSin + self.handLength * oldHandSin + self.robotHeight
x = self.armLength * armCos + self.handLength * handCos + self.robotWidth
y = self.armLength * armSin + self.handLength * handSin + self.robotHeight
if y < 0:
if yOld <= 0:
return math.sqrt(xOld*xOld + yOld*yOld) - math.sqrt(x*x + y*y)
return (xOld - yOld*(x-xOld) / (y - yOld)) - math.sqrt(x*x + y*y)
else:
if yOld >= 0:
return 0.0
return -(x - y * (xOld-x)/(yOld-y)) + math.sqrt(xOld*xOld + yOld*yOld)
raise 'Never Should See This!'
def draw(self, stepCount, stepDelay):
x1, y1 = self.getRobotPosition()
x1 = x1 % self.totWidth
## Check Lower Still on the ground
if y1 != self.groundY:
raise 'Flying Robot!!'
rotationAngle = self.getRotationAngle()
cosRot, sinRot = self.__getCosAndSin(rotationAngle)
x2 = x1 + self.robotWidth * cosRot
y2 = y1 - self.robotWidth * sinRot
x3 = x1 - self.robotHeight * sinRot
y3 = y1 - self.robotHeight * cosRot
x4 = x3 + cosRot*self.robotWidth
y4 = y3 - sinRot*self.robotWidth
self.canvas.coords(self.robotBody,x1,y1,x2,y2,x4,y4,x3,y3)
armCos, armSin = self.__getCosAndSin(rotationAngle+self.armAngle)
xArm = x4 + self.armLength * armCos
yArm = y4 - self.armLength * armSin
self.canvas.coords(self.robotArm,x4,y4,xArm,yArm)
handCos, handSin = self.__getCosAndSin(self.handAngle+rotationAngle)
xHand = xArm + self.handLength * handCos
yHand = yArm - self.handLength * handSin
self.canvas.coords(self.robotHand,xArm,yArm,xHand,yHand)
# Position and Velocity Sign Post
# time = len(self.positions) + 0.5 * sum(self.angleSums)
# velocity = (self.positions[-1]-self.positions[0]) / time
# if len(self.positions) == 1: return
steps = (stepCount - self.lastStep)
if steps==0:return
# pos = self.positions[-1]
# velocity = (pos - self.lastPos) / steps
# g = .9 ** (10 * stepDelay)
# g = .99 ** steps
# self.velAvg = g * self.velAvg + (1 - g) * velocity
# g = .999 ** steps
# self.velAvg2 = g * self.velAvg2 + (1 - g) * velocity
pos = self.positions[-1]
velocity = pos - self.positions[-2]
vel2 = (pos - self.positions[0]) / len(self.positions)
self.velAvg = .9 * self.velAvg + .1 * vel2
velMsg = '100-step Avg Velocity: %.2f' % self.velAvg
# velMsg2 = '1000-step Avg Velocity: %.2f' % self.velAvg2
velocityMsg = 'Velocity: %.2f' % velocity
positionMsg = 'Position: %2.f' % pos
stepMsg = 'Step: %d' % stepCount
if 'vel_msg' in dir(self):
self.canvas.delete(self.vel_msg)
self.canvas.delete(self.pos_msg)
self.canvas.delete(self.step_msg)
self.canvas.delete(self.velavg_msg)
# self.canvas.delete(self.velavg2_msg)
# self.velavg2_msg = self.canvas.create_text(850,190,text=velMsg2)
self.velavg_msg = self.canvas.create_text(650,190,text=velMsg)
self.vel_msg = self.canvas.create_text(450,190,text=velocityMsg)
self.pos_msg = self.canvas.create_text(250,190,text=positionMsg)
self.step_msg = self.canvas.create_text(50,190,text=stepMsg)
# self.lastPos = pos
self.lastStep = stepCount
# self.lastVel = velocity
def __init__(self, canvas):
## Canvas ##
self.canvas = canvas
self.velAvg = 0
# self.velAvg2 = 0
# self.lastPos = 0
self.lastStep = 0
# self.lastVel = 0
## Arm and Hand Degrees ##
self.armAngle = self.oldArmDegree = 0.0
self.handAngle = self.oldHandDegree = -PI/6
self.maxArmAngle = PI/6
self.minArmAngle = -PI/6
self.maxHandAngle = 0
self.minHandAngle = -(5.0/6.0) * PI
## Draw Ground ##
self.totWidth = canvas.winfo_reqwidth()
self.totHeight = canvas.winfo_reqheight()
self.groundHeight = 40
self.groundY = self.totHeight - self.groundHeight
self.ground = canvas.create_rectangle(0,
self.groundY,self.totWidth,self.totHeight, fill='blue')
## Robot Body ##
self.robotWidth = 80
self.robotHeight = 40
self.robotPos = (20, self.groundY)
self.robotBody = canvas.create_polygon(0,0,0,0,0,0,0,0, fill='green')
## Robot Arm ##
self.armLength = 60
self.robotArm = canvas.create_line(0,0,0,0,fill='orange',width=5)
## Robot Hand ##
self.handLength = 40
self.robotHand = canvas.create_line(0,0,0,0,fill='red',width=3)
self.positions = [0,0]
# self.angleSums = [0,0]
if __name__ == '__main__':
from graphicsCrawlerDisplay import *
run()