aa develop

開発と成長

PythonからArduinoにシリアル通信でデータを送る

Pythonからシリアル通信でArduinoにデータを送り、LEDの明るさを制御する。 Arduinoには、D13とGRDにLEDを接続する。

Pythonでのシリアル通信には、pySerialライブラリが必要。

$ pip install pyserial

以下、コード。 PythonからArduinoに数値を送るときは、chr()で変換する。

Arduino


int v = 0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(13, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  if(Serial.available() > 0){
    v = Serial.read();
  }
  int ms = map(v, 0, 255, 0, 10);
  digitalWrite(13, HIGH);
  delay(10 - ms);
  digitalWrite(13, LOW);
  delay(ms);
}

Python


#! -*- coding: utf-8 -*-

import serial
import time

ser = serial.Serial('/dev/tty.usbmodem1411', 9600)

v = 0
vc = 1

while True:
    v += vc
    if v > 255:
        v = 0
    print v
    ser.write(chr(v))

    time.sleep(0.02)

Museで取得した脳波に基づき,ArduinoでLEDを光らせる

脳波ヘッドバンドのMuseで脳波から集中度を所得し,その値をProcessingでArduinoに送ってLEDの明るさを変化させる. MuseはPCとBluetooth接続し,ターミナルから以下のコマンドを実行する.

$ muse-io --device Muse-XXXX --osc osc.udp://localhost:5000

LEDはArduinoのGRDとD13に接続する.

Processing

/**
*
* Before running this program,you need to launch 
* MuseIO by below command in your terminal.
* 
* $ muse-io --device Muse-XXXX --osc osc.udp://localhost:5000
*/


import processing.serial.*;
import oscP5.*;

OscP5 oscP5;
Serial serial;

float concentration = 0;

void setup(){
  size(300, 300);
  noStroke();
  fill(255, 20, 147);
  oscP5 = new OscP5(this, 5000);
  serial = new Serial(this, "/dev/tty.usbmodem1411", 9600);
}

void draw(){
  background(64);
  float radious = map(concentration, 0, 1, 50, 300);
  ellipse(width/2, height/2, radious, radious);
  println(concentration);
  serial.write(int(map(concentration, 0, 1, 0, 255)));
}

void oscEvent(OscMessage msg){
  if(msg.checkAddrPattern("/muse/elements/experimental/concentration")){
    concentration = msg.get(0).floatValue();  
  }
}
Arduino


int v = 0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(13, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  if(Serial.available() > 0){
    v = Serial.read();
  }
  int ms = map(v, 0, 255, 0, 10);
  digitalWrite(13, HIGH);
  delay(ms);
  digitalWrite(13, LOW);
  delay(10 - ms);
}

Arduino/signal_from_muse_to_arduino_via_processing at master · aa-debdeb/Arduino

シリアル通信でProcessingからArduinoに信号を送る

Processingでマウスのx座標を取得して,その値をシリアル通信でArduinoに送り,LEDの明るさとして表示する. ArduinoのGRDとD13にLEDを接続する.

Processing

import processing.serial.*;

Serial serial;

void setup(){
  size(500,200);
  stroke(128);
  strokeWeight(1);
  serial = new Serial(this, "/dev/tty.usbmodem1411", 9600);
}

void draw(){
  background(255);
  line(mouseX, 0, mouseX, height);
  serial.write(int(map(mouseX, 0, width, 0, 255)));
}
Arduino


int v = 0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(13, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  if(Serial.available() > 0){
    v = Serial.read();
  }
  int ms = map(v, 0, 255, 0, 10);
  digitalWrite(13, HIGH);
  delay(10 - ms);
  digitalWrite(13, LOW);
  delay(ms);
}

Arduino/signal_from_processing_to_arduino at master · aa-debdeb/Arduino

シリアル通信でArduinoからProcessingに信号を送る

シリアル通信でArduinoに接続した可変抵抗器の値をProcessingに送り,Processing上で円の大きさとして可視化する. 可変抵抗器はGRDと5V,A0に接続する.

Processing

import processing.serial.*;

Serial serial;

float diameter = 0;

void setup(){
  size(300, 300);
  noStroke();
  fill(255, 20, 147);
  
  serial = new Serial(this, "/dev/tty.usbmodem1411", 9600);
}

void draw(){
  background(64);
  ellipse(width/2, height/2, diameter, diameter);
}

void serialEvent(Serial p){
  int v = p.read();
  diameter = map(v, 0, 255, 50, 300);
  println(v, diameter);
}
Arduino

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  Serial.write(map(analogRead(A0), 0, 1023, 0, 255));
}

