Если у вас возникли трудности с чётким воспроизведением нот, скорее всего вам необходимо подобрать граничное значение limitValue. Нота начинает звучать, если получаемое с датчика значение меньше limitValue.
Получаемые с датчиков бампера значения могут сильно колебаться в зависимости от освещенности, а также из-за конструктивных особенностей самих датчиков. Поэтому, для более точной настройки рекомендуем проделать шаги, описанные ниже.
В данной статье мы производим все расчёты на примере наших значений. Не забывайте, что у вас значения будут отличаться.
Чтение сигналов с датчиков линии
Сперва считаем реальные значения, получаемые с датчиков. Для этого установите машинку на нотную трассу таким образом, чтобы 2 крайних правых датчика (8-й и 9-й) располагались над калибровочными полосками, а все остальные датчики (1-7) находились над белым фоном (чтобы под бампером не было нот).
Установите бампер в таком положении максимально ровно, чтобы датчики располагались точно над центрами дорожек.
После этого загрузите следующий скетч, позволяющий считать значения со всех 9-ти датчиков.
#include <Wire.h> // Подключаем библиотеку для работы с аппаратной шиной I2C #include <iarduino_I2C_Bumper.h> // Подключаем библиотеку для работы с бампером I2C-flash iarduino_I2C_Bumper bum(0x0C); // Объявляем объект bum для работы с функциями и методами библиотеки void setup(){ Serial.begin(9600); // Инициируем передачу данных в монитор последовательного порта bum.begin(); // Инициируем работу с бампером } void loop(){ for (uint8_t i = 1; i<= 9; i++){ Serial.print(bum.getLineAnalog(i)); // Читаем значение с датчика и выводим их в последовательный порт Serial.print(" "); } Serial.println(""); delay(100); }
После загрузки откройте монитор порта.
Вы видите значения, получаемые с датчиков (первый столбик - 1 датчик, второй столбик - 2 и т.д.). Можете снять галочку автопрокрутки, так будет удобнее.
Видим, что в примерное значение, соответствующее белому фону - 2470 (смотрим первые 7 колонок).
Теперь переставим машинку в место, где есть нота.
Обратите внимание, существенно изменились значения 6-го датчика, сейчас нота находится как раз под ним. Получаемое с него значение равно примерно 2200.
Итак, мы получили:
- 2470 - значение для белого фона;
- 2200 - значение для ноты.
Исходя из этого можем задать значение limitValue: среднее равно примерно 2300. Это значение можно считать граничным (переходным) между фоном и нотой. Запишите его в скетч и попробуйте запустить машинку. Также можно взять соседние значения (примерно с шагом в 100, т.е. 2400 и 2200). Если чёткого воспроизведения добиться не удалось, двигайтесь дальше.
Настройка значений калибровочных линий
Вы могли заметить, что значения, получаемые с двух крайних правых датчиков меньше значений нот. Это происходит из-за того, что калибровочные линии более толстые, чем метки-ноты, поэтому они более выразительно фиксируются датчиком.
При ровно стоящем бампере значения с калибровочных датчиков равны примерно 1600 (две крайние правые колонки на скриншоте монитора порта).
Сейчас в скетче значения для всех девяти датчиков сравниваются с limitValue. Давайте будем обрабатывать значения с калибровочных датчиков отдельно. Для этого введём новую переменную limitValueCal. Её значение должно быть немного больше 1600, пусть она будет равна 1700. Можете попробовать задать эту границу больше или меньше, а также взять соседние значения (1600, 1800).
#include <Wire.h> // Подключаем библиотеку для работы с шиной I2C #include <iarduino_I2C_Motor.h> // Подключаем библиотеку для работы с мотором #include <iarduino_I2C_Bumper.h> // Подключаем библиотеку для работы с бампером I2C-flash iarduino_I2C_Motor mot_R (0x0A); // Объявляем объект mot_R для правого мотора iarduino_I2C_Motor mot_L (0x0B); // Объявляем объект mot_L для левого мотора iarduino_I2C_Bumper bum (0x0C); // Объявляем объект bum для работы с бампером I2C-flash #define zumPin 6 // Зуммер подключен к 6 пину uint8_t speedMot = 20; // Скорость мотора, % ШИМ int limitValue = 2400; // Граничное значение, при котором датчик срабатывает на ноту int limitValueCal = 1600; // Граничное значение датчиков калибровочных линий const int fre[7] = {262, 294, 330, 349, 392, 440, 466}; // Массив частот нот (до, ре, ми, фа, соль, ля, си бемоль) uint8_t arr[7] = {0,0,0,0,0,0,0}; // Массив считанных значений uint8_t oldArr[7] = {0,0,0,0,0,0,0}; // Массив предыдущих значений uint8_t deltaArr[7] = {0,0,0,0,0,0,0}; // Массив изменившихся с прошлого измерения значений void setup() { mot_R.begin(); // Инициируем работу с правым мотором mot_L.begin(); // Инициируем работу с левым мотором mot_R.setDirection(true); // Задаём направление вращения правого мотора mot_L.setDirection(false); // Задаём направление вращения левого мотора bum.begin(); // Инициируем работу с бампером (датчиком линии) } void loop() { if (bum.getLineAnalog(8) < limitValueCal && bum.getLineAnalog(9) < limitValueCal){ // Если линия под двумя датчиками, едем прямо mot_L.setSpeed(speedMot, MOT_PWM); // Включаем левый мотор mot_R.setSpeed(speedMot, MOT_PWM); // Включаем правый мотор } else if (bum.getLineAnalog(8) < limitValueCal){ // Иначе, если линия только под левым датчиком mot_L.setSpeed(speedMot-speedMot/3, MOT_PWM); // Уменьшаем скорость левого мотора mot_R.setSpeed(speedMot, MOT_PWM); // Включаем правый мотор } else if (bum.getLineAnalog(9) < limitValueCal){ // Иначе, если линия только под правым датчиком mot_L.setSpeed(speedMot, MOT_PWM); // Включаем левый мотор mot_R.setSpeed(speedMot-speedMot/3, MOT_PWM); // Уменьшаем скорость правого мотора } for (uint8_t i = 0; i <= 6; i++){ // Чтение датчиков arr[i] = bum.getLineAnalog(i+1) < limitValue ? 1 : 0; // Если значение с датчика меньше граничного, записываем в массив 1, иначе 0 } for (uint8_t i = 0; i <= 6; i++){ // Вычисление разницы текущего и предыдущего массива deltaArr[i] = abs(arr[i] - oldArr[i]); // Записываем в массив отличия (по модулю) } for (uint8_t i = 0; i <= 6; i++){ // Обновляем предыдущий массив oldArr[i] = arr[i]; } uint8_t sum = 0; // Создаём переменную for (uint8_t i = 0; i <= 6; i++){ // Вычисляем сумму значений массива sum += arr[i]; } if (sum == 0) noTone(zumPin); // Если все элементы массива равным 0, выключаем зуммер else { for (uint8_t i = 0; i <= 6; i++){ // Иначе ищем первое изменившееся значение в массиве if (deltaArr[i] != 0) { tone(zumPin, fre[i]); // Воспроизводим зуммером соответствующую ноту break; }}}}
Обычно этого достаточно для стабильной работы машинки. Если стабильной работы добиться не удалось, переходите к следующему пункту.
Распознавание нот по массиву значений
Как мы уже писали выше, сами датчики бампера, в силу своих конструктивных особенностей, могут давать различные значения. Посмотрите на скриншот: у нас первый датчик выдаёт значения существенно отличающиеся от остальных.
Давайте запишем граничные значения каждого отдельного датчика, чтобы в дальнейшем сравнивать значения конкретного датчика не с общим limitValue, а со своим собственным граничным значением.
Для получения этих значений посмотрим на значения бампера, полученные при отсутствии нот под ним (первый пункт данного урока, первый скриншот).
Отнимем примерно 100-200 от значения каждого значения (1-7 датчик) и запишем в массив граничных значений limitValueArr (10 строка). Мы отняли 150. В нашем случае при таком значении наблюдалось самое устойчивое воспроизведение.
#include <Wire.h> // Подключаем библиотеку для работы с шиной I2C #include <iarduino_I2C_Motor.h> // Подключаем библиотеку для работы с мотором #include <iarduino_I2C_Bumper.h> // Подключаем библиотеку для работы с бампером I2C-flash iarduino_I2C_Motor mot_R (0x0A); // Объявляем объект mot_R для правого мотора iarduino_I2C_Motor mot_L (0x0B); // Объявляем объект mot_L для левого мотора iarduino_I2C_Bumper bum (0x0C); // Объявляем объект bum для работы с бампером I2C-flash #define zumPin 6 // Зуммер подключен к 6 пину uint8_t speedMot = 20; // Скорость мотора, % ШИМ int limitValueCal = 1600; // Граничное значение датчиков калибровочных линий int limitValueArr[7] = {2531, 2325, 2421, 2335, 2317, 2320, 2309}; // Граничное значение каждого датчка const int fre[7] = {262, 294, 330, 349, 392, 440, 466}; // Массив частот нот (до, ре, ми, фа, соль, ля, си бемоль) uint8_t arr[7] = {0,0,0,0,0,0,0}; // Массив считанных значений uint8_t oldArr[7] = {0,0,0,0,0,0,0}; // Массив предыдущих значений uint8_t deltaArr[7] = {0,0,0,0,0,0,0}; // Массив изменившихся с прошлого измерения значений void setup() { mot_R.begin(); // Инициируем работу с правым мотором mot_L.begin(); // Инициируем работу с левым мотором mot_R.setDirection(true); // Задаём направление вращения правого мотора mot_L.setDirection(false); // Задаём направление вращения левого мотора bum.begin(); // Инициируем работу с бампером (датчиком линии) } void loop() { if (bum.getLineAnalog(8) < limitValueCal && bum.getLineAnalog(9) < limitValueCal){ // Если линия под двумя датчиками, едем прямо mot_L.setSpeed(speedMot, MOT_PWM); // Включаем левый мотор mot_R.setSpeed(speedMot, MOT_PWM); // Включаем правый мотор } else if (bum.getLineAnalog(8) < limitValueCal){ // Иначе, если линия только под левым датчиком mot_L.setSpeed(speedMot-speedMot/3, MOT_PWM); // Уменьшаем скорость левого мотора mot_R.setSpeed(speedMot, MOT_PWM); // Включаем правый мотор } else if (bum.getLineAnalog(9) < limitValueCal){ // Иначе, если линия только под правым датчиком mot_L.setSpeed(speedMot, MOT_PWM); // Включаем левый мотор mot_R.setSpeed(speedMot-speedMot/3, MOT_PWM); // Уменьшаем скорость правого мотора } for (uint8_t i = 0; i <= 6; i++){ // Чтение датчиков arr[i] = bum.getLineAnalog(i+1) < limitValueArr[i] ? 1 : 0; // Если значение с датчика меньше граничного, записываем в массив 1, иначе 0 } for (uint8_t i = 0; i <= 6; i++){ // Вычисление разницы текущего и предыдущего массива deltaArr[i] = abs(arr[i] - oldArr[i]); // Записываем в массив отличия (по модулю) } for (uint8_t i = 0; i <= 6; i++){ // Обновляем предыдущий массив oldArr[i] = arr[i]; } uint8_t sum = 0; // Создаём переменную for (uint8_t i = 0; i <= 6; i++){ // Вычисляем сумму значений массива sum += arr[i]; } if (sum == 0) noTone(zumPin); // Если все элементы массива равным 0, выключаем зуммер else { for (uint8_t i = 0; i <= 6; i++){ // Иначе ищем первое изменившееся значение в массиве if (deltaArr[i] != 0) { tone(zumPin, fre[i]); // Воспроизводим зуммером соответствующую ноту break; }}}}
Обсуждение