Привет, Хабр!

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

Начало


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

Для общего понимания, ниже на изображении вы можете увидеть, что из себя представляет опорный ролик:


Концепция устройства


По сути, для оценки износа поверхности опорного ролика достаточно инструмента под названием «Мерное колесо», с помощью которого можно измерить длину окружности ролика и рассчитать его диаметр. Сравнивая полученное измерение с прошлыми замерами, мы можем определить степень деградации поверхности ролика. Другими словами, мне предстояло разработать более современный аналог «Мерного колеса» с некоторыми «плюшками» цифрового мира.
В процессе обсуждения концепции, «заказчиком» было высказано несколько пожеланий, будем их называть «техническим заданием»:
  • Устройство должно иметь автономное питание;
  • Устройство должно иметь функцию автоматического замера по датчику;
  • Устройство должно иметь возможность сохранения замеров;
  • И от себя я добавил функцию экспорта журнала замеров в файл;
  • Устройство должно иметь удобный интерфейс.

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

Ключевой элемент устройства


В качестве ключевого элемента системы будет выступать измерительный энкодер. Для текущего проекта был выбран энкодер от компании Autonics марки E50S8-5000-6-L-5. Инкрементальный энкодер E50S8-5000-6-L-5 точно замеряет угол перемещения объекта и преобразует его в электрический импульс. Он имеет сплошной вал диаметром 8 мм. В основе работы датчика лежит инкрементальный принцип. Электрическое соединение выполняется через фланцевый разъем, тип — осевое. Данный энкодер имеет разрешение 5000 шагов на оборот, чего вполне достаточно для наших целей. Энкодер имеет три выходных сигнала TTL: фазы A, B и Z, а напряжение питания составляет 5В.

Внешний вид энкодера:

Энкодер с выходами A, B и Z — это инкрементальный энкодер с тремя основными сигналами, каждый из которых выполняет свою функцию:

A (канал A) – основной инкрементальный выходной сигнал. Это импульсы, которые генерируются при вращении вала энкодера. Каждый импульс соответствует определённому углу поворота. С помощью этого сигнала можно отслеживать количество шагов, пройденных энкодером, что позволяет определить угол или положение вращения.

B (канал B) – второй инкрементальный выход, сдвинутый по фазе относительно канала A. Благодаря этому фазовому сдвигу можно определить направление вращения. Если сигнал A опережает сигнал B, это одно направление вращения, а если B опережает A — противоположное направление. Это позволяет системе различать, вращается ли вал энкодера по часовой или против часовой стрелки.

Z (канал Z, или индексный импульс) – это сигнал, который появляется только один раз за полный оборот энкодера. Этот импульс служит для точной синхронизации или калибровки системы. Он часто используется для того, чтобы определить начальную точку отсчёта или «нулевое» положение, откуда начинается отслеживание вращения.

Таким образом, сигналы A и B позволяют отслеживать положение и направление вращения, а Z используется для сброса или синхронизации на определённой позиции.

Ниже представлены формы сигналов выходных фаз энкодера:



И реальная осциллограмма выходных сигналов:



Ниже приведены габаритные характеристики энкодера:



Крепление энкодера


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

Модель фиксирующей конструкции энкодера:


Данная модель разрабатывалась в САПР FreeCAD, а далее элементы конструкции распечатывались на рабочем 3D принтере.

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

Фиксирующая конструкции энкодера после сборки:



Контроллер энкодера


Для обработки сигналов энкодера, как ни крути, необходим контроллер. В качестве микроконтроллера для данного модуля я выбрал ESP32, прежде всего из-за наличия встроенного модуля Bluetooth. И так как в планах реализовать питание от встроенного аккумулятора, то на плате контроллера реализован повышающий преобразователь на 5В, в котором применен ШИМ контроллер MAX1771. Как можно догадаться, на плате контроллера реализована не сложная схема, ознакомиться с которой вы можете ниже.

Принципиальная схема контроллера:



Трассировка дорожек печатной платы:



Модель печатной платы:


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

Сборка печатной платы:



Корпус блока электроники


Также как и крепление энкодера, модель корпуса разрабатывалась в САПР FreeCAD. В корпусе был предусмотрен отсек для размещения аккумулятора и модуля зарядки для Li-ion аккумулятора на базе контроллера TP4056.

