Если у вас возникли трудности с чётким воспроизведением нот, скорее всего вам необходимо подобрать граничное значение 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;
}}}} 
Обсуждение