绘制时发送arduino序列命令

发布于 2025-02-03 03:31:38 字数 7138 浏览 2 评论 0 原文

我有一个简单的PD Arduino控制器来旋转电动机。我想用它以图形方式演示系统响应。我可以使用它,因此我可以使用串行监视器给目标位置,但是我希望能够同时看到串行图输出。串行绘图仪中似乎有一个类似的对话框,但是从那里发送的命令似乎没有得到认可。是否有一种方法可以绘制传入的序列数据,同时也如上所述发送命令?我不介意是否需要其他库,但是我看不出为什么它不应该在本地工作,因为我可以在使用串行监视器接收信息时发送命令。也许我误解了这个过程。

任何帮助将不胜感激。请参阅下面的完整代码:

// Clockwise rotation direction.
#define CW 1
// Counter clockwise rotation direction.
#define CCW 0
// Frequency of output PWM signal.
#define PWM_FREQ 25000
// Update rate in microseconds.
#define CYCLE_TIME 1000
// Rate of sending position data to PC.
#define PLOT_RATE 200
#define PLOT_COUNTER CYCLE_TIME/PLOT_RATE

 // IO pins. //
// The pin connected to ENBble A on the driver. 
const int ENB = 14;
// Pins connected to IN3 and IN4 on the driver (for controlling the rotation direction).
const int IN4 = 15;
const int IN3 = 16;
// Signal A wire of the encoder.
const int ENCA = 17;
// Signal B wire of the encoder.
const int ENCB = 18;

// Value of ENCA.
int enca = 0;
// Value of ENCB.
int encb = 0;
// Value of IN3.
int in3 = 0;
// Value of IN4.
int in4 = 0;
// Motors position measure by encoder.
volatile long int motorPos = 0;


  // Communication variables. //
// The byte sent over serial to Teensy.
int incomingByte = 0;
// Input buffer for receiving user input over serial.
char inputBuffer[8];
int bufferCnt = 0;
// Counter for sending position over serial for plotting purposes.
int pltCounter = 0;


  // Controller variables./ /
// Last motor position.
long int lastPos = 0;
// Target motor position.
int targetPos = 0;
// Position at the start of the control loop.
int currentPos = 0;
// Position at the start of the previous control loop.
int prevPos = 0;
// Change in position (for approximating the derivative).
int dP = 0;
// Position error.
int pError = 0;
// P term of the controller.
int pTerm = 0;
// D term of the controller.
int dTerm = 0;
// Speed (= voltage = duty cycle). Controller output mapped to duty cycle range.
int spd = 0;
// Controller output.
int contOut = 0;
// Ratio for transforming counts to degrees (1920 count / 360 deg)
float ratio = static_cast<float>(360)/static_cast<float>(1920);

  // Controller tunable parameters. //
// P gain.
const int kP = 10;
// D gain.
const int kD = 0;
// Error in encoder pulses correponding to the minimum duty cycle.
const int minErr = 0;
// Error in encoder pulses corresponding to the maximum duty cycle.
const int maxErr = 1024;
// minDutyCycle and maxDutyCycle depend on PWM frequency and can be determined in dc_motor_speed_control . For example for frequency of 25k,
// minDutyCycle = 120 (Motor starts to move), 
// maxDutyCycle = 190 (Motor speed reaches its maximum given the supplied voltage).
const int minDutyCycle = 120;
const int maxDutyCycle = 190;

  // Controller update rate variables. //
// Difference in time between desired cycle period and its execution time (without any delay()s).
int cycleDiff;
// Control loop start time.
long int startTime;
// Control loop end time.
long int endTime;

// Plotting
float motorPosDeg = 0;
//Plotter p;

void setup() {
  Serial.begin(9600);
  // Initialize the pins.
  pinMode(IN3,OUTPUT);
  pinMode(IN4,OUTPUT);
  pinMode(ENB,OUTPUT);
  pinMode(ENCA,INPUT);
  pinMode(ENCB,INPUT);
  analogWriteFrequency(ENB, PWM_FREQ);
  // Set the initial rotation direction.
  setDirection(CCW);
  // Start with the motor at rest.
  analogWrite(ENB,0);
  // Encoder interrupt.
  attachInterrupt(digitalPinToInterrupt(ENCA), encoderAISRising, RISING);
  attachInterrupt(digitalPinToInterrupt(ENCB), encoderBISRising, RISING);
  //p.Begin();
  //p.AddTimeGraph("Position v Time", 1000, "Position", motorPosDeg);
}

// *** Encoder interrupt routines. See "Understanding Quadrature Encoded Signals" here: https://www.pjrc.com/teensy/td_libs_Encoder.html" *** //
void encoderAISRising(){
  if(digitalRead(ENCB) == HIGH)
    motorPos++;
  else
    motorPos--;

  attachInterrupt(digitalPinToInterrupt(ENCA), encoderAISFalling, FALLING);
}

