Chief Delphi

Chief Delphi (http://www.chiefdelphi.com/forums/index.php)
-   C/C++ (http://www.chiefdelphi.com/forums/forumdisplay.php?f=183)
-   -   Keeping two motors in sync (http://www.chiefdelphi.com/forums/showthread.php?t=134738)

amesmich 16-02-2015 22:09

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

wilsonmw04 16-02-2015 22:12

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.

Ether 16-02-2015 22:27

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).



Kevin Sevcik 16-02-2015 23:32

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

amesmich 17-02-2015 08:15

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.

gpetilli 17-02-2015 08:55

Re: Keeping two motors in sync
 
Quote:

Originally Posted by amesmich (Post 1445566)
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.

As said in a previous post, the simplest way is to mechanically tie the two systems together. Belts or chains work well for this - you can keep the two motors if you need the extra lift. It can be solved electronically, but it is much more difficult and any mismatch in either speed or position will likely bind up the carriage between the two screws. Our team went with a single screw to eliminate the matching issue.

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.

amesmich 17-02-2015 09:20

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++

gpetilli 17-02-2015 11:51

Re: Keeping two motors in sync
 
http://first.wpi.edu/FRC/roborio/rel...Subsystem.html

kylelanman 18-02-2015 01:07

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;
}


GeeTwo 18-02-2015 02:11

Re: Keeping two motors in sync
 
Quote:

Originally Posted by kylelanman (Post 1446194)
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.

Belt and suspenders? No argument here.

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.

Kevin Sevcik 18-02-2015 12:51

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.

amesmich 18-02-2015 17:02

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.

Kevin Sevcik 18-02-2015 17:21

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.

Ether 18-02-2015 17:27

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;




GeeTwo 18-02-2015 17:49

Re: Keeping two motors in sync
 
I am wondering about the slave part:
Quote:

Originally Posted by Ether (Post 1446600)
Code:

// 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;


Are you intentionally basing the P value on the error from the set point, but the I value on the error from the master? If so, I'm going to have to puzzle a bit as to what would happen if the master jammed.

Ether 18-02-2015 17:57

Re: Keeping two motors in sync
 
Quote:

Originally Posted by GeeTwo (Post 1446612)
I am wondering about the slave part:

Are you intentionally basing the P value on the error from the set point, but the I value on the error from the master?

Yes.

Quote:

If so, I'm going to have to puzzle a bit as to what would happen if the master jammed.
Additional logic could be added to detect and mitigate the consequences of either master or slave jamming.



Kevin Sevcik 18-02-2015 18:42

Re: Keeping two motors in sync
 
Quote:

Originally Posted by GeeTwo (Post 1446612)
I am wondering about the slave part:

Are you intentionally basing the P value on the error from the set point, but the I value on the error from the master? If so, I'm going to have to puzzle a bit as to what would happen if the master jammed.

That's Ether's proposed master-slave dual PID from the thread I linked above. The consequences of the master jamming would depend on the max setpoint error and the P and I values.
If P*error < i2bLimit, then the I term will halt the slave, quickly or slowly depending on I and possibly reverse it and drag it back down to e1pos. Things would obviously settle down somewhere near where P*error = i2bLimit, or at e1pos, once the I term stopped oscillating around e1pos.
If P*error > i2bLimit, then e2is going to go past e1pos and stabilize wherever P*error finds drops down below i2bLimit.

Ether,
I think your pseudo code implements your proposed controller correctly, I'm just not entirely certain the actual dynamics would work as intended. I think it'd take a good deal of tweaking to get your slave's I value tuned well enough that it's going to fight that RLPC trying to drag it upwards with ever increasing error.

Here's something that just popped into my head. What if you limit the RLPC sort of how you limit the integral error? Since we're not doing proper servoing with fatal following errors or anything, we can just choose to slow down (or stop) the ramp if a motor gets in a bind. Super Pseudo code:
1. Calculate RLPC as above, probably saving the change value / actual increment.
2. Calculate output commands for both motors as 2 standard independent PIDs following the RLPC.
3. If both motor outputs are less than abs(1.0) then you're done and you set values and wait for the next loop.
4. If a motor value is >= abs(1.0), then you back calculate the necessary incremental change to hit 1.0 output on the limited motor. Go back to step 1, but with your new (temporary) rate limit (which may be 0). Make sure you only loop back at most twice. Any more would be pointless since you've already tried to reduce things for both motors.

It's a good bit trickier with the back calculating and all, but I think it accomplishes our actual goal. If I'm thinking about it correctly, then what should happen is if either side gets stuck, the RLPC just stops moving. The free side will servo up to the RLPC, obviously, but the RLPC should only be a small-ish distance ahead. Whatever error it takes to get the stuck side to hit 1.0 output. (Possibly we ignore the I and D terms when figuring this.) I think initial moves from standstill and reversals would be interesting, since limiting the rate to only what's currently achievable by the motor would act as a defacto soft-ish start. With an RLPC controller (which is the modified PID I've already implemented) the starts can be a little bumpy, since you can pretty quickly hit maximum output cause your setpoint has infinite acceleration and the motors don't. The setpoint can get far ahead of the normal steady state error, so the motors can over shoot once they break free and start moving.

