Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Manufacturing Technology >> Производственный процесс

Умная система управления светофором с реальными данными

Компоненты и расходники

Arduino Yun × 1
Arduino UNO (или любая другая плата Arduino) × 1
Ультразвуковой датчик HC-SR04 × 1
DS3231 RTC (часы реального времени) × 1
Светодиоды (1 красный, 1 жёлтый, 1 зелёный) × 3
Резисторы 330 Ом × 3
Модули 433 МГц (передатчик + приёмник) × 1
Android-устройство × 1
SD-карта × 1
AllThingsTalk IOTOPIA Rapid Development kit × 1
Перемычки × 1

Приложения и онлайн-сервисы

О проекте

Введение

Пробки раздражают даже самых терпеливых водителей, и ситуация ухудшается. А что, если это можно изменить с помощью интеллектуальных светофоров? Мы создали систему информации о светофоре, которая сообщает водителю, с какой скоростью ему нужно двигаться, чтобы проехать перекрёсток на зелёном свете, не превышая допустимый лимит. И когда ожидание красного света неизбежно, система показывает, через сколько секунд загорится зелёный.

Умная система управления светофором с реальными данными

Светофор передаёт данные о длительности зелёного сигнала, времени до следующего зелёного и т.д. в онлайн-базу данных. GPS или приложение на смартфоне извлекает эти данные, вычисляет рекомендуемую скорость для проезда на зелёном и отображает обратный отсчёт оставшегося времени красного сигнала.

Пример

Вы едете в зоне 70 км/ч, соблюдая лимит. Впереди светофор красный, зелёный загорится через 30 секунд. Вы находитесь в 500 метрах от светофора, поэтому система рекомендует снизить скорость до 60 км/ч.

Умная система управления светофором с реальными данными

Демонстрация проекта

Как мы это сделали?

Для демонстрации мы собрали светофор на базе Arduino Yun. Он состоит из трёх светодиодов и часов реального времени (RTC). Сигналы переключаются с заданным интервалом. Arduino Yun сохраняет время переключения на зелёный/красный, затем запускает PHP-скрипт, который подключается к онлайн-базе MySQL и записывает данные. Android-приложение (созданное в Android Studio) получает данные из базы через другой PHP-скрипт, возвращающий JSON. Приложение вычисляет рекомендуемую скорость и отображает таймер обратного отсчёта, если проехать на зелёном невозможно.

Умная система управления светофором с реальными данными

Для расчёта скорости приложению нужно знать расстояние до светофора. Оно определяется по GPS-координатам, но точность смартфона недостаточна для малого масштаба. Поэтому мы использовали ультразвуковой датчик HC-SR04. Arduino Yun получает данные о расстоянии от второй Arduino через RF-модули 433 МГц. При каждом новом измерении база данных обновляется, и приложение может вычислить точную рекомендуемую скорость.

Умная система управления светофором с реальными данными

Инструкции

  1. Создайте онлайн-базу MySQL (мы использовали freemysqlhosting.net).
  2. Добавьте таблицу TrafficL_data согласно схеме.
  3. Соберите аппаратную часть (передатчик) по схеме «Arduino transmitter».
  4. Загрузите код передатчика в Arduino.
  5. Подключите Arduino Yun к Wi-Fi.
  6. Настройте Linux-сервер Arduino Yun через PuTTY:
    opkg update
    opkg install php5-mod-mysqli
    opkg install php5-cli
    nano /mnt/sda1/MySQL_UpdateTrafficLData.php
    
    Если nano не найден: opkg install nano.
    chmod 755 /mnt/sda1/MySQL_UpdateTrafficLData.php
    /mnt/sda1/MySQL_UpdateTrafficLData.php
    nano /mnt/sda1/MySQL_UpdateDistance.php
    chmod 755 /mnt/sda1/MySQL_UpdateDistance.php
    /mnt/sda1/MySQL_UpdateDistance.php
    
  7. Соберите приёмник на Arduino Yun по схеме.
  8. Загрузите код приёмника и работы с БД в Arduino Yun.
  9. Откройте Serial Monitor — должны появиться данные, похожие на скриншоты.
  10. Файлы Android Studio: activity_main.xml, MainActivity.java (замените URL), AndroidManifest.xml, strings.xml. Также нужен EchoJSON.php.
  11. Загрузите проект на смартфон.