Arduino/signal_from_arduino_to_processing at master · aa-debdeb/Arduino

Arduinoで可変抵抗器により圧電スピーカーの音の高さを制御する

Arduinoで可変抵抗器により,圧電スピーカーから出力する音の高さを制御する.可変抵抗器の左右をGRDと5Vに,真ん中をA0に接続する.スピーカーはGRDとD12に接続する.


#define DX 12

void mtone(int dx, int hz, unsigned long tm){
  unsigned long t = millis();
  unsigned long ns = (long)500000 / hz;
  while(millis() - t < tm){
    digitalWrite(dx, HIGH);
    delayMicroseconds(ns);
    digitalWrite(dx, LOW);
    delayMicroseconds(ns);  
  } 
}

void setup() {
  // put your setup code here, to run once:
  pinMode(DX, OUTPUT);
}

void loop(){
  float hz = map(analogRead(A0), 0, 1023, 0, 8000);
  mtone(DX, hz, 50);
}

Arduino/sound_test at master · aa-debdeb/Arduino

Museで取得した脳波データをProcessingで可視化する

Museという瞑想用の脳波計がある. MUSE ™ | Meditation Made Easy

このMuseiOSAndroid用のSDKが公開されている他,MacWindows向けにResearch Toolsが提供されている. Muse: the brain sensing headband Developer Portal

今回は,Mac用のResearch Toolsを用いて,脳波データをProcessingで可視化するプログラムを作成した. プログラムを走らせる前に,PCとMuseBluetoothで繋いで,ターミナルから以下のコマンドでMuseIOを実行する必要がある.


muse-io --device Muse-XXXX --osc osc.udp://localhost:5000

f:id:aa_debdeb:20160714113319j:plain

基本的には,OSC(Open Sound Control)プロトコルが使えるプログラミング言語なら,どれでもデータを扱えるよう.


/**
* Muse Visualizer
*
* @author aa_debdeb
* @date 2015/12/21
*
* Before running this program,you need to launch 
* MuseIO by below command in your terminal.
* 
* $ muse-io --device Muse-XXXX --osc osc.udp://localhost:5000
*
*/

import oscP5.*;

OscP5 oscP5;
color bgColor = color(32);
color baseColor = color(128);
color[] chColor = {color(0, 139,  139), color(50, 205, 50), color(255, 140, 0), color(199, 21, 133)};


ArrayList[] rawEEG;
float[] accelerometer;
float[][] rawFFT;
float[] lowFreqAbs;
float[] deltaAbs;
float[] thetaAbs;
float[] alphaAbs;
float[] betaAbs;
float[] gammaAbs;

int blink;
int jawClench;

float concentration;
float mellow;

void setup(){
  size(800, 600);
  smooth();
  frameRate(60);
  textSize(10);
  textAlign(CENTER);
  
  oscP5 = new OscP5(this, 5000);
  
  rawEEG = new ArrayList[4];
  for(int i = 0; i < 4; i++){
    rawEEG[i] = new ArrayList();
    for(int j = 0; j < 300; j++){
      rawEEG[i].add(1600.0);
    }
  }
  accelerometer = new float[3];
  rawFFT = new float[4][129];
  lowFreqAbs = new float[4];
  deltaAbs = new float[4];
  thetaAbs = new float[4];
  alphaAbs = new float[4];
  betaAbs = new float[4];
  gammaAbs = new float[4];
  blink = 0;
  jawClench = 0;
  concentration = 0.0;
  mellow = 0.0;
}