Mind you, it'll run a little strange what with the speeding up and slowing down, but I think if your PIDs are already pretty stable, it shouldn't add any weird oscillations from the two systems interacting with each other through mechanical delay. Worst case is the RLPC just stops moving completely because the two sides are oscillating around it out of phase and at max command values. You'd have to throw up your hands and curse me for peddling hare-brained poorly thought out control algorithms, but your system would probably survive.

amesmich 18-02-2015 19:34

Re: Keeping two motors in sync
 
Quote:

Originally Posted by Kevin Sevcik (Post 1446598)
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.

We are using C++. I just read through all these posts and they are a huge help. We appreciate the feed back, and would very much appreciate a look at your code. Thanks

Ether 18-02-2015 19:55

Re: Keeping two motors in sync
 
Quote:

Originally Posted by Kevin Sevcik (Post 1446649)
Ether,
I think your pseudo code implements your proposed controller correctly, I'm just not entirely certain the actual dynamics would work as intended. I think it'd take a good deal of tweaking to get your slave's I value tuned well enough that it's going to fight that RLPC trying to drag it upwards with ever increasing error.

The purpose of the position command rate-limiting (RLPC) is to help M2's integrator keep up. Yes it's an integrator so it's going to lag somewhat, but don't forget the contribution of M2's proportional action which has no lag.

The best way to settle the matter I guess is to run it on hardware or a simulation.



Jared Russell 18-02-2015 20:14

Re: Keeping two motors in sync
 
This is fundamentally no different than getting a differential drive robot to drive straight. You want both sides of the mechanism to end up in the same place at the same time.

In drivetrains people often use a controller that is something like:

Code:

output_steering = PID(steering_error)
output_throttle = PID(distance_error)

left_command = output_throttle + output_steering
right_command = output_throttle - output_steering

PID() could be any feedback (or feedforward + feedback) controller you want, and the steering and throttle controllers would have different gains.

distance_error is usually computed as:

Code:

distance_error = desired_distance - (left_distance + right_distance) / 2
...and steering error could be calculated by a gyro, or by integrating encoder values over time.

This has worked well on hundreds of FIRST robots for driving, and generalizes as a simple way for coordinating two separate mechanisms. You just need to be careful with how you handle saturation/integral windup and you're golden.

Ether 18-02-2015 21:27

Re: Keeping two motors in sync
 
Quote:

Originally Posted by Jared Russell (Post 1446708)
You want both sides of the mechanism to end up in the same place at the same time.

That's half the problem. The other half is keeping the sides synchronized while doing so.

For an elevator the synchronization is important because of binding. For drivetrains it's important because the final location of the robot depends on the sides staying in sync during the journey..


Quote:

In drivetrains people often use a controller that is something like:

Code:

output_steering = PID(steering_error)
output_throttle = PID(distance_error)

left_command = output_throttle + output_steering
right_command = output_throttle - output_steering


Nice and symmetric. No master/slave.


