Robotics: How to Get Started — Part 4: Robotic Behaviors

Robotics: How to Get Started — Part 4: Robotic Behaviors

By John Blankenship    View In Digital Edition  


It has come to my attention that some SERVO readers that classified themselves as novice robot hobbyists are finding this series of articles very complex even though they are the intended audience. Building a robot is a very complicated endeavor that involves many disciplines, and it is unreasonable to even hope that a few short articles can answer all your questions.

That said, I do understand how frustrating it can be to feel like there is so much to learn, and for a while, it can certainly feel like the more someone learns about robotics, the more they realize they don’t know.

I suggest that novice readers try not to view this series as a solution, but rather see it as an overview that can help them see how the various aspects of a robot work together to accomplish a goal. As you start to understand the various aspects of robotics, you’ll start reading other SERVO articles with a clearer understanding of what you should be trying to learn from them.

Finally, if during your studies you find any specific topics that you would like to see covered in a future article, then email your ideas to me at RobotBASIC@yahoo.com and I will do my best to accommodate.


The previous installment of this series developed an on-screen environment that can be explored by a simulated robot. With the environment complete, we are ready to create behaviors that can allow the robot to autonomously navigate through the obstacles in search of a specific goal.

In the previous article, a program was developed to create the environment shown in Figure 1.

Figure 1.


Notice the robot on the left side of the screen and the red goal on the right. Each time the program is run, a random number of random-sized circular objects lie between the robot and the goal. These objects can block the robot’s movements as well as its ability to see the goal.

The mission for this article is to create a series of robotic behaviors that will allow the robot to navigate through the environment and find the goal. Before we can program these behaviors, we need to develop an algorithm (a detailed plan for accomplishing our objective).

When developing an algorithm for controlling a robot’s behavior, it’s often valuable to imagine that you are the robot and think about how you might attempt to successfully complete the task at hand. In this case, the primary task is to find the goal, but that might feel complex or even overwhelming especially if you have never attempted such a task.

A good initial approach is to think about all the little things you (or the robot) might have to do to complete the task. At this stage, it’s important to just list some activities and behaviors or even skills or abilities that might be helpful or necessary in order to be successful.

Don’t worry, if some things seem too simple or too complex and don’t be overly concerned at this stage about making sure your list is complete. The details will be worked out later and the process of working out those details will help you realize any omissions you might have made. I suggest you actually try to create your list before reading further.

The List

Below is a quick list I came up with. Don’t expect it to be exactly like yours. Remember, this is a brainstorming session. The basic premise of my approach is that if you can see the goal, then just face it and move forward until you arrive at the goal.

The robot should be able to:

  • Wander around trying to see the goal.
  • Face the goal (when it’s seen) and move toward it.
  • Determine when the goal is reached.

These ideas only give us a general idea of how we might go about accomplishing the task, but now that we have something down on paper it’s easier to see additional problems that we might not have considered. Many readers probably already included some of these additional problems in their original list, but since this series of articles is intended for those who are new to robotics, it’s important to proceed slowly.

Looking for Problems

Once we get a basic plan (our initial list in this case) for finding the goal, we can start analyzing each of the points in the list to determine if they are achievable or if they are so complex that they need to be broken down further in order to accomplish that particular sub-task.

As tasks are broken down, it can become apparent that additional skills and/or actions are needed.

For example, how do you make the robot move around in random directions? How do you detect objects that might be in your way? How do you make the robot “look” for the goal and how will it know if it has reached the goal?

All these questions relate to programming and the answers will depend on what programming language you’re using as well as the hardware you’re trying to control.

This is why when you start thinking about solving a robotic problem, you need to first think in terms of what needs to be done to accomplish the task. Once you’ve come up with a basic plan or algorithm, then — and only then — should you turn your attention to how the individual actions in your algorithm can be implemented with your chosen programming language.

Choosing a Language

As you know from previous articles in this series, we’re using RobotBASIC because it’s easy to use, it includes a robot simulator so you can learn programming without building a robot, and it’s free from RobotBASIC.org.

You might decide to make RobotBASIC your main language because it can control real robots too, but it’s important to realize that most hobbyists learn many languages before they settle on one or two as their favorites. Plus, there will always be some situations that will require a special language with some special features needed for your project.

However, in situations like our current project, the point is that you should expect to study the language’s help file to see how you can use it to move your robot or see objects or colors, etc.

One of the best ways to discover these things with various languages is to read articles in magazines like SERVO. This article is a good example because it will show you how all these things can be accomplished with the simulator (and the next installment will show you how to use RobotBASIC to control real robots).

Developing the Algorithm

For now, though, let’s try to create a concise set of statements that describes an algorithm that can make our robot find the goal in an environment like Figure 1.

A reasonable approach is shown in Figure 2 which basically says you (or the robot) should wander around a little then look for the goal.

Wander around, stop, and look for the goal
If the goal is seen
    Face the goal
    Move toward it
Repeat all of the above steps till the goal is found   

Figure 2.


