Pid算法 + Arduino Uno + 控制小球在板子上运动

本文详细描述了一个使用ArduinoUno开发板、红外线传感器、SG90舵机、HC-05蓝牙模块和超声波传感器构建的项目,实现了根据小球位置调整板子倾斜、区域切换和安全暂停功能。代码中包含PID控制器和蓝牙通信的相关实现。
摘要由CSDN通过智能技术生成

一.硬件要求:

  1. Arduino Uno 开发板
  2. 两个红外线传感器用于检测小球位置
  3. 一个 SG90 舵机用于控制板子的倾斜角度
  4. 一个 HC-05 蓝牙模块,用于通过蓝牙与 Arduino 进行通信
  5. 一个超声波传感器用于测量距离

二.功能要求:

  1. 使用两个红外线传感器检测小球位置。
  2. 使用 SG90 舵机控制板子的倾斜角度。
  3. 使用超声波传感器测量距离。
  4. 使用 HC-05 蓝牙模块进行蓝牙通信。
  5. 实现了根据小球位置调整板子倾斜角度的功能。
  6. 实现了区域切换功能,并确保在每个区域停留的时间相同。
  7. 实现了在存在小球掉落风险时暂停蓝牙任务的功能。

三.代码实现

#include <Servo.h>

#include <SoftwareSerial.h>

// 定义最大测距范围

#define MAX_DISTANCE 200 // 最大测距距离,单位:厘米

// 舵机控制引脚

#define SERVO_PIN 9

// 红外线传感器引脚

#define IR_SENSOR_1 A0

#define IR_SENSOR_2 A1

// 超声波传感器引脚

#define TRIG_PIN 10

#define ECHO_PIN 11

// 蓝牙模块引脚

#define BLUETOOTH_RX 2

#define BLUETOOTH_TX 3

// 定义PID参数

#define KP 1.0 // 比例系数

#define KI 0.2 // 积分系数

#define KD 0.1 // 微分系数

// 定义PID控制器的输出范围

#define OUTPUT_MIN 0

#define OUTPUT_MAX 180

// 定义目标倾斜角度

#define TARGET_ANGLE_1 -30.0 // 第一个区域的倾斜角度

#define TARGET_ANGLE_2 0.0    // 第二个区域的倾斜角度

#define TARGET_ANGLE_3 30.0   // 第三个区域的倾斜角度

// 定义每个区域停留的时间(毫秒)

#define STAY_TIME 3000 // 3秒

// 创建舵机对象

Servo servo;

// 创建PID控制器对象

class PIDController {

private:

  double lastError;

  double integral;

public:

  PIDController() : lastError(0), integral(0) {}

  double compute(double input, double target) {

    double error = target - input;

    integral += error;

    double derivative = error - lastError;

    lastError = error;

   

    double output = KP * error + KI * integral + KD * derivative;

   

    // 限制输出范围

    if (output > OUTPUT_MAX) {

      output = OUTPUT_MAX;

    } else if (output < OUTPUT_MIN) {

      output = OUTPUT_MIN;

    }

   

    return output;

  }

};

PIDController pid;

// 定义当前区域

int currentZone = 1;

// 上次区域切换时间

unsigned long lastZoneChangeTime = 0;

// 声明蓝牙通信对象

SoftwareSerial bluetooth(BLUETOOTH_RX, BLUETOOTH_TX);

void setup() {

  // 初始化舵机控制引脚

  servo.attach(SERVO_PIN);

  // 初始化超声波引脚

  pinMode(TRIG_PIN, OUTPUT);

  pinMode(ECHO_PIN, INPUT);

  // 初始化蓝牙模块

  Serial.begin(9600);

  bluetooth.begin(9600);

}

void loop() {

  // 读取超声波传感器数据

  int distance = getDistance();

  // 根据超声波距离调整舵机角度

  double angle;

  if (distance <= 10) {

    angle = TARGET_ANGLE_1; // 如果小球距离太近,倾斜板子使小球往里滚

  } else {

    angle = pid.compute(getActualAngle(), getTargetAngle());

  }

  // 控制舵机

  servo.write(angle);

  // 如果距离上次区域切换超过STAY_TIME,则切换到下一个区域

  if (millis() - lastZoneChangeTime >= STAY_TIME) {

    currentZone = (currentZone % 3) + 1; // 循环切换区域

    lastZoneChangeTime = millis();

  }

  // 处理蓝牙指令

  handleBluetoothCommand();

}

// 函数:获取实际倾斜角度

double getActualAngle() {

  // 读取两个红外线传感器的值

  int sensorValue1 = analogRead(IR_SENSOR_1);

  int sensorValue2 = analogRead(IR_SENSOR_2);

  // 根据传感器读数计算倾斜角度

  double angle1 = map(sensorValue1, 0, 1023, -90, 90); // 将传感器1的值映射到-90到90度的范围

  double angle2 = map(sensorValue2, 0, 1023, -90, 90); // 将传感器2的值映射到-90到90度的范围

  // 计算平均倾斜角度作为实际倾斜角度

  double actualAngle = (angle1 + angle2) / 2;

  return actualAngle;

}