Модель корпуса блока электроники:



Установка электроники в корпус:



Kорпус блока электроники после печати и сборки:


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

Kорпус блока электроники в сборе:


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

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

Датчик для автоматического режима:



Программное обеспечение


Микро ПО контроллера


Прошивка контроллера разрабатывалась в среде Arduino IDE и не содержит какой-либо сложной логики. Наша задача — обработать сигналы энкодера, выполнить несложные вычисления и отдать результат в формате JSON по BLE-каналу в мобильное приложение.
На этапе разработки, когда у меня еще не было в наличии энкодера, мне пришлось написать его эмулятор и использовать его в процессе отладки. Эмулятор запускался на дешевой плате Arduino NANO и подключался к отладочной плате с микроконтроллером ESP32. Ниже представлен код эмулятора сигналов энкодера:
Код эмулятора сигналов энкодера
// Пины для имитации выходов энкодера
#define pinA 8
#define pinB 9
#define pinZ 10
#define pinR 11                                          // Канал для реверса направления

const int pulsesPerRevolution = 5000;                    // Количество импульсов на один оборот (например, 5000 импульсов)

int pulseCount = 0;                                      // Счётчик импульсов для канала Z

void setup() {

  pinMode(pinA, OUTPUT);
  pinMode(pinB, OUTPUT);
  pinMode(pinZ, OUTPUT);

  digitalWrite(pinA, LOW);
  digitalWrite(pinB, LOW);
  digitalWrite(pinZ, LOW);
}

void loop() {

   generateEncoderSignals();                               // Генерация импульсов на каналах A и B

  if (pulseCount >= pulsesPerRevolution) {
    pulseCount = 0;                                        // Сброс после полного оборота
    generateZSignal();                                     // Генерация сигнала Z
  }

  delay(55);                                               // Настроить для регулировки скорости
}

void generateEncoderSignals() {
  int delays = 55;
  if (digitalRead(pinR)) {                                    // Вращение по часовой стрелке (A опережает B)
    digitalWrite(pinA, HIGH);
    delay(delays);                                          // Половина периода сигнала
    digitalWrite(pinB, HIGH);
    delay(delays);
    digitalWrite(pinA, LOW);
    delay(delays);
    digitalWrite(pinB, LOW);
  } else {                                                  // Вращение против часовой стрелки (B опережает A)
    digitalWrite(pinB, HIGH);
    delay(delays);                                          // Половина периода сигнала
    digitalWrite(pinA, HIGH);
    delay(delays);
    digitalWrite(pinB, LOW);
    delay(delays);
    digitalWrite(pinA, LOW);
  }

  pulseCount++;                                             // Увеличение счётчика импульсов
}

void generateZSignal() {                                    // Имитация индекса (Z) — один импульс на оборот
  digitalWrite(pinZ, HIGH);
  delay(10);                                               
  digitalWrite(pinZ, LOW);
}


Что касается самого контроллера, обработка сигналов энкодера выполняется с помощью аппаратного прерывания:
  attachInterrupt(encoder0PinA, doEncoderA, CHANGE);           // Обработка внешнего прерывания фазы А
  attachInterrupt(encoder0PinZ, doEncoderZ, RISING);           // Обработка внешнего прерывания фазы Z

Ниже представлена функция обработки прерывания на фазе А:
void IRAM_ATTR doEncoderA() {                                           // Обработчик прерывания для канала A
  if(auto_measuring && auto_start or !auto_measuring){
       if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) {    // Определяем направление вращения по состоянию канала B
              direction = -1;                                           // Вращение против часовой стрелки
            } else {
              direction = 1;                                            // Вращение по часовой стрелке
       }
  encoder0Pos += direction;
  }
}

Функция обработки прерывания на фазе Z:
void IRAM_ATTR doEncoderZ(){                                           // Обработчик прерывания для канала Z
    if(auto_measuring && auto_start or !auto_measuring){ 
    if(digitalRead(encoder0PinZ)){
          encoder0Rotations++;
      }
   }
}

Как видите, здесь ничего сложного. Полный код прошивки контроллера я приложу в конце статьи.

Мобильное приложение


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

Код класса для работы с BLE
package vgc.cyberex.encoder_app;