Дополнительно: использование ESP8266 вместо Arduino Yun

Более дешёвый вариант — модуль на базе ESP8266 (например, NodeMCU V3).

  1. Создайте аккаунт на 000webhost.com.
  2. Создайте сайт и базу данных.
  3. Загрузите через File Manager файлы CreateTable.php и PostDemo.php, заменив username, password, dbname.
  4. Создайте таблицу, открыв CreateTable.php в браузере.
  5. В Arduino IDE добавьте URL пакета ESP8266: https://arduino.esp8266.com/stable/package_esp8266com_index.json и установите плату.
  6. Загрузите скетч ESP8266PostDemo.ino (обновляет БД каждые 10 секунд случайными значениями для теста).
  7. Настройте PHP-обработчик на сервере.
  8. В коде приёмника замените функции MySQL_UpdateTrafficLData() и MySQL_UpdateDistance() на работу с новым API.

Совет: тестируйте каждый компонент отдельно, прежде чем объединять их. Задавайте вопросы в комментариях с подробным описанием проблемы.

Код

Arduino Transmitter Code

/* IOTOPIA - 2016-2017 - Traffic Light Information System (Transmitter)
 * by Pieter Luyten & Joppe Smeets
 * Send distance to another arduino via RF 433MHz modules
 * RF 433MHz module: tx_pin -> pin 3
 * HC-SR04 module: trig_pin -> pin 5, echo_pin -> pin 6
 */
#define tx_pin 3
#define trig_pin 5
#define echo_pin 6
#include <VirtualWire.h>
#include "HCSR04.h"
HCSR04 ultrasonic(trig_pin, echo_pin);
int distance;
char CharMsg[21];

void setup() {
  Serial.begin(9600);
  vw_setup(2000);
  vw_set_tx_pin(tx_pin);
}

void loop() {
  distance = ultrasonic.Ranging(CM);
  while (distance < 0) {
    distance = ultrasonic.Ranging(CM);
  }
  sprintf(CharMsg, "%d,", distance);
  vw_send((uint8_t *)CharMsg, strlen(CharMsg));
  vw_wait_tx();
  Serial.print("Distance (cm): ");
  Serial.println(distance);
  delay(250);
}

Arduino Receiver & MySQLdb

/* IOTOPIA - 2016-2017 - Traffic Light Information System (Receiver + Upload to MySQLdb)
 * by Pieter Luyten & Joppe Smeets
 * Receive distance from another arduino via RF
 * Upload traffic light data and distance to MySQL database
 */
#define RedLED 5
#define OrangeLED 6
#define GreenLED 7
#define rx_pin 9
#include <VirtualWire.h>
#include <TimeAlarms.h>
#include <Wire.h>
#include "RTClib.h"
#include <Process.h>

long UnixTime_green_1, UnixTime_red_1, UnixTime_green_2, UnixTime_red_2;
long s_till_orange;
RTC_DS3231 rtc;
char StringReceived[22];
boolean i;
int interval = 5;
int distance;

void setup() {
  rtc.begin();
  Serial.begin(9600);
  Bridge.begin();
  vw_setup(2000);
  vw_set_rx_pin(rx_pin);
  vw_rx_start();
  Alarm.timerRepeat(interval, UpdateTrafficLData);
  Alarm.timerRepeat(1, PrintCurrentTime);
  pinMode(GreenLED, OUTPUT);
  pinMode(OrangeLED, OUTPUT);
  pinMode(RedLED, OUTPUT);
  digitalWrite(GreenLED, LOW);
  digitalWrite(OrangeLED, LOW);
  digitalWrite(RedLED, LOW);
  Serial.println("Setup finished");
}

void loop() {
  Alarm.delay(0);
  RF_Listen();
}