void encoderAISFalling(){
  if(digitalRead(ENCB) == LOW)
    motorPos++;
  else
    motorPos--;

  attachInterrupt(digitalPinToInterrupt(ENCA), encoderAISRising, RISING);
}

void encoderBISRising(){
  if(digitalRead(ENCA) == LOW)
    motorPos++;
  else
    motorPos--;

  attachInterrupt(digitalPinToInterrupt(ENCB), encoderBISFalling, FALLING);
}

void encoderBISFalling(){
  if(digitalRead(ENCA) == HIGH)
    motorPos++;
  else
    motorPos--;

  attachInterrupt(digitalPinToInterrupt(ENCB), encoderBISRising, RISING);
}
// ***          ***//

// Default rotation direction is CCW.
void setDirection(bool dir){
  // CCW
  if (dir == CCW){
    digitalWrite(IN3,HIGH);
    digitalWrite(IN4,LOW);
  }else{
    digitalWrite(IN3,LOW);  
    digitalWrite(IN4,HIGH);  
  }
}

void loop() {
  if (Serial.available() > 0) {
    // Read the incoming bytes, until a next line character (Enter) is encountered.
    while (1){
    incomingByte = Serial.read();
      // We have read all the bytes.
        if (incomingByte == '\n' || incomingByte == '\r'){
          Serial.read();
          break;
        }else{
          // Store the byte in the buffer and move on to the next.
          inputBuffer[bufferCnt] = incomingByte;
          bufferCnt++;    
        }
    }
    // Add a NULL character to the end of the array. Required for using atoi.
    inputBuffer[bufferCnt] = '\0';
    bufferCnt = 0;
    // Convert string to integer.
    targetPos = atoi(inputBuffer);
    targetPos = targetPos / ratio;
  }
//  int i = 0;
//  if (i % 2 == 0){
//    targetPos = 360;
//  } else {
//    targetPos = 0;
//  }
  startTime = micros();
  // Get the latest motor position.
  currentPos = motorPos;
  // Position error.
  //pError = targetPos - motorPos;
  pError = targetPos - currentPos;
  // P term of the controller.
  pTerm = kP * pError;
  dP = currentPos - prevPos;
  // D term of the controller. CYCLE_TIME/1000 normalizes the denominator, otherwise dTerm would always be zero (integer division).
  dTerm = kD * (dP/(CYCLE_TIME/1000));
  
  contOut = pTerm + dTerm; 
  // Set the target duty cycle (i.e. speed (i.e. voltage)).
  // Error (in terms of encoder pulses) in the range minErr-maxErr is mapped to speed range corresponding to minDutyCycle-maxDutyCycle.  
  // 4 parameters to tune here.
  spd = map(abs(contOut),minErr,maxErr,minDutyCycle,maxDutyCycle);
  
  // Set the direction according to sign of position error (CCW is positive), and then speed.
  // One optimization would be calling analogWrite(ENB,abs(spd)) at the start or end of the loop instead
  // (at the expense of readibility).
  if (pError > 0){
      setDirection(CCW);
      analogWrite(ENB,abs(spd));
  }else if (pError < 0){
     setDirection(CW);
     analogWrite(ENB,abs(spd));
  }
  if (pltCounter == PLOT_COUNTER){
    float mtrPos = static_cast<float>(motorPos);
    motorPosDeg = mtrPos * ratio;
    Serial.print(int(motorPosDeg));
    Serial.println();
    pltCounter = 0;
  }
  pltCounter++;
  
  
  prevPos = currentPos;
  cycleDiff = micros() - startTime;
  // Adjust the update rate.
  if (cycleDiff < CYCLE_TIME){
    delayMicroseconds(CYCLE_TIME - cycleDiff);
  }
  //i++;
  
}

I have a simple PD Arduino controller to spin a motor. I want to use it to demonstrate system responses graphically. I have it working so I can give a target position using the serial monitor, but I want to be able to see the serial plot output at the same time. There seems to be a similar dialogue box in the Serial Plotter, but commands sent from there don't seem to be recognized. Is there a way to plot incoming serial data while also sending commands as described above? I don't mind if I need additional libraries, but I can't see why it shouldn't work natively since I can already send commands while receiving info using the Serial Monitor. Maybe I'm misunderstanding that process.

Any help would be very appreciated. See full code below:

// Clockwise rotation direction.
#define CW 1
// Counter clockwise rotation direction.
#define CCW 0
// Frequency of output PWM signal.
#define PWM_FREQ 25000
// Update rate in microseconds.
#define CYCLE_TIME 1000
// Rate of sending position data to PC.
#define PLOT_RATE 200
#define PLOT_COUNTER CYCLE_TIME/PLOT_RATE

 // IO pins. //