// 函数:获取目标倾斜角度

double getTargetAngle() {

  // 根据当前区域选择目标倾斜角度

  switch(currentZone) {

    case 1:

      return TARGET_ANGLE_1;

    case 2:

      return TARGET_ANGLE_2;

    case 3:

      return TARGET_ANGLE_3;

    default:

      return 0.0;

  }

}

// 函数:获取超声波测距结果

int getDistance() {

  // 发送一个10微秒的高电平脉冲给Trig引脚

  digitalWrite(TRIG_PIN, LOW);

  delayMicroseconds(2);

  digitalWrite(TRIG_PIN, HIGH);

  delayMicroseconds(10);

  digitalWrite(TRIG_PIN, LOW);

  // 计算脉冲持续的时间

  long duration = pulseIn(ECHO_PIN, HIGH);

  // 将时间转换为距离(单位:厘米)

  int distance = duration * 0.034 / 2;

  // 如果距离超出最大测距范围,返回最大距离

  if (distance >= MAX_DISTANCE || distance <= 0) {

    return MAX_DISTANCE;

  } else {

    return distance;

  }

}

// 函数:处理蓝牙指令

void handleBluetoothCommand() {

  // 检查是否有数据可读

  if (bluetooth.available() > 0) {

    // 读取接收到的数据

    char command = bluetooth.read();

   

    // 根据接收到的指令设置目标区域

    switch(command) {

      case '1': // 调整舵机到第一个区域的角度

        currentZone = 1;

        break;

      case '2': // 调整舵机到第二个区域的角度

        currentZone = 2;

        break;

      case '3': // 调整舵机到第三个区域的角度

        currentZone = 3;

        break;

      default:

        break;

    }

  }

  // 每个区域停留时间相同

  if (currentZone == currentZone) {

    // 如果当前区域与目标区域相同,则等待STAY_TIME毫秒

    delay(STAY_TIME);

    // 切换到下一个区域

    currentZone = (currentZone % 3) + 1;

    // 更新上次区域切换时间

    lastZoneChangeTime = millis();

  }

}

功能再度说明:

使用两个红外线传感器检测小球位置。

使用 SG90 舵机控制板子的倾斜角度。

使用超声波传感器测量距离。

使用 HC-05 蓝牙模块进行蓝牙通信。

实现了根据小球位置调整板子倾斜角度的功能。

实现了区域切换功能,并确保在每个区域停留的时间相同。

实现了在存在小球掉落风险时暂停蓝牙任务的功能

四。存在的(隐藏)问题:

1.硬件连接的效果可能和代码有一定出入。

2.运行过程中的实际运动可能与整体不符。

3.硬件本身可能存在一定问题,就比如舵机的齿轮卡住不转需要拧几下才能转动

4.硬件组装不稳定

5.虽然代码在编译器上通过,但难免会存在隐含问题导致硬件运动过程中出现偏差,要根据整体还要不断调试,是必不可少也是最麻烦的环节。

文章说明:为打电赛做准备。由于是三人一组,在商量好任务后,大家一起先以2023年的电赛C题的题目为原型进行制作,这里我做的是软件部分,首先我进行的是初步编写的工作,在后面与硬件连接时的适配说不定还要改好多东西,光想想就让人头疼,就相当于留个初稿纪念纪念吧。

#include <Servo.h>

#include <SoftwareSerial.h>

// 定义舵机控制引脚

#define SERVO_PIN 9

// 定义超声波传感器引脚

#define TRIG_PIN 10

#define ECHO_PIN 11

// 定义红外线传感器引脚

#define IR_SENSOR_LEFT A0

#define IR_SENSOR_RIGHT A1

// 定义蓝牙模块引脚

#define BLUETOOTH_RX 2

#define BLUETOOTH_TX 3

// 定义PID参数

#define KP 1.0 // 比例系数

#define KI 0.2 // 积分系数

#define KD 0.1 // 微分系数

// 定义PID控制器的输出范围

#define OUTPUT_MIN 0

#define OUTPUT_MAX 180

// 定义每个区域停留的时间(毫秒)

#define STAY_TIME 2000 // 2秒

// 定义每个区域的倾斜角度

#define ANGLE_1 -30.0 // 第一个区域的倾斜角度

#define ANGLE_2 -15.0 // 第二个区域的倾斜角度

#define ANGLE_3 0.0   // 第三个区域的倾斜角度

#define ANGLE_4 15.0  // 第四个区域的倾斜角度

#define ANGLE_5 30.0  // 第五个区域的倾斜角度

// 定义红外线传感器阈值