void RF_Listen() {
  uint8_t buf[VW_MAX_MESSAGE_LEN];
  uint8_t buflen = VW_MAX_MESSAGE_LEN;
  if (vw_get_message(buf, &buflen)) {
    for (int a = 0; a < buflen; a++) {
      StringReceived[a] = char(buf[a]);
    }
    sscanf(StringReceived, "%d", &distance);
    MySQL_UpdateDistance();
    Serial.print("Distance: ");
    Serial.println(distance);
  }
  memset(StringReceived, 0, sizeof(StringReceived));
}

void UpdateTrafficLData() {
  DateTime now = rtc.now();
  if (i == 1) {
    UnixTime_green_1 = now.unixtime() + interval + 1;
    UnixTime_red_1 = now.unixtime() + 2 * interval + 1;
    UnixTime_green_2 = now.unixtime() + 3 * interval + 1;
    UnixTime_red_2 = now.unixtime() + 4 * interval + 1;
    MySQL_UpdateTrafficLData();
    i = 0;
  } else {
    UnixTime_red_1 = now.unixtime() + interval + 1;
    UnixTime_green_1 = now.unixtime() + (2 * interval) + 1;
    UnixTime_red_2 = now.unixtime() + 3 * interval + 1;
    UnixTime_green_2 = now.unixtime() + 4 * interval + 1;
    MySQL_UpdateTrafficLData();
    i = 1;
  }
  Serial.print(" Green_1 at "); Serial.print(UnixTime_green_1);
  Serial.print(" Red_1 at "); Serial.print(UnixTime_red_1);
  Serial.print(" Green_2 at "); Serial.print(UnixTime_green_2);
  Serial.print(" Red_2 at "); Serial.println(UnixTime_red_2);
}

void MySQL_UpdateTrafficLData() {
  long dataArray[4] = {UnixTime_green_1, UnixTime_red_1, UnixTime_green_2, UnixTime_red_2};
  Process p;
  p.begin("/mnt/sda1/MySQL_UpdateTrafficLData.php");
  for (int b = 0; b < 4; b++) p.addParameter(String(dataArray[b]));
  p.run();
  while (p.available() > 0) { char c = p.read(); Serial.print(c); }
  Serial.flush();
}

void MySQL_UpdateDistance() {
  Process p;
  p.begin("/mnt/sda1/MySQL_UpdateDistance.php");
  p.addParameter(String(distance));
  p.run();
  while (p.available() > 0) { char c = p.read(); Serial.print(c); }
  Serial.flush();
}

void PrintCurrentTime() {
  DateTime now = rtc.now();
  SetLEDsTrafficL();
  Serial.print(now.hour(), DEC); Serial.print(':');
  Serial.print(now.minute(), DEC); Serial.print(':');
  Serial.print(now.second(), DEC); Serial.print(" ");
  Serial.println(now.unixtime());
}

void SetLEDsTrafficL() {
  DateTime now = rtc.now();
  s_till_orange = UnixTime_red_1 - now.unixtime();
  if (i == 0) {
    digitalWrite(GreenLED, LOW);
    digitalWrite(OrangeLED, LOW);
    digitalWrite(RedLED, HIGH);
  }
  if (i == 1 && s_till_orange <= 3) {
    digitalWrite(GreenLED, LOW);
    digitalWrite(OrangeLED, HIGH);
    digitalWrite(RedLED, LOW);
  }
  if (i == 1 && s_till_orange > 3) {
    digitalWrite(GreenLED, HIGH);
    digitalWrite(OrangeLED, LOW);
    digitalWrite(RedLED, LOW);
  }
}

MySQL_UpdateTrafficLData.php

#!/usr/bin/php-cli
<?php
$UnixTime_green_1 = $argv[1];
$UnixTime_red_1 = $argv[2];
$UnixTime_green_2 = $argv[3];
$UnixTime_red_2 = $argv[4];
$DBServer = 'yourServerName';
$DBUser = 'yourUserName';
$DBPass = 'yourPassword';
$DBName = 'yourDBName';
$conn = new mysqli($DBServer, $DBUser, $DBPass, $DBName);
if ($conn->connect_error) {
  trigger_error('Database connection failed: ' . $conn->connect_error, E_USER_ERROR);
}
$sql = "UPDATE TrafficL_data SET UnixTime_green_1='$UnixTime_green_1', UnixTime_red_1='$UnixTime_red_1', UnixTime_green_2='$UnixTime_green_2', UnixTime_red_2='$UnixTime_red_2' WHERE id=1";
if ($conn->query($sql) === false) {
  trigger_error('Wrong SQL: ' . $sql . ' Error: ' . $conn->error, E_USER_ERROR);
} else {
  echo "Data inserted!\n";
}
?>