import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.util.Log;

import androidx.core.app.ActivityCompat;

import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.UUID;

import vgc.cyberex.encoder_app.service.DataCallback;

public class BLEManager {

    private static final UUID SERVICE_UUID = UUID.fromString("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
    private static final UUID CHARACTERISTIC_UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361b26a8");

    private final Context context;
    private BluetoothAdapter bluetoothAdapter;
    private BluetoothGatt bluetoothGatt;
    private BluetoothGattCharacteristic characteristic;
    private boolean connected;
    private boolean presend;
    private final DataCallback callback;

    public BLEManager(Context context, DataCallback callback) {
        this.context = context;
        this.callback = callback;
        BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
        if (bluetoothManager != null) {
            bluetoothAdapter = bluetoothManager.getAdapter();
        }
    }


    public void connectToDevice(String mac) {
        if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
            return;
        }
        if (!Objects.equals(mac, "00:00:00:00:00")) {
            BluetoothDevice device = bluetoothAdapter.getRemoteDevice(mac);
            if (ActivityCompat.checkSelfPermission(context, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                // TODO: Consider calling
                //    ActivityCompat#requestPermissions
                // here to request the missing permissions, and then overriding
                //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                //                                          int[] grantResults)
                // to handle the case where the user grants the permission. See the documentation
                // for ActivityCompat#requestPermissions for more details.
                return;
            }
            bluetoothGatt = device.connectGatt(context, false, gattCallback);
        }
    }

    private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            if (newState == BluetoothGatt.STATE_CONNECTED) {
                connected = true;
                callback.connectStatusBT(connected);
                if (ActivityCompat.checkSelfPermission(context, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                    // TODO: Consider calling
                    //    ActivityCompat#requestPermissions
                    // here to request the missing permissions, and then overriding
                    //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                    //                                          int[] grantResults)
                    // to handle the case where the user grants the permission. See the documentation
                    // for ActivityCompat#requestPermissions for more details.
                    return;
                }
                gatt.discoverServices();
            } else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
                connected = false;
                callback.connectStatusBT(connected);
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                BluetoothGattService service = gatt.getService(SERVICE_UUID);
                if (service != null) {
                    characteristic = service.getCharacteristic(CHARACTERISTIC_UUID);
                    if (characteristic != null) {
                        //readCharacteristic(characteristic);
                        //setCharacteristicNotification(characteristic,true);
                        process_time(characteristic);
                    }
                }
            }
        }

