PID控制在机器人中的应用:从参数获取到代码实现

一、机器人PID控制典型应用场景

1. 电机转速控制

在这里插入图片描述

2. 机器人直线运动控制

在这里插入图片描述

二、直流电机PID控制完整示例(Arduino)

1. 硬件连接

Arduino UNO ---- L298N驱动 ---- 直流电机
         |--- 编码器A相 -- D2
         |--- 编码器B相 -- D3
         |--- PWM输出 -- D9

2. 获取初始参数(Z-N法)

1、系统辨识法(需MATLAB工具)(MATLAB自动给出推荐PID参数)
2、齐格勒-尼古尔斯(Z-N)法(设置Ki=0, Kd=0,逐渐增大Kp直到系统出现等幅振荡(临界振荡),临界增益Ku(此时的Kp值),振荡周期Tu(峰值到峰值时间))
3、经验参数法(快速启动)
等等等

// 步骤1:纯P控制测试
void zn_tuning() {
  float Ku = 0;
  float Tu = 0;
  
  // 逐步增加Kp直到等幅振荡
  for(float Kp = 0.1; Kp < 5.0; Kp += 0.1) {
    myPID.SetTunings(Kp, 0, 0);
    if(oscillation_detected()) {  // 检测振荡
      Ku = Kp;
      Tu = measure_oscillation_period();
      break;
    }
    delay(3000);  // 每个参数测试3秒
  }
  
  // 根据Z-N规则设置PID
  float Kp = 0.6 * Ku;
  float Ki = 2 * Kp / Tu;
  float Kd = Kp * Tu / 8;
  myPID.SetTunings(Kp, Ki, Kd);
}

3. 完整电机控制代码

#include <PID_v1.h>
#include <Encoder.h>

// 编码器引脚定义
Encoder myEnc(2, 3);
const int motorPWM = 9;

// PID参数
double Kp = 0.8, Ki = 0.2, Kd = 0.05;
double Setpoint, Input, Output;

// 创建PID对象
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

void setup() {
  Serial.begin(115200);
  pinMode(motorPWM, OUTPUT);
  
  // PID初始化
  myPID.SetMode(AUTOMATIC);
  myPID.SetSampleTime(10);  // 10ms采样周期
  myPID.SetOutputLimits(0, 255);
  
  // 初始目标转速(转/分钟)
  Setpoint = 100.0;
}

void loop() {
  // 1. 读取编码器速度(实际值)
  static long oldPos = 0;
  long newPos = myEnc.read();
  float speed_rpm = (newPos - oldPos) * 60.0 / (ENCODER_PPR * SAMPLE_TIME); 
  oldPos = newPos;
  Input = speed_rpm;
  
  // 2. 计算PID输出
  myPID.Compute();
  
  // 3. 输出到电机
  analogWrite(motorPWM, Output);
  
  // 4. 串口监控
  static unsigned long lastPrint = 0;
  if(millis() - lastPrint > 100) {
    Serial.print("Setpoint:"); Serial.print(Setpoint);
    Serial.print(" Actual:"); Serial.print(Input);
    Serial.print(" Output:"); Serial.println(Output);
    lastPrint = millis();
  }
  
  delay(10);  // 保持10ms控制周期
}

三、机器人循迹PID控制(STM32 HAL库示例)

1. 红外传感器阵列读取