MySQL_UpdateDistance.php

#!/usr/bin/php-cli
<?php
$Distance = $argv[1];
$DBServer = 'yourServerName';
$DBUser = 'yourUserName';
$DBPass = 'yourPassword';
$DBName = 'yourDBName';
$conn = new mysqli($DBServer, $DBUser, $DBPass, $DBName);
if ($conn->connect_error) {
  trigger_error('Database connection failed: ' . $conn->connect_error, E_USER_ERROR);
}
$sql = "UPDATE TrafficL_data SET Distance='$Distance' WHERE id=1";
if ($conn->query($sql) === false) {
  trigger_error('Wrong SQL: ' . $sql . ' Error: ' . $conn->error, E_USER_ERROR);
} else {
  echo "Distance inserted!\n";
}
?>

EchoJSON.php

<?php
define('HOST', 'yourServerName');
define('USER', 'yourUserName');
define('PASS', 'yourPassword');
define('DB', 'yourDBName');
$con = mysqli_connect(HOST, USER, PASS, DB);
$sql = "select * from TrafficL_data";
$res = mysqli_query($con, $sql);
while ($row = mysqli_fetch_array($res, MYSQLI_BOTH)) {
  printf('[ { "TrafficLArray": [ { "UnixTime_green_1":"%s", "UnixTime_red_1":"%s", "UnixTime_green_2":"%s", "UnixTime_red_2":"%s", "Distance":"%s" } ] } ]',
    $row['UnixTime_green_1'], $row['UnixTime_red_1'], $row['UnixTime_green_2'], $row['UnixTime_red_2'], $row['Distance']);
}
mysqli_close($con);
?>

MainActivity.java (фрагмент)

package com.example.xxx.xxx; // измените пакет
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonArrayRequest;
import com.android.volley.toolbox.Volley;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class MainActivity extends AppCompatActivity {
  TextView results, recSpeed;
  String JsonURL = "YourURLhere";
  RequestQueue requestQueue;
  private final static double maxSpeed = 18; // см/с
  private double speed;
  public final static int NUMBER_OF_ENTRIES = 2;
  long[] unixTimesGreen = new long[NUMBER_OF_ENTRIES];
  long[] unixTimesRed = new long[NUMBER_OF_ENTRIES];
  private double distance;
  private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
  private Future<?> timingTask;

  public void tick(long milliseconds) {
    timingTask = scheduler.scheduleAtFixedRate(new Runnable() {
      public void run() { updateTable(); }
    }, 0, milliseconds, TimeUnit.MILLISECONDS);
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    results = findViewById(R.id.jsonData);
    recSpeed = findViewById(R.id.recommendedSpeed);
    updateTable();
    tick(500);
  }

  private void updateTable() {
    requestQueue = Volley.newRequestQueue(this);
    JsonArrayRequest arrayreq = new JsonArrayRequest(JsonURL,
      new Response.Listener<JSONArray>() {
        @Override
        public void onResponse(JSONArray response) {
          try {
            JSONObject TrafficLObj = response.getJSONObject(0);
            JSONArray trafficArry = TrafficLObj.getJSONArray("TrafficLArray");
            for (int i = 0; i < trafficArry.length(); i++) {
              JSONObject jsonObject = trafficArry.getJSONObject(i);
              String[] green = new String[NUMBER_OF_ENTRIES];
              String[] red = new String[NUMBER_OF_ENTRIES];
              String distanceTemp = jsonObject.getString("Distance");
              distance = (double) Long.parseLong(distanceTemp);
              green[0] = jsonObject.getString("UnixTime_green_1");
              red[0] = jsonObject.getString("UnixTime_red_1");
              green[1] = jsonObject.getString("UnixTime_green_2");
              red[1] = jsonObject.getString("UnixTime_red_2");
              for (int j = 0; j < unixTimesRed.length; j++) {
                unixTimesGreen[j] = Long.parseLong(green[j]);
                unixTimesRed[j] = Long.parseLong(red[j]);
              }
            }
            updateTextView();
          } catch (JSONException e) { e.printStackTrace(); }
        }
      },
      new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) { Log.e("Volley", "Error"); }
      });
    requestQueue.add(arrayreq);
  }

  private void updateTextView() {
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < unixTimesGreen.length; i++) {
      builder.append("unixTimeGreen").append(i).append(": ")
        .append(new Date(unixTimesGreen[i] * 1000)).append("\n");
      builder.append("unixTimeRed").append(i).append(": ")
        .append(new Date(unixTimesRed[i] * 1000)).append("\n");
    }
    builder.append("distance: ").append(distance);
    speedCalc();
    String s = String.format("%.0f", speed);
    recSpeed.setText(s);
  }

  private void speedCalc() {
    int interval = 0;
    double time = (double) System.currentTimeMillis() / 1000;
    for (int i = 0; i < NUMBER_OF_ENTRIES; i++) {
      double diff = unixTimesRed[interval] - time;
      speed = distance / (diff / 1000);
      interval++;
    }
    interval--;
    if (distance / (unixTimesGreen[interval] - time) > maxSpeed) speed = maxSpeed;
    else speed = distance / (unixTimesGreen[interval] - time);
    speed = speed * 5;
  }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
  package="com.example.xxx.xxx"> <!-- измените пакет -->
  <uses-permission android:name="android.permission.INTERNET"/>
  <application android:allowBackup="true" android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name" android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
  </application>
