一、机器人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. 现场调试步骤
- 基础测试:先设置
Kp=Ki=Kd=0
,逐步增加Kp
直到机器人开始振荡 - 记录临界值:记下开始振荡时的
Ku
和振荡周期Tu
- 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;
};
六、调试工具推荐
-
实时曲线工具:
- 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)
-
移动端调试APP:
- Bluetooth Terminal
- Dabble(支持Arduino)
七、安全注意事项
-
电机保护:
void Emergency_Stop() { digitalWrite(MOTOR_EN, LOW); // 立即禁用电机驱动 myPID.SetMode(MANUAL); // 停止PID计算 }
-
参数保存:
// 保存优化后的参数到EEPROM EEPROM.write(0, Kp * 100); EEPROM.write(4, Ki * 100); EEPROM.write(8, Kd * 100);
通过本教程,您已经掌握了PID在机器人中的完整实现流程。建议先从电机控制开始实验,再逐步扩展到更复杂的运动控制场景!