If you see it, turn to face it, and move forward. You should just repeat these steps until you find the goal.

This algorithm (at least on the surface) seems like it should work, but if you think about each of these steps individually, you should realize that we aren’t considering that objects might get in our way. For example, it’s certainly possible that you could see the goal, but not have a big enough path to walk directly to it.

Another situation that needs to be analyzed is the wandering around behavior. This sounds easy at first too, but then you realize you must decide what you’re going to do if you are about to bump into an obstacle that prevents you from wandering. We could try to solve all these problems and outline the solutions in Figure 2, but that can become very complex.

A much better way is to just keep our initial algorithm (Figure 2) simple and assume that we will build modules that can make the robot wander or move toward the goal, etc. It will be the responsibility of those individual modules to handle their associated problems (such as obstacles).

This idea of letting modules solve their own problems is not unlike how a company works. The president of a company decides what needs to be done to accomplish their goals (the overall algorithm), but the details of how things are actually accomplished are left to the employees. This allows the president to concentrate on a big-picture plan without getting bogged down in details.

Individual employees only have to think about solving tasks associated with their specific job. If an employee’s job becomes too large or complex, we can create a department with numerous employees so that each can handle smaller aspects of the larger problem.

Large companies work this way, and this is exactly how large programs are built too. A main module acts like the president and decides what needs to be done. Other modules are used to perform the work unless the task is too complex. When this happens, we just break down the task into smaller pieces and assign those to lower-level modules.

Starting to Program

It’s important that you don’t start programming until you’ve developed an algorithm that you at least think will work. Sometimes it won’t work properly, of course, but you will always come out ahead if you perfect some kind of plan before you start to code.

Space doesn’t permit a lengthy detailed account of how the algorithm discussed above can be transformed into a program, but understanding the algorithm should help you follow the code and start to understand how programs are written.

Recall from the last article in this series that we had a main routine that called other sub-routines to perform the actual work. The new main module (shown in Figure 3) is almost exactly the same as last time.

main:
    gosub DrawEnvironment
    gosub CreateRobot
    gosub CreateGoal
    // below is the new line
    gosub FindGoal
end

Figure 3.


The first three routines that are called simply create the environment, robot, and goal. It was the responsibility of these routines to handle any problems associated with performing their tasks (see the previous article for details).

A new line in the main program calls a module that finds the goal. This is the module that implements the algorithm discussed earlier, but it will do so by calling other modules as previously described. The FindGoal module is shown in Figure 4.

FindGoal:
  AtGoal = FALSE
  while not AtGoal
    call LookForGoal(seen)
    if seen
      call MoveTowardGoal(AtGoal)
      // Note: MoveTowardGoal returns if blocked or at goal
      if not AtGoal then call AvoidObstacle()
    else
      call Wander()
    endif
  wend
  setcolor black,white
  xyString 728,Ygoal-5, “Found”
return

Figure 4.


A variable, AtGoal, will keep track of whether the goal has been found and will be used to decide when the program should end. Variables like this allow information to be shared among various modules.

A while loop calls routines to implement the algorithm as long as the goal has not been found. Inside the loop, a call is made to the module that looks for the goal. The details of how this works will be handled by that module. Let’s continue to examine the FindGoal module to see how it implements the algorithm in Figure 2.

When LookForGoal is called, we pass it a variable called seen. The LookForGoal module (see Figure 5) will be responsible for setting the value of seen to true or false depending on if the goal is within view.

Sub LookForGoal(&seen)
  seen = FALSE
  for a=1 to 360
    if rLook(0) = RED
      seen = TRUE
      break
    endif
    rTurn -1 // turns robot 1 degree to the left
  next
return

Figure 5.


It does this by rotating the robot 360° and using a simulated camera to look for the color Red. If Red is detected, the variable seen will be set to true and the robot stops turning (because it’s facing the goal).

If Red is never detected, then the robot completes 360 turns and the module is terminated. It returns to the FindGoal module and execution continues with the line following the call to LookForGoal. The code in FindGoal can look at the value of seen to determine what to do.

If the goal is not seen, then the robot just continues to wander around by calling the routine Wander. If the goal was seen, then we call another module to move the robot toward the goal. That module will terminate and return if the goal is reached or if the robot encounters another object.

It also sets the variable AtGoal to true or false so the calling module can determine if the robot should avoid an obstacle or if it should terminate the main loop. If the goal has been found, the while loop will terminate and a message is displayed saying the goal was found.

Hopefully, it will be easy to see how the code in these first two modules perform the necessary tasks. When you feel comfortable with these, let’s look at the remaining routines to see how they work.

The Wander routine is shown in Figure 6.

sub Wander()
  blocked = FALSE
  rTurn -90+ random(180)
  for i=1 to 100+random(100)
    if not (rFeel()&14)
      rForward 1
    else
      blocked = TRUE
      break
    endif
  next
  if blocked then call AvoidObstacle()
return

Figure 6.