void draw(){

  background(bgColor);
  noFill();
  stroke(chColor[0]);
  drawRawEEG(rawEEG[0], 25, 25, 362.5, 50);
  stroke(chColor[1]);
  drawRawEEG(rawEEG[1], 412.5, 25, 362.5, 50);
  stroke(chColor[2]);
  drawRawEEG(rawEEG[2], 25, 175, 362.5, 50);
  stroke(chColor[3]);
  drawRawEEG(rawEEG[3], 412.5, 175, 362.5, 50);
  
  noStroke();
  fill(chColor[0]);
  drawRawFFT(rawFFT[0], 25, 100, 362.5, 50);
  fill(chColor[1]);
  drawRawFFT(rawFFT[1], 412.5, 100, 362.5, 50);
  fill(chColor[2]);
  drawRawFFT(rawFFT[2], 25, 250, 362.5, 50);
  fill(chColor[3]);
  drawRawFFT(rawFFT[3], 412.5, 250, 362.5, 50);
  
  drawAbsBandPower(lowFreqAbs, 25, 350, 115, 100);
  drawAbsBandPower(deltaAbs, 152, 350, 115, 100);
  drawAbsBandPower(thetaAbs, 279, 350, 115, 100);
  drawAbsBandPower(alphaAbs, 406, 350, 115, 100);
  drawAbsBandPower(betaAbs, 533, 350, 115, 100);
  drawAbsBandPower(gammaAbs, 660, 350, 115, 100);
  fill(baseColor);
  text("low freqs(1~8Hz)", 25, 340, 115, 100);
  text("delta(1~4Hz)", 152, 340, 115, 100);
  text("theta(5~8Hz)", 279, 340, 115, 100);
  text("alpha(9~13Hz)", 406, 340, 115, 100);
  text("beta(12~30Hz)", 533, 340, 115, 100);
  text("gamma(30~50Hz)", 660, 340, 115, 100);

  fill(baseColor);
  text("blink", 0, 470, 200, 100);
  text("jaw clench", 200, 470, 200, 100);
  text("concentration", 400, 470, 200, 100);
  text("mellow", 600, 470, 200, 100);
  drawMuscleMovement(blink, 100, 530, 50);
  drawMuscleMovement(jawClench, 300, 530, 50);
  drawExperimental(concentration, 500, 530, 50);
  drawExperimental(mellow, 700, 530, 50);
}

void drawRawEEG(ArrayList data, float x, float y, float w, float h){
  pushMatrix();
  translate(x, y);
  beginShape();
  for(int i = 0; i < data.size(); i++){
    vertex(map(i, 0, data.size(), 0, w), map(data.get(i), 0, 1682.815, h, 0));
  }
  endShape();
  popMatrix();
}

void drawRawFFT(float[] data, float x, float y, float w, float h){
  pushMatrix();
  translate(x, y);
  float binW = w / data.length;
  for(int i = 0; i < data.length; i++){
    float binH = map(data[i], -40.0, 20.0, 0, h);
    rect(i * binW, h - binH, binW, binH);
  }
  popMatrix();
}

void drawAbsBandPower(float[] data, float x, float y, float w, float h){
  pushMatrix();
  translate(x, y);
  float binW = w / data.length;
  for(int i = 0; i < data.length; i++){
    fill(chColor[i]);
    float binH = map(data[i], -2.5, 2.5, 0, h);
    rect(i * binW, h - binH, binW, binH);
  }
  popMatrix();
}

void drawMuscleMovement(int data, float x, float y, float radious){
  float diameter = data == 1 ? radious * 2 * 0.8 : radious * 2 * 0.2;
  ellipse(x, y, diameter, diameter);  
}

void drawExperimental(float data, float x, float y, float radious){
  float diameter = radious * 2 * data;
  ellipse(x, y, diameter, diameter);  
}

void oscEvent(OscMessage msg){
  
  if(msg.checkAddrPattern("/muse/eeg")){
    for(int i = 0; i < 4; i++){
      rawEEG[i].remove(0);
      rawEEG[i].add(msg.get(i).floatValue());
    }
  }
  
  if(msg.checkAddrPattern("/muse/elements/raw_fft0")){
    for(int j = 0; j < 129; j++){
      rawFFT[0][j] = msg.get(j).floatValue();
    }
  }
  if(msg.checkAddrPattern("/muse/elements/raw_fft1")){
    for(int j = 0; j < 129; j++){
      rawFFT[1][j] = msg.get(j).floatValue();
    }
  }
  if(msg.checkAddrPattern("/muse/elements/raw_fft2")){
    for(int j = 0; j < 129; j++){
      rawFFT[2][j] = msg.get(j).floatValue();
    }
  }
  if(msg.checkAddrPattern("/muse/elements/raw_fft3")){
    for(int j = 0; j < 129; j++){
      rawFFT[3][j] = msg.get(j).floatValue();
    }
  }
  
  if(msg.checkAddrPattern("/muse/elements/low_freqs_absolute")){
    for(int i = 0; i < 4; i++){
      lowFreqAbs[i] = msg.get(i).floatValue();
    }
  }  
  if(msg.checkAddrPattern("/muse/elements/low_freqs_absolute")){
    for(int i = 0; i < 4; i++){
      lowFreqAbs[i] = msg.get(i).floatValue();
    }
  }  
  if(msg.checkAddrPattern("/muse/elements/delta_absolute")){
    for(int i = 0; i < 4; i++){
      deltaAbs[i] = msg.get(i).floatValue();
    }
  }  
  if(msg.checkAddrPattern("/muse/elements/theta_absolute")){
    for(int i = 0; i < 4; i++){
      thetaAbs[i] = msg.get(i).floatValue();
    }
  }  
  if(msg.checkAddrPattern("/muse/elements/alpha_absolute")){
    for(int i = 0; i < 4; i++){
      alphaAbs[i] = msg.get(i).floatValue();
    }
  }    
  if(msg.checkAddrPattern("/muse/elements/beta_absolute")){
    for(int i = 0; i < 4; i++){
      betaAbs[i] = msg.get(i).floatValue();
    }
  }    
  if(msg.checkAddrPattern("/muse/elements/gamma_absolute")){
    for(int i = 0; i < 4; i++){
      gammaAbs[i] = msg.get(i).floatValue();
    }
  }    
  
  if(msg.checkAddrPattern("/muse/elements/blink")){
    blink = msg.get(0).intValue();
  }    
  if(msg.checkAddrPattern("/muse/elements/jaw_clench")){
    jawClench = msg.get(0).intValue();
  }
 
  if(msg.checkAddrPattern("/muse/elements/experimental/concentration")){
    concentration = msg.get(0).floatValue();
  }    
  if(msg.checkAddrPattern("/muse/elements/experimental/mellow")){
    mellow = msg.get(0).floatValue();
  } 
}

