Стенд для оборонки)
- Войдите на сайт для отправки комментариев
Всем доброго времени суток, на форум захожу часто, а зарегистрировался только сейчас. Хочу поделиться с вами своим небольшим проектом.
Начну с предыстории, я работаю на одном из предприятий ВПК в маленьком городе, наш отдел делает систему наведения-навигации для некоторой военной техники. Все компоненты системы проходят множество испытаний и проверку функционирования в присутствии контролеров. Все оборудование, мягко говоря, устаревшее и работать на нем не всегда удобно. Вот я и решил хоть немного улучшить себе и коллегам условия труда.
В общем, есть у нас в системе датчики, которые связаны с ходовой частью, по ним определяется пройденный путь, для проверки они устанавливаются в некий стенд, который имитирует движение, выдавая заданное число оборотов на выходном валу.
Этот стенд и решено было улучшить, т.к. он частенько «пролетал» момент остановки, усложняя нам сдачу системы.
Про Arduino я узнал не так давно, а программирование это вообще не моя тема, но сделать что-то полезное очень хотелось, поэтому стал понемногу разбираться. Сначала слепил прототип.

Убедившись, что все не так страшно, как казалось сначала, начал думать над реализацией.
е смотря на то что наше предприятие выпускает довольно широкий ассортимент продукции специального назначения, найти что-то подходящее для своего стенда я не смог, как говориться: «все уже украдено до нас» (с).)) Поэтому решил делать из самых доступных «гражданских» компонентов, чтобы повысить ремонтопригодность.
На соседнем заводе помогли с деталями корпуса, а знакомый сварщик согласился все это сварить в обмен на модернизацию его сварочника, что было мне совсем не сложно.

После сборки всего этого безобразия, наступила самая мучительная для меня часть – отладка программы))). Хотелок, как водиться, было много, но я почти все реализовал!
В общем суть работы этого стенда, выдавать заданное количество оборотов на выходном валу, с плавным разгоном и торможением, возможностью плавного регулирования скорости, автоматической остановкой и легкостью повтора последней установки. «Рулит» стендом NANO, управление, по сути, трёхкнопочное, в качестве энкодера диск с 12 «зубами» и самодельный оптический щелевой датчик.
После отладки, все было покрашено «военной» краской в местной малярке. Собрал и начал гонять стенд в разных режимах, для проверки надежности и теплового режима.
Получилось вполне симпатично, стенд работает и обеспечивает достаточную точность до 30º , хотя нам достаточно до 2х оборотов)) Стенд аттестован и пущен в эксплуатацию, я даже его продал предприятию с небольшой прибылью)
старый и новый стенды, большой весит около 50 кг)) и стоит 400тыр)