It simply moves the robot around randomly. It starts by turning a random amount away from its current heading (between -90 to +90 degrees) and then moves forward a random distance that is always less than 100 pixels. It only moves one pixel at a time, though, so the front three feel sensors can be checked to prevent possible collisions. Should a potential collision be detected, the robot tries to go around the object blocking its path by calling AvoidObstacle.

The MoveTowardGoal routine that was called in Figure 4 is shown in Figure 7.

sub MoveTowardGoal(&AtGoal)
  AtGoal = FALSE // assume not at goal
  // move forward until something is encountered
  rForward 2 //
  while not (rFeel()&14) // check 3 sensors
    rForward 1
  wend
  // now blocked by obstacle or goal
  // see if it is the goal
  if rSense(LightBlue) then AtGoal=True
return

Figure 7.


It moves the robot forward until something (an obstacle or the goal) is detected by one of three proximity sensors on the front of the robot. The simulated robot can sense colors underneath the robot and it looks for the color LightBlue to determine if the robot has found the goal (there’s a LightBlue area on the floor around the goal). When this routine terminates, it returns the value of the variable AtGoal. If the value is true, we know the goal was found. Otherwise, it can be assumed that some obstacle prevented the robot from reaching the goal.

When the MoveTowardGoal routine terminates, execution continues in the FindGoal routine. If the robot is not at the goal (meaning some object must have blocked its path), then the routine AvoidObstacle is called.

Notice that this routine is used twice in the overall algorithm. It’s used here when an object is detected while moving toward the goal and it was also used in the Wander routine to go around objects that were encountered while trying to find a place where it could see the goal.

Encapsulating a function within a modular routine like this makes it easy to create reusable blocks of code.

Figure 8 shows the AvoidObstacle routine.

sub AvoidObstacle()
    CW = 1
    CCW = -1
    //if rFeel()&3
    if random(100)<50
      call FollowWall(CW)
    else
      call FollowWall(CCW)
    endif
return

Figure 8.


It makes the robot go around the object blocking its path by calling FollowWall. The variable passed to FollowWall determines if the following action will be clockwise or counterclockwise.

The if statement being used in the figure uses a random number to ensure that both clockwise and counterclockwise motions are both used about half the time. If you comment out that if statement and remove the comments from the one above it, the robot will make a more intelligent choice of which way to circumvent the obstacle.

Using this method, the if checks the feel sensors to see if the object is on the robot’s right side. If it is, the robot uses the appropriate clockwise motion. Otherwise, a counterclockwise path will be used. If you can understand both of these choices, you are on your way to understanding how a robot can be programmed to make intelligent choices.

The only module left to be explained is the FollowWall which is shown in Figure 9.

sub FollowWall(dir)
  if dir = 1 // ClockWise
    sensor = 1
  else
    sensor = 16
  endif
  while not(rFeel()&sensor)   // prevents stuck situations
    rTurn dir
  wend
  for i=1 to 100+random(100)
    if not (rFeel()&14) then rForward 1
    if rRange(dir*70)<49 then rTurn -dir
    if rRange(dir*70)>51 then rTurn dir
  next
  rTurn -dir*50
return

Figure 9.


It’s the most complicated routine in this program, so don’t get discouraged if it’s hard to understand.

At the beginning of this routine, the direction of the follow (CW or CCW) is used to select the sensor to use to align the robot with the wall of the object it’s trying to avoid. Sensor 1 is on the robot’s right side and Sensor 16 is on the left. A while loop is used to turn the robot until it’s somewhat aligned on a tangent with the obstacle’s perimeter (this makes it easier to start following the wall of the object). Some Boolean logic is used in the while statement to keep the robot turning until that sensor detects the object.

Once the robot is aligned, a for loop moves the robot a random distance around the object. As the robot moves, it uses its ranging sensor to determine how far it’s from the object. If it gets too far, it turns back toward the object. If it gets too close, the robot turns away. At the end of the follow behavior, the robot turns 50° away from the object to make it easier to begin wandering again if necessary.

There are many things in this module that might not make sense to someone new to programming, but since we’re out of space, some readers might just have to accept that this module makes the robot follow around the edge of an obstacle.

The Algorithm in Action

Now that all the routines have been discussed, let’s put them all together and run the program to see how the robot responds. Figure 10 shows the robot navigating a random environment.

Figure 10.


In this example, the robot can’t initially see the goal, so it wanders a couple times before encountering an obstacle. After going around the object, the robot wanders three more times before it sees the goal. When it tries to move toward the goal, though, another object blocks its path. After going around this object, the robot sees the goal and moves to it without problems.

Hopefully, this article has been more inspiring than frustrating to novice readers. Learning a programming language is not an easy task (remember how long it took you to become proficient in English). You can get the complete program from the article downloads or RobotBASIC.org. If you don’t already have a copy of RobotBASIC, it’s free from RobotBASIC.org.

In the next article of this series, we’ll discuss how the algorithms used to control RobotBASIC’s simulated robot can be used to control real robots.  SV


Downloads

202005-Blankenship.zip

What’s in the zip?
Complete BAS Program



Article Comments