Quote:

distance_error is usually computed as:

Code:

distance_error = desired_distance - (left_distance + right_distance) / 2

I think that's a big part of making this work, so that the throttle and steer controllers don't fight each other. They can be satisfied independently.

Quote:

...and steering error could be calculated by a gyro, or by integrating encoder values over time.
Could you elaborate on what you mean here by "integrating encoder values over time"? Could it be as simple as just using encoder counts instead of rate?




kylelanman 18-02-2015 23:02

Re: Keeping two motors in sync
 
Quote:

Originally Posted by GeeTwo (Post 1446225)
Belt and suspenders? No argument here.

OBTW, by "criss-cross chain", you don't mean a figure 8, do you?

Yes I do! We have a guide/spacer at the crossing point to ensure the chain doesn't hit itself.

Jared Russell 18-02-2015 23:35

Re: Keeping two motors in sync
 
Quote:

Originally Posted by Ether (Post 1446751)
Could you elaborate on what you mean here by "integrating encoder values over time"? Could it be as simple as just using encoder counts instead of rate?

Yeah. The process variable for the steering controller in a differentially steered vehicle is heading, which is just the difference in encoder counts times a constant (to do a unit conversion). The constant is theoretically the inverse of the radius of the wheelbase; in practice you can tune it empirically to incorporate slip (if desired).

In the generalized motor synchronization case, using (left_distance - right_distance) would be fine.

Using a trajectory generator to make nice smooth, non-saturated position setpoint profiles is a great way to help solve the saturation/windup problem.

GeeTwo 18-02-2015 23:40

Re: Keeping two motors in sync
 
Quote:

Originally Posted by kylelanman (Post 1446821)
[Figure 8 chain] Yes I do! We have a guide/spacer at the crossing point to ensure the chain doesn't hit itself.

What's the angle between the two axles? Teeth on each sprocket? Distance between axles' CPA? Chain size? We're trying to do a figure 8 roller chain at a massive scale for our team motto, "Infinite Possibilities", and could use a bit of help in art of the possible here. Our team logo is a 7-tooth sprocket, so we're really limited in how much horizontal spacing we get for any given angle. Our massive scale last year didn't work out so well - we went with about an 8" pitch on the chain (don't quote me; I wasn't part of the team on this) using masonite board. At some point, the chain fell on the floor and shattered. We turned off the motor and just put up the two sprockets with the "tiger eye" in the center hole. It still looked cool, but not nearly as cool as we'd hoped.

Kevin Sevcik 18-02-2015 23:58

Re: Keeping two motors in sync
 
1 Attachment(s)
So here's a stripped down version of our robot code with just the lift stuff and the rate controlled PID that I worked up.

There's some features I added to the PID class that may not be relevant to your system. I added a Kv term for velocity feedforward based on whatever your ramp rate is. There's also limit switches integrated in there so the PID stops if a limit is hit in the direction you're moving. It both prohibits motor command in that direction, and updates the target setpoint to whatever the current position is when you run into a limit. If you're not using limits, just leave those blank or set to NULL and that logic will be skipped. To accommodate different wiring practices, the bool limitHit parameter sets what value indicates that a limit has been hit/tripped. There's a few others as well, so ask if there's any confusion

Ether 19-02-2015 00:43

Re: Keeping two motors in sync
 
Quote:

Originally Posted by Jared Russell (Post 1446837)
Using a trajectory generator to make nice smooth, non-saturated position setpoint profiles is a great way to help solve the saturation/windup problem.

Attached is a simple trajectory generator in Maxima. It inputs max accel, max speed, and distance to destination... and outputs equations for and a plot of speed and distance vs time.

Edit: moved attachments here.



amesmich 19-02-2015 06:37

Re: Keeping two motors in sync
 
This is amazing. I am learned so much. Kevin, Ether, and Jared. You have helped more than you know. I wish I had more to say but I am busy reading whay you wrote and this site which has helped me wrap myhead around the variables and acronyms you have been using. Thanks.


All times are GMT -5. The time now is 13:12.

Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.
Copyright © Chief Delphi