#define IR_THRESHOLD 500

// 定义超声波传感器最大测距范围(厘米)

#define MAX_DISTANCE 200

// 创建PID控制器对象

class PIDController {

private:

  double lastError;

  double integral;

public:

  PIDController() : lastError(0), integral(0) {}

  double compute(double input, double target) {

    double error = target - input;

    integral += error;

    double derivative = error - lastError;

    lastError = error;

    double output = KP * error + KI * integral + KD * derivative;

    // 限制输出范围

    if (output > OUTPUT_MAX) {

      output = OUTPUT_MAX;

    } else if (output < OUTPUT_MIN) {

      output = OUTPUT_MIN;

    }

    return output;

  }

};

// 创建PID控制器对象

PIDController pid;

// 创建舵机对象

Servo servo;

// 定义当前区域

int currentZone = 1;

// 上次区域切换时间

unsigned long lastZoneChangeTime = 0;

// 创建蓝牙通信对象

SoftwareSerial bluetooth(BLUETOOTH_RX, BLUETOOTH_TX);

void setup() {

  // 初始化舵机控制引脚

  servo.attach(SERVO_PIN);

  // 初始化超声波引脚

  pinMode(TRIG_PIN, OUTPUT);

  pinMode(ECHO_PIN, INPUT);

  // 初始化红外线传感器引脚

  pinMode(IR_SENSOR_LEFT, INPUT);

  pinMode(IR_SENSOR_RIGHT, INPUT);

  // 初始化蓝牙模块

  Serial.begin(9600);

  bluetooth.begin(9600);

}

void loop() {

  // 读取超声波传感器数据

  int distance = getDistance();

  // 根据超声波距离调整舵机角度

  double angle;

  if (distance <= 10) {

    angle = ANGLE_1; // 如果小球距离太近,倾斜板子使小球往里滚

  } else {

    // 使用getActualAngle()函数获取实际倾斜角度

    angle = pid.compute(getActualAngle(), ANGLE_3);

  }

  // 控制舵机

  servo.write(angle);

  // 如果距离上次区域切换超过STAY_TIME,则切换到下一个区域

  if (millis() - lastZoneChangeTime >= STAY_TIME) {

    currentZone = (currentZone % 5) + 1; // 循环切换区域

    lastZoneChangeTime = millis();

  }

  // 处理蓝牙指令

  handleBluetoothCommand();

}

// 函数:获取超声波测距结果

int getDistance() {

  // 发送一个10微秒的高电平脉冲给Trig引脚

  digitalWrite(TRIG_PIN, LOW);

  delayMicroseconds(2);

  digitalWrite(TRIG_PIN, HIGH);

  delayMicroseconds(10);

  digitalWrite(TRIG_PIN, LOW);

  // 计算脉冲持续的时间

  long duration = pulseIn(ECHO_PIN, HIGH);

  // 将时间转换为距离(单位:厘米)

  int distance = duration * 0.034 / 2;

  // 如果距离超出最大测距范围,返回最大距离

  if (distance >= MAX_DISTANCE || distance <= 0) {

    return MAX_DISTANCE;

  } else {

    return distance;

  }

}

// 函数:处理蓝牙指令

void handleBluetoothCommand() {

  // 检查是否有数据可读

  if (bluetooth.available() > 0) {

    // 读取接收到的数据

    char command = bluetooth.read();

    // 根据接收到的指令设置目标区域

    switch (command) {

    case '1': // 调整舵机到第一个区域的角度

      currentZone = 1;

      break;

    case '2': // 调整舵机到第二个区域的角度

      currentZone = 2;

      break;

    case '3': // 调整舵机到第三个区域的角度

      currentZone = 3;

      break;

    case '4': // 调整舵机到第四个区域的角度

      currentZone = 4;

      break;

    case '5': // 调整舵机到第五个区域的角度

      currentZone = 5;

      break;

    default:

      break;

    }

  }

}

// 函数:获取实际倾斜角度

double getActualAngle() {

  // 读取红外线传感器数据

  int leftSensorValue = analogRead(IR_SENSOR_LEFT);

  int rightSensorValue = analogRead(IR_SENSOR_RIGHT);

  // 根据传感器数据判断小球所在区域,并返回对应的倾斜角度

  if (leftSensorValue > IR_THRESHOLD && rightSensorValue > IR_THRESHOLD) {

    return ANGLE_3; // 小球在区域3

  } else if (leftSensorValue > IR_THRESHOLD) {

    return ANGLE_4; // 小球在区域4

  } else if (rightSensorValue > IR_THRESHOLD) {

    return ANGLE_2; // 小球在区域2

  } else {

    return ANGLE_1; // 小球在区域1

  }

}


这个代码是对真实电赛题的进一步靠近,比上面稍微全面了一点,能力到这了,在精确的写不下去了……



 

  • 15
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值