#define SENSOR_NUM 5
uint8_t Read_Line_Sensors(void) {
  uint8_t sensor_value = 0;
  for(int i=0; i<SENSOR_NUM; i++) {
    if(HAL_GPIO_ReadPin(GPIOA, sensor_pins[i]) 
      sensor_value |= (1 << i);
  }
  return sensor_value;
}

2. 位置误差计算

float Calculate_Position_Error(uint8_t sensor_data) {
  // 传感器加权计算
  // [-2, -1, 0, 1, 2] 对应最左到最右
  static const float weights[SENSOR_NUM] = {-2.0, -1.0, 0.0, 1.0, 2.0};
  float error = 0.0;
  uint8_t active_sensors = 0;
  
  for(int i=0; i<SENSOR_NUM; i++) {
    if(sensor_data & (1<<i)) {
      error += weights[i];
      active_sensors++;
    }
  }
  
  return (active_sensors > 0) ? error/active_sensors : 0;
}

3. PID循迹控制实现

PID_HandleTypeDef hpid;

void MX_PID_Init(void) {
  hpid.Kp = 0.5;   // 初始参数
  hpid.Ki = 0.01;
  hpid.Kd = 0.2;
  hpid.IntegralLimit = 100;
  hpid.OutputLimit = 100;
}

void Control_Loop(void) {
  // 1. 读取传感器
  uint8_t sensors = Read_Line_Sensors();
  
  // 2. 计算位置误差
  float error = Calculate_Position_Error(sensors);
  
  // 3. PID计算
  float output = PID_Calculate(&hpid, error);
  
  // 4. 差速控制
  int16_t left_speed = BASE_SPEED - output;
  int16_t right_speed = BASE_SPEED + output;
  
  // 5. 电机输出
  Set_Motor_Speed(MOTOR_LEFT, left_speed);
  Set_Motor_Speed(MOTOR_RIGHT, right_speed);
}

四、PID参数优化技巧

1. 现场调试步骤

  1. 基础测试:先设置Kp=Ki=Kd=0,逐步增加Kp直到机器人开始振荡
  2. 记录临界值:记下开始振荡时的Ku和振荡周期Tu
  3. Z-N参数计算
    # Python计算示例
    def zn_pid(Ku, Tu):
        Kp = 0.6 * Ku
        Ki = 2 * Kp / Tu
        Kd = Kp * Tu / 8
        return Kp, Ki, Kd
    

2. 常见问题处理

现象可能原因解决方案
机器人左右摇摆Kp太大或Kd太小减小Kp 10%或增大Kd 20%
转弯反应迟钝Kp太小增加Kp 20%
偏离路线后不纠正Ki太小增加Ki 30%
遇到直角弯冲出路外D项不足增加Kd 50%

五、进阶应用:自适应PID

class AdaptivePID {
public:
    void Update(float error) {
        // 根据误差大小动态调整参数
        if(fabs(error) > 2.0) {  // 大偏差
            Kp = 1.2; Ki = 0.0; Kd = 0.3;  // 快速响应
        } 
        else {  // 小偏差
            Kp = 0.8; Ki = 0.1; Kd = 0.5;  // 精细控制
        }
    }
    
    float Compute(float error, float dt) {
        // ...标准PID计算...
    }
private:
    float Kp, Ki, Kd;
};

六、调试工具推荐

  1. 实时曲线工具

    • Arduino Serial Plotter
    • Python Matplotlib
    # 简单串口绘图示例
    import serial
    import matplotlib.pyplot as plt
    
    ser = serial.Serial('COM3', 115200)
    plt.ion()
    while True:
        data = ser.readline().decode().strip().split(',')
        plt.plot([float(x) for x in data])
        plt.pause(0.01)
    
  2. 移动端调试APP

    • Bluetooth Terminal
    • Dabble(支持Arduino)

七、安全注意事项

  1. 电机保护

    void Emergency_Stop() {
        digitalWrite(MOTOR_EN, LOW);  // 立即禁用电机驱动
        myPID.SetMode(MANUAL);        // 停止PID计算
    }
    
  2. 参数保存

    // 保存优化后的参数到EEPROM
    EEPROM.write(0, Kp * 100); 
    EEPROM.write(4, Ki * 100);
    EEPROM.write(8, Kd * 100);
    

通过本教程,您已经掌握了PID在机器人中的完整实现流程。建议先从电机控制开始实验,再逐步扩展到更复杂的运动控制场景!

04-16 872
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值