// The pin connected to ENBble A on the driver. 
const int ENB = 14;
// Pins connected to IN3 and IN4 on the driver (for controlling the rotation direction).
const int IN4 = 15;
const int IN3 = 16;
// Signal A wire of the encoder.
const int ENCA = 17;
// Signal B wire of the encoder.
const int ENCB = 18;

// Value of ENCA.
int enca = 0;
// Value of ENCB.
int encb = 0;
// Value of IN3.
int in3 = 0;
// Value of IN4.
int in4 = 0;
// Motors position measure by encoder.
volatile long int motorPos = 0;


  // Communication variables. //
// The byte sent over serial to Teensy.
int incomingByte = 0;
// Input buffer for receiving user input over serial.
char inputBuffer[8];
int bufferCnt = 0;
// Counter for sending position over serial for plotting purposes.
int pltCounter = 0;


  // Controller variables./ /
// Last motor position.
long int lastPos = 0;
// Target motor position.
int targetPos = 0;
// Position at the start of the control loop.
int currentPos = 0;
// Position at the start of the previous control loop.
int prevPos = 0;
// Change in position (for approximating the derivative).
int dP = 0;
// Position error.
int pError = 0;
// P term of the controller.
int pTerm = 0;
// D term of the controller.
int dTerm = 0;
// Speed (= voltage = duty cycle). Controller output mapped to duty cycle range.
int spd = 0;
// Controller output.
int contOut = 0;
// Ratio for transforming counts to degrees (1920 count / 360 deg)
float ratio = static_cast<float>(360)/static_cast<float>(1920);

  // Controller tunable parameters. //
// P gain.
const int kP = 10;
// D gain.
const int kD = 0;
// Error in encoder pulses correponding to the minimum duty cycle.
const int minErr = 0;
// Error in encoder pulses corresponding to the maximum duty cycle.
const int maxErr = 1024;
// minDutyCycle and maxDutyCycle depend on PWM frequency and can be determined in dc_motor_speed_control . For example for frequency of 25k,
// minDutyCycle = 120 (Motor starts to move), 
// maxDutyCycle = 190 (Motor speed reaches its maximum given the supplied voltage).
const int minDutyCycle = 120;
const int maxDutyCycle = 190;

  // Controller update rate variables. //
// Difference in time between desired cycle period and its execution time (without any delay()s).
int cycleDiff;
// Control loop start time.
long int startTime;
// Control loop end time.
long int endTime;

// Plotting
float motorPosDeg = 0;
//Plotter p;

void setup() {
  Serial.begin(9600);
  // Initialize the pins.
  pinMode(IN3,OUTPUT);
  pinMode(IN4,OUTPUT);
  pinMode(ENB,OUTPUT);
  pinMode(ENCA,INPUT);
  pinMode(ENCB,INPUT);
  analogWriteFrequency(ENB, PWM_FREQ);
  // Set the initial rotation direction.
  setDirection(CCW);
  // Start with the motor at rest.
  analogWrite(ENB,0);
  // Encoder interrupt.
  attachInterrupt(digitalPinToInterrupt(ENCA), encoderAISRising, RISING);
  attachInterrupt(digitalPinToInterrupt(ENCB), encoderBISRising, RISING);
  //p.Begin();
  //p.AddTimeGraph("Position v Time", 1000, "Position", motorPosDeg);
}

// *** Encoder interrupt routines. See "Understanding Quadrature Encoded Signals" here: https://www.pjrc.com/teensy/td_libs_Encoder.html" *** //
void encoderAISRising(){
  if(digitalRead(ENCB) == HIGH)
    motorPos++;
  else
    motorPos--;

  attachInterrupt(digitalPinToInterrupt(ENCA), encoderAISFalling, FALLING);
}

void encoderAISFalling(){
  if(digitalRead(ENCB) == LOW)
    motorPos++;
  else
    motorPos--;

  attachInterrupt(digitalPinToInterrupt(ENCA), encoderAISRising, RISING);
}

void encoderBISRising(){
  if(digitalRead(ENCA) == LOW)
    motorPos++;
  else
    motorPos--;

  attachInterrupt(digitalPinToInterrupt(ENCB), encoderBISFalling, FALLING);
}

void encoderBISFalling(){
  if(digitalRead(ENCA) == HIGH)
    motorPos++;
  else
    motorPos--;

  attachInterrupt(digitalPinToInterrupt(ENCB), encoderBISRising, RISING);
}
// ***          ***//

