|
|
|
![]() |
|
|||||||
|
||||||||
![]() |
| Thread Tools | Rate Thread | Display Modes |
|
#1
|
||||
|
||||
|
Keeping two motors in sync
Excuse any ignorance I may have since I am reaching out on behalf of our programing team. We have two lead screws that we are trying to keep in sync with each other. We are using these encoders which are mechanically attached to the screws. http://www.andymark.com/product-p/am-2899.htm
Does anyoen have any ideas or input as to how they would incorporate a PID loop that would take the encoder values and slow the faster screw down so that both are in sync? I know it may seem simple to some btu our team is strugling to find an exampl as to how to implement this type of design. ANy info or direction is appreciated. Thanks. http://cdn3.volusion.com/vyfsn.knvgw...jpg?1421043935 |
|
#2
|
||||
|
||||
|
Re: Keeping two motors in sync
how close are the screws to each other? The easiest way to do this is to tie them mechanically together with a chain and a pair of sprockets.
|
|
#3
|
||||
|
||||
|
Re: Keeping two motors in sync
What is the driver interface to the lead screws? Is the driver commanding speed or position? speed: driver pulls joystick back and expects the leadscrews to turn faster in proportion to how far he pulls back position: driver pulls joystick back and expects the leadscrews to "follow" the joystick angle (height proportional to joystick angle). Last edited by Ether : 02-16-2015 at 10:36 PM. |
|
#4
|
|||||
|
|||||
|
Re: Keeping two motors in sync
What ether said. The key question is if your pid loop is controlling position or speed.
If you're controlling speed then it's nearly hopeless, since any speed errors will build up over time into large position errors. If you're controlling position, then it's relatively easy. I'm assuming you have basic familiarity with position PID loops, though. With that caveat, you have two options: 1. Pick one of the screws as a master. This is the only screw you directly command a position for. Set up the other screw as a slave by passing the master's feedback position as the position setpoint/command for the slave. pro: slightly easier to set up, guarantees the slave will be at the same position as the master, even if the master hangs up. Con: slave will always trail the master, since the best it can do is zero error to the master position, and there's bound to be some error. Best for really tightly controlled servos. 2. Two separate pid loops, one for each screw. Command for both pid loops is a single position value that you slowly ramp from one position to the next. Instant changes won't work, because each screw will try to get there as fast as possible, which means at different speeds. Slowly ramping the commanded position means each screw follows the ramped position second by second, which means the screws go up evenly since they're both trying to hit the same target. pros: even movement aside from whatever error is in your pid. Con's: somewhat more complicated, you need to ramp the position somewhat slower than the top speed of the slower motor. Ramping the position too fast means the motors can't keep up and it basically turns back into the instant setpoint change scenario |
|
#5
|
||||
|
||||
|
Re: Keeping two motors in sync
Thank you all. We are doing this by position not speed. Are there any examples of code that you may know of that are similar to this setup? If not no big deal, thought this was common enough that there may be.
|
|
#6
|
|||
|
|||
|
Re: Keeping two motors in sync
Quote:
If you are committed to doing this electronically, Ether posted an architecture in another thread that could work. First you need to "Home" your carriage to a known stop each time you boot and zero your two encoders while you know the carriage is level. Then you make one motor a "partial" slave to the other. In a PID like loop, you drive the master to the desired position P as sensed by encoder E1. You simultaneously drive the second motor to the same position P as sensed by encoder E2 but in addition add the error between the master and slave (E2-E1). This way both motors start moving at the same time, but you also drive the error in final position towards zero. If you update your motor drive voltages fast enough, you may be able to match velocities as well. |
|
#7
|
||||
|
||||
|
Re: Keeping two motors in sync
Yes we thought our programing team was capable of this so we did not tie them with a timing belt. We have it 100% built and at this time adding the mechanical tie is not realistic since this is the last day. We do have the limit switches for the home position the problem is our programing team bit off more than they could chew so now we are tryign to help them. That wahy I was looking for sample code of PID loops in C++
|
|
#8
|
|||
|
|||
|
Re: Keeping two motors in sync
|
|
#9
|
||||
|
||||
|
Re: Keeping two motors in sync
We started out with 2 motors and 2 encoders on a lift with 2 PID loops and could not keep the 2 in sync close enough to prevent binding. We linked them with a criss-cross chain and 2 sprockets and then tied them together in software under the control of 1 PID controller and 1 encoder.
Here was the code we used to tie them together. We like to implement limit switches and inverting of motors if needed as close to the hardware as possible. This ensures the motors are never fighting each other. Code:
/*
* DualCANTalon.h
*
* Created on: Jan 29, 2015
* Author: Team2481
*/
#ifndef SRC_COMPONENTS_DUALCANTALON_H_
#define SRC_COMPONENTS_DUALCANTALON_H_
#include "WPILib.h"
class DualCANTalon : public PIDOutput {
private:
CANTalon* mAMotor;
CANTalon* mBMotor;
bool mAInverted;
bool mBInverted;
public:
DualCANTalon(int motorA, int motorB, bool AInvert, bool BInvert);
virtual ~DualCANTalon();
void PIDWrite(float output);
void Set(float speed);
float GetOutputCurrent();
float GetOutputVoltage();
};
#endif /* SRC_COMPONENTS_DUALCANTALON_H_ */
Code:
/*
* DualCANTalon.cpp
*
* Created on: Jan 29, 2015
* Author: Team2481
*/
#include <Components/DualCANTalon.h>
DualCANTalon::DualCANTalon(int motorA, int motorB, bool AInvert, bool BInvert) {
mAMotor = new CANTalon(motorA);
mBMotor = new CANTalon(motorB);
mAInverted = AInvert;
mBInverted = BInvert;
mAMotor->ConfigNeutralMode(CANTalon::kNeutralMode_Brake);
mBMotor->ConfigNeutralMode(CANTalon::kNeutralMode_Brake);
}
DualCANTalon::~DualCANTalon() {
delete mAMotor;
delete mBMotor;
}
void DualCANTalon::PIDWrite(float output) {
Set(output);
}
void DualCANTalon::Set(float speed) {
mAMotor->Set(mAInverted ? speed * -1 : speed);
mBMotor->Set(mBInverted ? speed * -1 : speed);
}
float DualCANTalon::GetOutputCurrent() {
float outputCurrentA = mAMotor->GetOutputCurrent();
float outputCurrentB = mBMotor->GetOutputCurrent();
float averageCurrent = (outputCurrentA + outputCurrentB) / 2;
return averageCurrent;
}
float DualCANTalon::GetOutputVoltage() {
float outputVoltageA = mAMotor->GetOutputVoltage();
float outputVoltageB = mBMotor->GetOutputVoltage();
float averageVoltage = (outputVoltageA + outputVoltageB) / 2;
return averageVoltage;
}
|
|
#10
|
|||||
|
|||||
|
Re: Keeping two motors in sync
Quote:
OBTW, by "criss-cross chain", you don't mean a figure 8, do you? It's an idea I had about two years ago when thinking about linkage drives, and that we tried to implement as a novelty in our "infinite possibilities" sign over the pit last year. Operationally, it sounds like a recipe for short chain life. |
|
#11
|
|||||
|
|||||
|
Re: Keeping two motors in sync
Important question. Are the two lead screws pushing the same carriage, or are they pushing separate carriages?
On our bot, we have two screws and two independent carriages. Each carriage lifts one side of the crate. So we only have to stay mostly level, since we won't have any binding issues or anything. I modified the PID controller class so it ramps setpoints, and we're running two PID loops that ramp the setpoint position at the same rate. If the screws start level, the setpoint positions will stay level since they're moving at the same rate. The screws aren't guaranteed to stay level due to following error, but they manage fairly well. The biggest problem is that (currently) the moving setpoint is the only synchronization between the sides. If the left side jams or hits max command and starts falling behind the setpoint, the right side doesn't know or care and will continue merrily on its way. Practically speaking, this limits our max speed, since we have to make sure we never have to hit max command for either motor, even under varying battery conditions and loading. That's obviously not ideal, but I'm still working out the most robust way to tackle that problem. Anyways, I have code for this approach if you're interested. Another approach is what Ether talks about in this thread. There, you're semi-slaving one motor to another, and just commanding pure setpoint positions. I think that will work as well, and will take care of things if the master motor jams or maxes out, but it still doesn't handle things if the slave motor jams or maxes out. You're basically assuming that the master motor/screw is the weaker/easier to jam of the two. I don't have code for that, so you'd have to work out your own modifications to the PIDController class. |
|
#12
|
||||
|
||||
|
Re: Keeping two motors in sync
We have the same setup as you two lead screws with two seperate carriages each with their own motors and encoders. So binding is only an issue with the tote. We would like to keep it within 2 inches. We can tolerate more but within 2 is acceptable.
If you dont mind giving us an example we would greatly appreciate it. I could email you or PM you. Our programers understand the concept of PID but were not sure what values to put where. After some reading its becoming more clear, but we have never done it so not sure what we dont know. Our main problem is that with no load at full speed one side is slightly faster. We want the PID to slow the faster since the slower woudl already be at 100%. So based on this I assume we woudl make our slower lift the "master" and slave the faster side to track the slower? I understand what your saying about the master side continuing even if the other side got stoped for some reason. I would assume you could code it so that if a max seperation value was exceeded you could stop the higher side. Thanks for your response. This will be our project over the next few weeks. |
|
#13
|
|||||
|
|||||
|
Re: Keeping two motors in sync
I don't have code for a master-slave system yet. I do have C++ code for a PIDController derivative that will let you smoothly ramp the setpoint from A to B at a given rate. As long as you have the PID loops tuned well and you keep the rate below the max speed for your slower side, things should work and stay level.
I should've asked earlier, but what language are you guys using? If it's C++, I can cleanup and better document my code tonight and post it tomorrow. If it's Java, I'll have to port things, which will take a day or two, and won't be tested, obviously. If it's Labview.... I'll have to think about how to approach the problem in Labview. |
|
#14
|
||||
|
||||
|
Re: Keeping two motors in sync
I threw this together quickly. Can you guys critique it? Maybe someone can debug it on real hardware, or in a simulation. Code:
double change, PosCmd, error1, e1pos, error1i, error1d, cmd1, error2a, e2pos, cmd2a, error2b, error2bi, cmd2b, cmd2; // initialize these: double RLPC = 0; // Rate-Limited Position Command double previous_error1 = 0; // tuning constants: double rateLimit = 1; // allowable position change per iteration double i1Limit = 1; // integrator clamp for M1 double i2bLimit = 1; // integrator clamping value for M2 double Kp1 = 0; // PID gains double Ki1 = 0; double Kd1 = 0; double Kp2 = 0; double Ki2 = 0; // this is your control loop: // rate-limit the change in position command change = PosCmd - RLPC; if (change>rateLimit) change = rateLimit; else if (change<-rateLimit) change = -rateLimit; RLPC += change; // PID controller for M1 (Master) error1 = RLPC - e1pos; // closed loop error error1i += error1; // integrate the error if (error1i>i1Limit) error1i = i1Limit; // clamp the integrated error else if (error1i<-i1Limit) error1i = -i1Limit; error1d = error1 - previous_error1; // rate of change in error1 for D term previous_error1 = error1; // save for next iteration cmd1 = Kp1*error1 + Ki1*error1i + Kd1*error1d; // P + I controller for M2 (slave) error2a = RLPC - e2pos; // closed loop error for Proportional part cmd2a = Kp2*error2a; error2b = e1pos - e2pos; // closed loop error for Integral part error2bi += error2b; // integrate the error if (error2bi>i2bLimit) error2bi = i2bLimit; // clamp the integrated error else if (error2bi<-i2bLimit) error2bi = -i2bLimit; cmd2b = Ki2*error2bi; cmd2 = cmd2a + cmd2b; Last edited by Ether : 02-18-2015 at 05:44 PM. Reason: added code comments |
|
#15
|
|||||
|
|||||
|
Re: Keeping two motors in sync
I am wondering about the slave part:
Quote:
|
![]() |
| Thread Tools | |
| Display Modes | Rate This Thread |
|
|