宜昌老张

Arduino与Processing互动之震动泡泡

0
阅读(6598)

   

   这篇文章介绍的传感器是一款基于弹簧摆动原理设计的震动传感器,震动势能传到弹簧末端引起共振,左右摆动碰触到金属外壁形成通路。由于导通时间和弹簧的刚性有关,所以该震动传感器的输出带宽很窄,建议使用外部中断功能来读取震动触发。

    这个Processing与Arduino互动作品视频说明:当用震动传感器敲击桌面时,产生触发信号,引发Ardunio控制器的外部中断,自动执行中断服务子程序,把传感器的震动触发信息上传Processing上位机程序,于是其画布里的泡泡弹起,跳跃,撞击,好一派欢快景象,过一会,由于泡泡重力作用下,泡泡平静下来,落在矩形框的底部。如果您再次用震动传感器敲击桌面,位于矩形框的底部的泡泡们又开始弹起,跳跃,重复上述过程。

视频:

    实验用的Arduino设备可从淘宝找到,http://dfrobot.taobao.com/。我的上一篇有关震动传感器的博客文章地址:http://blog.chinaaet.com/detail/23455.html

    在优酷网里找到了个视频,也是用Arduino震动传感器与Processing互动的视频,蛮有趣的,像是“无厘头”电影。
     这个作品的道具-大葵扇里面,夹藏着一个小型的震动装置,这个装置用导线链接到Arduino主板上,同时也将震动装置的操作讯号输入到主板上,主板同时链接到电脑的Arduino客户端,客户端与原本制作好的FLV视频作出联动,根据上传数据标志的不同要控制不同视频段(酷热难耐和凉爽惬意)的播放,从而制作出互动的效果。
   
    我的震动泡泡作品用到了面向对象的编程,class Ball{}。修改变量声明程序段中的四个变量numBalls、spring、friction和gravity,画布里的泡泡震动景象会发生变化。numBalls定义泡泡的数量,spring定义泡泡之间撞击后的弹性系数、friction定义泡泡与墙壁之间撞击后的弹性系数和gravity定义泡泡运动时的重力系数,numBalls值越大,画布里的泡泡越多;spring、friction值越大,撞击时弹力越大;gravity值越大,泡泡在重力作用下,沉降下来的速度越快。还有画布尺寸也可以变大,用 size(700, 250)命令。试试修改这些参数,泡泡们的震动情景是否能更壮观。实际上您把程序贴到软件编程界面里运行,实际场景要比我的视频效果好得多,试试! 
 Processing程序:
import processing.serial.*;//导入serial通信库
Serial duankou;//创建对象duankou
int data;//变量data作为接受到的数据
//修改这四个变量numBalls、spring、friction和gravity
//观察画布里的泡泡震动景象的变化
int numBalls =13;//定义泡泡的数量
float spring = 0.8;//泡泡之间撞击后的弹性系数
float friction = -0.9;//泡泡与墙壁之间撞击后的弹性系数
float gravity = 0.4;//泡泡运动时的重力系数
Ball[] balls = new Ball[numBalls];
//初始化程序
void setup() 
{
  size(700, 250);//设置画布尺寸,可以再修改变大,试试
  noStroke();
  smooth();
  //设定通讯端口为COM3,波特率为9600
  duankou = new Serial(this,"COM3",9600);
  //呼叫构造器,初始化Ball对象
  for (int i = 0; i < numBalls; i++) {
    balls[i] = new Ball(random(width), random(height), random(20, 40), i, balls);}
}
//主程序
void draw() 
{
   if(duankou.available()>0)
  {
    //把Arduino传来的字节存入变量data
    data=duankou.read();
    //把Arduino传来的字节显示在控制台中
    println(data);
 //如果震动传感器引发了中断,Arduino上传标志值1   
  if(data==1)  {      
    //调用构造器,初始化Ball对象
    for (int i = 0; i < numBalls; i++) {
    balls[i] = new Ball(random(width), random(height), random(20, 40), i, balls);
    }
  }
  }
  background(128);//画布用灰色背景刷新
  for (int i = 0; i < numBalls; i++) {
    balls[i].collide();//调用Ball对象的collide函数   
    balls[i].move();//调用Ball对象的move函数 
    balls[i].display();//调用Ball对象的display函数   
  }
}
//对象名称为Ball
class Ball {
  //定义对象用到的局域变量
  float x, y;
  float diameter;
  float vx = 0;
  float vy = 0;
  int id;
  Ball[] others;
 //定义构造器,给Ball对象变量赋初值
  Ball(float xin, float yin, float din, int idin, Ball[] oin) {
    x = xin;
    y = yin;
    diameter = din;
    id = idin;
    others = oin;
  } 
 //定义Ball对象的collide函数
 //判断泡泡之间撞击条件,并修正撞击后,泡泡的运动参数 
  void collide() {
    for (int i = id + 1; i < numBalls; i++) {
      float dx = others[i].x - x;
      float dy = others[i].y - y;
      float distance = sqrt(dx*dx + dy*dy);
      float minDist = others[i].diameter/2 + diameter/2;
      if (distance < minDist) { 
        float angle = atan2(dy, dx);
        float targetX = x + cos(angle) * minDist;
        float targetY = y + sin(angle) * minDist;
        float ax = (targetX - others[i].x) * spring;
        float ay = (targetY - others[i].y) * spring;
        vx -= ax;
        vy -= ay;
        others[i].vx += ax;
        others[i].vy += ay;
      }
    }   
  }
 //定义Ball对象的move函数
 //判断泡泡与墙体之间撞击条件,并修正撞击后,泡泡的运动参数
 //根据泡泡的运动参数,计算泡泡运动轨迹中各点的坐标值
  void move() {
    vy += gravity;
    x += vx;
    y += vy;
    if (x + diameter/2 > width) {
      x = width - diameter/2;
      vx *= friction; 
    }
    else if (x - diameter/2 < 0) {
      x = diameter/2;
      vx *= friction;
    }
    if (y + diameter/2 > height) {
      y = height - diameter/2;
      vy *= friction; 
    } 
    else if (y - diameter/2 < 0) {
      y = diameter/2;
      vy *= friction;
    }
  }
  //定义Ball对象的dispaly函数
  //以泡泡运动轨迹中各点的坐标值为圆心画圆
  //显示泡泡们的动画效果
  void display() {
    //fill(255, 204);
    fill(255,255,0,100);
    ellipse(x, y, diameter, diameter);
  }
}
 
    震动传感器摇动一次,可能引发了多次中断,当第一次中断时,中断计数器state值加1,不等于0,把传感器摇动了一次的信息上传给Processing,命令为Serial.print(1,BYTE);,后续的几次中断,则通过延时命令delay(500);和中断计数器清零命令state = 0;,把它们屏蔽掉了,这样就可以做到即使多次中断,传感器摇动一次,也只上传一次“摇动”信息。当震动传感器不动时,把传感器没有摇动的信息上传给Processing,命令为Serial.print(0,BYTE)。
    下面用红色字体标示与Arduino中断相关的指令。

 Arduino程序:

 

//震动传感器的输出带宽很窄,在使用Arduino读取时,使用Arduino外部中断功能
 
//数字接口2:振动,水银,干簧管 数字输入传感器。
#define SensorLED     13
#define SensorINPUT   2 
//中断次数计数器初始化为0
unsigned char state = 0;

void setup() 
  pinMode(SensorLED, OUTPUT);//设置LED为输出 
  pinMode(SensorINPUT, INPUT);
  Serial.begin(9600);//设置串行通信的波特率
  attachInterrupt(0, blink, FALLING);//D2口为外部中断0,当有下降沿触发的时候调用blink函数

}
void loop()
{
      //如果中断次数不为0
      if(state!=0)
      {
        //如果震动传感器引发中断
        Serial.print(1,BYTE);//向Processing发送标志值1   
        digitalWrite(SensorLED,LOW);//灯亮
        delay(500);//延时500,
        //中断计数器清零
        state = 0;//把震动引发的第一次中断后的若干个中断屏蔽掉         
      }  
      //如果震动传感器不动,没有引发中断
      else 
      {
        Serial.print(0,BYTE); //向Processing发送标志值0       
        digitalWrite(SensorLED,HIGH);//灯灭 
        delay(20); //每隔20ms,向Processing发送一次标志值0    
      }

//如果振动传感器引发中断,将自动调用中断服务程序
void blink()//数字输入传感器下降沿触发中断服务函数
{
  state++;//中断计数器加1  
}