aa-debdeb/Muse

参考

OpenCVで特徴点をもとに画像をボロノイ分割・ドロネー分割する

この前に,ランダムに選んだ母点をもとに画像をボロノイ分割,ドロネー分割して,モザイク画像を生成した.

今回は,母点の粗密がいい感じになることを期待して,画像の特徴点をもとに分割する. それぞれの特徴検出器のパラメータ設定は適当.

固有値に基づく特徴点検出

特徴点

f:id:aa_debdeb:20160714112351j:plain

ボロノイ図

f:id:aa_debdeb:20160714112409j:plain

ドロネー分割

f:id:aa_debdeb:20160714112429j:plain

Harris検出器に基づく特徴点検出

特徴点

f:id:aa_debdeb:20160714112443j:plain

ボロノイ図

f:id:aa_debdeb:20160714112459j:plain

ドロネー分割

f:id:aa_debdeb:20160714112513j:plain

FAST検出器に基づく特徴点検出

特徴点

f:id:aa_debdeb:20160714112530j:plain

ボロノイ図

f:id:aa_debdeb:20160714112546j:plain

ドロネー分割

f:id:aa_debdeb:20160714112600j:plain

Star検出器に基づく特徴点検出

特徴点

f:id:aa_debdeb:20160714112618j:plain

ボロノイ図

f:id:aa_debdeb:20160714112632j:plain

ドロネー分割

f:id:aa_debdeb:20160714112649j:plain

SIFT検出器に基づく特徴点検出

特徴点

f:id:aa_debdeb:20160714112705j:plain

ボロノイ図

f:id:aa_debdeb:20160714112720j:plain

ドロネー分割

f:id:aa_debdeb:20160714112738j:plain

SURF検出器に基づく特徴点検出

特徴点

f:id:aa_debdeb:20160714112755j:plain

ボロノイ図

f:id:aa_debdeb:20160714112813j:plain

ドロネー分割

f:id:aa_debdeb:20160714112836j:plain

MSER検出器に基づく特徴点検出

特徴点

f:id:aa_debdeb:20160714112856j:plain

ボロノイ図

f:id:aa_debdeb:20160714112913j:plain

ドロネー分割

f:id:aa_debdeb:20160714112928j:plain

ORB検出器に基づく特徴点検出

特徴点

f:id:aa_debdeb:20160714112942j:plain

ボロノイ図

f:id:aa_debdeb:20160714112954j:plain

ドロネー分割

f:id:aa_debdeb:20160714113009j:plain

FAST検出器+Gridアダプタ に基づく特徴点検出

特徴点

f:id:aa_debdeb:20160714113026j:plain

ボロノイ図

f:id:aa_debdeb:20160714113043j:plain

ドロネー分割

f:id:aa_debdeb:20160714113056j:plain

FAST検出器+Pyramidアダプタ に基づく特徴点検出

特徴点

f:id:aa_debdeb:20160714113114j:plain

ボロノイ図

f:id:aa_debdeb:20160714113131j:plain

ドロネー分割

f:id:aa_debdeb:20160714113148j:plain

main.cpp


#include 
#include "opencv2/opencv.hpp"
#include "opencv2/nonfree/nonfree.hpp"

