Differences between revisions 3 and 5 (spanning 2 versions)
Revision 3 as of 2009-09-12 09:17:23
Size: 6537
Editor: RanaAdhikari
Comment:
Revision 5 as of 2009-09-12 09:18:53
Size: 6589
Editor: RanaAdhikari
Comment:
Deletions are marked like this. Additions are marked like this.
Line 2: Line 2:


=== The Code ===
The below is the code as of 9/9/09
Line 3: Line 7:
{{{#!perl {{{

The FSS's SLOW PID Servo

The Code

The below is the code as of 9/9/09 attachment:slow.pl

# PID Servo for PSL-FSS (Slow)
# Tobin Fricke 2007-01-09

use strict;
#use Scalar::Util qw(looks_like_number);

sub looks_like_number {
    return ($_[0] =~ /^-?\d+\.?\d*$/);  #FIXME
}

use EpicsTools;

# Parameters
my $process  = 'C1:PSL-FSS_FAST';
my $actuator = 'C1:PSL-FSS_SLOWDC';
my $setpoint = 0;
my $blinkystatus = 0;

my ($KpParam, $KiParam, $KdParam) = 
        ('C1:PSL-FSS_SLOWKP', 'C1:PSL-FSS_SLOWKI', 'C1:PSL-FSS_SLOWKD');
my $timestepParam = 'C1:PSL-FSS_TIMEOUT';

#($KpParam, $KiParam, $KdParam) = (-0.001, -0.0025, -0.005);
# Old values were Kp=-.15, Ki=0,Dk=-0.005
# More parameters -- these ones actually have to be numbers
my @hard_stops = (-5.0, 5.0);  # Perl5 apparently doesn't have an Inf value
my $increment_limit = 0.01;

# Variables
my @u;  # outputs to the actuator
my @e;  # error signal                                                                                            

my $debug = 1;

print "Starting FSS Slow Servo\n";

# Shift some initial values into our registers                                                                    

while ($#u < 2) {
    unshift(@u, get_value($actuator));
    unshift(@e, 0);
    print("Current value of actuator = $u[0]\n");
}

# Start our controller                                                                                            

while (1) {
    # Get the current time step                                                                                   
    my $timestep = get_value($timestepParam);

    # Sleep for the rest of the time interval                                                                     
    sleep($timestep);

    # Blink the blinky light                                                                                      
    if ($blinkystatus) {
        $blinkystatus = 0;
    } else {
        $blinkystatus = 1;
    }

    if ($debug) {
        print "\nSLOW_BEAT --> $blinkystatus\n";
    }
    epWrite('C1:PSL-FSS_SLOWBEAT', $blinkystatus);


    # Make sure the "ENABLE" button is checked                                                                    
    if (get_value("C1:PSL-FSS_SLOWLOOP") == 0) {
        printf("FSS_SLOWLOOP disabled -- control loop disabled.\n");
        next;
    }

    # Make sure the loop is supposed to be active                                                                 
    if (get_value("C1:PSL-FSS_RCTRANSPD") < get_value("C1:PSL-FSS_LOCKEDLEVEL")) {
        print("Reference Cavity not locked -- control loop disabled.\n");
        next;
    }

    # Instead of using the actuation we requested in the previous step as the                                     
    # previous value of the actuation, we'll read the current actuation from                                      
    # EPICS.  This prevents us from monopolizing the slider.                                                      
    $u[0] = get_value($actuator);

    if ($debug) {
        print "\nActuator --> $u[0]\n";
   }

    # Make room for the present time                                                                              
    unshift(@e, undef);
    unshift(@u, undef);

    # Read the PID parameters in case they have changed                                                           
    my ($Kp, $Ki, $Kd) = get_value($KpParam, $KiParam, $KdParam);
    $Ki *= $timestep;
    $Kd /= $timestep;

    if ($debug) {
         print("Kp = $Kp\tKi = $Ki\tKd = $Kd\n");
    }

    # Read the current value of the Process Variable and the Setpoint                                             
    my $p = &get_value($process);
    my $s = &get_value($setpoint);

    # The basic finite-difference PID approximation                                                               
    $e[0] = $p - $s;
    # $u[0] = $u[1] + ($Kp + $Ki + $Kd)*$e[0] - ($Kp + 2*$Kd)*$e[1] + $Kd * $e[2];                                
    $u[0] = $u[1];
    $u[0] = $u[0] + $Ki * ($e[0]);
    $u[0] = $u[0] + $Kp * ($e[0] - $e[1]);
    $u[0] = $u[0] + $Kd * ($e[0] - 2*$e[1] + $e[2]);

    # Enforce hard stops and maximum |increment|                                                                  
    $u[0] = $u[1] + rail($u[0] - $u[1],$increment_limit);
    $u[0] = rail($u[0], @hard_stops);

    # Bullshit rounding to prevent epWrite from complaining about the                                             
    # number of digits in the $u[0] variable                                                                      
    my $tempn = int($u[0]*10000)/10000;
    $u[0] = $tempn;

    # Perform the actuation                                                                                       
    if ($debug) {
        print "Actuator <-- $u[0]\n";
    }
    epWrite($actuator, $u[0]);

    # Discard samples sufficiently far in the past                                                                
    pop(@e);
    pop(@u);
}

# We need to distinguish between literal numbers and EPICS                                                        
# process variables.  If we're given the name of a process                                                        
# variable, we'll use ezcaread to dereference it.                                                                 

sub get_value {
    my @results = ();
    foreach (@_) {
        if (looks_like_number($_)) {
            push(@results, $_);
        } else {
            my @epResult = epRead($_);
            pop @epResult or die "Could not read EPICS process variable.";
            push(@results, pop(@epResult));
        }
    }
    if ($#results == 0) {  #FIXME: Is this necessary?                                                             
        return $results[0];
    } else {
        return @results;
    }
}

sub rail {
    my $value = shift(@_);
    my @limits = @_;

    # If we're only given one value for the limits, we'll                                                         
    # assume the limits are symmetric, i.e. lim ==> (-lim,lim)                                                    
    if ($#limits == 0) {
        unshift(@limits, -$limits[0]);
    }

    if ($limits[1] < $limits[0]) {
        die "Upper limit is lower than lower limit!";
    }

    ($value < $limits[0] ? $limits[0] :
    ($value > $limits[1] ? $limits[1] : $value));
}

Frequency_Stabilization_Servo/SLOW_PID_servo (last edited 2012-01-03 23:02:39 by localhost)