        @SuppressLint("MissingPermission")
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                byte[] data = characteristic.getValue();
                String dataString = new String(data, StandardCharsets.UTF_8);
                // Обработка данных
                try {
                    callback.onRData(dataString);
                } catch (Exception e) {
//                    throw new RuntimeException(e);
                }
              //  Log.d("BLEDataRead", "Received: " + dataString);
            }
        }

        private void readCharacteristic(BluetoothGattCharacteristic characteristic) {
            if (ActivityCompat.checkSelfPermission(context, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                // TODO: Consider calling
                //    ActivityCompat#requestPermissions
                // here to request the missing permissions, and then overriding
                //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                //                                          int[] grantResults)
                // to handle the case where the user grants the permission. See the documentation
                // for ActivityCompat#requestPermissions for more details.
                return;
            }
            bluetoothGatt.readCharacteristic(characteristic);
        }

    };

    private void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
        if (ActivityCompat.checkSelfPermission(context, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        bluetoothGatt.setCharacteristicNotification(characteristic, enabled);

        BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
        descriptor.setValue(enabled ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
        bluetoothGatt.writeDescriptor(descriptor);
    }
    @SuppressLint("MissingPermission")
    public void sendData(String data) {
        //setCharacteristicNotification(characteristic,false);
        if (bluetoothGatt != null && characteristic != null) {
            characteristic.setValue(data.getBytes());
            bluetoothGatt.writeCharacteristic(characteristic);
            this.presend = false;
            process_time(characteristic);

        }
    }

    @SuppressLint("MissingPermission")
    public void disconnect() {
        if (bluetoothGatt != null) {
            bluetoothGatt.disconnect();
            bluetoothGatt.close();
        }
    }
    public boolean isConnected() {
        return connected;
    }
    public void precends(boolean presends, String data ){
            this.presend = presends;
        long time = System.currentTimeMillis();
        while (System.currentTimeMillis() - time < 800 ){}
        sendData(data);
    }
    private void process_time(BluetoothGattCharacteristic characteristics){
        @SuppressLint("MissingPermission") Runnable runnable = () -> {
        long time = System.currentTimeMillis();
         while (!presend){
            if(System.currentTimeMillis() - time >= 1 ){
                bluetoothGatt.readCharacteristic(characteristics);
                time = System.currentTimeMillis();
            }
        }
    };
        Thread thread = new Thread(runnable);
        thread.start();
    }
}


Подключение к устройству выполняется с помощью метода connectToDevice(), в качестве аргумента в данном методе передается mac адрес устройства. Обмен данными с устройством начинается после подключения и реализуется с помощью функции process_time(), где запускается процесс чтения характеристики устройства, а метод onCharacteristicRead() возвращает прочитанные данные, и с помощью метода callback.onRData() отправляет их в широковещательную рассылку. Для отправки данных в контроллер энкодера, используется метод sendData(), куда в качестве аргумента передается строка в формате JSON. Да, код это, наверное, скучно, поэтому давайте перейдем к описанию интерфейса и функционала приложения.

Ниже представлены скриншоты экранов мобильного приложения.

Скриншоты экранов мобильного приложения:





Системное уведомление:



Функционал мобильного приложения


  • Одна из основных функций — это отображение данных контроллера энкодера и рассчитываемых им параметров.
  • Функция автоматического замера, точнее, функция управления автоматическим замером, так как сам автоматический замер реализован в прошивке контроллера энкодера. Автоматический замер активируется с помощью одноименного переключателя, и в процессе активации данные измерения сбрасываются. В режиме автоматического замера запуск измерения начинается по сигналу датчика и останавливается при повторной активации датчика. Данные замера фиксируются, и последующий автоматический замер можно будет выполнить только при повторной активации функции. О завершении автоматического замера сообщит всплывающее окно. Индикатором данного режима является зеленая иконка с буквой «A», а индикатором активации датчика служит иконка двойной красной стрелки.
  • Функция сохранения замера: данная функция активируется при нажатии на кнопку «Сохранить замер». Далее, данные текущего замера сохраняются во внутренний архив приложения. При активации данной функции необходимо указать описание замера в окне ввода.
  • Журнал замеров – данная функция активируется по нажатию одноименной кнопки. Журнал замеров представляет собой отдельный экран приложения, где отображен список сохраненных измерений. Каждый элемент списка может быть удален при долгом нажатии на элемент списка.
  • Сброс замера — данная функция активируется по нажатию кнопки «Сбросить замер», при активации данной функции, выполняется сброс внутренних счетчиков контроллера энкодера и рассчитываемых параметров.
  • Функция ввода коэффициента шага энкодера — данная функция активируется при долгом нажатии на значение текущего коэффициента шага. При активации данной функции, открывается форма ввода, где необходимо указать новый коэффициент шага.
  • Функция экспорта архива замеров в файл формата CSV — данная функция активируется при нажатии на синюю иконку со стрелкой вниз в верхнем правом углу на экране архива измерений. После удачного экспорта файла, пользователю будет выведено сообщение с указанием пути сохранения и именем экспортного файла.
  • Функция отображения данных энкодера в режиме блокировки смартфона — данная функция позволяет наблюдать значения энкодера в режиме блокировки в виде системного уведомления, так же данное уведомление может быть отображено (в режиме блокировки смартфона) на подключенных смарт-часах пользователя.

Что касается коэффициента шага, здесь я хотел бы добавить небольшое пояснение:

Коэффициент шага энкодера — это соотношение между количеством импульсов (или шагов) на один оборот энкодера и расстоянием, которое проходит измерительное колесо, но так как у нас реализована система передачи, то необходимо учитывать и передаточный коэффициент. Ниже представлен пример расчета коэффициента шага для нашей конструкции:
Расчет коэффициента шага
Для примера расчета возьмем следующие данные:
  • Приводной шкив энкодера (Dэ): ∅ 50 мм.
  • Шкив на оси (Do): ∅ 15 мм.
  • Измерительное колесо (Dи): ∅ 50 мм.
  • Количество импульсов на оборот (PPR): 5000.

Расчет:

1. Передаточное отношение между приводного шкива энкодера и шкивом на оси: Передаточное отношение ременной передачи можно рассчитать как отношение диаметров двух шкивов:

iрп = Dо / Dэ = 15 / 50 = 3,333


Это означает, что один полный оборот приводного шкива энкодера приводит к 3,333 оборотам шкива на оси.

2. Обороты измерительного колеса: Поскольку шкив на оси и измерительное колесо жестко связаны через ось (без передаточного механизма), они будут вращаться с одинаковой скоростью. То есть за 3,333 оборота шкива на оси измерительное колесо тоже сделает 3,333 оборота.

3. Окружность измерительного колеса: Для измерительного колеса с ∅ 50 мм окружность вычисляется так:

Cи = π × Dи = 3,1416 × 50 мм = 157,08 мм


Cи — это длина, которую пройдет измерительное колесо за один оборот.

4. Путь, пройденный измерительным колесом за один оборот приводного шкива: Так как за один оборот приводного шкива измерительное колесо делает 3,333 оборота, общее расстояние, которое проходит измерительное колесо за один оборот приводного шкива, равно:

Pопш = iрп × Cи = 3,333 × 157,08  = 523,547 мм


5. Коэффициент шага будет равен:

Kш = Pопш / PPR = 523,547 / 5000 = 0,1047 мм*имп



Ниже изображен пример просмотра экспортного файла CSV с архивом измерений в приложении Numbers.

Просмотр таблицы измерений:


Итоги


Не смотря на то, что я так и не получил* обещанную премию за данный проект, это был достаточно интересный опыт реализации DIY проекта в условиях предприятия. Надеюсь статья вас не утомила. Большое спасибо за Ваше внимание! Всем добра и интересных проектов!

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

Ссылки к статье:


Комментарии (18)


  1. GoldGoblin
    15.10.2024 13:42

    Я не очень понял принцип измерения. Устанавливаем датчик и потом даем команду опираемой конструкции провернуться ровно на 1 оборот? Я сталкивался с похожими механизмами и там нет возможность повернуться строго на 360 градусов (в моем случае). А так же не стоит забывать о возможности проскальзывания не ведущих роликов.


    1. alex323
      15.10.2024 13:42

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


      1. alexhott
        15.10.2024 13:42

        Геркон - не очень точное средство. Я ждал что будет какй-то оптический датчик и метка на ролике. Хотя судя по фото там кострукция ролика такая, что его длина окружности мало говорит об износе, его скорее раскает в большую сторону, раскатает неравномерно, сколы появятся - и износ будет виден невооруженным взглядом. А поверхностный равномерный износ этому ролику работать не помешает нисколько


  1. alex323
    15.10.2024 13:42

    я так и не получил* обещанную премию за данный проект

    Вполне ожидаемо. На заводе это, как правило, на энтузиазме делается.


    1. Radisto
      15.10.2024 13:42

      Или отрицательное подкрепление (((


      1. CyberexTech Автор
        15.10.2024 13:42

        Да, есть такое. Изначально, как только я пришел в эту компанию, одурманенный эффектом новизны, у меня было много идей по улучшению процессов, но со временем я понял, что идеи в этой компании не ценятся, а тех, кто их предлагает, ненавидят. В компании ходила поговорка: "Инициатива 'любит' инициатора" — цензурная версия. Что касается данного кейса, то инициатива исходила не от меня, и я бы не взялся её реализовывать, если бы ты мне не гарантировал материальную компенсацию моих стараний.

        И, проработав шесть лет в компании, я понял, что здесь настолько несправедливая среда, что решил покинуть компанию.


  1. fagoth
    15.10.2024 13:42

    1. Какие требуемые точность и разрешение измерений?

    2. Как верифицировались измерения?


    1. CyberexTech Автор
      15.10.2024 13:42

      Какие требуемые точность и разрешение измерений?

      Микронная точность здесь явно не нужна. Точность измерения зависит от механики устройства.

      Как верифицировались измерения?

      Здесь я совсем не понял вопроса. Что значит "верифицировать" измерения? На этапе наладки, устройство тестируется на ошибки измерений и доводится к требуемым параметрам, если есть какие-либо отклонения.


  1. shadrap
    15.10.2024 13:42

    а почему геркон а не датчик холла , например? я так понимаю скорость вращения ролика не слишком высокая геркон может "подлипать"


    1. CyberexTech Автор
      15.10.2024 13:42

      Можно любой датчик поставить индукционный/оптический/etc, но с герконом проще и удобнее: прилепил магнит сбоку исследуемого валика и всё. Скорость вращения валика очень низкая, поэтому "подлипания" геркона, если они имеют место быть, никаким образом не сказываются на замер.


      1. shadrap
        15.10.2024 13:42

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


        1. CyberexTech Автор
          15.10.2024 13:42

          Согласен, но в данном случае это никак не влияет на точность работы системы. Что касается дребезга контактов геркона, данное событие маловероятно, а если посмотрите исходный код микро ПО контроллера, то сможете убедиться, что и эта потенциальная проблема учтена.


  1. BSOZ
    15.10.2024 13:42

    Передаточное отношение пары Измеряемое тело вращения / Измеряющее колесо явно сильно выше 1, стало быть износ самого измеряющего колеса будет сказываться на результатах измерения выше, чем износ измеряемого тела. Притом т.к. оно совершит большее количество оборотов, то при прочих равных в процессе измерения само будет изнашиваться сильнее, чем измеряемое тело. Предусмотрена ли какая-то компенсация на изменение диаметра самого контактирующего колеса или хотя бы индикация пределов такого износа?

    Сталкивался сильно давно с устройством, где были рельсы и колесо с инкрементальным энкодером: этот узел должен был определять местоположение тележки, которая перемещается по этим рельсам наподобие поезда. Начальное положение определялось концевым датчиком. А я участвовал в разработке ПО для этого адского устройства. Так вот, во-первых колесо изнашивалось, но было известно, сколько оборотов оно совершит при какой температуре для потери грамма материала с поверхности. По сложной формуле компенсировался этот износ, притом диаметр убывает нелинейно. Во-вторых если эта тележка в одну сторону двигалась ну очень быстро, а затем обратно уже медленно, то как ни крути, в ноль измерение не сходилось при срабатывании концевика: центробежное ускорение влияет на диаметр колеса, трение качения может комбинироваться с трением скольжения т.к. колесо имеет не нулевую массу и т.д. И был код, который агрегировал данные о положении из нескольких источников (в т.ч. с этого колеса) и отдавал измерительному оборудованию, которое на этой тележке каталось по цеху. Притом потрогать в живую это оборудование мне так и не удалось, только коллега фотографии показывал.


  1. CyberexTech Автор
    15.10.2024 13:42

    износ самого измеряющего колеса будет сказываться на результатах измерения выше

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

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


  1. TsalTsalko
    15.10.2024 13:42

    В данный момент у меня на столе прототип похожего устройства. Собрал на raspberry pi pico + магнитный энкодер MT6701 дисплей 1602, так получилось что для отсчёта оборота тоже взял геркон. Код написал на micropython. Осталось разработать корпус. Единственное я колесо выполнил из стали напрямую к энкодеру. На дисплее вывожу 3 цифры  в первой строке общий угол поворота  и длинна пройдённого пути. Во второй строке по завершении цикла измерения диаметр измеряемого колеса.  

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

    Автор молодец, только пластиковые колеса доверия не внушают.  Хотя тут главное определить форму износа опорного ролика какой износ (Конус, седловидный или локальный)?


    1. CyberexTech Автор
      15.10.2024 13:42

      Автор молодец, только пластиковые колеса доверия не внушают.

      Пластиковые колеса - это просто демонстрация моего концепта механики. В дальнейшем "заказчик" устройства планировал изготовить их из металла.


      1. TsalTsalko
        15.10.2024 13:42

        Понятно думаю, что никто нечего не делал. Интересно измерения кто-то делает?

        Обидно, когда разработка пропадает.

        +1 в skill автору.


        1. CyberexTech Автор
          15.10.2024 13:42

          Интересно измерения кто-то делает?

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

          Обидно, когда разработка пропадает.

          Здесь ничего страшного нет, я получил интересный опыт в процессе реализации, а тем, кому данная идея интересна, могут безвозмездно воспользоваться исходниками данного проекта, - для этих целей и опубликована данная статья.