工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Manufacturing Technology >> 製造プロセス

リアルタイムデータを活用したスマート信号機制御システム

部品と材料

部品 数量 備考
Arduino Yun 1 メイン制御ボード(Linux搭載)
Arduino UNO 1 送信側用(他のArduinoボードでも可)
超音波距離センサー HC-SR04 1 車両と信号機の距離測定用
DS3231 RTCモジュール 1 リアルタイムクロック(高精度時計)
LED(赤・黄・緑) 各1 信号機表示用、330Ω抵抗とセットで使用
抵抗 330Ω 3 LED電流制限用
433MHz RFモジュール(送信機・受信機) 1セット Arduino間の無線通信用
Androidデバイス 1 ドライバー向けアプリ実行用
SDカード 1 PHPスクリプト格納用(Arduino Yun搭載スロット)
AllThingsTalk IOTOPIA 開発キット 1 オプション
ジャンパーワイヤー 1セット 配線用

アプリとオンラインサービス

プロジェクト概要

イントロダクション

渋滞は誰もがイライラさせられる問題であり、年々深刻化しています。もしインテリジェントな信号機によってこの問題を解決できたらどうでしょうか? 私たちは「信号機情報システム」を開発しました。このシステムは、ドライバーが交差点で青信号を逃さず通過できる最適な速度を提示します(法定速度を超えない範囲で)。また、やむを得ず赤信号で停止する場合には、青信号に変わるまでの残り待ち時間を表示します。

リアルタイムデータを活用したスマート信号機制御システム

信号機は「青信号の継続時間」や「次回青信号までの時間」などのデータをオンラインデータベースに送信します。スマートフォンのGPSやアプリがデータベースから情報を取得し、青信号通過に必要な速度を計算して表示します。赤信号で待たざるを得ない場合は、青信号までのカウントダウンタイマーを表示します。

動作例

制限速度70km/hの道路を走行中、前方の信号機が赤で、30秒後に青に変わるとします。あなたが信号機から500m手前にいる場合、システムは「60km/hで進行すれば青信号で通過できる」と推奨します。

リアルタイムデータを活用したスマート信号機制御システム

プロジェクトデモンストレーション

システム構成と仕組み

デモンストレーション用に、Arduino Yunを用いた信号機モデルを作成しました。3つのLEDとRTCで構成され、設定されたインターバルで信号が切り替わります。Arduino Yunは青/赤信号の切り替わり時刻(Unixタイムスタンプ)を計算し、SDカード上のPHPスクリプトを実行してMySQLデータベースにデータを書き込みます。

Androidアプリ(Android Studioで開発)は、別のPHPスクリプト経由でJSON形式のデータを取得し、最適速度を計算します。青信号通過が不可能な場合はカウントダウンタイマーを表示します。

リアルタイムデータを活用したスマート信号機制御システム

最適速度の計算には、車両と信号機の距離が必要です。本来はGPS座標から距離を算出しますが、スマートフォンのGPS精度では小規模な実証実験には不十分です。そこで、超音波測距モジュール(HC-SR04)を用い、433MHz RFモジュールでArduino Yunに距離データを送信する方式を採用しました。Yunは新しい測定値を受信するたびにデータベースを更新し、アプリは常に最新の距離情報を用いて計算を行います。

リアルタイムデータを活用したスマート信号機制御システム

実装手順

  1. オンラインMySQLデータベースの作成
    (本プロジェクトでは freemysqlhosting.net を使用)
  2. テーブル 'TrafficL_data' の作成
    スキーマはプロジェクトページの図を参照してください。
  3. 送信側ハードウェアの組み立て
    回路図「Arduino transmitter」に従って配線します。
  4. 送信側Arduinoへスケッチ書き込み
    • 超音波センサーで距離を測定
    • 433MHz送信機でデータ送信
  5. Arduino YunをWi-Fiネットワークに接続
  6. Arduino YunのLinuxサーバーをPuttyで操作し、必要なパッケージをインストール
    opkg update
    opkg install php5-mod-mysqli
    opkg install php5-cli
    nano /mnt/sda1/MySQL_UpdateTrafficLData.php
    

    '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)の組み立て
    回路図「Arduino Yun receiver」に従って配線します。
  8. 受信側スケッチ 'Arduino Receiver & MySQLdb' を書き込み
  9. シリアルモニターで動作確認
    正常に動作していれば、信号切り替え時刻と距離データが表示されます。
  10. Android Studioプロジェクトのセットアップ
    • activity_main.xml
    • MainActivity.java(URLを自分の環境に変更)
    • AndroidManifest.xml
    • strings.xml
    • EchoJSON.php をサーバーに配置
  11. スマートフォンにアプリをインストール

