/** This example uses line sensors and infraed sensors for autonomous walking in a restricted area with obstacles. Rodger (Jieliang) Luo Feb 9th, 2017 */ #include #include #include #define DIST_THRESHOLD 9 // brightness level, for infraed sensors #define QTR_THRESHOLD 500 // microseconds, for line sensors // These might need to be tuned for different motor types. #define REVERSE_SPEED 100 // 0 is stopped, 400 is full speed #define TURN_SPEED 200 #define FORWARD_SPEED 200 #define REVERSE_DURATION 300 // ms #define TURN_DURATION 500 // ms // Accelerometer Settings #define RA_SIZE 3 // number of readings to include in running average of accelerometer readings #define XY_ACCELERATION_THRESHOLD 2400 // for detection of contact (~16000 = magnitude of acceleration due to gravity) Zumo32U4LCD lcd; Zumo32U4ButtonA buttonA; Zumo32U4Buzzer buzzer; Zumo32U4Motors motors; Zumo32U4LineSensors lineSensors; Zumo32U4ProximitySensors proxSensors; #define NUM_SENSORS 3 unsigned int lineSensorValues[NUM_SENSORS]; unsigned long loop_start_time; unsigned long last_turn_time; #define MIN_DELAY_AFTER_TURN 1000 // ms = min delay before detecting contact event // RunningAverage class // based on RunningAverage library for Arduino // source: http://playground.arduino.cc/Main/RunningAverage template class RunningAverage { public: RunningAverage(void); RunningAverage(int); ~RunningAverage(); void clear(); void addValue(T); T getAverage() const; void fillValue(T, int); protected: int _size; int _cnt; int _idx; T _sum; T * _ar; static T zero; }; // Accelerometer Class -- extends the LSM303 class to support reading and averaging the x-y acceleration // vectors from the onboard LSM303DLHC accelerometer/magnetometer class Accelerometer : public LSM303 { typedef struct acc_data_xy { unsigned long timestamp; int x; int y; float dir; } acc_data_xy; public: Accelerometer() : ra_x(RA_SIZE), ra_y(RA_SIZE) {}; ~Accelerometer() {}; void enable(void); void getLogHeader(void); void readAcceleration(unsigned long timestamp); float len_xy() const; float dir_xy() const; int x_avg(void) const; int y_avg(void) const; long ss_xy_avg(void) const; float dir_xy_avg(void) const; private: acc_data_xy last; RunningAverage ra_x; RunningAverage ra_y; }; Accelerometer lsm303; void waitForButtonAndCountDown() { ledYellow(1); lcd.clear(); lcd.print(F("Press A")); buttonA.waitForButton(); ledYellow(0); lcd.clear(); // Play audible countdown. for (int i = 0; i < 3; i++) { delay(1000); buzzer.playNote(NOTE_G(3), 200, 15); } delay(1000); buzzer.playNote(NOTE_G(4), 500, 15); delay(1000); last_turn_time = millis(); } void setup() { // Uncomment if necessary to correct motor directions: //motors.flipLeftMotor(true); //motors.flipRightMotor(true); //Initial two groups of sensors lineSensors.initThreeSensors(); proxSensors.initThreeSensors(); // Initialize the Wire library and join the I2C bus as a master Wire.begin(); // Initialize LSM303 lsm303.init(); lsm303.enable(); waitForButtonAndCountDown(); } void loop() { if (buttonA.isPressed()) { // If button is pressed, stop and wait for another press to go again. motors.setSpeeds(0, 0); buttonA.waitForRelease(); waitForButtonAndCountDown(); } loop_start_time = millis(); lsm303.readAcceleration(loop_start_time); proxSensors.read(); lineSensors.read(lineSensorValues); uint8_t sum[4] = { proxSensors.countsFrontWithRightLeds() + proxSensors.countsFrontWithLeftLeds(), proxSensors.countsLeftWithLeftLeds() + proxSensors.countsFrontWithLeftLeds(), proxSensors.countsRightWithRightLeds() + proxSensors.countsFrontWithRightLeds(), proxSensors.countsFrontWithRightLeds() + proxSensors.countsRightWithRightLeds() }; if (sum[0] > DIST_THRESHOLD || sum[1] > DIST_THRESHOLD || sum[2] > DIST_THRESHOLD || sum[3] > DIST_THRESHOLD || lineSensorValues[0] < QTR_THRESHOLD || lineSensorValues[NUM_SENSORS - 1] < QTR_THRESHOLD) { turn(); } // Otherwise, go straight. else { if(check_for_contact()) turn(); motors.setSpeeds(FORWARD_SPEED, FORWARD_SPEED); } } void turn(){ motors.setSpeeds(-REVERSE_SPEED, -REVERSE_SPEED); delay(REVERSE_DURATION); motors.setSpeeds(-TURN_SPEED, TURN_SPEED); delay(TURN_DURATION); last_turn_time = millis(); } // check for contact, but ignore readings immediately after turning or losing contact bool check_for_contact() { static long threshold_squared = (long) XY_ACCELERATION_THRESHOLD * (long) XY_ACCELERATION_THRESHOLD; return (lsm303.ss_xy_avg() > threshold_squared) && (loop_start_time - last_turn_time > MIN_DELAY_AFTER_TURN); } // class Accelerometer -- member function definitions // enable accelerometer only // to enable both accelerometer and magnetometer, call enableDefault() instead void Accelerometer::enable(void) { // Enable Accelerometer // 0x27 = 0b00100111 // Normal power mode, all axes enabled writeAccReg(LSM303::CTRL_REG1_A, 0x27); if (getDeviceType() == LSM303::device_DLHC) writeAccReg(LSM303::CTRL_REG4_A, 0x08); // DLHC: enable high resolution mode } void Accelerometer::getLogHeader(void) { Serial.print("millis x y len dir | len_avg dir_avg | avg_len"); Serial.println(); } void Accelerometer::readAcceleration(unsigned long timestamp) { readAcc(); if (a.x == last.x && a.y == last.y) return; last.timestamp = timestamp; last.x = a.x; last.y = a.y; ra_x.addValue(last.x); ra_y.addValue(last.y); } float Accelerometer::len_xy() const { return sqrt(last.x*a.x + last.y*a.y); } float Accelerometer::dir_xy() const { return atan2(last.x, last.y) * 180.0 / M_PI; } int Accelerometer::x_avg(void) const { return ra_x.getAverage(); } int Accelerometer::y_avg(void) const { return ra_y.getAverage(); } long Accelerometer::ss_xy_avg(void) const { long x_avg_long = static_cast(x_avg()); long y_avg_long = static_cast(y_avg()); return x_avg_long*x_avg_long + y_avg_long*y_avg_long; } float Accelerometer::dir_xy_avg(void) const { return atan2(static_cast(x_avg()), static_cast(y_avg())) * 180.0 / M_PI; } // RunningAverage class // based on RunningAverage library for Arduino // source: http://playground.arduino.cc/Main/RunningAverage // author: Rob.Tillart@gmail.com // Released to the public domain template T RunningAverage::zero = static_cast(0); template RunningAverage::RunningAverage(int n) { _size = n; _ar = (T*) malloc(_size * sizeof(T)); clear(); } template RunningAverage::~RunningAverage() { free(_ar); } // resets all counters template void RunningAverage::clear() { _cnt = 0; _idx = 0; _sum = zero; for (int i = 0; i< _size; i++) _ar[i] = zero; // needed to keep addValue simple } // adds a new value to the data-set template void RunningAverage::addValue(T f) { _sum -= _ar[_idx]; _ar[_idx] = f; _sum += _ar[_idx]; _idx++; if (_idx == _size) _idx = 0; // faster than % if (_cnt < _size) _cnt++; } // returns the average of the data-set added so far template T RunningAverage::getAverage() const { if (_cnt == 0) return zero; // NaN ? math.h return _sum / _cnt; } // fill the average with a value // the param number determines how often value is added (weight) // number should preferably be between 1 and size template void RunningAverage::fillValue(T value, int number) { clear(); for (int i = 0; i < number; i++) { addValue(value); } }