问题描述
小故事: 我目前正在做这个项目,涉及使用两辆汽车,分别称为A块和B块,B块必须使用PID,PD,PI或P与A块保持10厘米的距离。我正在使用PID 。 B块使用Arduino,而A块则由用户手动控制。模块B使用单极步进电机作为执行器,并使用超声波传感器来感应距离。我的教授希望电动机沿两个方向移动并具有不同的速度(慢速,中速和快速)。我一直在使用brett的PID,因为我以前在以前的实验室中都使用过它。
问题:
我对于如何为B块创建不同的速度存在问题,就像直观地知道,我想让B块移动,例如,如果汽车大于20厘米,则快;如果汽车大于20 cm,则 medium汽车在20厘米至14厘米之间,如果在14厘米至10厘米之间则慢。但是我只是不能直接使用从传感器获取的输入值来控制电动机,因为这会使它成为开放系统。因此,我使用了从Brett的PID代码中检索到的错误来控制步进电机。到目前为止,我已经通过设置myPID.SetoutputLimits(-800,800);
得到了工作指导。但是,当它试图使用误差来控制速度时,这是不可能的,因为误差总是在特定距离处波动。例如,在12厘米处,我会得到800或300左右。目前,我对如何通过PID实现对步进电机速度的控制感到困惑,对此问题的任何帮助将不胜感激。
#include "SR04.h"
#include <Stepper.h>
#include <PID_v1.h>
#define TRIG_PIN 7
#define ECHO_PIN 6
//intialization of Ultrasonic sensor
SR04 sr04 = SR04(ECHO_PIN,TRIG_PIN);
long s;
//intializing motor variables
int stepsPerRevolution = 2048;
int motorSpeed = 6;
Stepper myStepper (stepsPerRevolution,8,10,9,11);
//Declared PID variables
double Setpoint = 10; //desired temp value
double Input; //thermsitor
double Output; //DC motor
double Error;
//defined variables for PID parameters
double Kp=100,Ki=10,Kd=1;
//PID equation
PID myPID(&Input,&Output,&Setpoint,Kp,Kd,Ki,REVERSE);
void setup(){
Serial.begin(9600);
//setting PID
myPID.SetMode(AUTOMATIC);
myPID.SetoutputLimits(-800,800);
//speed intialized
myStepper.setSpeed(motorSpeed);
}
void loop(){
s=sr04.distance();
Input = s;
myPID.Compute();
Error = Input - Setpoint;
//Serial.print(Input);
//Serial.print(",");
//Serial.println(Setpoint);
Serial.println(Output);
//Serial.print(",");
//Serial.println(Error);
Error = Output;
//Away from Block B
if (0<Error<800){
myStepper.setSpeed(motorSpeed);
myStepper.step(-300);
} //slow speed
if (Error>=800){
myStepper.setSpeed(motorSpeed*2);
myStepper.step(-128);
} //fast speed
//Towards Block B
if (-800<Error<0) {
myStepper.setSpeed(motorSpeed);
myStepper.step(128);
} //slow speed
if (Error<=-800) {
myStepper.setSpeed(motorSpeed*2);
myStepper.step(128);
}//Fast speed
}
解决方法
您需要做的是计算需要多少更改当前速度以最小化距离误差。
您的错误计算方法不正确。
void loop()
{
long s=sr04.Distance();
Input = s; // using global variables to pass values to your PID
// is not a good idea. Use function parameters instead.
// You are storing a 32 bit value in a 16 bit variable!!!
// That's only the start of your problems.
myPID.Compute();
Error = Input - Setpoint; //
由于我们从一个主要的设计缺陷开始,所以我必须假设您将解决此问题并更改PID代码以接受和计算长整数,既作为输入值作为函数参数,又作为函数的类型。它的返回值。
您要做的是根据距设定点的距离误差计算PID,然后相应地调节当前速度。直接使用PID时效果最好,可以使用7种速度(1个停止,3个前进/ 3个倒退),但是我认为效果不会更好,我会把练习留给您。
我还没有尝试过,我手边没有任何汽车。这是我如何去做的骨架。调整PID应该是花费您最长的时间。
//...
// speeds are in RPMs.
long curSpeed = 0;
const long MAX_SPEED = XXX; // whatever you max speed is for your car.
const long MIN_NEG_SPEED = -XXX; // whatever you max speed is for your car going reverse.
const long MIN_SPEED = XXX; // below this absolute speed,we're stopped.
const int SLICE_TIME = 10; // time between readings and adjustments,in ms.
// you'll need to adjust this according to you minimum speed,and steps per turn.
const long STEPS_PER_TURN = 200; // change to whatever you steps/turn value is.
// you'll need to limit the output of your PID to match the acceleration your
// motors can handle for your particular car.
// returns the number of steps to run for our slice time.
long Steps(int speed)
{
if (-MIN_SPEED <= speed && speed <= MIN_SPEED)
return 0;
// compute number of steps for our slice time.
// choose slice time and minimum speed wisely!!
long steps = (SLICE_TIME * (speed * STEPS_PER_TURN)) / (60000L);
// for very low speeds. I've added this,because I'm unsure of the
// time domain behaviour of stepper library with less than 2 steps
if (-1 <= steps && steps <= 1)
{
if (speed < 0)
return -2;
else
return 2;
}
return int(steps);
}
void loop()
{
// You may want to filter the sr04 readings with a median of 5
// filter to limit input noise.
// You want to keep the car at a distance of 'set_point'
// from the leading car. distance_error is the error you want to
// minimize to zero by using the PID,and that's what should be
// the PID input.
//
// The way this works. We are rolling at speed curSpeed,we
// measure the error in distance from our set_point,feed that
// to the PID,then accelerate or decelerate by subtracting
// the output of the PID from the current speed.
//
// Note: you can add or subtract the PID to/from the current speed,// the sign of the PID depends on you coefficients and sensor.
// I've used subtraction here because that's how you express
// negative feedback mathematically. In real life,we'll use what
// best fits our needs. Usually it's the operation that makes P
// positive.
long distance_error = sr04.Distance() - setPoint;
long pid_out = myPID.Compute(distance_error);
// increment or decrement the current speed to try and reduce the error.
long speed = curSpeed - pid_out; // As usual,PID coefficients do matter
// for this to work well.
if (speed > MAX_SPEED)
speed = MAX_SPEED;
if (speed < MIN_NEG_SPEED)
speed = MIN_NEG_SPEED;
curSpeed = speed;
if (speed < 0)
speed = -speed;
myStepper.setSpeed(speed); // modulate speed
int steps = Steps(curSpeed);
if (steps)
myStepper.step(steps); // keep rolling.
}
我也没有尝试对其进行编译,因此这可能无法按原样进行编译。但是,大多数技巧和陷阱都已涵盖,如果您要使用PID路径,这应该给您一个领先的机会。但是我认为您的教授真的会想知道那个人是从哪里来的:)尽管如此,您还是应该尝试使其运行起来,这很有趣。
反之,没有PID,使用设定的速度要简单得多。它也可能更接近练习所需的内容。当然,汽车之间的距离会有所不同。而且它根本不使用PID。
const int MAX_SPEED = 3;
int speed = 0; // value range is [-MAX_SPEED,+MAX_SPEED]
long RPMS[MAX_SPEED + 1] = { 0,200,400,800 }; // in RPMs,assuming average speed will be around 400,in this case.
// For 3 speeds,the difference between speeds cannot be higher than max acceleration.
// You can add as many speeds as desired. More speeds = more precision.
const long STEPS_PER_TURN = 200; // change to whatever you steps/turn value is. MUST be 200 or more.
const int STEPS = STEPS_PER_TURN / 100; // 3.6° between speed adjustment.
// it is very small right now,so
// you will want to play with this value.
// this threshold gives some control over aceleration.
// and 'hardness' of distance tracking.
const long THRESHOLD = 0;
void loop()
{
// get the error in distance.
long distance_error = sr04.Distance() - setPoint;
// modulate speed.
if (distance_error > THRESHOLD)
++speed;
if (distance_error < -THRESHOLD)
--speed;
if (speed > MAX_SPEED)
speed = MAX_SPEED;
if (speed < -MAX_SPEED)
speed = -MAX_SPEED;
long rpm = RPMS[(speed < 0) : -speed : speed];
if (rpm)
{
myStepper.setSpeed(rpm);
myStepper.setSpeed((speed < 0) ? -STEPS : STEPS)
}
}
对于此代码,您必须选择速度和STEPS值,以使您获得加速而不会错过任何步骤。