LoDrive.c

Go to the documentation of this file.
00001 /**
00002    \file  Robots/LoBot/irccm/LoDrive.c
00003    \brief Driving API for Robolocust iRobot Create Command Module control
00004    program.
00005 
00006    This file defines the functions that implement the driving API for a
00007    control program meant to be run on the iRobot Create's Command Module.
00008    These functions accept the high-level drive commands issued by the
00009    higher layers of the Robolocust controller and convert them to their
00010    equivalent Open Interface byte sequences.
00011 */
00012 
00013 /*
00014  ************************************************************************
00015  * The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   *
00016  * by the University of Southern California (USC) and the iLab at USC.  *
00017  * See http://iLab.usc.edu for information about this project.          *
00018  *                                                                      *
00019  * Major portions of the iLab Neuromorphic Vision Toolkit are protected *
00020  * under the U.S. patent ``Computation of Intrinsic Perceptual Saliency *
00021  * in Visual Environments, and Applications'' by Christof Koch and      *
00022  * Laurent Itti, California Institute of Technology, 2001 (patent       *
00023  * pending; application number 09/912,225 filed July 23, 2001; see      *
00024  * http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     *
00025  ************************************************************************
00026  * This file is part of the iLab Neuromorphic Vision C++ Toolkit.       *
00027  *                                                                      *
00028  * The iLab Neuromorphic Vision C++ Toolkit is free software; you can   *
00029  * redistribute it and/or modify it under the terms of the GNU General  *
00030  * Public License as published by the Free Software Foundation; either  *
00031  * version 2 of the License, or (at your option) any later version.     *
00032  *                                                                      *
00033  * The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  *
00034  * that it will be useful, but WITHOUT ANY WARRANTY; without even the   *
00035  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      *
00036  * PURPOSE.  See the GNU General Public License for more details.       *
00037  *                                                                      *
00038  * You should have received a copy of the GNU General Public License    *
00039  * along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   *
00040  * to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   *
00041  * Boston, MA 02111-1307 USA.                                           *
00042  ************************************************************************
00043 */
00044 
00045 /*
00046    Primary maintainer for this file: mviswana usc edu
00047    $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Robots/LoBot/irccm/LoDrive.c $
00048    $Id: LoDrive.c 13769 2010-08-08 01:34:02Z mviswana $
00049 */
00050 
00051 /*------------------------------ HEADERS ------------------------------*/
00052 
00053 // lobot headers
00054 #include "LoDrive.h"
00055 #include "LoSensors.h"
00056 #include "LoUtils.h"
00057 #include "LoIO.h"
00058 #include "LoTimer.h"
00059 #include "LoOpenInterface.h"
00060 
00061 // avr headers
00062 #include <avr/interrupt.h>
00063 
00064 // Standard C headers
00065 #include <stdlib.h>
00066 
00067 /*------------------------------ GLOBALS ------------------------------*/
00068 
00069 // The lobot controller's higher layers operate in terms of speed and
00070 // steering directions. The iRobot Create's motion primitives, however,
00071 // combine the speed and steering states into a single drive command
00072 // (with special values to indicate driving straight ahead and turning
00073 // in-place).
00074 //
00075 // Since the higher layers specify motor commands separately, we maintain
00076 // two independent variables to keep track of the current speed and turn
00077 // requests and update them as required based on the command sent in.
00078 //
00079 // Thus, when the high level issues turn commands, we update the turn
00080 // radius but leave the speed variable alone. Similarly, when drive
00081 // commands come in, we leave the turn radius as-is and update only the
00082 // speed variable. But both variables get combined when we need to
00083 // despatch the Open Interface drive command to the iRobot Create.
00084 static volatile int g_speed ;
00085 static volatile int g_radius = LOBOT_OI_DRIVE_STRAIGHT ;
00086 
00087 // These variables are used to implement the acceleration/decelartion
00088 // functionality asynchronously w.r.t. the main "thread."
00089 //
00090 // We need to have the speed ramping up/down operation performed
00091 // asynchronously to ensure that the main "thread," which is responsible
00092 // for listening and reacting to the robot's sensors, doesn't get caught
00093 // up in a long loop that can cause it to miss a potentially dangerous
00094 // situation (e.g., wheel drops or cliffs).
00095 //
00096 // We use the low-level controller's 10ms timer to achieve the desired
00097 // asynchrony. Every time this timer fires, we ramp the speed up or down
00098 // as required. As its name suggests, the g_target_speed variable is used
00099 // to specify the desired speed to which the robot must ramp up or down.
00100 // However, we need another variable (viz., g_ramp_flag) to indicate
00101 // whether or not speed ramping should even occur. This is important
00102 // because the drive module provides low-level functions that allow other
00103 // parts of the low-level controller to bypass the
00104 // acceleration/deceleration functionality (important, for instance, when
00105 // reacting to wheel drops or cliffs).
00106 static volatile char g_ramp_flag ;
00107 static volatile int  g_target_speed ;
00108 
00109 /*-------------------------- INITIALIZATION ---------------------------*/
00110 
00111 // Forward declaration
00112 static void accelerate_decelerate(void) ;
00113 
00114 // This function initializes the drive system's acceleration/deceleration
00115 // functionality by hooking into the 10ms timer's callback list.
00116 void lo_init_drive(void)
00117 {
00118    lo_add_timer10_cb(accelerate_decelerate) ;
00119 }
00120 
00121 /*--------------------- HIGH-LEVEL DRIVE COMMANDS ---------------------*/
00122 
00123 // Sometimes, the high level may not have any commands queued. At those
00124 // times, it should send a NOP to the low level. A NOP is exactly what
00125 // it says: a no/null operation. If the high level doesn't send a NOP or
00126 // sends some other unrecognized command (e.g., command code zero), the
00127 // low-level controller will stop the robot, which may not be what is
00128 // desired.
00129 void lo_nop(int param){}
00130 
00131 // Driving the robot forward
00132 void lo_forward(int speed)
00133 {
00134    lo_drive(lo_clamp(speed, 0, 500), g_radius, 1) ;
00135 }
00136 
00137 // Driving the robot backward
00138 void lo_reverse(int speed)
00139 {
00140    lo_drive(-lo_clamp(speed, 0, 500), g_radius, 1) ;
00141 }
00142 
00143 // Stopping the robot.
00144 //
00145 // DEVNOTE: The speed parameter is neither necessary nor utilized in this
00146 // function. However, because the main module despatches incoming
00147 // high-level commands using a map, we need to define all command
00148 // handlers as taking one parameter.
00149 void lo_stop(int speed)
00150 {
00151    lo_drive(0, LOBOT_OI_DRIVE_STRAIGHT, 1) ;
00152 }
00153 
00154 // Turning the robot left
00155 void lo_left(int turn_radius)
00156 {
00157    if (turn_radius < 100)
00158       turn_radius = LOBOT_OI_TURN_INPLACE_CCW ;
00159    else
00160       turn_radius = lo_clamp(turn_radius, 100, 2000) ;
00161    lo_drive(g_speed, turn_radius, 0) ;
00162 }
00163 
00164 // Turning the robot right
00165 void lo_right(int turn_radius)
00166 {
00167    if (turn_radius < 100)
00168       turn_radius = LOBOT_OI_TURN_INPLACE_CW ;
00169    else
00170       turn_radius = -lo_clamp(turn_radius, 100, 2000) ;
00171    lo_drive(g_speed, turn_radius, 0) ;
00172 }
00173 
00174 // Straightening the robot.
00175 //
00176 // DEVNOTE: The turn_radius parameter is neither necessary nor utilized
00177 // in this function. However, because the main module despatches incoming
00178 // high-level commands using a map, we need to define all command
00179 // handlers as taking one parameter.
00180 void lo_straight(int turn_radius)
00181 {
00182    lo_drive(g_speed, LOBOT_OI_DRIVE_STRAIGHT, 0) ;
00183 }
00184 
00185 // In-place turns
00186 void lo_cmd_spin(int angle)
00187 {
00188    // First, spin the robot by the requested amount and record the actual
00189    // amount of rotation.
00190    angle = lo_spin(225, lo_clamp(angle, -360, 360)) ;
00191 
00192    // Then, retrieve any unintended translational motion that occured
00193    // during the spin (can happen due to actuation errors).
00194    lo_wait(15) ;
00195    lo_tx(LOBOT_OI_CMD_QUERY_LIST) ;
00196    lo_tx(1) ;
00197    lo_tx(LOBOT_OI_SENSOR_DISTANCE) ;
00198    char buf[2] = {0} ;
00199    lo_rx(buf, 2, 20) ;
00200 
00201    // Finally, mark the actual amount of rotation and translation for the
00202    // next sensor packet so that high-level won't miss any crucial motion
00203    // updates.
00204    lo_update_pending_odometry(lo_make_word(buf[0], buf[1]), angle) ;
00205 }
00206 
00207 /*--------------------- LOW-LEVEL DRIVE COMMANDS ----------------------*/
00208 
00209 // Helper function to send specified drive params in terms of Open
00210 // Interface specs.
00211 static void drive_oi(int speed, int turn)
00212 {
00213    lo_tx(LOBOT_OI_CMD_DRIVE) ;
00214    lo_tx(lo_hibyte(speed)) ;
00215    lo_tx(lo_lobyte(speed)) ;
00216    lo_tx(lo_hibyte(turn))  ;
00217    lo_tx(lo_lobyte(turn))  ;
00218 }
00219 
00220 // This function takes care of accelerating or decelerating the robot
00221 // asynchronously w.r.t. the main "thread" to ensure that the robot
00222 // continues to listen and react to the sensors instead of getting tied
00223 // up in a long loop that ramps the speed up or down. It is invoked by
00224 // the 10ms timer's callback triggering mechanism.
00225 static void accelerate_decelerate(void)
00226 {
00227    if (g_ramp_flag)
00228    {
00229       if (g_speed < g_target_speed)
00230       {
00231          g_speed += 25 ;
00232          if (g_speed > g_target_speed)
00233             g_speed = g_target_speed ;
00234       }
00235       else if (g_speed > g_target_speed)
00236       {
00237          g_speed -= 25 ;
00238          if (g_speed < g_target_speed)
00239             g_speed = g_target_speed ;
00240       }
00241       else // target speed achieved
00242       {
00243          g_ramp_flag = 0 ;
00244          return ;
00245       }
00246       drive_oi(g_speed, g_radius) ;
00247    }
00248 }
00249 
00250 // Send Create drive commands in terms of speed and turn radius.
00251 //
00252 // DEVNOTE: To support the acceleration/deceleration functionality
00253 // without tying up the main "thread" in a long loop that can make the
00254 // robot deaf to its sensors while the speed ramps up/down, we use the
00255 // ATmega168's Timer2 ISR (via the low-level controller's 10ms
00256 // "generalized" timer) to perform the necessary speed ramping. The ISR
00257 // uses a flag that is set or cleared by this function to determine
00258 // whether or not perform the ramping operations.
00259 //
00260 // Additionally, the ISR uses and/or modifies some other variables
00261 // that are also used in the main "thread" (e.g., g_speed).
00262 //
00263 // To ensure that the variables shared by the main thread and the Timer2
00264 // ISR don't get mixed up, we need some sort of mutex mechanism.
00265 // Unfortunately, avr-libc doesn't provide such a beast. And rolling our
00266 // own is too much work. As a workaround, we simply disable the Timer2
00267 // interrupt prior to modifying the shared variables and reenable it once
00268 // we're done with the shared variables.
00269 void lo_drive(int speed, int turn_radius, char smooth)
00270 {
00271    if (g_speed == speed && g_radius == turn_radius)
00272       return ;
00273 
00274    g_radius = turn_radius ;
00275    if (smooth)
00276    {
00277       lo_suspend_timer10() ; // for atomicity, temporarily disable Timer2 int.
00278          g_ramp_flag = 1 ;
00279          g_target_speed = speed ;
00280       lo_resume_timer10()  ; // reenable Timer2 interrupt
00281    }
00282    else
00283    {
00284       lo_suspend_timer10() ; // for atomicity, temporarily disable Timer2 int.
00285          g_ramp_flag = 0 ;
00286       lo_resume_timer10()  ; // reenable Timer2 interrupt
00287       g_speed = speed ;
00288       drive_oi(g_speed, g_radius) ;
00289    }
00290 }
00291 
00292 // This function backs up the robot at the specified speed, moving it by
00293 // an amount at least equal to the specified distance (in mm). It returns
00294 // the actual distance backed up.
00295 int lo_backup(int speed, int distance)
00296 {
00297    // Start backing up
00298    lo_drive(speed, LOBOT_OI_DRIVE_STRAIGHT, 1) ;
00299 
00300    // Wait until the specified distance is reached
00301    int D = 0 ;
00302    while (abs(D) < abs(distance))
00303    {
00304       lo_wait(15) ;
00305 
00306       lo_tx(LOBOT_OI_CMD_QUERY_LIST) ;
00307       lo_tx(1) ;
00308       lo_tx(LOBOT_OI_SENSOR_DISTANCE) ;
00309 
00310       char buf[2] = {0} ;
00311       if (lo_rx(buf, 2, 20) >= 2)
00312          D += lo_make_word(buf[0], buf[1]) ;
00313    }
00314    lo_drive(0, LOBOT_OI_DRIVE_STRAIGHT, 1) ;
00315    return D ;
00316 }
00317 
00318 // Spin the robot at the specified speed by an amount equal to at least
00319 // the specified angle. Return the actual amount spun.
00320 int lo_spin(int speed, int angle)
00321 {
00322    // Start spinning
00323    int turn = (angle < 0) ? LOBOT_OI_TURN_INPLACE_CW
00324                           : LOBOT_OI_TURN_INPLACE_CCW ;
00325    lo_drive(speed, turn, 0) ;
00326 
00327    // Wait until the specified angle is reached
00328    int A = 0 ;
00329    while (abs(A) < abs(angle))
00330    {
00331       lo_wait(15) ;
00332 
00333       lo_tx(LOBOT_OI_CMD_QUERY_LIST) ;
00334       lo_tx(1) ;
00335       lo_tx(LOBOT_OI_SENSOR_ANGLE) ;
00336 
00337       char buf[2] = {0} ;
00338       if (lo_rx(buf, 2, 20) >= 2)
00339          A += lo_make_word(buf[0], buf[1]) ;
00340    }
00341    lo_drive(0, LOBOT_OI_DRIVE_STRAIGHT, 0) ;
00342    return A ;
00343 }
00344 
00345 /*---------------------------- DRIVE STATE ----------------------------*/
00346 
00347 // Is the robot stationary or moving?
00348 char lo_stopped(void)
00349 {
00350    return g_speed == 0 ;
00351 }
Generated on Sun May 8 08:05:55 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3