</manifest>

strings.xml

<resources>
  <string name="app_name">NooitRood</string> <!-- "Никогда не красный" -->
</resources>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:tools="https://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_marginLeft="16dp"
  android:layout_marginRight="16dp"
  tools:context="com.example.xxx.xxx.MainActivity"
  android:orientation="vertical">
  <TextView android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:paddingBottom="10dp"
    android:textAppearance="?android:attr/textAppearanceSmall"
    android:text="Рекомендуемая скорость:"
    android:textSize="20sp"
    android:textStyle="bold"
    android:id="@+id/jsonData"
    android:textColor="#000000" />
  <TextView android:layout_width="900px"
    android:layout_height="900px"
    android:layout_gravity="center_horizontal"
    android:paddingTop="10dp"
    android:id="@+id/recommendedSpeed"
    android:textSize="120sp"
    android:textColor="#000000"
    android:textStyle="bold"
    android:gravity="center"
    android:background="@drawable/gebodsbord"/>
</LinearLayout>

Схемы

Передатчик:
RF 433МГц: tx_pin → pin 3
HC-SR04: trig_pin → pin 5, echo_pin → pin 6

Умная система управления светофором с реальными данными

Приёмник (Arduino Yun):
RF 433МГц: rx_pin → pin 3
Светофор: RedLED → pin 5, OrangeLED → pin 6, GreenLED → pin 7
DS3231 RTC: SCL → SCL (pin 3), SDA → SDA (pin 2)

Умная система управления светофором с реальными данными

Производственный процесс

  1. Газовые фонари: эволюция, дизайн и современное производство
  2. Лазерный указатель: дизайн, производство и обзор безопасности
  3. Калейдоскоп: от науки Брюстера к современному дизайну
  4. Светофоры: инженерия, история и будущие инновации
  5. Светодиоды (LED): от зарождения к 100-летнему ресурсу
  6. Разборка умной лампы Sengled: технический разбор и обзор Zigbee-подключения
  7. Проектирование высокоточных Continuous-Wave ToF систем: комплексный обзор
  8. Умный светофор: адаптивное уличное освещение на базе IoT
  9. ИИ-управляемая система дальнего света улучшает ночное зрение и предотвращает слепоту
  10. Освоение аксессуаров для LED лент: подключение, управление и кастомизация