【问题标题】:How to control multiple servos with a PS4 Controller using Processing Game Control Plus如何使用 Processing Game Control Plus 使用 PS4 控制器控制多个舵机
【发布时间】:2020-07-01 23:13:33
【问题描述】:

我过去 3 天试图用我的基本编程知识来解决这个问题,这就是我所取得的成就:使用处理程序从 PS4 控制器收集数据并通过串行端口将其发送到 Arduino 能够使用控制一个伺服串行和 Game Control Plus 库。

好的,那我应该不会有更多的问题吧?正确的?不。 我不知道我将如何通过 PS4 控制的其他 3 个模拟轴并让 Arduino 获取它们并拆分为变量来控制其他伺服系统。

我将分享我的 2 个代码:

//Processing Code

import processing.serial.*;

import org.gamecontrolplus.gui.*;
import org.gamecontrolplus.*;
import net.java.games.input.*;

Serial myPort;

ControlIO control;
ControlDevice stick;
float px, py, pz, pw;


int[] lista;


public void setup() {
  lista = new int[4];
  String portName = Serial.list()[2]; 
  myPort = new Serial(this, portName, 9600);
  surface.setTitle("GCP Joystick example");

  control = ControlIO.getInstance(this);


  stick = control.filter(GCP.STICK).getMatchedDevice("joystick");
  if (stick == null) {
    println("No suitable device configured");
    System.exit(-1); 
  }

}


public void getUserInput() {
  px = map(stick.getSlider("X").getValue(), -1, 1, 0, width);
  py = map(stick.getSlider("Y").getValue(), -1, 1, 0, height);
  pz = map(stick.getSlider("Z").getValue(), -1, 1, 0, width);
  pw = map(stick.getSlider("W").getValue(), -1, 1, 0, height);
}

// Event handler for the SHADOW button


public void draw() {
  getUserInput(); // Polling the input device
  background(255, 255, 240);

  fill(0, 0, 255, 32);
  noStroke();
  println(int(px)); 
  myPort.write(int(px));

// I tried to pass this way, but I still don't know how make Arduino recognize and split them
/**  lista[0] = int(px); 
  lista[1] = int(py);
  lista[2] = int(pz);
  lista[3] = int(pw);

for (int i = 0; i < 4) {
  println(lista[i]); 
  myPort.write(lista[i]);
  if (myPort.readStringUntil('\n') == "k"){ 
    i += 1
}
  else{}
println("---------"); 
  */
}

现在,Arduino 代码:

#include <Servo.h>

char val;
Servo servo1;
int x_pos;
int servo1_pin = 9;
int initial_position = 90;
int x_pos_pas = 50;

void setup() {
  Serial.begin(9600);
  servo1.attach(servo1_pin); 
  servo1.write (initial_position);
}

void loop() {
  if (Serial.available()) 
   { 
     val = Serial.read();
     x_pos = int(int(val) * 1.8);
   }
  if(x_pos != x_pos_pas){
    servo1.write(x_pos);    
  }
  else{}
  x_pos_pas = x_pos;
  delay(10);
}

