在Processing中使用距离传感器来控制形状的属性

时间:2015-08-19 16:57:25

标签: arduino processing arduino-uno

我试图制作一个程序,使用从距离传感器获取的读数来控制圆的属性(大小,xy和颜色)。要做到这一点,我试图让它记录当前值,并在按下相关键时将其应用于该值(例如,按下'并将其大小更改为距离的任何距离在那时候)。 - 理想情况下,当你将手移到传感器上时,我希望圆圈能够动态地改变下一个字段,但这似乎有点超出我的范围。

我尽力尽力,但我不确定我已经评论过的一切。任何提示或建议?我真的不确定在做类和构造函数时我做了什么。

编辑:当我运行代码时,没有任何反应。

import processing.serial.*;

int xpos, ypos, s, r, g, b;

Circle circle;
int shapeSize, distance;
String comPortString;
Serial myPort;


void setup(){
 size(displayWidth,displayHeight); //Use entire screen size.

//Open the serial port for communication with the Arduino
myPort = new Serial(this, "/dev/cu.usbmodem1411", 9600);
myPort.bufferUntil('\n'); // Trigger a SerialEvent on new line
}

void draw(){
 background(0);
 delay(50); //Delay used to refresh screen

println(distance);
}


void serialEvent(Serial cPort){
comPortString = (new String(cPort.readBytesUntil('\n')));
 if(comPortString != null) {
 comPortString=trim(comPortString);

 /* Use the distance received by the Arduino to modify the y position
 of the first square (others will follow). Should match the
 code settings on the Arduino. In this case 200 is the maximum
 distance expected. The distance is then mapped to a value
 between 1 and the height of your screen */
 distance = int(map(Integer.parseInt(comPortString),1,200,1,height));
 if(distance<0){
 /*If computer receives a negative number (-1), then the
 sensor is reporting an "out of range" error. Convert all
 of these to a distance of 0. */
 distance = 0;
 }
 }
}



void keyPressed()
{
  // N for new circle (and keep old one)
  if((key == 'N') || (key == 'n')) {
    println("n");
  circle = new Circle(1,1,1,1,1,1);
    }

    //r - change red
      if((key == 'R') || (key == 'r')) {
    float red = map(distance, 0, 700, 0, 255);

    r = int(red);
    println("r " + r);
    }

       //g - change green
      if((key == 'G') || (key == 'g')) {
    float green = map(distance, 0, 700, 0, 255);
   g = int(green);
     println("g " + g);
    }

          //b - change blue
      if((key == 'B') || (key == 'b')) {
    float blue = map(distance, 0, 700, 0, 255);
     b = int(blue);
     println("b " + b);

    }

    //S - change Size
      if((key == 'S') || (key == 's')) {
       s = distance;
        println("s " + s);
    }

    //X - change x  pos
      if((key == 'X') || (key == 'x')) {
     xpos = distance;
         println("x " + xpos);
    }

    //y - change y pos
      if((key == 'Y') || (key == 'y')) {
            ypos = distance;
      println("y " + ypos);
    }
  } 

  class Circle {

   Circle(int xpos, int ypos, int s, int r, int g, int b){
   ellipse(xpos, ypos, s, s);
   color(r, g, b);
   }
   int getX(){
     return xpos;
   }
      int getY(){
     return ypos;
   }
  } 

1 个答案:

答案 0 :(得分:1)

我会把它分成步骤/任务:

  1. 连接到Arduino
  2. 从Arduino读取值
  3. 映射读取值
  4. 控制映射
  5. 你已经有了Arduino部分,但是在尝试将读取值映射到屏幕上的圆圈时,事情看起来很混乱。 目前,为简单起见,让我们忽略类,并专注于简单地绘制一个具有x,y,size,r,g,b属性的椭圆。

    要读取抖动,您应该连续更新属性椭圆,而不仅仅是在按键时。在关键事件上,您应该只更改更新的属性。 您可以使用额外的变量来跟踪您正在更新的椭圆属性。

    以下是基于以上几点的重构代码版本:

    import processing.serial.*;
    
    int xpos,ypos,s,r,g,b;
    int distance;
    
    int propertyID = 0;//keep track of what property should be updated on distance
    int PROP_XPOS = 0;
    int PROP_YPOS = 1;
    int PROP_S = 2;
    int PROP_R = 3;
    int PROP_G = 4;
    int PROP_B = 5;
    
    void setup(){
      size(400,400);
      //setup some defaults to see something on screen
      xpos = ypos = 200;
      s = 20;
      r = g = b = 127;
      //initialize arduino - search for port based on OSX name
      String[] portNames = Serial.list();
      for(int i = 0 ; i < portNames.length; i++){
        if(portNames[i].contains("usbmodem")){
          try{
            Serial arduino = new Serial(this,portNames[i],9600);
            arduino.bufferUntil('\n');
            return;
          }catch(Exception e){
            showSerialError();
          }
        }
      }
      showSerialError();
    }
    void showSerialError(){
      System.err.println("Error connecting to Arduino!\nPlease check the USB port");
    }
    void draw(){
      background(0);
      fill(r,g,b);
      ellipse(xpos,ypos,s,s);
    }
    void serialEvent(Serial arduino){
      String rawString = arduino.readString();//fetch raw string
      if(rawString != null){
        String trimmedString = rawString.trim();//trim the raw string
        int rawDistance = int(trimmedString);//convert to integer
        distance = (int)map(rawDistance,1,200,1,height);
        updatePropsOnDistance();//continously update circle properties
      }
    }
    void updatePropsOnDistance(){
      if(propertyID == PROP_XPOS) xpos = distance;
      if(propertyID == PROP_YPOS) ypos = distance;
      if(propertyID == PROP_S) s = distance;
      if(propertyID == PROP_R) r = distance;
      if(propertyID == PROP_G) g = distance;
      if(propertyID == PROP_B) b = distance;
    }
    void keyReleased(){//only change what proprty changes on key press
      if(key == 'x' || key == 'X') propertyID = PROP_XPOS;
      if(key == 'y' || key == 'Y') propertyID = PROP_YPOS;
      if(key == 's' || key == 'S') propertyID = PROP_S;
      if(key == 'r' || key == 'R') propertyID = PROP_R;
      if(key == 'g' || key == 'G') propertyID = PROP_G;
      if(key == 'b' || key == 'B') propertyID = PROP_B;
    }
    //usually a good idea to test - in this case use mouseY instead of distance sensor
    void mouseDragged(){
      distance = mouseY;
      updatePropsOnDistance();
    }
    

    如果这是有道理的,它可以很容易地封装在一个类中。 我们可以使用数组来存储这些属性,但是如果类似于道具[0]用于x,道具1用于y等等,则难以阅读,您可以使用IntDict允许您索引值基于字符串而不是值(因此您可以props["x"]代替props[0])。

    这是代码的封装版本:

    import processing.serial.*;
    
    Circle circle = new Circle();
    
    void setup(){
      size(400,400);
      //initialize arduino - search for port based on OSX name
      String[] portNames = Serial.list();
      for(int i = 0 ; i < portNames.length; i++){
        if(portNames[i].contains("usbmodem")){
          try{
            Serial arduino = new Serial(this,portNames[i],9600);
            arduino.bufferUntil('\n');
            return;
          }catch(Exception e){
            showSerialError();
          }
        }
      }
      showSerialError();
    }
    void showSerialError(){
      System.err.println("Error connecting to Arduino!\nPlease check the USB port");
    }
    void draw(){
      background(0);
      circle.draw();
    }
    void serialEvent(Serial arduino){
      String rawString = arduino.readString();
      if(rawString != null){
        String trimmedString = rawString.trim();
        int rawDistance = int(trimmedString);
        int distance = (int)map(rawDistance,1,200,1,height);
        circle.update(distance);
      }
    }
    void keyReleased(){
      circle.setUpdateProperty(key+"");//update the circle property based on what key gets pressed. the +"" is a quick way to make a String from the char
    }
    //usually a good idea to test - in this case use mouseY instead of distance sensor
    void mouseDragged(){
      circle.update(mouseY);
    }
    
    class Circle{
      //an IntDict (integer dictionary) is an associative array where instead of accessing values by an integer index (e.g. array[0]
      //you access them by a String index (e.g. array["name"])
      IntDict properties = new IntDict();
    
      String updateProperty = "x";//property to update
    
      Circle(){
        //defaults
        properties.set("x",200);
        properties.set("y",200);
        properties.set("s",20);
        properties.set("r",127);
        properties.set("g",127);
        properties.set("b",127);
      }
    
      void draw(){
        fill(properties.get("r"),properties.get("g"),properties.get("b"));
        ellipse(properties.get("x"),properties.get("y"),properties.get("s"),properties.get("s"));
      }
    
      void setUpdateProperty(String prop){
        if(properties.hasKey(prop)) updateProperty = prop;
        else{
          println("circle does not contain property: " + prop+"\navailable properties:");
          println(properties.keyArray());
        } 
      }
    
      void update(int value){
        properties.set(updateProperty,value);   
      }
    
    }
    

    在这两个示例中,您都可以通过在Y轴上拖动鼠标来测试距离值。

    关于HC-SR04传感器,您可以找到code on the Arduino Playground以获得以cm为单位的距离。我自己还没有使用传感器,但我注意到其他人有一些问题,所以值得检查this post。如果你想推出自己的Arduino代码,没问题,你可以使用HC-SR04 datasheet(pdf链接)来获得公式:

      

    公式:uS / 58 =厘米或uS / 148 =英寸;或:范围=高   水平时间*速度(340M / S)/ 2;我们建议使用超过60毫秒   测量周期,以防止触发信号到回声   信号。

    获得准确的值非常重要(在处理过程中使用这些值时,您将避免抖动)。此外,您可以使用easingmoving average

    这是一个基本的移动平均线示例:

    int historySize = 25;//remember a number of past values
    int[] x = new int[historySize];
    int[] y = new int[historySize];
    
    void setup(){
      size(400,400);
      background(255);
      noFill();
    }
    void draw(){
      //draw original trails in red
      stroke(192,0,0,127);
      ellipse(mouseX,mouseY,10,10);
    
      //compute moving average 
      float avgX = average(x,mouseX);
      float avgY = average(y,mouseY);
    
      //draw moving average in green
      stroke(0,192,0,127);
      ellipse(avgX,avgY,10,10); 
    }
    void mouseReleased(){
      background(255);
    }
    float average(int[] values,int newValue){
      //shift elements by 1, from the last to the 2nd: count backwards
      float total = 0;
      int size = values.length;
      for(int i = size-1; i > 0; i--){//count backwards
        values[i] = values[i-1];//copy previous value into current
        total += values[i];//add values to total
      }
      values[0] = newValue;//add the newest value at the start of the list
      total += values[0];//add the latest value to the total
      return (float)total/size;//return the average
    }