ROS Robotics Projects
上QQ阅读APP看书,第一时间看更新

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 the category, pattern, and template 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 that WHAT IS YOUR NAME is the user input. There will only be one pattern inside a category, placed after the category tag. Inside a pattern, we can include wild cards such as * or -, which can replace a string in the corresponding position.
  • <template>: The template 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. The n 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:

Loading multiple AIML files

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:

Creating an AIML bot in ROS

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:

The 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.

Installing the ROS sound_play package

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.

Installing the dependencies of sound_play

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.

Installing the sound_play ROS package

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.

Note

You can clone the source code discussed in the book from the following Git repository:

https://github.com/qboticslabs/ros_robotics_projects

Creating 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:

Creating 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.

The aiml_server node

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.

Note

You can clone the source code discussed in the book from the following Git repository:

https://github.com/qboticslabs/ros_robotics_projects

The AIML client node

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 aiml_tts client node

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 AIML speech recognition node

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.

start_chat.launch

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> 

start_tts_chat.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> 

start_speech_chat.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
start_speech_chat.launch

Figure 7: Output of the start_speech_chat launch file

Here are the topics generated when we run this launch file:

start_speech_chat.launch

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:

start_speech_chat.launch

Figure 9: Manually publishing input to speech topic