【问题讨论】:

    标签: arduino controls processing servo ps4


    【解决方案1】:

    您正在尝试做的事情有点令人困惑,但据我所知,困惑在于串行通信。

    以下是我如何理解与您当前的代码进行通信的方式以及与此相关的一些问题:

    1.截断

    您正在编写px,它映射到草图尺寸(我在任何地方都看不到,所以猜测它是默认的 100x100 吗?)。

    如果尺寸大于 255,这可能是个问题。

    即使通过处理序列write(int) 获取一个 int,它在幕后使用的是 jssc.Serial.writeInt(),它的值从 0 到 255。 (本质上它写入一个字节,但在 0-255 范围内,其中 byte type in Processing java 是从 -127 到 128)。

    如果要发送大于 255 的值,则需要将它们拆分为单独的字节。 (参见使用bit shifting and AND masking 和使用OR 将各个字节组合成多字节整数的ARGB 值拆分示例)

    2。重复

    px 在 for 循环中再次发送:myPort.write(lista[i]); 无论如何,代码都会被注释,但需要注意。 就我个人而言,我制作了新的简单测试草图来解决单个简单问题,而不是大型未使用的块,这使得代码更难阅读/导航并最终调试。 我鼓励您将较大的问题分解为多个更简单的单个问题,并一次解决一个。

    3.数据终止

    注释代码尝试发送 4 个整数,但 Arduino 无法知道数据到达的顺序。如果出现任何问题,将很难说出顺序是什么。

    有多种方法可以做到这一点。理想情况下,您会编写一个通信协议。 (关于这个话题有很多resources。)

    这听起来像是一个有趣的项目,可以学习使用位/字节/字节数组,并从头开始将通信协议与某种形式的数据验证(校验和 / CRC / 等)组合在一起

    如果您时间紧迫,并且只想驱动伺服器而不用担心通过串行方式可靠地发送多个(可能)大值,我建议您尝试Firmata Processing

    您需要将 Firmata 固件刷入您的 Arduino,然后使用 Firmata 处理库连接到串行端口(使用正确的波特率)并根据需要调用 arduino.servoWrite()。另请参阅库的 arduino_servo 示例。

    根据您对介于 0 -> 100 之间的映射值的评论,您可以使用大于 100 的任何其他字符重新调整 SerialEvent example 的用途,以区分数据与字符串终止符字符。请记住,您将失去精度:

    1. getValue() 返回一个具有 32 位精度的 float
    2. 理论上,您可以对 0-254 使用相同的技术(使用 255 作为终止符)。 0-100 范围使用 6 位

    这里是一个粗略的代码示例,考虑到代码尚未在实际设备上进行测试:

    处理:

    import processing.serial.*;
    
    import org.gamecontrolplus.gui.*;
    import org.gamecontrolplus.*;
    import net.java.games.input.*;
    
    Serial myPort;
    
    ControlIO control;
    ControlDevice stick;
    float px, py, pz, pw;
    
    // px,py,pz,pw remapped to 0-100 range
    int[] lista = new int[4];
    // px,py,pz as bytes + terminator character (255)
    byte[] toArduino = {0,0,0,0,(byte)255};
    
    public void setup() {
      size(100,100);
      lista = new int[4];
      String portName = Serial.list()[2]; 
      try{
        myPort = new Serial(this, portName, 9600);
      }catch(Exception e){
        println("error connecting to serial port: " + portName);
        e.printStackTrace();
      }
      surface.setTitle("GCP Joystick example");
    
      control = ControlIO.getInstance(this);
    
      try{
        stick = control.filter(GCP.STICK).getMatchedDevice("joystick");
      }catch(Exception e){
        e.printStackTrace();
      }
      if (stick == null) {
        println("No suitable device configured");
      }
    
    }
    
    
    public void getUserInput() {
      if(stick == null){
        return;
      }
      // map values
      px = map(stick.getSlider("X").getValue(), -1, 1, 0, width);
      py = map(stick.getSlider("Y").getValue(), -1, 1, 0, height);
      pz = map(stick.getSlider("Z").getValue(), -1, 1, 0, width);
      pw = map(stick.getSlider("W").getValue(), -1, 1, 0, height);
      // cast to int
      lista[0] = (int)px;
      lista[1] = (int)py;
      lista[2] = (int)pz;
      lista[3] = (int)pw;
      // update bytearray
      toArduino[0] = (byte)lista[0];
      toArduino[1] = (byte)lista[1];
      toArduino[2] = (byte)lista[2];
      toArduino[3] = (byte)lista[3];
    }
    
    // Event handler for the SHADOW button
    
    
    public void draw() {
      getUserInput(); // Polling the input device
      background(255, 255, 240);
    
      fill(0, 0, 255, 32);
      noStroke();
    
      text("px: " + px
        +"\npy: " + py
        +"\npz: " + pz
        +"\npw: " + pw
        ,5,15);
    
      if(myPort != null){
        myPort.write(toArduino);
      }
    }
    

    阿杜诺:

    /*
      Serial Event example
    
      When new serial data arrives, this sketch adds it to a String.
      When a newline is received, the loop prints the string and clears it.
    
      A good test for this is to try it with a GPS receiver that sends out
      NMEA 0183 sentences.
    
      NOTE: The serialEvent() feature is not available on the Leonardo, Micro, or
      other ATmega32U4 based boards.
    
      created 9 May 2011
      by Tom Igoe
    
      This example code is in the public domain.
    
      http://www.arduino.cc/en/Tutorial/SerialEvent
    */
    String inputString = "";         // a String to hold incoming data
    bool stringComplete = false;  // whether the string is complete
    
    const char TERMINATOR = (char)255;
    
    void setup() {
      // initialize serial:
      Serial.begin(9600);
      // reserve 200 bytes for the inputString:
      inputString.reserve(200);
    }
    
    void loop() {
      // in case serialEvent() isn't called automatically
      serialEvent();
      // print the string when a newline arrives:
      if (stringComplete) {
        // process string
        uint8_t px = inputString.charAt(0);
        uint8_t py = inputString.charAt(1);
        uint8_t pz = inputString.charAt(2);
        uint8_t pw = inputString.charAt(3);
        // TODO: pass this to servos
        // debug print
        Serial.print("px:");
        Serial.print(px);
        Serial.print("\tpy:");
        Serial.print(py);
        Serial.print("\tpz:");
        Serial.print(pz);
        Serial.print("\tpw:");
        Serial.println(pw);
        // clear the string:
        inputString = "";
        stringComplete = false;
      }
    }
    
    /*
      SerialEvent occurs whenever a new data comes in the hardware serial RX. This
      routine is run between each time loop() runs, so using delay inside loop can
      delay response. Multiple bytes of data may be available.
    */
    void serialEvent() {
      while (Serial.available()) {
        // get the new byte:
        char inChar = (char)Serial.read();
        // add it to the inputString:
        inputString += inChar;
        // if the incoming character is a newline, set a flag so the main loop can
        // do something about it:
        if (inChar == TERMINATOR) {
          stringComplete = true;
        }
      }
    }
    

    【讨论】:

    • 感谢您的回答。因此,px 是一个变量,其中保存了第一个模拟量的 x 轴。 println(int(px)); 在处理的终端中打印 px,这样我就可以了解正在发生的事情。 myPort.write(int(px)); Arduino串口打印,到现在我只能控制一个舵机,我更大的困难是发送4个值,每个轴一个,来控制四个不同的舵机。我对编程不太了解,如何通过串口向 Arduino 发送 0-100 之间的四个不同值,他区分这些值,以便我可以控制 4 个舵机?
    • 如果您知道值永远不会超过 100,您可以使用 101-255 范围内的数字作为终止符字节,在 Arduino 端,您将缓冲所有数据直到此终止符字节,然后读取你的价值观(例如myPort.write(new byte[]{10,20,30,40,255}); 发送)。在 Arduino 方面,我建议尝试 Examples > 04.Communication > SerialEvent。您需要更改代码,使if (inChar == '\n') 变为类似于if (inChar == 255)。在loop() if (stringComplete) { 条件中,您应该能够为每个值添加另一个打印...
    • ...例如(Serial.print(inputString.charAt(0,DEC)); 应该打印第一个值 (10),charAt(3) 应该打印 40,等等。如果您是编程新手,我建议您先尝试使用 Firmata。那里有大量资源(例如 youtube.com/watch?v=l4jFQIoY9ZY)。否则,您至少需要了解 Arduino 中的int,char,byte,String。如果有帮助,请随意投票/标记答案;)玩得开心!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多