Платформа солнечных панелей с сервоуправлением и отслеживанием света
Компоненты и расходные материалы
| | × | 4 | |
| Светодиоды Adafruit Flora RGB Neopixel - 4 шт. | | × | 1 | |
| Кнопочный переключатель SparkFun, 12 мм | | × | 2 | |
| | × | 1 | |
| | × | 1 | |
| RobotGeek 180-градусный сервопривод робота | | × | 2 | |
| | × | 6 | |
| | × | 1 | |
| | × | 2 | |
| солнечная панель (80 мм x 80 мм) | | × | 1 | |
Приложения и онлайн-сервисы
| | |
| Microsoft Excel для документации и примечаний | | |
Об этом проекте
Это еще одна реализация использования двух сервоприводов на 180 градусов и четырех фоторезисторов, чтобы иметь возможность отслеживать небольшую солнечную панель для выравнивания по области наибольшей интенсивности света.
Двигатели делают небольшие движения, чтобы направить солнечную панель на самый яркий свет.
Также имеется многоцветный светодиод, показывающий, включена система или нет. И две кнопки для включения / выключения двигателей.
Есть несколько процедур, которые можно использовать для перемещения двигателей в заданные положения. Команды передаются через интерфейс последовательного порта и серию числовых кодов / команд, которые могут быть отправлены на Arduino Uno.
Команды независимого перемещения запрограммированы таким образом, чтобы обеспечивать (в некоторой степени) плавные движения вместо команд немедленного перемещения, которые вызывают резкое движение механического механизма.
Код
Код Q2 Arduino
// Скотт Мангиакотти // Тусон, Аризона, США // Май 2018 // Q2 // Версия 2.0 # include // Константы int const GIVE_BACK_TIME =125; // Константы для входов int const I_RED_BUTTON_PIN =4; int const I_GREEN_BUTTON_PIN =2; int const I_LDR1_PIN =A3; // верхнее левое int const I_LDR2_PIN =A2; // верхнее правое int const I_LDR3_PIN =A1; // нижний левый int const I_LDR4_PIN =A0; // справа внизу // Константы для выходов int const O_RED_LED_PIN =9; int const O_GREEN_LED_PIN =11; int const O_BLUE_LED_PIN =10; int const O_TWIST_SERVO_PIN =5; int const O_TILT_SERVO_PINT =6; // Глобальные переменныеbool gRunning =false;; bool gVerboseDiagMode =false; Servo gServoTwist; int gServoTwistPositionCommand; // заданное positionbool gServoTwistMoveIP; // выполняется перемещениеServo gServoTilt; bool gServoTiltMoveIP; // идет движение int gServoTiltPositionCommand; // заданное положение int gLDR1; int gLDR2; int gLDR3; int gLDR4; // Запускается один разvoid setup () {// Открытие последовательного порта Serial.begin (9600); // Настройка цифровых входов pinMode (I_RED_BUTTON_PIN, INPUT); pinMode (I_GREEN_BUTTON_PIN, ВХОД); // Настройка цифровых выходов pinMode (O_RED_LED_PIN, OUTPUT); pinMode (O_GREEN_LED_PIN, ВЫХОД); pinMode (O_BLUE_LED_PIN, ВЫХОД); // Запуск подпрограммы запуска startup ();} // Выполняется постоянноvoid loop () {// Обработка получения сообщения через последовательный порт if (Serial.available ()> 0) {int iControlCode; iControlCode =Serial.parseInt (); processSerialMessage (iControlCode); } // Считываем зеленую кнопку int iGreenButton; iGreenButton =digitalRead (I_GREEN_BUTTON_PIN); если (iGreenButton ==HIGH &&gRunning ==false) {enableTracking (); gTrackToLightEnabled =true; } // Считываем красную кнопку int iRedButton; iRedButton =digitalRead (I_RED_BUTTON_PIN); если (iRedButton ==HIGH &&gRunning ==true) {disableTracking (); } // Считываем все инструменты в глобальные переменные readPhotoResistors (); averageTopTwoSensors (); averageBottomTwoSensors (); // Регулируем положение сервоприводов в соответствии с освещением фоторезисторов if (gRunning ==true) {if (gTrackToLightEnabled ==true) {// Делаем небольшие движения сервоприводов на основе уровней освещенности фоторезисторов trackToLightSensors (); } else {// Делаем небольшие шаги на основе пользовательских команд через последовательный порт. Избегает движений с высокой скоростью на механизме smoothMoveTwist (); smoothMoveTilt (); }} // Даем небольшую обратную задержку (GIVE_BACK_TIME);} // Включаем сервотрекингvoid enableTracking () {// Устанавливаем глобальные переменные, чтобы другие части программы знали, что двигатели готовы к запуску gRunning =true; // Присоединяемся к серводвигателям gServoTwist.attach (O_TWIST_SERVO_PIN); gServoTilt.attach (O_TILT_SERVO_PIN); // Включить зеленый светодиод и выключить красный светодиод digitalWrite (O_GREEN_LED_PIN, HIGH); digitalWrite (O_RED_LED_PIN, LOW); // Публикация результатов Serial.println ("servos enabled);} // Отключить отслеживание сервоприводовvoid disableTracking () {gRunning =false; gTrackToLightEnabled =false; // Отключение от серводвигателей gServoTwist.detach (); gServoTilt.detach (); // Очистить команду перемещения и переместить внутрипроцессные (IP) переменные gServoTwistPositionCommand =gServoTwist.read (); gServoTwistMoveIP =false; // Очистить команду перемещения и переместить внутрипроцессные (IP) переменные gServoTiltPositionCommand =gServoTilt.read (); gServoTiltMoveIP =false; // Включить красный светодиод, выключить зеленый светодиод digitalWrite (O_RED_LED_PIN, HIGH); digitalWrite (O_GREEN_LED_PIN, LOW); // Публикация результатов Serial.println ("сервоприводы отключены");} // Отслеживание света на основе значений фотосенсораvoid trackToLightSensors () {float fTop; float fBottom; float fLeft; float fRight; int iTwistMoveCommand; int iTiltMoveCommand; int iMoveAmount; // Инициализация // Переменная ниже определяет, сколько градусов потенциального движения для обоих сервоприводов // на сканирование программы. Это число в сочетании с глобальной константой // с именем 'GIVE_BACK_TIME' определяет, насколько агрессивными будут движения. iMoveAmount =5; // Получить текущие позиции сервопривода iTwistMoveCommand =gServoTwist.read (); iTiltMoveCommand =gServoTilt.read (); // Получение средних значений fTop =averageTopTwoSensors (); fBottom =averageBottomTwoSensors (); fLeft =averageLeftTwoSensors (); fRight =averageRightTwoSensors (); // Вычислить поворотное движение if (fLeft> fRight) {// Переместить положительное значение iTwistMoveCommand + =iMoveAmount; } else if (fRight> fLeft) {// Переместить отрицательное значение iTwistMoveCommand - =iMoveAmount; } else {// То же. don't move} // Расчет наклона if (fTop>
fBottom) {// Положительное перемещение iTiltMoveCommand + =iMoveAmount; } else if (fBottom> fTop) {// Переместить отрицательное значение iTiltMoveCommand - =iMoveAmount; } else {// То же. don't move} // Проверка границ команда перемещения сервопривода поворота if (iTwistMoveCommand <0) {iTwistMoveCommand =0; } если (iTwistMoveCommand> 179) {iTwistMoveCommand =179; } // Границы проверяют команду перемещения сервопривода наклона if (iTiltMoveCommand <45) {iTiltMoveCommand =45; } если (iTiltMoveCommand> 135) {iTiltMoveCommand =135; } // Выполнение перемещений gServoTwist.write (iTwistMoveCommand); gServoTilt.write (iTiltMoveCommand); // Публикуем результаты if (gVerboseDiagMode ==true) {Serial.println ("tl, tr, bl, br, верхнее среднее, нижнее среднее, левое среднее, правое среднее, поворотное движение, наклонное перемещение:"); Serial.print (gLDR1); Serial.print (","); Серийный принт (gLDR2); Serial.print (","); Серийный принт (gLDR3); Serial.print (","); Serial.print (gLDR4); Serial.print (","); Serial.print (fTop); Serial.print (","); Serial.print (fBottom); Serial.print (","); Serial.print (fLeft); Serial.print (","); Serial.print (fRight); Serial.print (","); Serial.print (iTwistMoveCommand); Serial.print (","); Serial.println (iTiltMoveCommand); }} // считываем значения фоторезистора в глобальные переменныеvoid readPhotoResistors () {// Значения прибывают в масштабе 0-1024 gLDR1 =analogRead (I_LDR1_PIN); gLDR2 =аналоговое чтение (I_LDR2_PIN); gLDR3 =аналоговое чтение (I_LDR3_PIN); gLDR4 =analogRead (I_LDR4_PIN);} // Когда сервоприводы получают команду в положение, они перемещаются с большой скоростью. // Слишком быстрая скорость потенциально может повлиять на механическую структуру, которая удерживает и перемещает // солнечную панель и платформу светового датчика. Эта процедура принимает "команду перемещения" // и выполняет небольшие пошаговые движения, пока сервопривод не окажется в желаемой позиции. int iMoveAmountPerScan; int iNewMoveCommand; // Устанавливаем величину перемещения за сканирование в градусах. // Комбинация этой переменной и глобальной константы GIVE_BACK_TIME определяет общую скорость перемещения iMoveAmountPerScan =1; // Определить текущую позицию iCurrentPos =gServoTwist.read (); // Мы на позиции? если (iCurrentPos ==gServoTwistPositionCommand) {gServoTwistMoveIP =false; возвращение; } еще {gServoTwistMoveIP =true; } // Начнем с того места, где мы сейчас находимся iNewMoveCommand =iCurrentPos; // Определение суммы перемещения if (iCurrentPos 179) {iNewMoveCommand =179; } // Перемещение gServoTwist.write (iNewMoveCommand); // Публикуем результаты if (gVerboseDiagMode ==true) {// todo:Serial.print ("Поверните сервомеханизм (это движение, всего):"); Serial.print (iNewMoveCommand); Serial.print (","); Serial.println (gServoTwistPositionCommand); }} // Когда сервоприводы получают команду в положение, они движутся с большой скоростью. // Слишком высокая скорость потенциально может повлиять на механическую структуру, которая удерживает и перемещает // солнечную панель и платформу световых датчиков. Эта процедура принимает "команду перемещения" // и выполняет небольшие пошаговые движения, пока сервопривод не окажется в желаемом положении. // Эта процедура предназначена для сервомотора наклона, установленного на кронштейне. int iMoveAmountPerScan; int iNewMoveCommand; // Устанавливаем величину перемещения за сканирование в градусах. // Комбинация этой переменной и глобальной константы GIVE_BACK_TIME определяет общую скорость перемещения iMoveAmountPerScan =1; // Определить текущую позицию iCurrentPos =gServoTilt.read (); // Мы на позиции? если (iCurrentPos ==gServoTiltPositionCommand) {gServoTiltMoveIP =false; возвращение; } еще {gServoTiltMoveIP =true; } // Начнем с того места, где мы сейчас находимся iNewMoveCommand =iCurrentPos; // Определение суммы перемещения if (iCurrentPos 179) {iNewMoveCommand =179; } // Перемещение gServoTilt.write (iNewMoveCommand); // Публикация результатов if (gVerboseDiagMode ==true) {// todo:Serial.print ("Перемещение сервопривода наклона (это перемещение, всего):"); Serial.print (iNewMoveCommand); Serial.print (","); Serial.println (gServoTiltPositionCommand); }} // Возьмем математическое среднее двух LDR наверху panelfloat averageTopTwoSensors () {float fAvg; // Математика fAvg =(gLDR1 + gLDR2) / 2.0; return fAvg;} // Возьмите математическое среднее двух LDR внизу панелиfloat averageBottomTwoSensors () {float fAvg; // Математика fAvg =(gLDR3 + gLDR4) / 2.0; return fAvg;} // Возьмите математическое среднее двух LDR слева от панелиfloat averageLeftTwoSensors () {float fAvg; // Математика fAvg =(gLDR1 + gLDR3) / 2.0; return fAvg;} // Возьмите математическое среднее двух LDR справа от панелиfloat averageRightTwoSensors () {float fAvg; // Математика fAvg =(gLDR2 + gLDR4) / 2.0; return fAvg;} // Обрабатываем полученные сообщения от интерфейса последовательного порта // Входной параметр iControlCode - это значение, полученное от последовательного порта для обработки // Первые две цифры - это управляющая команда, остальные три - это значение для processvoid processSerialMessage (int iControlCode) {int iControlCommand; int iControlValue; // Вычислить команду и значение iControlCommand =iControlCode / 1000; iControlValue =iControlCode% 1000; // Сообщите команду и значение Serial.print ("контрольный код:"); Serial.println (iControlCode); Serial.print ("управляющая команда:"); Serial.println (iControlCommand); Serial.print ("контрольное значение:"); Serial.println (iControlValue); // Категория других команд if (iControlCommand ==10) {if (iControlValue ==0) {gVerboseDiagMode =true; digitalWrite (O_BLUE_LED_PIN, HIGH); Serial.println («режим диагностики запущен»); } иначе, если (iControlValue ==1) {gVerboseDiagMode =false; digitalWrite (O_BLUE_LED_PIN, LOW); Serial.println («режим диагностики остановлен»); } иначе, если (iControlValue ==2) {reportProductInfo (); } else if (iControlValue ==3) {// Красный светодиод на digitalWrite (O_RED_LED_PIN, HIGH); Serial.println («красный светодиод горит»); } else if (iControlValue ==4) {// Красный светодиод не горит digitalWrite (O_RED_LED_PIN, LOW); Serial.println («красный светодиод не горит»); } else if (iControlValue ==5) {// Зеленый светодиод на digitalWrite (O_GREEN_LED_PIN, HIGH); Serial.println («зеленый светодиод горит»); } else if (iControlValue ==6) {// Зеленый светодиод не горит digitalWrite (O_GREEN_LED_PIN, LOW); Serial.println («зеленый светодиод не горит»); } else if (iControlValue ==7) {// Синий светодиод на digitalWrite (O_BLUE_LED_PIN, HIGH); Serial.println («горит синий светодиод»); } else if (iControlValue ==8) {// Синий светодиод не горит digitalWrite (O_BLUE_LED_PIN, LOW); Serial.println («синий светодиод не горит»); } else if (iControlValue ==9) {// Отображение значения LDR1 Serial.print ("Значение LDR1:"); Serial.println (gLDR1); } else if (iControlValue ==10) {// Отображение значения LDR2 Serial.print ("Значение LDR2:"); Serial.println (gLDR2); } else if (iControlValue ==11) {// Отображение значения LDR3 Serial.print ("Значение LDR3:"); Serial.println (gLDR3); } else if (iControlValue ==12) {// Отображение значения LDR4 Serial.print ("Значение LDR4:"); Serial.println (gLDR4); } else if (iControlValue ==13) {// Включить режим отслеживания enableTracking (); } else if (iControlValue ==14) {// Отключить режим отслеживания disableTracking (); } else if (iControlValue ==19) {if (gRunning ==true &&gServoTwistMoveIP ==false &&gServoTiltMoveIP ==false) {gServoTwistPositionCommand =90; gServoTiltPositionCommand =90; Serial.println («сервоприводы поворота и наклона, управляемые на 90 градусов»); }} else if (iControlValue ==21) {if (gRunning ==true) {gTrackToLightEnabled =true; Serial.println («Трек до источника света включен»); }} иначе, если (iControlValue ==22) {gTrackToLightEnabled =false; Serial.println («Отслеживание до источника света отключено»); } else {Serial.print ("недопустимое контрольное значение:"); Serial.println (iControlValue); }} // Категория команды Servo1 (twist) if (iControlCommand ==11) {if (iControlValue> =0 &&iControlValue <=179) {// Перемещаем servo1 в позицию if (gRunning ==true &&gServoTwistMoveIP ==false) { gServoTwistPositionCommand =iControlValue; gServoTwistMoveIP =true; Serial.print ("Команда сервопривода поворота:"); Serial.println (gServoTwistPositionCommand); }} else {Serial.print ("недопустимое контрольное значение:"); Serial.println (iControlValue); }} // Категория команды Servo2 (tilt) if (iControlCommand ==12) {if (iControlValue> =0 &&iControlValue <=179) {// Перемещение сервопривода 2 в положение // Перемещение сервопривода 1 в положение if (gRunning ==true &&gServoTiltMoveIP ==false) {gServoTiltPositionCommand =iControlValue; gServoTiltMoveIP =true; Serial.print ("Команда сервомеханизма наклона:"); Serial.println (gServoTiltPositionCommand); } else {Serial.print ("сервопривод наклона не включен или выполняется движение"); Serial.print (gRunning); Serial.print (","); Serial.println (gServoTiltMoveIP); }} else {Serial.print ("недопустимое контрольное значение:"); Serial.println (iControlValue); }} // Конец строки запроса Serial.println ("-----");} // Выполнить последовательность шагов для самопроверки функций, включить сервоприводы и войти в режим отслеживания светаvoid startup () {int iDelay; // Инициализируем iDelay =500; // Отображение отчета с информацией о приложенииProductInfo (); задержка (iDelay); // Включаем красный светодиод processSerialMessage (10003); задержка (iDelay); // Выключить красный светодиод, включить зеленый светодиод processSerialMessage (10004); processSerialMessage (10005); задержка (iDelay); // Выключить зеленый светодиод, включить синий светодиод processSerialMessage (10006); processSerialMessage (10007); задержка (iDelay); // Выключаем синий светодиод, отображаем значения фоторезисторов (все четыре) processSerialMessage (10008); processSerialMessage (10009); processSerialMessage (10010); processSerialMessage (10011); processSerialMessage (10012); задержка (iDelay); // Включить сервоприводы enableTracking (); задержка (iDelay); // Перемещение сервоприводов в исходное положение processSerialMessage (10019); задержка (iDelay); // Отключить сервоприводы disableTracking (); // Попрощайтесь с Serial.println ("последовательность запуска завершена");} // Отправьте информацию о продукте в последовательный порт. ); Serial.println («Тусон, Аризона, США»); Serial.println («май 2018»); Serial.print («контрольная сумма»); Serial.println ("58BA-D969-2F82-08FD-2078-2777-396D-E1AA");}
Изготовленные на заказ детали и корпуса
q2_assembly_details_RCbi893qzA.zip Схема
q2_schematic_TVp7kakwNa.fzz