# coding=latin-1 # # File: scribpro.py # by Justin Mangue # # Description: # This is a script that enables use of a Prolog natural language parsing component to control a Scribbler II robot # over bluetooth. Use in sync with nlp.pl. # # Dependencies: # Python 2.7.4 http://www.python.org/getit/ # PySwip 0.2.3 https://code.google.com/p/pyswip/ # PySpeech https://code.google.com/p/pyspeech/ # Myro 2.9.5 http://myro.roboteducation.org/download/ # Swi-Prolog 6.2.6 http://www.swi-prolog.org/Download.html # IMPORTS from sys import exit from myro import * from pyswip import Prolog import speech as pyspeech # GLOBAL SETTINGS verNum = '0.6' # ScribPro Version Number scribblerPort = 'COM5' # Scribbler Bluetooth Port prologFile = 'nlp.pl' # Prolog file to consult debug = 'on' # Debug mode, if enabled, shows detailed prolog parse information inputMode = "text" # Input in text mode by default outputMode = "text" # Output to text by default # CLASSES class PrologResult(object): def __init__(self, prologcall, status, pythoncode): self.prologcall = prologcall self.status = status self.pythoncode = pythoncode # FUNCTIONS def strClean(string): """ strClean(str) -- converts "A string like This" into a list of lowercase atoms i.e. [a,string,like,this] for use in Prolog. also converts commas to "and"s """ finalstring = "[" loweredstring = string.lower() exclude = '!"#$%&'()*+-./:;<=>?@[\]^_`{|}~' for c in exclude: loweredstring = loweredstring.replace(c,"") for ch in loweredstring: if ch == ' ': finalstring = finalstring + "," elif ch == ',': finalstring = finalstring + ",comma" #elif ch == u'xb0' or ch == '°': # finalstring = finalstring + ",degrees" else: finalstring = finalstring + ch return finalstring + "]" def parse(inStr): """parse(str) -- cleans the user input and then throws it at Prolog for analysis""" codeOut = "null" sentence = strClean(inStr) prologCall = 'parseToCode(' + sentence + ', Status, CodeOut).' for soln in prolog.query(prologCall, maxresult=1): statusOut = ''.join(soln["Status"]) if statusOut == 'valid': codeOut = buildMultiCode(soln["CodeOut"]) return PrologResult(prologCall,statusOut,codeOut) def buildCode(prologterms): """converts a [instruction, param1, param2, ...] list into Python-exec-friendly "instruction(param1,param2,...)" functor format""" parameters = [str(item) for item in prologterms] if len(parameters) > 2: codeOut = parameters[0] + '(' for x in range(len(parameters)-2): codeOut = codeOut + parameters[x+1] + ', ' codeOut = codeOut + parameters[len(parameters)-1] + ')' elif len(parameters) == 2: codeOut = parameters[0] + '(' + parameters[1] + ')' else: codeOut = parameters[0] + '()' return codeOut def buildMultiCode(instructions): """converts a list of [instruction, param1, param2] codes into a list of Python-exec-friendly "instruction(param1,param2,...)" strings i.e. buildMultiCode([('forward',['1.0','3.0']),('stop',[])]) -> ['forward(1.0, 3.0)', 'stop()']""" multiCodeOut = [] for x in instructions: line = buildCode(x) multiCodeOut.append(line) return multiCodeOut def execute(code): # potential security concerns here! """executes multiple lines of python script in sequence""" for line in code: if type(line) is list: execute(line) else: exec line def toggle_inputMode(mode): """toggles between text and voice mode""" if mode == "text": print "Now activating voice input." return "voice" else: print "Switching back to text-input mode." return "text" def toggle_outputMode(mode): """toggles between text and voice mode""" if mode == "text": print "Now activating voice output." return "voice" else: print "Switching back to text-only output mode." return "text" def toggle_debugMode(): """toggles between debug mode being on/off""" if debug == "on": print "Disabling debug mode." return "off" else: print "Enabling debug mode." return "on" # addon robot commands def takePhoto(mode): """Take and display a photo in the specified color mode. 2=color(fast), 1=color, 0=gray""" if mode == 2: pic = takePicture("jpeg-fast") elif mode == 1: pic = takePicture("color") else: pic = takePicture("gray") show(pic,"Scribby Cam") return def move_until_wall(): """Repeatedly moves forward in small increments until wall is sensed""" while not wall(): forward(1.0, 0.7) return def moonwalk(time): """Moonwalk backwards for time""" while timeRemaining(time): forward(.25,.1) backward(1,.3) # MAIN def main(): # global variable references global debug global prolog global inputMode global outputMode # initialize the robot print "***************** ScribPro v" + verNum + " *****************" init(scribblerPort) print "Scribbler II found on bluetooth (" + scribblerPort + ")." # instantiation of Prolog interface prolog = Prolog() prolog.consult(prologFile) print "Prolog has been initialized." print "Loaded " + prologFile + " into SWI-Prolog!" print "Starting in " + inputMode + " input mode. ['voice' to toggle]" print "Starting in " + outputMode + " output mode. ['sound' to toggle]" print "*************************************************" # main routine while(1): # grab a line of input, either text or voice if inputMode == "voice": try: inStr = pyspeech.input("nAwaiting voice command. (Ctrl-C to enter text)") print "Voice command: " + inStr + "n" except KeyboardInterrupt: inStr = raw_input("Type in a command: ") else: inStr = raw_input("nEnter a command: ") # parse the input and behave appropriately if (inStr == 'quit' or inStr == 'exit'): print "Exiting..." exit(0) elif inStr == 'reload': newprolog = Prolog() newprolog.consult(prologFile) prolog = newprolog print "Reloaded " + prologFile + " into Prolog!" elif inStr == 'voice': inputMode = toggle_inputMode(inputMode) elif inStr == 'sound': outputMode = toggle_outputMode(outputMode) elif inStr == 'debug': debug = toggle_debugMode() else: Result = parse(inStr) if Result.status == 'valid': if outputMode == "voice": pyspeech.say("Okay") print "Okay." execute(Result.pythoncode) else: if outputMode == "voice": errorMsg = "I don't understand " + inStr pyspeech.say(errorMsg) print "I don't understand "" + inStr + ""." # Print detailed debug information if debug == "on": print "nProlog query executed:", Result.prologcall print "Status:", Result.status print "CodeOut:", Result.pythoncode if __name__ == "__main__": main()