Getting started with AIML
AIML (Artificial Intelligence Markup Language) is an XML-based language to store segments of knowledge inside XML tags. AIML files help us store knowledge in a structured way so that we can easily access it whenever required.
AIML was developed by Richard Wallace and the free software community worldwide between 1995 and 2002. You may have heard about chatter bots such as Artificial Linguistic Internet Computer Entity (A.L.I.C.E.) and ELIZA. AIML is the base of these chatter bots. The dataset of the A.L.I.C.E. chatter bot is available under the GNU GPL license, and there are open source AIML interpreters available in C++, Python, Java, and Ruby. We can use these interpreters to feed our input to the knowledge base and retrieve the best possible reply from it.
AIML tags
There is a set of AIML tags to represent knowledge inside files. The following are some of the important tags and their uses:
<aiml>
: Each AIML file starts with this tag and ends with the</aiml>
tag. Basically, it holds the version of AIML and character encoding of the file. The<aiml>
tag is not mandatory, but it will be useful when handling a huge AIML dataset. Here is the basic usage of the<aiml>
tag:<aiml version="1.0.1" encoding="UTF-8"?> </aiml>
<category>
: Each knowledge segment is kept under this tag. This tag holds the input pattern from the user and outputs a response for it. The possible input from the user is kept under the<pattern>
tag, and the corresponding response is under the<template>
tag. Here is an example of thecategory
,pattern
, andtemplate
tags:<aiml version="1.0.1" encoding="UTF-8"> <category> <pattern> WHAT IS YOUR NAME </pattern> <template> MY NAME IS ROBOT </template> </category> </aiml>
When a user asks the robot, "What is your name?", the robot replies, "My name is Robot." This is how we store knowledge for the robot.
<pattern>
: This tag consists of user input. From the preceding code, we can see thatWHAT IS YOUR NAME
is the user input. There will only be one pattern inside a category, placed after thecategory
tag. Inside a pattern, we can include wild cards such as*
or-
, which can replace a string in the corresponding position.<template>
: Thetemplate
tag consists of responses to user input. In the previous code,MY NAME IS ROBOT
is the response.<star index = "n" />
: This tag helps extract a word from a sentence. Then
indicates which word of the sentence is to be extracted:<star index= "1" />
: This indicates the first fragment of the template sentence.<star index= "2" />
: This indicates the second fragment of the template sentence.Using the star index, we can extract the word from user input and insert the word into the response if needed.
Here is an example of using wildcards and a start index:
<aiml version="1.0.1" encoding="UTF-8"> <category> <pattern> MY NAME IS * </pattern> <template> NICE TO SEE YOU <star index="1"/> </template> </category> <category> <pattern> MEET OUR ROBOTS * AND * </pattern> <template> NICE TO SEE <star index="1"/> AND <star index="2"/>. </template> </category> </aiml>
Here, we can reuse the word that comes in the
*
position in the<template>
tag. Consider this input:You: MY NAME IS LENTIN Robot: NICE TO SEE YOU LENTIN
In the second category, you will get the following reply from the robot for the given input:
You: MEET OUR ROBOTS ROBIN AND TURTLEBOT Robot: NICE TO SEE ROBIN AND TURTLEBOT
These are the basic tags used inside AIML files. Next, we'll see how to load these files and retrieve an intelligent reply from the AIML knowledge base for a random input from the user.
Note
The following link will give you the list of AIML tags: http://www.alicebot.org/documentation/aiml-reference.html
The PyAIML interpreter
There are AIML interpreters in many languages used to load the AIML knowledge base and interact with it. One of the easiest ways of loading and interacting with AIML files is using an AIML interpreter in Python called PyAIML. The PyAIML module can read all the categories, patterns, and templates and can build a tree. Using a backtracking depth-first search algorithm, it can search for the appropriate response from the user in order to give the proper reply.
PyAIML can be installed on Windows, Linux, and Mac OS X. In Ubuntu, there are prebuilt DEB binaries that we can install from Software Center. We can also install PyAIML from source code. The current PyAIML will work well in Python 2.7. Let's look at how we can install it.
Installing PyAIML on Ubuntu 16.04 LTS
Installing PyAIML on Ubuntu is pretty easy and straightforward. We can install the package using the following command:
$ sudo apt-get install python-aiml
The version of PyAIML will be 0.86.
We can also install PyAIML from source code. Clone the source code from Git using the following command:
$ git clone https://github.com/qboticslabs/pyaiml
After cloning the package, switch to the PyAIML folder and install using the following command:
$ sudo python setup.py install
Great! You are done with the installation. Let's check whether your installation is correct.
Playing with PyAIML
Take a Python interpreter Terminal and just try to import the AIML module using the following command:
>>> import aiml
If the module is loaded properly, the pointer you will come to the next line without getting an error. Congratulations! Your installation is correct.
Let's see how to load an AIML file using this module.
To play with this module, first we need an AIML file. Save the following content in an AIML file called sample.aiml
in the home
folder. You can save the file anywhere, but it should be in the same path where the Python Terminal was started.
<aiml version="1.0.1" encoding="UTF-8"> <category> <pattern> MY NAME IS * </pattern> <template> NICE TO SEE YOU <star/> </template> </category> <category> <pattern> MEET OUR ROBOTS * AND * </pattern> <template> NICE TO SEE <star index="1"/> AND <star index="2"/>. </template> </category> </aiml>
After saving the AIML file, let's try to load it. The first step is to build an object of the PyAIML module called Kernel()
. The object name here is bot
:
>>> bot = aiml.Kernel()
Kernel()
is the main class doing the searching from the AIML knowledge base.
We can set the robot name using the following command:
>>> bot.setBotPredicate("name", ROBIN)
The next step is to load the AIML files; we can load one or more AIML files to memory.
To learn a single AIML file, use the following command:
>>> bot.learn('sample.aiml")
If the AIML file is correct, then you will get a message like this:
Loading sample.aiml... done (0.02 seconds)
This means that the sample AIML file is loaded properly in memory.
We can retrieve the response from the AIML file using the following command:
>>> print bot.respond("MY NAME IS LENTIN") 'NICE TO SEE YOU LENTIN'
If the user input is not in the file, you will get the following message:
'WARNING: No match found for input:'
Loading multiple AIML files
We have seen how to load a single AIML file to memory and retrieve response for a user input. In this section, we are going to see how to load multiple AIML files to memory; we are going to use these files for our AIML-based bots. Various AIML datasets are available on the Web, and some are also included in the code bundle. Given here is a file called startup.xml
that helps us load all AIML files in a single run. It's a simple AIML file with a pattern called LOAD AIML B
. When it gets this input from the user, it will learn all AIML files in that path using <learn>*.aiml</learn>
tags:
<aiml version="1.0"> <category> <pattern>LOAD AIML B</pattern> <template> <!-- Load standard AIML set --> <learn>*.aiml</learn> </template> </category> </aiml>
We can use the following code to load this XML file and "learn
" all the XML files to memory. After loading the AIML files, we can save the memory contents as a brain file. The advantage is that we can avoid the reloading of AIML files. Saving into a brain file will be helpful when we have thousands of AIML files:
#!/usr/bin/env python import aiml import sys import os #Changing current directory to the path of aiml files #This path will change according to your location of aiml files os.chdir('/home/robot/Desktop/aiml/aiml_data_files') bot = aiml.Kernel() #If there is a brain file named standard.brn, Kernel() will initialize using bootstrap() method if os.path.isfile("standard.brn"): bot.bootstrap(brainFile = "standard.brn") else: #If there is no brain file, load all AIML files and save a new brain bot.bootstrap(learnFiles = "startup.xml", commands = "load aiml b") bot.saveBrain("standard.brn") #This loop ask for response from user and print the output from Kernel() object while True: print bot.respond(raw_input("Enter input >"))
You can see that the AIML files are stored at /home/robot/Desktop/aiml/aiml_data_files/
. All AIML files including startup.xml
and AIML brain files are stored in the same folder. You can choose any folder you want. In the previous code, we are using a new API called bootstrap()
for loading, saving, and learning AIML files. The program tries to load a brain file called standard.brn
first, and if there is no brain file, it will learn from startup.xml
and save the brain file as standard.brn
. After saving the brain file, it will start a while
loop to start interacting with the AIML file.
If you run the code and there is no brain file, you may get output like this:
Figure 3: Loading multiple AIML files
Creating an AIML bot in ROS
The previous subsections were about understanding AIML tags and how to work with them using the PyAIML module. Let's see how to create an interactive AIML bot using ROS. The following figure shows the complete block diagram of the interactive bot:
Figure 4: Interactive AIML bot
Here is how the entire system works: The speech of the user is converted into text using the speech recognition system in ROS. Then, it will input either to the AIML engine or send as a robot command. The robot commands are specific commands meant for robot control. If the text is not a robot command, it will send it to the AIML engine, which will give an intelligent reply from its database. The output of the AIML interpreter will be converted to speech using the text-to-speech module. The speech will be heard through speaker at the same time a virtual face of the robot will be animated on the screen, syncing with the speech.
In this chapter, we are mainly dealing with the AIML part and TTS using ROS; you can refer to other sources to perform speech recognition in ROS as well.
The AIML ROS package
In this section, we are going to create a simple package to load the AIML files to memory using ROS nodes. The following is the block diagram of the working AIML ROS package:
Figure 5: Working of the AIML ROS package
Here's the explanation for the nodes shown in the diagram:
- aiml_server: This ROS node loads AIML files from the database and saves them into brain files. It subscribes to a topic called /chatter (
std_msgs/String
). The string data from the /chatter topic is the input of the AIML interpreter. The response from the AIML interpreter is published through the /response (std_msgs/String
) topic. - aiml_client: This ROS node waits for user input, and once it gets the input, it will publish it to the /chatter topic.
- aiml_tts_client: The AIML server publishes the response to the /response topic. The
tts
client node will subscribe to this topic and convert it to speech. - aiml_speech_recognition_client: This node will subscribe to the output from the speech recognition system and publish it to the /chatter topic.
The user can interact with AIML either by text chatting or speech. The speech recognition node will not do speech recognition; instead, it will receive the converted text from a speech recognition system and input it to the AIML server.
To create or install the ros-aiml
package, you may need to install some dependency packages.
The sound_play
package is a TTS convertor package in ROS. You can obtain more information about the package from http://wiki.ros.org/sound_play. To install this package, you will need install some Ubuntu package dependencies. Let's go through the commands to install them.
Update your Ubuntu repositories using the following command:
$ sudo apt-get update
These are the dependencies required for the sound_play
package:
$ sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0 gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-ugly python-gi festival
After installing these Ubuntu packages, you can install the sound_play
package using the following steps.
Clone the audio-common
packages into ros_project_dependencies_ws
:
ros_project_dependencies_ws/src$ git clone https://github.com/ros-drivers/audio_common
Install the packages using catkin_make
.
After installing these packages, you can make sure it is properly installed using the following command:
$ roscd sound_play
If it switches to the sound_play
package, you have installed it successfully.
Congratulations! You are done with all dependencies! Next, we will create the ros-aiml
package.
Using the following command, we can create the ros_aiml
package:
$ catkin_create_pkg ros_aiml rospy std_msgs sound_play
Inside the ros_aiml
package, create folders called data
, scripts
, and launch
to store the AIML files, Python scripts, and ROS launch files. This is the structure of the ros_aiml
package:
Figure 6: Structure of ros_aiml
You can keep the AIML files inside the data
folder, and all launch files can be kept inside the launch
folder. The scripts are saved inside the scripts
folder. Let's look at each script.
As we've already discussed, aiml_server
is responsible for loading and saving the AIML and AIM brain files. It is subscribed to the /chatter
topic, which is the input of the AIML interpreter and publishes the /response
topic, which is the response from the AIML interpreter. This is the main code snippet of aiml_server.py
:
def load_aiml(xml_file): data_path = rospy.get_param("aiml_path") print data_path os.chdir(data_path) if os.path.isfile("standard.brn"): mybot.bootstrap(brainFile = "standard.brn") else: mybot.bootstrap(learnFiles = xml_file, commands = "load aiml b") mybot.saveBrain("standard.brn") def callback(data): input = data.data response = mybot.respond(input) rospy.loginfo("I heard:: %s",data.data) rospy.loginfo("I spoke:: %s",response) response_publisher.publish(response) def listener(): rospy.loginfo("Starting ROS AIML Server") rospy.Subscriber("chatter", String, callback) # spin() simply keeps python from exiting until this node is stopped rospy.spin() if __name__ == '__main__': load_aiml('startup.xml') listener()
This ROS node is doing the same thing as the code that we used to load and save the AIML files. That code is converted into a ROS node that can accept input and send the response through a topic.
The client code will wait for user input and publish the user input to the /chatter
topic:
#!/usr/bin/env python import rospy from std_msgs.msg import String pub = rospy.Publisher('chatter', String,queue_size=10) rospy.init_node('aiml_client') r = rospy.Rate(1) # 10hz while not rospy.is_shutdown(): input = raw_input("\nEnter your text :> ") pub.publish(input) r.sleep()
The TTS client subscribes to the /response
topic and converts the response to speech using the sound_play
APIs:
#!/usr/bin/env python import rospy, os, sys from sound_play.msg import SoundRequest from sound_play.libsoundplay import SoundClient from std_msgs.msg import String rospy.init_node('aiml_soundplay_client', anonymous = True) soundhandle = SoundClient() rospy.sleep(1) soundhandle.stopAll() print 'Starting TTS' def get_response(data): response = data.data rospy.loginfo("Response ::%s",response) soundhandle.say(response) def listener(): rospy.loginfo("Starting listening to response") rospy.Subscriber("response",String, get_response,queue_size=10) rospy.spin() if __name__ == '__main__': listener()
The speech recognition node subscribes to /recognizer/output
and publishes to the /chatter
topic:
#!/usr/bin/env python import rospy from std_msgs.msg import String rospy.init_node('aiml_speech_recog_client') pub = rospy.Publisher('chatter', String,queue_size=10) r = rospy.Rate(1) # 10hz def get_speech(data): speech_text=data.data rospy.loginfo("I said:: %s",speech_text) pub.publish(speech_text) def listener(): rospy.loginfo("Starting Speech Recognition") rospy.Subscriber("/recognizer/output", String, get_speech) rospy.spin() while not rospy.is_shutdown(): listener()
The /recognizer/output
topic is published by ROS speech recognition packages such as Pocket Sphinx (http://wiki.ros.org/pocketsphinx).
Next, we'll look at the launch files used for starting each node.
The start_chat.launch
launch file launches the aiml_server
and aiml_client
nodes. Before running this launch file, you have to set the data folder path that is set as the ROS parameter. You can set it as your AIML data folder path:
<launch> <param name="aiml_path" value="/home/robot/ros_robotics_projects_ws/src/ros_aiml/data" /> <node name="aiml_server" pkg="ros_aiml" type="aiml_server.py" output="screen"> </node> <node name="aiml_client" pkg="ros_aiml" type="aiml_client.py" output="screen"> </node> </launch>
The launch file launches the aiml_server
, aiml_client
, and aiml_tts
nodes. The difference between the previous launch file and this one is that this will convert the AIML server response into speech:
<launch> <param name="aiml_path" value="/home/robot/ros_robotics_projects_ws/src/ros_aiml/data" /> <node name="aiml_server" pkg="ros_aiml" type="aiml_server.py" output="screen"> </node> <include file="$(find sound_play)/soundplay_node.launch"> </include> <node name="aiml_tts" pkg="ros_aiml" type="aiml_tts_client.py" output="screen"> </node> <node name="aiml_client" pkg="ros_aiml" type="aiml_client.py" output="screen"> </node> </launch>
The start_speech_chat.launch
launch file will start the AIML server, AIML TTS node, and speech recognition node:
<launch> <param name="aiml_path" value="/home/robot/ros_robotics_projects_ws/src/ros_aiml/data" /> <node name="aiml_server" pkg="ros_aiml" type="aiml_server.py" output="screen"> </node> <include file="$(find sound_play)/soundplay_node.launch"> </include> <node name="aiml_tts" pkg="ros_aiml" type="aiml_tts_client.py" output="screen"> </node> <node name="aiml_speech_recog" pkg="ros_aiml" type="aiml_speech_recog_client.py" output="screen"> </node> </launch>
After creating the launch file, change its permission using the following command:
$ sudo chmod +x *.launch
Use the following command to start interacting with the AIML interpreter:
$ roslaunch ros_aiml start_chat.launch
We can use the following command to start interacting with the AIML interpreter. The response will be converted to speech as well:
$ roslaunch ros_aiml start_tts_chat.launch
The following command will enable speech recognition and TTS:
$ roslaunch ros_aiml start_speech_chat.launch
If you set up the pocketsphinx
package for speech recognition, you can run it using the following command:
$ roslaunch pocketsphinx robotcup.launch
Figure 7: Output of the start_speech_chat launch file
Here are the topics generated when we run this launch file:
Figure 8: List of ROS topics
We can test the entire system without the speech recognition system too. You can manually publish the string to the /recognizer/output
topic, as shown here:
Figure 9: Manually publishing input to speech topic