Вот мой кривоватый, но работающий код!)
// Программа для электронного стенда "ввода пути"; плата arduino nano rev 4.0 // 20.05.2014 #include <LiquidCrystal.h> // библиотека для работы с жк дисплеем #include <Bounce.h> // библиотека для обработки кнопок (устранение дребезга) #define BRAKE_PIN 6 // электротормоз по высокому уровю #define COUNT_BUTTON_PIN 7 // кнопка ввода числа #define SIDE_BUTTON_PIN 8 // кнопка сдвига вправо #define START_BUTTON_PIN 9 // кнопка старт #define MOTOR_PIN 3 // ШИМ на затвор транзистора управления двигателем #define STOP_BUTTON_PIN 14 // (А0) кнопка стоп #define PWM_SETUP_RES_PIN A2 // потенциометр, регулятор скорости LiquidCrystal lcd(5, 4, 13, 12, 11, 10); // назначаем выводы для управления дисплеем (E,RS,D0..D4) volatile int Chislo_Impulsov = 0; // число импульсов с датчика Bounce count = Bounce(COUNT_BUTTON_PIN,20); // создаем объекты в рамках библиотекаи bounce Bounce side = Bounce(SIDE_BUTTON_PIN,20); // для обработки кнопок выставляем Bounce start = Bounce(START_BUTTON_PIN,20); // время задержки в мс для стабильного срабатывания Bounce stopp = Bounce(STOP_BUTTON_PIN,20); int accel_end; // флаг завершения цикла разгона int enable_accel = 1; // флаг разрешения разгона static int enable_start = 0; // флаг для сохранения текущего состояния static int brake_cycle = 0; // флаг цикла торможения static unsigned int rotation; // переменная для значения с АЦП (регулятор скорости) static unsigned int PWM_Value_PreSet= 0; // переменная для значения ШИМ при пересчете значения с АЦП static unsigned int Kolich_Oborotov = 0; // заданное количество оборотов static unsigned int Count_Thous = 0; // 1 столбец тысячи static unsigned int Count_Hundr = 0; // 2 столбец сотни static unsigned int Count_Dec = 0; // 3 столбец десятки static unsigned int Count_Unit = 0; // 4 столбец единицы static unsigned int Stolbec = 0; // положение курсора по горизонтали static unsigned int PWM_Value = 0; // значение ШИМ static unsigned int PWM_Value_Max = 252; // максимальное значение ШИМ static unsigned int PWM_Value_Min = 50; // минимальное значение ШИМ static unsigned int PWM_Value_SETUP = 0; // промежуточная переменная для хранения значения ШИМ static unsigned int Chislo_Oborotov = 0; // начальное число оборотов =0 void otrisovka (int Stolbec, int Count_Thous,int Count_Hundr,int Count_Dec,int Count_Unit, int PWM_Value_SETUP, int Chislo_Oborotov ) { lcd.setCursor(Stolbec, 0); // функция для вывода информации на дисплей, вверху все переменные которые можно выводить lcd.home(); lcd.print(Count_Thous); lcd.print(Count_Hundr); lcd.print(Count_Dec); lcd.print(Count_Unit); lcd.setCursor(5, 0); lcd.print("Kolich_Obor"); lcd.setCursor(0, 1); lcd.print(Chislo_Oborotov); if(Chislo_Oborotov == 0) // для удаления "артефактов" с дисплея при изменении значения с трехзначного { lcd.setCursor(0, 1); // на двухзначное lcd.print("0 "); // посредством вывода пробелов для очистки нужных знакомест } lcd.setCursor(5, 1); lcd.print("SPD:"); lcd.print(round(PWM_Value_SETUP/2.25)); // вывод установленного значения скорости на дисплей // с пересчетом в настоящую скорость с округлением if(PWM_Value_SETUP < 225) { lcd.setCursor(11, 1); lcd.print(" "); } lcd.setCursor(12, 1); lcd.print("kM/h"); lcd.home(); lcd.cursor(); lcd.setCursor(Stolbec, 0); } void setup() { TCCR2B = TCCR2B & 0b11111000 | 0x01; // задаём частоту аппаратного ШИМа 31 250 Гц // конфигурируем выводы pinMode(BRAKE_PIN, OUTPUT); pinMode(MOTOR_PIN, OUTPUT); pinMode(COUNT_BUTTON_PIN, INPUT_PULLUP); // кнопка ввода числа с подтяжкой к +5 pinMode(SIDE_BUTTON_PIN, INPUT_PULLUP); // кнопка сдвига вправо с подтяжкой к +5 pinMode(START_BUTTON_PIN, INPUT_PULLUP); // кнопка старт с подтяжкой к +5 pinMode(STOP_BUTTON_PIN, INPUT_PULLUP); // кнопка стоп с подтяжкой к +5 pinMode(PWM_SETUP_RES_PIN, INPUT); // потенциометр - регулятор скорости attachInterrupt(0,counter, RISING); // прерывание №0 на пине 2 по фронту импульса lcd.begin(16, 4); // инициализация дисплея размерности 16х2 } void counter() // функция обработки прерываний { Chislo_Impulsov++; // при каждом прерывании счетчик числа импульсов инкрементируется } void loop() { if (stopp.update()) { //если нажата кнопка стоп, if (stopp.read() == LOW) { enable_start = 0; PWM_Value =0; //ШИМ=0 brake_cycle = 0; analogWrite(MOTOR_PIN, PWM_Value) ; digitalWrite (BRAKE_PIN, HIGH); // тормоз включить delay(1000); // подождать секунду digitalWrite (BRAKE_PIN, LOW); // тормоз выключить Chislo_Impulsov = 0; Chislo_Oborotov = 0; // обнулить посчитанное } } if (side.update()) { if (side.read() == LOW) { Stolbec = (Stolbec+1)%4; } // если была нажата кнопка сдвига к значению "столбец" прибавляем 1 } switch (Stolbec) { // "подключаем" значение столбца для ввода разрядов по очереди case 0: if (count.update() ) { if (count.read() == LOW) { Count_Thous =(Count_Thous+1)%10; } // вводим тысячи кнопкой вода числа } break; case 1: if (count.update() ) { if (count.read() == LOW) { Count_Hundr =(Count_Hundr+1)%10; } // вводим сотни } break; case 2: if (count.update() ) { if (count.read() == LOW) { Count_Dec =(Count_Dec+1)%10; } // вводим десятки } break; case 3: if (count.update() ) { if (count.read() == LOW) { Count_Unit =(Count_Unit+1)%10; } // вводим единицы } break; // ввод числа завершен, при дальнейшем нажатии кнопки сдвига курсор вернётся в начало } Chislo_Oborotov = (Chislo_Impulsov/12); // отношение числа импульсов с датчика к целым оборотам (количество зубцов на диске) if (start.update()) { if (start.read() == LOW) // по нажатию кнопки старт { Kolich_Oborotov = (Count_Thous * 1000 + Count_Hundr * 100 + Count_Dec * 10 + Count_Unit); //вычисляем введеное кол-во оборотов digitalWrite (BRAKE_PIN, LOW); // выключаем тормоз enable_start = 1; // Поднимаем флаг состояния для возможности регулировать скорость brake_cycle = 0; } } if (enable_start) { if ((Kolich_Oborotov >0) && (Kolich_Oborotov <= 10)){ // если заданное число оборотов больше 0 но меньше 10 PWM_Value_Max = PWM_Value_Min; // отрабатываем с минимальной скоростью enable_accel = 0; } if ((Kolich_Oborotov >11) && (Kolich_Oborotov <= 20)){ // если заданное число оборотов меньше 20 PWM_Value_Max = PWM_Value_Min + 15; // разгоняемся чуть больше но до заданного уровня } if ((Kolich_Oborotov >21) && (Kolich_Oborotov <= 60)){ // если заданное число оборотов больше 20 но меньше 60 PWM_Value_Max = PWM_Value_Min + 35; // разгоняемся чуть больше но до заданного уровня, регулировка работает } if ((Kolich_Oborotov >61) && (Kolich_Oborotov <= 99)){ // если заданное число оборотов больше 60 но меньше 99 PWM_Value_Max = PWM_Value_Min + 65; // разгоняемся чуть больше но до заданного уровня, регулировка работает } if (Kolich_Oborotov >=100) // если заданное число оборотов больше 100 скорость без ограничений { // регулируется потенциометром PWM_Value_Max = PWM_Value_PreSet; } while (PWM_Value < PWM_Value_SETUP) // если текущее значение ШИМ меньше максимально установленного, разгон { if(brake_cycle) break; // если произошло торможение то разгон уже не возможен if (Kolich_Oborotov ==0) break; // запрет на старт если не введено число оборотов PWM_Value++; if(PWM_Value > PWM_Value_SETUP){ PWM_Value = PWM_Value_SETUP; } // ограничиваем увеличение ШИМ только до установленного значения analogWrite(MOTOR_PIN, PWM_Value ); // выводим текущее значение ШИМ delay(8); if(PWM_Value == PWM_Value_SETUP) accel_end = 1; // по завершении разгона поднимаем флаг } if (enable_start && accel_end && enable_accel ) // если была нажата кнопка старт, то можно регулировать скорость { // в ходе отработки PWM_Value = PWM_Value_SETUP; analogWrite(MOTOR_PIN, PWM_Value); } } if(millis()%50==0) { otrisovka(Stolbec, Count_Thous, Count_Hundr, Count_Dec, Count_Unit, PWM_Value_SETUP, Chislo_Oborotov ); } if (Chislo_Impulsov > (Kolich_Oborotov * 12 - 143)) // за 10 оборотов до заданного значения уменьшаем ШИМ до минимума (плавная остановка) { enable_start = 0; brake_cycle = 1; while (PWM_Value > PWM_Value_Min) { PWM_Value--; if(PWM_Value <PWM_Value_Min){ // цикл торможения до минимальной скорости PWM_Value = PWM_Value_Min; } analogWrite(MOTOR_PIN, PWM_Value); delay(3); // эта задержка определяет продолжительность процесса торможения } } if (Chislo_Impulsov > (Kolich_Oborotov * 12 - 1)) // за N импульсов до заданного значения { PWM_Value =0; analogWrite(MOTOR_PIN, PWM_Value); // ШИМ=0 digitalWrite (BRAKE_PIN, HIGH); // включаем тормоз enable_start = 0; delay(500); digitalWrite (BRAKE_PIN, LOW); // выключаем тормоз и обнуляем счетчик Chislo_Impulsov = 0; accel_end = 0; } if(millis()%100==0) { otrisovka(Stolbec, Count_Thous, Count_Hundr, Count_Dec, Count_Unit, PWM_Value_SETUP, Chislo_Oborotov ); } //общая функция отрисовки, дисплей обновляетя каждые 100 мс rotation = analogRead(PWM_SETUP_RES_PIN); // присваиваем переменной rotation значение считанное с АЦП PWM_Value_PreSet = rotation/4; if(PWM_Value_Min <= PWM_Value_PreSet <= PWM_Value_Max ) // если текущее значение скорости считанное с потенциометра в допуске { PWM_Value_SETUP = PWM_Value_PreSet; // то можно регулировать скорость в пределах поддиапазона кол-ва оборотов } if(PWM_Value_PreSet < PWM_Value_Min) { PWM_Value_SETUP = PWM_Value_Min; // ограничение минимальной скорости } if(PWM_Value_PreSet > PWM_Value_Max) { PWM_Value_SETUP = PWM_Value_Max; // ограничение максимальной скорости } }Молодцом! А шуруповерт не DeWalt случайно разобрали? Цвет уж больно редкий. За наклейку отдельный жирный +
И в догонку - для перепрошивки надо разбирать или Вы вывели наружу?
ПС: посмотрите тему "как вставлять код".
Да точно! деволт, купил без акума за 900р, для прошивки надо разбирать, но скоро думаю вывести, незаметно для контролеров))
Вот и схема
Тогда заранее озаботьтесь защитой выводов от статики.
На мой взгляд: лучше - не выводить наружу, а перепрошивать откинув крышку, чтобы случайно туда кто-нибудь не прошил лишнего. :)
В процессе изготовления были "убиты" 2 дисплея 1602 и 2 БП АТХ))) С блоком питания вообще отдельная тема, сейчас стоит 450Вт и нормально работает, но только при условии плавного старта, если резко увеличивать скважность ШИМа то уходит в защиту, а еще пришлось поднимать частоту ШИМ до 31кГц иначе тоже "вышибало" в некоторых режимах! В качестве силового транзистора использую logic level MOSFET 45N03LT очень удобно, взял его из старого БП от компа! В схему встроен "электротормоз" так же на полевике, только P-канальном, выходной вал останавливается практически мгновенно.
К сожалению любые стенды нам приходиться пломбировать, чтобы никто несанкционированно не мог в них залезть) если сломается и придется официально вскрывать, то что-нибудь придумаю с прошивкой!
Вы молодец. Уважаю. Без пафоса и на коленке сделал устройство, которое решает задачу лучше, чем штатный стенд умопомрачительной стоимости. Инженерный подход в лучшем виде.
Да классно получилось
тут был один человек. они настраивали радиоаппаратуру 3 конденсаторами. помогал ему как определить характеристику, но похоже человек забил на проект
Да вам памятьник Motomechanick поставить надо, сердце кровью обливается глядя на то как говорят какие деньги бухают в оборонку, и видя по таким постам ваше настоящее положение вещей...