int main(int argc, const char * argv[]) {
    cv::Mat img = cv::imread("lena.jpg", 1);
    if(img.empty()) return -1;
    
    cv::Mat gray_img;
    cv::cvtColor(img, gray_img, CV_BGR2GRAY);
    cv::normalize(gray_img, gray_img, 0, 255, cv::NORM_MINMAX);
    
    std::vector keypoints;
    std::vector::iterator itk;
    cv::GoodFeaturesToTrackDetector detector(1600, 0.01, 5, 3); // 固有値に基づく特徴点検出
//    cv::GoodFeaturesToTrackDetector detector(1600, 0.01, 5, 3, true); // Harris検出器に基づく特徴点検出
//    cv::FastFeatureDetector detector(25, false); // FAST検出器に基づく特徴点検出
//    cv::StarFeatureDetector detector(16, 5); // Star検出器に基づく特徴点検出
//    cv::SiftFeatureDetector detector(0.001, 3.0); // SIFT検出器に基づく特徴点検出
//    cv::SurfFeatureDetector detector(100); // SURF検出器に基づく特徴点検出
//    cv::MserFeatureDetector detector; // MSER検出器に基づく特徴点検出
//    cv::OrbFeatureDetector detector(1500); // ORB検出器に基づく特徴点検出
//    cv::GridAdaptedFeatureDetector detector(new cv::FastFeatureDetector(10), 5000, 5, 5); // FAST検出器+Gridアダプタ に基づく特徴点検出
//    cv::PyramidAdaptedFeatureDetector detector(new cv::FastFeatureDetector(10), 3); // FAST検出器+Pyramidアダプタ に基づく特徴点検出

    detector.detect(img, keypoints);
    cv::Mat feature_img = img.clone();
    
    cv::Subdiv2D subdiv;
    subdiv.initDelaunay(cv::Rect(0, 0, img.rows, img.cols));
    std::vector points;
    
    for(itk = keypoints.begin(); itk!=keypoints.end(); ++itk) {
        cv::circle(feature_img, itk->pt, 5, cv::Scalar(184, 210, 0), 1, CV_AA);
        points.push_back(itk->pt);
    }

    points.push_back(cv::Point2f(0, 0));
    points.push_back(cv::Point2f(img.rows - 1, 0));
    points.push_back(cv::Point2f(0, img.cols - 1));
    points.push_back(cv::Point2f(img.rows - 1, img.cols - 1));
    subdiv.insert(points);
    
    // ボロノイモザイクの作成
    std::vector idx;
    std::vector> facetLists;
    std::vector facetCenters;
    subdiv.getVoronoiFacetList(idx, facetLists, facetCenters);
    
    cv::Mat voronoi_img = cv::Mat::zeros(img.rows, img.cols, CV_8UC3);
    for(auto facetList = facetLists.begin(); facetList != facetLists.end(); facetList++){
        cv::Point *vertices = new cv::Point[facetList->size()];
        cv::Vec3f polyColor = cv::Vec3f(0, 0, 0);
        int polyColorNum = 0;
        for(int i = 0; i < facetList->size(); i++){
            cv::Point vertex = facetList->at(i);
            vertices[i] = vertex;
            if(0 <= vertex.x && vertex.x < img.rows && 0 <= vertex.y && vertex.y < img.cols){
                polyColor += img.at(vertex.y, vertex.x);
                polyColorNum++;
            }
        }
        polyColor /= float(polyColorNum);
        cv::fillConvexPoly(voronoi_img, vertices, int(facetList->size()), cv::Scalar(polyColor[0], polyColor[1], polyColor[2]), CV_AA);
        delete[] vertices;
    }
    
    // ドロネーモザイクの作成
    std::vector triangles;
    subdiv.getTriangleList(triangles);
    
    cv::Mat dalaunay_img = cv::Mat::zeros(img.rows, img.cols, CV_8UC3);
    for(auto it = triangles.begin(); it != triangles.end(); it++)
    {
        cv::Vec6f &vec = *it;
        cv::Point vertices[3];
        vertices[0] = cv::Point(vec[0], vec[1]);
        vertices[1] = cv::Point(vec[2], vec[3]);
        vertices[2] = cv::Point(vec[4], vec[5]);
        cv::Vec3f polyColor = cv::Vec3f(0, 0, 0);
        polyColor += img.at(vertices[0].y, vertices[0].x);
        polyColor += img.at(vertices[1].y, vertices[1].x);
        polyColor += img.at(vertices[2].y, vertices[2].x);
        polyColor /= float(3);
        cv::fillConvexPoly(dalaunay_img, vertices, 3, cv::Scalar(polyColor[0], polyColor[1], polyColor[2]), CV_AA);
    }
    
    
    cv::imshow("feature", feature_img);
    cv::imshow("voronoi", voronoi_img);
    cv::imshow("dalaunay", dalaunay_img);

    cv::waitKey(0);
    
    
}

OpenCV/dalaunay_feature at master · aa-debdeb/OpenCV

参考