ESP8266を用いた低コスト実装(追加情報)

より安価なESP8266ベースのモジュール(NodeMCU等)でArduino Yunを置き換えることも可能です。手順の概要は以下の通りです。

  1. 000webhost.com でアカウント作成・ウェブサイト作成・データベース作成
  2. CreateTable.php と PostDemo.php をファイルマネージャーでアップロードし、DB接続情報を設定
  3. CreateTable.php にアクセスしてテーブル作成
  4. Arduino IDEにESP8266ボードパッケージを追加(URL: https://arduino.esp8266.com/stable/package_esp8266com_index.json)
  5. ESP8266PostDemo.ino を書き込み(10秒ごとにランダム値でDB更新するデモコード)
  6. 動作確認後、Arduino Yun側の MySQL_UpdateTrafficLData() と MySQL_UpdateDistance() をESP8266用に書き換え

注意: 一度に全てを実装せず、各コンポーネントを個別にテストしてから統合することを強く推奨します。質問はコメント欄で詳細に記述してください。

ソースコード

Arduino送信側スケッチ

/* IOTOPIA - 2016-2017 - Traffic Light Information System (Transmitter)
 * by Pieter Luyten & Joppe Smeets
 * RF 433MHzモジュールで距離データを別Arduinoへ送信
 * RF tx_pin -> pin 3
 * HC-SR04: 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受信側スケッチ(Yun用・MySQL連携)

/* IOTOPIA - 2016-2017 - Traffic Light Information System (Receiver + MySQL)
 * by Pieter Luyten & Joppe Smeets
 * RF受信・信号機制御・MySQLアップロード
 * RF rx_pin -> pin 9 (注: コメントと実装でpin番号が異なるため要確認)
 * LED: Red=5, Orange=6, Green=7
 * DS3231: SCL=pin3, SDA=pin2 (Yun)
 * SDカード: Yun内蔵スロット
 */
#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) Serial.print(p.read());
  Serial.flush();
}

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

void PrintCurrentTime() {
  DateTime now = rtc.now();
  SetLEDsTrafficL();
  Serial.print(now.hour()); Serial.print(':');
  Serial.print(now.minute()); Serial.print(':');
  Serial.print(now.second()); 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 (SDカード上で実行)

#!/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('DB 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('DB 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 (Androidアプリ用JSON出力)

<?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 (Androidアプリメインロジック)

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; // cm/s
  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(() -> 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,
      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(); }
      },
      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();
    recSpeed.setText(String.format("%.0f", speed));
  }

  private void speedCalc() {
    int interval = 0;
    double time = System.currentTimeMillis() / 1000.0;
    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 *= 5; // 表示用スケーリング
  }
}

AndroidManifest.xml / strings.xml / activity_main.xml

プロジェクトページのコードセクションを参照してください。strings.xml のアプリ名は「NooitRood」(オランダ語で「Never Red=赤信号なし」の意)です。

回路図

送信側 (Arduino UNO + HC-SR04 + 433MHz送信機)

  • 433MHz tx_pin → pin 3
  • HC-SR04 trig_pin → pin 5, echo_pin → pin 6

受信側 (Arduino Yun + 433MHz受信機 + LED信号機 + DS3231)

  • 433MHz rx_pin → pin 9
  • LED: Red→pin5, Orange→pin6, Green→pin7
  • DS3231: SCL→pin3, SDA→pin2 (Yun)

製造プロセス

  1. ガスランタン:進化、設計、現代製造
  2. レーザーポインター:設計、製造、安全性の概要
  3. 万華鏡:ブレッサーの科学から現代デザインへ
  4. 信号機:工学、歴史、未来の革新
  5. 発光ダイオード(LED):誕生から100年寿命へ
  6. スマート信号機:IoTで実現する適応型街路灯
  7. AI制御ハイビームで夜間視界向上と眩惑防止を両立
  8. LEDストリップアクセサリー完全ガイド:接続、制御、カスタマイズ
  9. ロボット溶接システム導入ガイド:製造業者必見の投資判断ポイント
  10. 高精度連続波ToFシステムの設計:包括的な概要
  11. SengledスマートLED電球の分解調査:Zigbee接続の技術的レビュー