Стенд для оборонки)

Motomechanick
Offline
Зарегистрирован: 19.09.2014

  Всем доброго времени суток, на форум захожу часто, а зарегистрировался только сейчас. Хочу поделиться с вами своим небольшим проектом.

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

  В общем, есть у нас в системе датчики, которые связаны с ходовой частью, по ним определяется пройденный путь, для проверки они устанавливаются в некий стенд, который имитирует движение, выдавая заданное число оборотов на выходном валу.

 

 

 

 

 

 

 

 

 

 

  Этот стенд и решено было улучшить, т.к. он частенько «пролетал» момент остановки, усложняя нам сдачу системы.

  Про Arduino я узнал не так давно, а программирование это вообще не моя тема, но сделать что-то полезное очень хотелось, поэтому стал понемногу разбираться. Сначала слепил прототип.

Убедившись, что все не так страшно, как казалось сначала, начал думать над реализацией.

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

 На соседнем заводе помогли с деталями корпуса, а знакомый сварщик согласился все это сварить в обмен на модернизацию его сварочника, что было мне совсем не сложно.

         После сборки всего этого безобразия, наступила самая мучительная для меня часть – отладка программы))). Хотелок, как водиться, было много, но я почти все реализовал!

 В общем суть работы этого стенда, выдавать заданное количество оборотов на выходном валу, с плавным разгоном и торможением, возможностью плавного регулирования скорости, автоматической остановкой и легкостью повтора последней установки. «Рулит» стендом NANO, управление, по сути, трёхкнопочное, в качестве энкодера диск с 12 «зубами» и самодельный оптический щелевой датчик.

  После отладки, все было покрашено «военной» краской в местной малярке. Собрал и начал гонять стенд в разных режимах, для проверки надежности и теплового режима.

  Получилось вполне симпатично, стенд работает и обеспечивает достаточную точность до 30º , хотя нам достаточно до 2х оборотов)) Стенд аттестован и пущен в эксплуатацию, я даже его продал предприятию с небольшой прибылью)

старый и новый стенды, большой весит около 50 кг)) и стоит 400тыр)

 

 

Motomechanick
Offline
Зарегистрирован: 19.09.2014

Вот мой кривоватый, но работающий код!)

// Программа для электронного стенда "ввода пути"; плата 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;             // ограничение максимальной скорости
  }


}

 

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

Молодцом! А шуруповерт не DeWalt случайно разобрали? Цвет уж больно редкий. За наклейку отдельный жирный +

JollyBiber
JollyBiber аватар
Offline
Зарегистрирован: 08.05.2012

И в догонку - для перепрошивки надо разбирать или Вы вывели наружу?

 

ПС: посмотрите тему "как вставлять код".

Motomechanick
Offline
Зарегистрирован: 19.09.2014

Да точно! деволт, купил без акума за 900р, для прошивки надо разбирать, но скоро думаю вывести, незаметно для контролеров))

Вот и схема

Andrey_Y_Ostanovsky
Offline
Зарегистрирован: 03.12.2012

Motomechanick пишет:
для прошивки надо разбирать, но скоро думаю вывести, незаметно для контролеров))

Тогда заранее озаботьтесь защитой выводов от статики.

На мой взгляд: лучше - не выводить наружу, а перепрошивать откинув крышку, чтобы случайно туда кто-нибудь не прошил лишнего. :)

Motomechanick
Offline
Зарегистрирован: 19.09.2014

В процессе изготовления были "убиты" 2 дисплея 1602 и 2 БП АТХ))) С блоком питания вообще отдельная тема, сейчас стоит 450Вт и нормально работает, но только при условии плавного старта, если резко увеличивать скважность ШИМа то уходит в защиту, а еще пришлось поднимать частоту ШИМ до 31кГц иначе тоже "вышибало" в некоторых режимах! В качестве силового транзистора использую logic level MOSFET 45N03LT  очень удобно, взял его из старого БП от компа! В схему встроен "электротормоз" так же на полевике, только P-канальном, выходной вал останавливается практически мгновенно.

Motomechanick
Offline
Зарегистрирован: 19.09.2014

К сожалению любые стенды нам приходиться пломбировать, чтобы никто несанкционированно не мог в них залезть) если сломается и придется официально вскрывать, то что-нибудь придумаю с прошивкой!

a5021
Offline
Зарегистрирован: 07.07.2013

Вы молодец. Уважаю. Без пафоса и на коленке сделал устройство, которое решает задачу лучше, чем штатный стенд умопомрачительной стоимости. Инженерный подход в лучшем виде.

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

Да классно получилось

тут был один человек. они настраивали радиоаппаратуру 3 конденсаторами. помогал ему как определить характеристику, но похоже человек забил на проект

minamonra
Offline
Зарегистрирован: 18.06.2014

Да вам памятьник Motomechanick поставить надо, сердце кровью обливается глядя на то как говорят какие деньги бухают в оборонку, и видя по таким постам ваше настоящее положение вещей...