// Default rotation direction is CCW.
void setDirection(bool dir){
  // CCW
  if (dir == CCW){
    digitalWrite(IN3,HIGH);
    digitalWrite(IN4,LOW);
  }else{
    digitalWrite(IN3,LOW);  
    digitalWrite(IN4,HIGH);  
  }
}

void loop() {
  if (Serial.available() > 0) {
    // Read the incoming bytes, until a next line character (Enter) is encountered.
    while (1){
    incomingByte = Serial.read();
      // We have read all the bytes.
        if (incomingByte == '\n' || incomingByte == '\r'){
          Serial.read();
          break;
        }else{
          // Store the byte in the buffer and move on to the next.
          inputBuffer[bufferCnt] = incomingByte;
          bufferCnt++;    
        }
    }
    // Add a NULL character to the end of the array. Required for using atoi.
    inputBuffer[bufferCnt] = '\0';
    bufferCnt = 0;
    // Convert string to integer.
    targetPos = atoi(inputBuffer);
    targetPos = targetPos / ratio;
  }
//  int i = 0;
//  if (i % 2 == 0){
//    targetPos = 360;
//  } else {
//    targetPos = 0;
//  }
  startTime = micros();
  // Get the latest motor position.
  currentPos = motorPos;
  // Position error.
  //pError = targetPos - motorPos;
  pError = targetPos - currentPos;
  // P term of the controller.
  pTerm = kP * pError;
  dP = currentPos - prevPos;
  // D term of the controller. CYCLE_TIME/1000 normalizes the denominator, otherwise dTerm would always be zero (integer division).
  dTerm = kD * (dP/(CYCLE_TIME/1000));
  
  contOut = pTerm + dTerm; 
  // Set the target duty cycle (i.e. speed (i.e. voltage)).
  // Error (in terms of encoder pulses) in the range minErr-maxErr is mapped to speed range corresponding to minDutyCycle-maxDutyCycle.  
  // 4 parameters to tune here.
  spd = map(abs(contOut),minErr,maxErr,minDutyCycle,maxDutyCycle);
  
  // Set the direction according to sign of position error (CCW is positive), and then speed.
  // One optimization would be calling analogWrite(ENB,abs(spd)) at the start or end of the loop instead
  // (at the expense of readibility).
  if (pError > 0){
      setDirection(CCW);
      analogWrite(ENB,abs(spd));
  }else if (pError < 0){
     setDirection(CW);
     analogWrite(ENB,abs(spd));
  }
  if (pltCounter == PLOT_COUNTER){
    float mtrPos = static_cast<float>(motorPos);
    motorPosDeg = mtrPos * ratio;
    Serial.print(int(motorPosDeg));
    Serial.println();
    pltCounter = 0;
  }
  pltCounter++;
  
  
  prevPos = currentPos;
  cycleDiff = micros() - startTime;
  // Adjust the update rate.
  if (cycleDiff < CYCLE_TIME){
    delayMicroseconds(CYCLE_TIME - cycleDiff);
  }
  //i++;
  
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

如痴如狂 2025-02-10 03:31:38

据我了解,它利用主要的Arduino连接起作用。根据Arduino UART的工作方式,您每个COM端口只能具有1个COM端口连接。这意味着您可以为每个UART连接打开图或命令行。不同版本的Arduino可以具有多个COM端口。在Arduino Uno上,只有一个com端口“序列”。在巨型上,我认为有3个UART端口。如果您使用外部FTDI UART板,则可以为Serial0打开“绘图窗口”,并将FTDI板连接在Serial1上以使命令行窗口打开。您将不得不更改代码以将命令发送到Serial1。

这是一些可以帮助您的链接。
https://docs.arduino.cc/tutorials/commumunication /communication /communication /communication /twoportreceive

=“ https://docs.arduino.cc/built-in-examples/communication/multiserialmega” rel =“ nofollow noreferrer”> https://docs.arduino.cc/built-in-built-in-built-in-communication/communication/communication/multiserialialmeialmeialmeialmelmega

From what i understand of the plot function it utilizes the main arduino connexion to work. Based on how the arduino uart work you can only have 1 com port connexion per com port. This means you can either have the plot or command line open for each uart connexion. It is possible with different version of arduino to have multiple com ports. On the arduino uno there is only one com port "Serial". On the mega i think there are 3 uart ports. If you use a external FTDI UART board you can have the plot window open for serial0 and have the FTDI board connected on Serial1 to have the command line window open. You will have to change your code a little to send commands to serial1.

Here are a couple links to help you.
https://docs.arduino.cc/tutorials/communication/TwoPortReceive

https://docs.arduino.cc/built-in-examples/communication/MultiSerialMega

https://www.amazon.fr/AZDelivery-Adaptateur-FT232RL-s%C3%A9rie-book/dp/B01N9RZK6I?th=1

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文