00001 /** 00002 \file Robots/LoBot/irccm/LoSensors.c 00003 \brief Sensors API for Robolocust iRobot Create Command Module control 00004 program. 00005 00006 This file defines the functions that implement the sensor retrieval 00007 API for the low-level Robolocust control program meant to be run on 00008 the iRobot Create's Command Module. 00009 */ 00010 00011 /* 00012 ************************************************************************ 00013 * The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 * 00014 * by the University of Southern California (USC) and the iLab at USC. * 00015 * See http://iLab.usc.edu for information about this project. * 00016 * * 00017 * Major portions of the iLab Neuromorphic Vision Toolkit are protected * 00018 * under the U.S. patent ``Computation of Intrinsic Perceptual Saliency * 00019 * in Visual Environments, and Applications'' by Christof Koch and * 00020 * Laurent Itti, California Institute of Technology, 2001 (patent * 00021 * pending; application number 09/912,225 filed July 23, 2001; see * 00022 * http://pair.uspto.gov/cgi-bin/final/home.pl for current status). * 00023 ************************************************************************ 00024 * This file is part of the iLab Neuromorphic Vision C++ Toolkit. * 00025 * * 00026 * The iLab Neuromorphic Vision C++ Toolkit is free software; you can * 00027 * redistribute it and/or modify it under the terms of the GNU General * 00028 * Public License as published by the Free Software Foundation; either * 00029 * version 2 of the License, or (at your option) any later version. * 00030 * * 00031 * The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope * 00032 * that it will be useful, but WITHOUT ANY WARRANTY; without even the * 00033 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * 00034 * PURPOSE. See the GNU General Public License for more details. * 00035 * * 00036 * You should have received a copy of the GNU General Public License * 00037 * along with the iLab Neuromorphic Vision C++ Toolkit; if not, write * 00038 * to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * 00039 * Boston, MA 02111-1307 USA. * 00040 ************************************************************************ 00041 */ 00042 00043 /* 00044 Primary maintainer for this file: mviswana usc edu 00045 $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Robots/LoBot/irccm/LoSensors.c $ 00046 $Id: LoSensors.c 13798 2010-08-18 19:43:24Z mviswana $ 00047 */ 00048 00049 /*------------------------------ HEADERS ------------------------------*/ 00050 00051 // lobot headers 00052 #include "LoSensors.h" 00053 #include "LoIO.h" 00054 #include "LoTimer.h" 00055 #include "LoUtils.h" 00056 #include "LoCMInterface.h" 00057 #include "LoOpenInterface.h" 00058 00059 // avr-libc headers 00060 #include <avr/io.h> 00061 00062 /*----------------------------- CONSTANTS -----------------------------*/ 00063 00064 enum { 00065 // lobot's rear bump sensors consist of a pair of Sharp GP2D15 IR 00066 // proximity detectors connected to the Command Module's cargo bay 00067 // ePort's digital I/O lines 1 and 2. These, in turn, connect to the 00068 // ATmega168's C register (bits 4 and 0 respectively). 00069 // 00070 // These bit masks are used to specify the PINC bits for checking the 00071 // bump sensor states. 00072 LOBOT_BUMP_RL_PIN = 0x10, // rear left connected to C4 00073 LOBOT_BUMP_RR_PIN = 0x01, // rear right connected to C0 00074 00075 // Instead of adding a new sensor data byte for the rear bumpers, we 00076 // simply use bits 5 and 6 of the bumps + wheel drops sensor data byte 00077 // (which are not used by the Open Interface). 00078 LOBOT_BUMP_RL_POS = 6, // rear left bump sensor's bit position 00079 LOBOT_BUMP_RR_POS = 5, // rear right bump sensor's bit position 00080 00081 // To debounce the rear bumpers, we expect to see a continuous logic 00082 // low for at least the following amount of time (in ms). 00083 LOBOT_REAR_BUMP_DEBOUNCE_THRESHOLD = 15, 00084 } ; 00085 00086 /*------------------------------ GLOBALS ------------------------------*/ 00087 00088 // We cannot retrieve the sensor data from the Create and directly send 00089 // it off to the high level because the Command Module has a single USART 00090 // that must be switched between talking to the robot's internal serial 00091 // port and the Command Module's (external) USB port. Thus, when the 00092 // high-level sends the command to retrieve sensor data, we store/buffer 00093 // the data locally after getting it from the robot (via the Open 00094 // Interface). 00095 // 00096 // Then, when the main loop of this low-level control program switches 00097 // the USART to talk to the Robolocust computer, it will see if there is 00098 // sensor data pending that needs to be sent to the high level. If so, it 00099 // will perform the necessary sensor data send operation. 00100 // 00101 // The following variable is used to keep track of whether sensor data is 00102 // pending or not. 00103 static char g_sensors_pending ; 00104 00105 // The sensor data returned by the Create robot is buffered in this array 00106 // prior to transmission to the high level. 00107 static char g_sensors[LOBOT_SENSORS_SIZE] ; 00108 00109 // Due to motor/encoder errors, the actual amount of rotation in response 00110 // to the SPIN command may be a little more or less than the requested 00111 // amount. Furthermore, the robot may experience a slight translation in 00112 // addition to the rotation. To ensure that the high-level controller 00113 // does not miss out on any low-level motion updates, we have to add the 00114 // actual amount of rotation and any unintended translation to the next 00115 // sensor packet that will be sent to the high-level controller. 00116 // 00117 // These two variables temporarily record the values of the rotation and 00118 // translation resulting from the SPIN command. They will be added to the 00119 // encoder data for the next sensor packet. 00120 static int g_pending_distance ; 00121 static int g_pending_rotation ; 00122 00123 // On start-up, the rear bump sensors, a pair of Sharp GP2D15 IR 00124 // proximity detectors, are ignored to prevent the robot from reacting to 00125 // the user's hands as soon as the Command Module on/off switch is 00126 // activated or when the user is trying to place the robot at some 00127 // specific starting point before an experiment/run. 00128 // 00129 // This flag keeps track of whether the rear bumpers are enabled or not. 00130 // The high-level controller can turn these two sensors on/off by using 00131 // the appropriate commands. 00132 static volatile char g_rear_bumps_enabled ; 00133 00134 // This flag indicates whether or not the robot should respond to rear 00135 // bump events by spinning in-place and moving up a little or by simply 00136 // stopping. The default behaviour is to just stop the robot. However, 00137 // the high-level controller can instruct the low-level controller to 00138 // spin and move the robot by passing 1 as the parameter to the 00139 // LOBOT_CMD_ENABLE_REAR_BUMPS command. 00140 static char g_rear_bumps_spin ; 00141 00142 // To debounce the rear proximity detectors, we use the following 00143 // variables to count how many times the sensors are active in 1ms 00144 // increments. If the count for a sensor exceeds the debounce threshold, 00145 // only then is that detector considered active. 00146 static volatile unsigned char g_rlb_count ; // rlb = rear left bump 00147 static volatile unsigned char g_rrb_count ; // rrb = rear right bump 00148 00149 /*-------------------------- INITIALIZATION ---------------------------*/ 00150 00151 // Forward declaration 00152 static void debounce_rear_bumps(void) ; 00153 00154 // Setup 1ms timer callback to perform debouncing asynchronously w.r.t. 00155 // the main "thread." Otherwise, the main thread can get tied up in a 00156 // reasonably long busy wait just to debounce the rear bumpers and 00157 // potentially miss other important sensory input. 00158 void lo_init_sensors(void) 00159 { 00160 lo_add_timer1_cb(debounce_rear_bumps) ; 00161 } 00162 00163 // Enable/disable the two rear bump sensors 00164 void lo_enable_rear_bumps(int param) 00165 { 00166 g_rear_bumps_enabled = 1 ; 00167 g_rear_bumps_spin = lo_lobyte(param) ; 00168 } 00169 00170 void lo_disable_rear_bumps(int unused) 00171 { 00172 g_rear_bumps_enabled = 0 ; 00173 g_rlb_count = g_rrb_count = 0 ; 00174 } 00175 00176 void lo_toggle_rear_bumps(void) 00177 { 00178 g_rear_bumps_enabled = ! g_rear_bumps_enabled ; 00179 g_rlb_count = g_rrb_count = 0 ; 00180 } 00181 00182 // Enable/disable spin-and-move action for rear bump events 00183 void lo_toggle_rear_bumps_spin(void) 00184 { 00185 g_rear_bumps_spin = ! g_rear_bumps_spin ; 00186 } 00187 00188 /*---------------------- RETRIEVING SENSOR DATA -----------------------*/ 00189 00190 // Debouncing logic for the rear bump sensors: in 1ms increments, we 00191 // check if the pins to which these sensors are connected report a logic 00192 // high. If so, we increment the corresponding count variable for that 00193 // sensor. If not, we reset the count to zero. 00194 // 00195 // Thus, when the main thread retrieves the counts, it will know 00196 // immediately whether or not the rear bumpers were active for at least 00197 // the last T milliseconds (where T is the debounce threshold). 00198 // 00199 // NOTE: It is possible for the main thread to check the counts when they 00200 // are, say, halfway to the debounce threshold and then check again after 00201 // several milliseconds. In the mean time, the 1ms timer might well have 00202 // bumped the count past the threshold and, depending on the situation or 00203 // stray noise, reset the counter. Thus, when the main thread checks 00204 // next, it can potentially miss a crucial rear obstacle. Even worse, 00205 // this could actually go on for a good, long time. 00206 // 00207 // The best way to avoid the situation described above would be to 00208 // implement the debouncing using a Schmitt trigger rather than a simple 00209 // count threshold. Nonetheless, in practice, this straightforward 00210 // debouncing logic works fine. 00211 static void debounce_rear_bumps(void) 00212 { 00213 if (g_rear_bumps_enabled) 00214 { 00215 if (PINC & LOBOT_BUMP_RL_PIN) // rear left proximity detector active 00216 ++g_rlb_count ; 00217 else if (g_rlb_count > 0) 00218 g_rlb_count = 0 ; 00219 00220 if (PINC & LOBOT_BUMP_RR_PIN) // rear right proximity detector active 00221 ++g_rrb_count ; 00222 else if (g_rrb_count > 0) 00223 g_rrb_count = 0 ; 00224 } 00225 } 00226 00227 // Retrieve all the built-in sensors plus lobot's custom sensor states 00228 void lo_sensors(void) 00229 { 00230 // Send sensor query to the Create robot to return all sensor packets 00231 lo_tx(LOBOT_OI_CMD_SENSORS) ; 00232 lo_tx(LOBOT_OI_SENSOR_GROUP_6) ; 00233 00234 // Retrieve sensor data from Create robot, waiting a maximum of 50ms 00235 // to get the data. Taking longer than 50ms to retrieve sensor data is 00236 // probably indicative of a temporary serial communication glitch with 00237 // the Create robot. We shouldn't keep waiting for the data as that 00238 // can hold up the robot in case the high level has detected a 00239 // problematic situation and issued appropriate commands to handle it. 00240 lo_rx(g_sensors, LOBOT_OI_SENSOR_SIZE_GROUP_6, 50) ; 00241 00242 // Set the sensors pending flag, so that the low-level control 00243 // program's main loop can send the sensor data out to the high level. 00244 // But don't indicate to the main loop that sensor data is pending in 00245 // case the Create robot timed out and failed to respond to the above 00246 // sensor query. 00247 g_sensors_pending = ! lo_io_error() ; 00248 00249 // When the high-level controller sends a SPIN command, the drive 00250 // module will mark the actual amount spun and any additional 00251 // translational motion as pending for the next SENSORS 00252 // acknowledgement. We need to add those amounts to the encoder data 00253 // returned by the above Open Interface exchange with the Create so 00254 // that the high-level doesn't miss any low-level motion updates and 00255 // end up getting mislocalized or experience some other related 00256 // problem. 00257 g_sensors[LOBOT_SENSORS_SPIN_FLAG] = 00258 g_sensors_pending && (g_pending_distance || g_pending_rotation) ; 00259 if (g_sensors[LOBOT_SENSORS_SPIN_FLAG]) { 00260 lo_update_odometry(g_pending_distance, g_pending_rotation) ; 00261 g_pending_distance = 0 ; 00262 g_pending_rotation = 0 ; 00263 } 00264 00265 // Check the rear bump/proximity sensors connected to the Command 00266 // Module's cargo bay ePort. 00267 // 00268 // NOTE: Instead of adding a new byte of sensor data for the rear bump 00269 // sensors, we simply stuff these two bit flags into bits 5 and 6 of 00270 // the wheel drops + bumps sensor data byte. 00271 if (g_rlb_count >= LOBOT_REAR_BUMP_DEBOUNCE_THRESHOLD) { 00272 g_sensors[LOBOT_SENSORS_BUMPS] |= (1 << LOBOT_BUMP_RL_POS) ; 00273 g_rlb_count = 0 ; 00274 } 00275 if (g_rrb_count >= LOBOT_REAR_BUMP_DEBOUNCE_THRESHOLD) { 00276 g_sensors[LOBOT_SENSORS_BUMPS] |= (1 << LOBOT_BUMP_RR_POS) ; 00277 g_rrb_count = 0 ; 00278 } 00279 } 00280 00281 // Return a sensor byte given its index. 00282 char lo_get_sensor(unsigned char index) 00283 { 00284 return g_sensors[index] ; 00285 } 00286 00287 /*----------------------- UPDATING SENSOR DATA ------------------------*/ 00288 00289 // Update odometry information stored in pending sensor packet (e.g., in 00290 // response to bumps/cliffs action). 00291 void lo_update_odometry(int distance, int angle) 00292 { 00293 if (g_sensors_pending) 00294 { 00295 distance += lo_make_word(g_sensors[LOBOT_SENSORS_DISTANCE_HI], 00296 g_sensors[LOBOT_SENSORS_DISTANCE_LO]) ; 00297 g_sensors[LOBOT_SENSORS_DISTANCE_HI] = lo_hibyte(distance) ; 00298 g_sensors[LOBOT_SENSORS_DISTANCE_LO] = lo_lobyte(distance) ; 00299 00300 angle += lo_make_word(g_sensors[LOBOT_SENSORS_ANGLE_HI], 00301 g_sensors[LOBOT_SENSORS_ANGLE_LO]) ; 00302 g_sensors[LOBOT_SENSORS_ANGLE_HI] = lo_hibyte(angle) ; 00303 g_sensors[LOBOT_SENSORS_ANGLE_LO] = lo_lobyte(angle) ; 00304 } 00305 } 00306 00307 // Update odometry information for next sensor packet 00308 void lo_update_pending_odometry(int distance, int angle) 00309 { 00310 g_pending_distance += distance ; 00311 g_pending_rotation += angle ; 00312 } 00313 00314 /*------------------------ SENSOR DATA STATE --------------------------*/ 00315 00316 // Check if sensor data is pending so that main loop will send out the 00317 // data the next time it switches to talking to the high level via the 00318 // Command Module's USB port. 00319 char lo_sensors_pending(void) 00320 { 00321 return g_sensors_pending ; 00322 } 00323 00324 // Check if rear bump action should be spin-and-move or just a plain stop 00325 char lo_rear_bumps_spin(void) 00326 { 00327 return g_rear_bumps_spin ; 00328 } 00329 00330 /*------------------------ SENDING SENSOR DATA ------------------------*/ 00331 00332 // Send pending sensor data to the high level 00333 void lo_send_sensors(void) 00334 { 00335 lo_tx(LOBOT_ACK_SENSORS) ; 00336 for (unsigned char i = 0; i < LOBOT_SENSORS_SIZE; ++i) 00337 lo_tx(g_sensors[i]) ; 00338 00339 // Sensor data is no longer pending after it has been sent to the high 00340 // level. 00341 g_sensors_pending = 0 ; 00342 }