SOM (自己組織化マップ) 作ってみた。
ここら辺を参考に。
#ifdef _DEBUG //Debugモードの場合 #pragma comment(lib,"C:\\opencv\\build\\x86\\vc12\\lib\\opencv_world300d.lib") // opencv_core #else //Releaseモードの場合 #pragma comment(lib,"C:\\opencv\\build\\x86\\vc12\\lib\\opencv_world300.lib") #endif #include <iostream> #include <vector> #include <stdlib.h> #include "opencv2/opencv.hpp" #include "opencv2/core.hpp" #include "opencv2/highgui.hpp" #include <omp.h> using namespace std; using namespace cv; class Node{ public : vector<double> param; Node(){ param = vector<double>(3); } }; class SomLearn{ int learning_range; double learning_rate; public: vector< vector<Node> > map; SomLearn(int x, int y){ map = vector< vector<Node> >(x, vector<Node>(y)); InitMap(); } int InitMap(){ srand(10); for (int i = 0; i < map.size(); ++i){ for (int j = 0; j < map[0].size(); ++j){ for (int k = 0; k < map[0][0].param.size(); ++k){ map[i][j].param[k] = double(rand() / double(RAND_MAX) * 255.0); } } } return 0; } double GetEuqridDinstance(Node data1, Node data2){ double n = 0; for (int i = 0; i < data1.param.size(); ++i){ n += (data2.param[i] - data1.param[i]) * (data2.param[i] - data1.param[i]); } return n; } int GetMinIndex(Node data, int &i_index, int &j_index){ double min_val = 0; double temp = 0; i_index = 0; j_index = 0; min_val = GetEuqridDinstance(data, map[0][0]); for (int i = 0; i < map.size(); ++i){ for (int j = 0; j < map[0].size(); ++j){ temp = GetEuqridDinstance(data, map[i][j]); if (min_val > temp){ min_val = temp; i_index = i; j_index = j; } } } return 0; } int Exposure(Node data){ int i_index = 0; int j_index = 0; GetMinIndex(data, i_index, j_index); int i_small = i_index - learning_range; int i_big = i_index + 1 + learning_range; int j_small = j_index - learning_range; int j_big = j_index + 1 + learning_range; if (i_small < 0){ i_small = 0; } if (map.size() < i_big){ i_big = map.size(); } if (j_small < 0){ j_small = 0; } if (map[0].size()< j_big){ j_big = map[0].size(); } for (int i = i_small; i < i_big; ++i){ for (int j = j_small; j < j_big; ++j){ for (int k = 0; k < data.param.size(); ++k){ map[i][j].param[k] += (data.param[k] - map[i][j].param[k]) * learning_rate; } } } return 0; } int Train(vector<Node> &node, int learning_range_in, double learning_rate_in){ learning_range = learning_range_in; learning_rate = learning_rate_in; for (int i = 0; i < node.size(); ++i){ Exposure(node[i]); } return 0; } }; int main(){ vector<Node> node(100); srand(1000); for (int i = 0; i < node.size(); ++i){ for (int k = 0; k < node[0].param.size(); ++k){ node[i].param[k] = double(rand()) / double(RAND_MAX) * 255.0; } } int x = 30; int y = 30; int lrange = 6; double lrate = 0.01; SomLearn som(x, y); Mat src(Size(som.map.size(), som.map[0].size()), CV_8UC3, Scalar::all(0)); Mat dst(Size(som.map.size(), som.map[0].size()), CV_8UC3, Scalar::all(0)); for (int i = 0; i < som.map.size(); ++i){ for (int j = 0; j < som.map[0].size(); ++j){ for (int k = 0; k < som.map[0][0].param.size(); ++k){ src.at<Vec3b>(Point(i, j))[k] = som.map[i][j].param[k]; } } } som.Train(node, lrange, lrate); for (int i = 0; i < som.map.size(); ++i){ for (int j = 0; j < som.map[0].size(); ++j){ for (int k = 0; k < som.map[0][0].param.size(); ++k){ dst.at<Vec3b>(Point(i, j))[k] = som.map[i][j].param[k]; } } } //resize(src, src, Size(), 25, 25); //resize(dst, dst, Size(), 25, 25); imshow("src", src); imshow("dst", dst); waitKey(0); return 0; }
voronoi図を作ってみた。
ここのアルゴリズムを参考にして作りました。
voronoifield.hpp
#ifndef VORONOIFIELD_HPP #define VORONOIFIELD_HPP #include "opencv2/opencv.hpp" #include <vector> #include <omp.h> class Voronoi{ double CalcDistance(cv::Point a, cv::Point b){ return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); } void CellProcessing(cv::Point center, cv::Point side){ if ((side.x < 0) || (image_size.x <= side.x) || (side.y < 0) || (image_size.y <= side.y)){ return; } if (cells[side.x][side.y].flag == MOTHER){ if (cells[center.x][center.y].flag == NONE){ cells[center.x][center.y].flag = DETECTED; cells[center.x][center.y].distance = CalcDistance(center, side); cells[center.x][center.y].point_mother = side; } else if (cells[center.x][center.y].flag == DETECTED){ if (cells[center.x][center.y].distance > CalcDistance(center, side)){ cells[center.x][center.y].distance = CalcDistance(center, side); cells[center.x][center.y].point_mother = side; } } } else if (cells[side.x][side.y].flag == DETECTED){ if (cells[center.x][center.y].flag == NONE){ cells[center.x][center.y].flag = DETECTED; cells[center.x][center.y].distance = CalcDistance(center, cells[side.x][side.y].point_mother); cells[center.x][center.y].point_mother = cells[side.x][side.y].point_mother; } else if (cells[center.x][center.y].flag == DETECTED){ if (cells[center.x][center.y].distance > CalcDistance(center, cells[side.x][side.y].point_mother)){ cells[center.x][center.y].distance = CalcDistance(center, cells[side.x][side.y].point_mother); cells[center.x][center.y].point_mother = cells[side.x][side.y].point_mother; } } } } public: class cell_info{ public: int flag; cv::Point point_mother; double distance; cell_info(){ flag = 0; point_mother = cv::Point(-1, -1); distance = 0; } }; cv::Point image_size; double max_distance; std::vector<std::vector<cell_info> > cells; int NONE; int MOTHER; int DETECTED; Voronoi(){ NONE = 0; MOTHER = 1; DETECTED = -1; } Voronoi(cv::Point size){ NONE = 0; MOTHER = 1; DETECTED = -1; SetMap(size); } void SetMap(cv::Point size){ image_size = size; cells = std::vector<std::vector<cell_info> >(image_size.x, std::vector<cell_info>(image_size.y)); max_distance = CalcDistance(cv::Point(0, 0), size); } void SetPoints(std::vector<cv::Point> points){ for (std::vector<cv::Point>::iterator itr = points.begin(); itr < points.end(); ++itr){ if ((itr->x < 0) || (image_size.x <= itr->x) || (itr->y < 0) || (image_size.y <= itr->y)){ return; } cells[itr->x][itr->y].flag = MOTHER; } } void CreateVoronoi(){ for (int k = image_size.x; k >= 1; k /= 2){ double naname = sqrt(k*k + k* k); #pragma omp parallel for for (int i = 0; i < image_size.x; ++i){ for (int j = 0; j < image_size.y; ++j){ CellProcessing(cv::Point(i, j), cv::Point(i - k, j - k)); CellProcessing(cv::Point(i, j), cv::Point(i + k, j - k)); CellProcessing(cv::Point(i, j), cv::Point(i - k, j + k)); CellProcessing(cv::Point(i, j), cv::Point(i + k, j + k)); CellProcessing(cv::Point(i, j), cv::Point(i, j - k)); CellProcessing(cv::Point(i, j), cv::Point(i, j + k)); CellProcessing(cv::Point(i, j), cv::Point(i - k, j)); CellProcessing(cv::Point(i, j), cv::Point(i + k, j)); } } } } void GetImage(cv::Mat &image){ #pragma omp parallel for for (int i = 0; i < image_size.x; ++i){ for (int j = 0; j < image_size.y; ++j){ image.at<unsigned char>(cv::Point(i, j)) = cells[i][j].distance * (512 / max_distance); } } } void GetData(cv::Mat &image, double max_d){ #pragma omp parallel for for (int i = 0; i < image_size.x; ++i){ for (int j = 0; j < image_size.y; ++j){ image.at<double>(cv::Point(i, j)) = cells[i][j].distance / max_d; } } } }; #endif
main.cpp
#include "voronoifield.hpp" /*標準入出力ライブラリ*/ #include <iostream> /*可変長リストライブラリ*/ #include <vector> /*OpenCVライブラリ*/ #include "opencv2/opencv.hpp" #include "opencv2/highgui/highgui_c.h" /*処理時間測定用ライブラリ*/ #include <time.h> #include <omp.h> using namespace std; using namespace cv; int main() { int i = 2; Voronoi voronoi(Point(128 * i, 128 * i)); //vector<Point> points(7); //points[0] = Point(32 * i, 32 * i); //points[1] = Point(20 * i, 10 * i); //points[2] = Point(4 * i, 50 * i); //points[3] = Point(50 * i, 33 * 8); //points[4] = Point(100 * i, 100 * i); //points[5] = Point(100 * i, 10 * i); //points[6] = Point(10 * i, 100 * i); vector<Point> points; for (int x = 100; x < 140; ++x){ for (int y = 100; y < 130; ++y){ points.push_back(Point(x, y)); } } for (int x = 10; x < 14; ++x){ for (int y = 10; y < 13; ++y){ points.push_back(Point(x, y)); } } voronoi.SetPoints(points); voronoi.CreateVoronoi(); Mat image = Mat(voronoi.image_size, CV_8UC1, Scalar::all(0)); voronoi.GetImage(image); imshow("voronoi", image); waitKey(0); return 0; }
vimの実際の使い方
vimを始めるうえでコマンド覚えるより大切なことかもしれない
おまけ
Arduinoで外部割込みとシリアル割り込み
・外部割込み
- Arduino の attachInterrupt() での割り込み処理
- Arduinoの概要
- 割り込みと ISR - 基礎からの IoT 入門
- Arduino入門:外部割り込み | easy labo
簡単なスケッチを
#define INT_PIN 2 void gpio_isr(){ Serial.println("warikomi"); } void setup() { Serial.begin(115200); pinMode(INT_PIN, INPUT); // 割り込みのためのピンを設定 attachInterrupt(digitalPinToInterrupt(INT_PIN), gpio_isr, FALLING); // 割り込み開始 } void loop(){ }
pinMode()で割り込みに使うピンを入力に設定
attachInterrupt() で割り込みに使うピンとコールバック関数とピンの状態がどういうときに割り込みを発生させるかを設定。
ピンは普通のピン番号と割り込みに使うピン番号は違うらしいのでdigitalPinToInterrupt() で変換
・シリアル割り込み
void serialEvent() { } という名前でやりたい処理の関数を定義します。
ただ、割り込みと言っても loop()関数が一周したあとにserialEvent() が実行されているだけなので、割り込みっぽくないです。
ArduinoとEthernetシールドを使ってTCP通信
ArduinoとEthernetシールドの互換品を使ってTCP通信をします。
今はEthernetシールド2というものがあるらしいですが、今回使用するのは1の方だと思います。(たぶん)
Ethernetシールド2の場合、使うライブラリやArduinoIDEのバージョンが変わってくるので注意してください。
ちなみに純正品はもう1も2も生産していないらしいです。
使ったシールドはこれ
ここを参考に。
- Ethernet Shield
- Arduinoリファレンス(Ethernet)
- Arduino - Ethernet
- TCP通信でPCからLED(on Arduino)の点滅を制御する
- ArduinoからLEDN41(LEDネットワークディスプレイ)を表示させてみた - テクノベインズ ブログ
- Arduino と Ethernet シールドを用いた LED 遠隔操作アプリの作成 - 基礎からの IoT 入門
- HTTP通信でArduinoを操作する(Ethernetシールド使用)
- イーサーネットシールド2 (Ethernet Shield 2)
- ブラウザからArduino Unoを制御│レーザー加工機・レーザーカッターのsmartDIYs
簡単なスケッチを。
#include <SPI.h> #include <Ethernet.h> // Enter a MAC address and IP address for your controller below. The IP address will be dependent on your local network: byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; IPAddress ip(192, 168, 1, 177); // Initialize the Ethernet server library with the IP address and port you want to use (port 80 is default for HTTP): EthernetServer server(19227); EthernetClient client; char eth_recv_buf[128]; char eth_send_buf[37] = "M SD8 124 01 23 45 67 89 AB CD EF \r\n"; char eth_recv_len; char eth_send_len; void EthWrite(){ if (Serial.available()){ one_char = Serial.read(); if (one_char == 'M'){ serial_itr = 0; } serial_recv_buf[serial_itr] = one_char; if ((one_char == '\n') && (serial_itr == 35)){ client.write(serial_recv_buf, 36); } if ((++serial_itr) > 36) { serial_itr = 0; } } } void setup() { // Open serial communications and wait for port to open: Serial.begin(115200); while (!Serial) { ; } Ethernet.init(10); // Most Arduino shields Ethernet.begin(mac, ip); if (Ethernet.hardwareStatus() == EthernetNoHardware) { while (true) { delay(1); // do nothing, no point running without Ethernet hardware } } // start the server server.begin(); // listen相当 pinMode(8, OUTPUT); } char c; void loop() { // listen for incoming clients client = server.available(); // accept相当 if (client) { while (client.connected()) { if (eth_recv_len = client.available()) { digitalWrite(8, HIGH); c = client.read(eth_recv_buf, eth_recv_len); Serial.write(eth_recv_buf, eth_recv_len); } EthWrite(); digitalWrite(8, LOW); } delay(100); client.stop(); } }
Ethernet.init() はイーサネットシールドとのSPI通信につかうCSピンです。
server.available() でクライアントの接続を待ちます。
client.available() で受信したByte数を取得してそのByte数だけclient.read()で読み出します。
その他参考
- Arduino - EthernetServer
- やっと解決!Arduino Ethernet Shield2(シールド2)通信!
- Arduino イーサネットシールド2を使ってみた。
- 海外通販でArduino用Ethernetシールドを買った… - 記憶は人なり
- Arduinoを使ってWebサーバーをつくろう! | Device Plus - デバプラ
- WhiteBox
- ここも参考になるのかな?
- RXduino: クラス EthernetClient
- RXduino: クラス EthernetServer
TCPとUDPの違い
・TCP
送信したパケットは勝手に分割されて適当に結合されて受信される(データの境界保証なし)
つまり1回send() ≠1回recv()
- はてなブログ
- Socket通信でパケットを意図的に分割する: DOBON.NETプログラミング掲示板過去ログ
- ソケット通信メモ(Hishidama's TCP/UDP Socket Memo)
- python — Pythonソケットは大量のデータを受信します
送信したデータが受信されないと送信側はブロッキングする。
つまり、通信状況が悪いと送信タイミングのリアルタイム性が損なわれる。
・UDP
送信したパケットは勝手に分割されないが、あまりデータが大きいとIPフラグメンテーションされる可能性がある。(パケット分割とIPフラグメントは別物らしい)
その場合受信側でデータがすべて出揃うまで受信を完了しない。(データの境界保証あり)
つまり1回send() =1回recv()
また、データがいつまでたっても揃わなかったらそのデータは破棄される。
send()したデータの順番とrecv()したデータの順番が入れ替わる可能性がある。
送信側は受信側がちゃんと受信できたか確認しないで構わず送信し続けるため、
通信状況が悪くても送信タイミングのリアルタイム性が損なわれない。
- TCPとUDPの違いを10個挙げてみる - ikeas's blog
- 基礎から学ぶWindowsネットワーク - @IT
- UDP を使ってみよう (5)
- 第14回 TCPとUDP | 日経クロステック(xTECH)