如何在Java中实现多线程PID控制器?
-
06-07-2019 - |
题
我是Java的新手,所以还没有完全掌握多线程的概念。我想创建一个允许我这样做的PIDController类:
ControllerMethods methods = new ControllerMethods()
{
public long getError(long setpoint)
{
//get an input
}
public void setOutput(long value)
{
//do something
}
public void isComplete(long setpoint)
{
return getError() == 0;
}
};
PIDController motorPID = new PIDController(setpoint, kp, ki, kd, methods);
motorPID.run();
//runs the PID controller to completion (methods.isComplete() == true)
motorPID.run(false);
//starts the PID controller in a separate thread, allowing
//continual monitoring in the current thread
while(motorPID.isRunning())
{
//do something else
if(condition1)
motorPID.pause();
//pause the PID controller, preventing the integral from increasing
else if(condition2)
motorPID.stop();
}
我已经研究了如何计算标准PID算法,但我无法弄清楚如何提供异步功能。
有人可以告诉我如何实现类似的API吗?
解决方案
您已经为 PIDController
实现了 run()
方法,因此您还应该实现 Runnable
接口:
class PIDController implements Runnable {
....
}
现在你可以通过调用:
启动你的PIDController asynchonouspidControllerThread = new Thread( pidController );
pidControllerThread.start();
对于同步(如果需要),你应该看一下sun 并发指南。
其他提示
到目前为止,将线程附加到任何东西的最佳机制是将执行工作的对象与作为线程的对象分开。 Runnable接口很有吸引力,因为它允许人们将对象传递给Thread构造函数或Executor,然后运行它。但是,如果您的对象的生命周期管理要求不在“运行到完成”之外,例如暂停,那么在大多数情况下,您会发现更适合管理对象中的线程以便您知道哪个线程正在运行(是的,您可以在要运行的条目上将实例对象设置为Thread.currentThread(),但是......)。
所以,我认为你拥有的是一个良好的开端。您需要添加一些锁定来帮助自己管理pause()和其他线程控制。
public class PIDController { private final Object myLock = new Object(); private final ControllerMethods ctrl; private volatile Thread thread; private volatile Runner runner; private int pidInterval = 700; private final double setPoint, kp, ki, kd; public PIDController( double setPoint, double kp, double ki, double kd, ControllerMethods inst ) { this.ctrl = inst; this.setPoint = setPoint; this.kp = kp; this.ki = ki; this.kd = kd; } public void pause() { synchronized( myLock ) { if( runner.paused ) { throw new IllegalOperationException(this+": already paused"); } runner.paused = true; } } public void resume() { synchronized( myLock ) { if( !runner.paused ) { throw new IllegalOperationException(this+": already resumed"); } runner.paused = false; } } public bool isRunning() { return running; } public void start() { if( thread != null ) { throw new IllegalOperationException( this+": already running"); } myThread = new Thread( runner = new Runner() ); myThread.start(); } public void stop() { if( runner == null ) { throw new IllegalOperationException( this+": PID is not running"); } runner.running = false; if( runner.paused ) resume(); runner = null; } // It is important, anytime that you implement a stoppable Runnable, that // you include the "running" flag as a member of an innner instance class like // this so that when you ask this instance to stop, you can immediately restart // another instance and not have the two threads observing the same "running" flag private class Runner implements Runnable { volatile bool running = false, bool paused; public void run() { running = true; while( running ) { // do this at the top of the loop so that a wake from // pause will check running, before recomputing. reComputePID(); // Use the double check idiom to if( paused ) { synchronized( myLock ) { while( paused ) { myLock.wait(); } } } Thread.sleep( pidInterval ); } } } public void reComputePID() { ... } }
不隶属于 StackOverflow