Are threads stopped during step response tests?

Moderators: TomKerekes, dynomotion

Roamin
Posts: 25
Joined: Tue Dec 13, 2022 10:34 pm

Are threads stopped during step response tests?

Post by Roamin » Fri Jan 06, 2023 8:29 pm

Hi Tom ;)

I need your help once more. My machine is setup and everything works, now comes the tuning phase. I'm controlling old Fanuc drives that require a tachometer signal. This signal is generated within KFlop, using the encoders and the DAC. This works fine to move the axes , I tried simple G-Codes and everything reacts properly.

Now I want to use the step response screen for channel 0 to tune my motors but the moment I press either move or step, all 3 axes start moving and fall into TACH ALARM. I tried entering very low values for max limits, motion profile , step and PID is set to 0/0/0. As soon as I press move the X axis moves very quick and TACH ALARM is triggered.

What usually triggers this alarm is when the tach signal is not present at all. Since this signal is generated by thread 1 in a forever loop that monitors the position of the axes and generates the tach when the position moves, it's leading me to think that thread 1 stops working when using the step response tool? Are all threads stopped when trying the step response tool?

This is my first time ever trying to tune a motor so I'm still learning a lot. I started by reading different forums and the kflop manual on step response, and I thought I understood how V / A and step time / size and so I lowered them in hopes to have the axis move very slowly and confirm the tach is still generated , but even the lowest values have the axis move the exact same speed (very fast) and tach alarm is generated.

What is confusing me is that if I try axis Z it seems to be able to move super fast without triggering the tach alarm, but it moves completely all the way to the limit switch and obviously is stopped then.. Which leads me to think that the tach is still generated in thread 1 or otherwise it would also fall in tach alarm way before it can reach the limit switch.

Here's a screenshot of the step response for my z axis. No matter what I enter it shoots up like crazy until it hits the limit.
Attachments
step.png

User avatar
TomKerekes
Posts: 2546
Joined: Mon Dec 04, 2017 1:49 am

Re: Are threads stopped during step response tests?

Post by TomKerekes » Fri Jan 06, 2023 9:18 pm

Hi Roamin,
Are all threads stopped when trying the step response tool?
No

It rarely makes sense to use the Step Button (trajectory with infinite velocity) so please only use the Move button.

Plotting Position rather than Velocity usually is easier to understand. Actually if you post the raw data we can plot it however we wish.

Pushing the Move (or Step) button causes the axis to be disabled, downloads all parameters from the Step Response, Config, and IIR Filter screens to KFLOP. You didn't post those screens. Do all these parameters match whatever parameters are in use when it works with GCode and such?

The disable/enable should only take a few milliseconds but the plot starts off with a Velocity of -350000 encoder counts/second. This seems very strange if the axis wasn't moving before the button was pushed.
Regards,

Tom Kerekes
Dynomotion, Inc.

Roamin
Posts: 25
Joined: Tue Dec 13, 2022 10:34 pm

Re: Are threads stopped during step response tests?

Post by Roamin » Fri Jan 06, 2023 10:49 pm

Hi again!

I had not realized that if I were to save data from the response of the move that it would contain data that's easier to follow. Sadly right now I am no longer in front of the machine for the week-end. I do have the code so here is my config file containing the parameters that I use when I init the machine. As you can see they do not match the screenshot I posted earlier for the Z axis, as I have changed pretty much everything when trying to understand why the Z axis just shoots up like a bullet when I pressed either MOVE or STEP (which I won't press again ;) ). Although I did also change the data on the other axes as I tried to understand why all 3 axes go into Tach Alarm when I try to tune axes 0 or 1. Seems when I try with axis 2 (z) , the tach is still generated when it shoots up , otherwise it wouldn't travel much at all when the tach is missing.

When I get to the machine on monday I will re-enter all the data used in my config using the "import all from open C program" and start anew. I know the PID was the same between my config and the screen , but I can't say for sure about the other values. The Z axis wasn't moving at all before it shoots up, it's idling about in the middle of its course. It is held in place by the KFlop because the brake is deactivated for operation. If I disable the contactor of the axis without the brake engaged, the axis slowly lowers. I was watching the analog status and the axis movement was set to the max of the DAC at 10v.

I will match my config parameters to the step response screen and try it again. You've given me enough to think about and look at when I get back on monday. Controlling the axes work fine with jog statements, buttons and g code, so if the parameters match and it still doesn't work maybe the tach generator really does act up when Move is pressed.. If anything acts up , I will post the raw data and maybe something will show up.

Your prompt responses are always greatly appreciated! Thank you very much.

Code: Select all

main()
{

	ch0->InputMode=ENCODER_MODE;
	ch0->OutputMode=DAC_SERVO_MODE;
	ch0->Vel=40000;
	ch0->Accel=400000;
	ch0->Jerk=4e+06;
	ch0->P=0.6;
	ch0->I=0;
	ch0->D=0;
	ch0->FFAccel=0;
	ch0->FFVel=0;
	ch0->MaxI=2000;
	ch0->MaxErr=1e+08;
	ch0->MaxOutput=2000;
	ch0->DeadBandGain=1;
	ch0->DeadBandRange=0;
	ch0->InputChan0=0;
	ch0->InputChan1=1;
	ch0->OutputChan0=0;
	ch0->OutputChan1=1;
	ch0->MasterAxis=-1;
	ch0->LimitSwitchOptions=0x100;
	ch0->LimitSwitchNegBit=0;
	ch0->LimitSwitchPosBit=0;
	ch0->SoftLimitPos=1e+09;
	ch0->SoftLimitNeg=-1e+09;
	ch0->InputGain0=-1;
	ch0->InputGain1=1;
	ch0->InputOffset0=0;
	ch0->InputOffset1=0;
	ch0->OutputGain=-1;
	ch0->OutputOffset=0;
	ch0->SlaveGain=1;
	ch0->BacklashMode=BACKLASH_OFF;
	ch0->BacklashAmount=0;
	ch0->BacklashRate=0;
	ch0->invDistPerCycle=1;
	ch0->Lead=0;
	ch0->MaxFollowingError=10000000;
	ch0->StepperAmplitude=250;

	ch0->iir[0].B0=1;
	ch0->iir[0].B1=0;
	ch0->iir[0].B2=0;
	ch0->iir[0].A1=0;
	ch0->iir[0].A2=0;

	ch0->iir[1].B0=1;
	ch0->iir[1].B1=0;
	ch0->iir[1].B2=0;
	ch0->iir[1].A1=0;
	ch0->iir[1].A2=0;

	ch0->iir[2].B0=1;
	ch0->iir[2].B1=0;
	ch0->iir[2].B2=0;
	ch0->iir[2].A1=0;
	ch0->iir[2].A2=0;
    
	ch1->InputMode=ENCODER_MODE;
	ch1->OutputMode=DAC_SERVO_MODE;
	ch1->Vel=80000;
	ch1->Accel=400000;
	ch1->Jerk=4e+06;
	ch1->P=0.6;
	ch1->I=0;
	ch1->D=0;
	ch1->FFAccel=0;
	ch1->FFVel=0;
	ch1->MaxI=2000;
	ch1->MaxErr=1e+08;
	ch1->MaxOutput=2000;
	ch1->DeadBandGain=1;
	ch1->DeadBandRange=0;
	ch1->InputChan0=1;
	ch1->InputChan1=1;
	ch1->OutputChan0=1;
	ch1->OutputChan1=1;
	ch1->MasterAxis=-1;
	ch1->LimitSwitchOptions=0x100;
	ch1->LimitSwitchNegBit=0;
	ch1->LimitSwitchPosBit=0;
	ch1->SoftLimitPos=1e+09;
	ch1->SoftLimitNeg=-1e+09;
	ch1->InputGain0=-1;
	ch1->InputGain1=1;
	ch1->InputOffset0=0;
	ch1->InputOffset1=0;
	ch1->OutputGain=-1;
	ch1->OutputOffset=0;
	ch1->SlaveGain=1;
	ch1->BacklashMode=BACKLASH_OFF;
	ch1->BacklashAmount=0;
	ch1->BacklashRate=0;
	ch1->invDistPerCycle=1;
	ch1->Lead=0;
	ch1->MaxFollowingError=10000000;
	ch1->StepperAmplitude=250;

	ch1->iir[0].B0=1;
	ch1->iir[0].B1=0;
	ch1->iir[0].B2=0;
	ch1->iir[0].A1=0;
	ch1->iir[0].A2=0;

	ch1->iir[1].B0=1;
	ch1->iir[1].B1=0;
	ch1->iir[1].B2=0;
	ch1->iir[1].A1=0;
	ch1->iir[1].A2=0;

	ch1->iir[2].B0=1;
	ch1->iir[2].B1=0;
	ch1->iir[2].B2=0;
	ch1->iir[2].A1=0;
	ch1->iir[2].A2=0;
	
	ch2->InputMode=ENCODER_MODE;
	ch2->OutputMode=DAC_SERVO_MODE;
	ch2->Vel=40000;
	ch2->Accel=400000;
	ch2->Jerk=4e+06;
	ch2->P=0.6;
	ch2->I=0;
	ch2->D=0;
	ch2->FFAccel=0;
	ch2->FFVel=0;
	ch2->MaxI=2000;
	ch2->MaxErr=1e+08;
	ch2->MaxOutput=2000;
	ch2->DeadBandGain=1;
	ch2->DeadBandRange=0;
	ch2->InputChan0=2;
	ch2->InputChan1=1;
	ch2->OutputChan0=2;
	ch2->OutputChan1=1;
	ch2->MasterAxis=-1;
	ch2->LimitSwitchOptions=0x100;
	ch2->LimitSwitchNegBit=0;
	ch2->LimitSwitchPosBit=0;
	ch2->SoftLimitPos=1e+09;
	ch2->SoftLimitNeg=-1e+09;
	ch2->InputGain0=-1;
	ch2->InputGain1=1;
	ch2->InputOffset0=0;
	ch2->InputOffset1=0;
	ch2->OutputGain=-1;
	ch2->OutputOffset=0;
	ch2->SlaveGain=1;
	ch2->BacklashMode=BACKLASH_OFF;
	ch2->BacklashAmount=0;
	ch2->BacklashRate=0;
	ch2->invDistPerCycle=1;
	ch2->Lead=0;
	ch2->MaxFollowingError=10000000;
	ch2->StepperAmplitude=250;

	ch2->iir[0].B0=1;
	ch2->iir[0].B1=0;
	ch2->iir[0].B2=0;
	ch2->iir[0].A1=0;
	ch2->iir[0].A2=0;

	ch2->iir[1].B0=1;
	ch2->iir[1].B1=0;
	ch2->iir[1].B2=0;
	ch2->iir[1].A1=0;
	ch2->iir[1].A2=0;

	ch2->iir[2].B0=1;
	ch2->iir[2].B1=0;
	ch2->iir[2].B2=0;
	ch2->iir[2].A1=0;
	ch2->iir[2].A2=0;
	
	EnableAxis(0);
	EnableAxis(1);
	EnableAxis(2);

	EnableAxisDest(0,0);
	EnableAxisDest(1,1);
	EnableAxisDest(2,2);
	
	DefineCoordSystem(0,1,2,-1);
	
	InitAux();
	AddKonnect(0,&VirtualBits,VirtualBitsEx);
	

Roamin
Posts: 25
Joined: Tue Dec 13, 2022 10:34 pm

Re: Are threads stopped during step response tests?

Post by Roamin » Mon Jan 09, 2023 3:49 pm

Good morning Tom,

I'm happy to say that once I imported my config back into the settings screens, the machine no longer freaks out when I try to move ;). I've been slowly changing parameters based on the following logic :


Set P high enough so that the axis oscillates.
Lower P until it stops oscillating

Set I high enough so that it oscillates.
Lower I until it stops oscillating

Set D high enough so that it oscillates
Lower D until it stops oscillating

I'm getting decent results , but I am unsure if I'm supposed to try and clean the graph even more than what's on my screenshot.
Attachments
step2.png

User avatar
TomKerekes
Posts: 2546
Joined: Mon Dec 04, 2017 1:49 am

Re: Are threads stopped during step response tests?

Post by TomKerekes » Mon Jan 09, 2023 6:18 pm

Hi Roamin,
I'm happy to say that once I imported my config back into the settings screens, the machine no longer freaks out when I try to move ;).
Great!
Set P high enough so that the axis oscillates.
Lower P until it stops oscillating
Correct. Note the plots show P=0.805. It typically takes +/-10% change in a parameter to make a noticeable change. And if a tiny change does make a difference it probably won't be consistent over time position etc. So I'd use a round number like P=0.8
Set I high enough so that it oscillates.
Lower I until it stops oscillating
I would normally tune D next instead of I. It doesn't look like you did this as I is zero or near zero.

It is helpful to plot Output. Again if you would post the raw data we could do this. This would show what is being commanded to the Drive which would show us if the command is causing velocity or acceleration. I'm guessing the Drives are of Velocity Type as they have a Tach input? But it's hard to say how well that works being simulated. You might post your Tach Program.
Set D high enough so that it oscillates
Lower D until it stops oscillating
Again the plots all show 0 so it doesn't seem you did this. Its not clear if D is needed as this is supposedly supplied by the Tach Program and analog feedback. But there still may be some usefulness. When D Gain is used it is useful to add a low pass filter to add smoothing to the spikes caused by the integer steps in the encoder reading. Typically 2nd Order Low Pass Freq=500 Q=1.4.

D gain adds damping and makes the system more stable (up to a point where the implementation of the Damping itself goes unstable). I like to think of this like submersing the system in honey. It moves slower and oscillates less which should allow other gains to be higher. So after optimizing D the P gain should then be re-tuned.
I'm getting decent results , but I am unsure if I'm supposed to try and clean the graph even more than what's on my screenshot.
It looks like the plots are tracking within a few counts. Plotting Error is more useful at this point.

What is the resolution of the system in counts/inch? That would tell us what these errors are physically. What are your expectations in terms of precision, velocity, acceleration?

Note the Move of 5000 counts is not long enough to reach full velocity of 40000 counts/sec.

HTH
Regards,

Tom Kerekes
Dynomotion, Inc.

Roamin
Posts: 25
Joined: Tue Dec 13, 2022 10:34 pm

Re: Are threads stopped during step response tests?

Post by Roamin » Tue Jan 10, 2023 4:41 pm

Hi Tom,

I have gathered all (i think) the information you asked for. I included the raw data for all 3 axes , the code that generates the tach (it constantly loops in thread 1 after setting up the axes. Within that loop I also check a few bits to make sure all switches are in the correct state.)

I believe the machine (Fanuc 5m) originally has the following capabilities : 400 IPM , 0.0001 inch precision, unsure about acceleration?

The drives are indeed of Velocity type, with the dac sending voltage straight to the Velocity Control Unit VCMD wire.
The tach is generated based on position and time, with the dac connected to the TSA wire.

I got the data from a 40k steps move and a 75k steps move in all axes.

You might noticed some comments in the tach code are in french , as I am French Canadian , we tend to mix both languages as we do things.. Although perfectly fluent in English , sometimes my comprehension of more technical things (especially things I'm not familiar with) takes some time..

If there's anything more you need, let me know. If you have documentation I should read to grasp all the tuning concept better, I'd definitely look into it.

I am unsure how you plan to plot errors ? By looking at the graph or the raw data?

Your help is greatly appreciated, as always!

Code: Select all

#include "KMotionDef.h"

// Constantes a modifier
#define POSITION_PER_ROTATION 8000
#define RESOLUTION_OUTPUT_PWM 2048
#define VOLT_PER_RPM 0.003 // 1000 PRM for 3v

// Constante utilise durant le programme
#define INCREMENT_PER_VOLT (RESOLUTION_OUTPUT_PWM/10) // e.g. 2048 increments divise sur 10v





 // Get the position of the encoder of channel 0 (Axis X)
  double initPosX = ch0->Position;
  // Get the time since startup in seconds (chiffre a virgule, ex. 0.009 secondes)
  double initTimeX = Time_sec();
  
  // Get the position of the encoder of channel 1 (Axis Y)
  double initPosY = ch1->Position;
  // Get the time since startup in seconds (chiffre a virgule, ex. 0.009 secondes)
  double initTimeY = Time_sec();
  
  // Get the position of the encoder of channel 1 (Axis Z)
  double initPosZ = ch2->Position;
  // Get the time since startup in seconds (chiffre a virgule, ex. 0.009 secondes)
  double initTimeZ = Time_sec();


  // Initiliaze the variable used in the main loop for Axis X
  double tLastExecutionX = initTimeX, tCurrentExecutionX = initTimeX;
  double posLastExecutionX = initPosX, posCurrentExecutionX = initPosX;
  // Initiliaze the variable used in the main loop for Axis Y
  double tLastExecutionY = initTimeY, tCurrentExecutionY = initTimeY;
  double posLastExecutionY = initPosY, posCurrentExecutionY = initPosY;
  // Initiliaze the variable used in the main loop for Axis Z
  double tLastExecutionZ = initTimeZ, tCurrentExecutionZ = initTimeZ;
  double posLastExecutionZ = initPosZ, posCurrentExecutionZ = initPosZ;



    
  while(1)
{
	// Get the time and the position at the start of the execution window of the thread for Axis X
	posCurrentExecutionX = ch0->Position;
	tCurrentExecutionX = Time_sec();
	// Get the time and the position at the start of the execution window of the thread for Axis Y
	posCurrentExecutionY = ch1->Position;
	tCurrentExecutionY = Time_sec();
	// Get the time and the position at the start of the execution window of the thread for Axis Z
	posCurrentExecutionZ = ch2->Position;
	tCurrentExecutionZ = Time_sec();
	
	// Compute the current RPM of the encoder for Axis X
	// RPM = step/sec * R/step * sec/min
	double stepPerSecX = (posCurrentExecutionX-posLastExecutionX)/(tCurrentExecutionX - tLastExecutionX);
	double encoderRPMX = (stepPerSecX / POSITION_PER_ROTATION) * 60; 
	// Compute the current RPM of the encoder for Axis Y
	// RPM = step/sec * R/step * sec/min
	double stepPerSecY = (posCurrentExecutionY-posLastExecutionY)/(tCurrentExecutionY - tLastExecutionY);
	double encoderRPMY = (stepPerSecY / POSITION_PER_ROTATION) * 60; 
	// Compute the current RPM of the encoder for Axis Z
	// RPM = step/sec * R/step * sec/min
	double stepPerSecZ = (posCurrentExecutionZ-posLastExecutionZ)/(tCurrentExecutionZ - tLastExecutionZ);
	double encoderRPMZ = (stepPerSecZ / POSITION_PER_ROTATION) * 60; 
	
	// Compute the required dacValue for Axis X
	// voltage = RPM * v/RPM * increment/v
    int dacValueX = (int)(encoderRPMX * VOLT_PER_RPM * INCREMENT_PER_VOLT);
	// Compute the required dacValue for Axis Y
	// voltage = RPM * v/RPM * increment/v
    int dacValueY = (int)(encoderRPMY * VOLT_PER_RPM * INCREMENT_PER_VOLT);
	// Compute the required dacValue for Axis Z
	// voltage = RPM * v/RPM * increment/v
    int dacValueZ = (int)(encoderRPMZ * VOLT_PER_RPM * INCREMENT_PER_VOLT);


    
    // Write the dac value in the required output for Axis X
    DAC(3, dacValueX); 
	// Write the dac value in the required output for Axis Y
    DAC(4, dacValueY); 
	// Write the dac value in the required output for Axis Z
    DAC(5, dacValueZ);
    
	// Set the variable for the new execution window of the thread for Axis X
    posLastExecutionX = posCurrentExecutionX;
    tLastExecutionX = tCurrentExecutionX;
	// Set the variable for the new execution window of the thread for Axis Y
    posLastExecutionY = posCurrentExecutionY;
    tLastExecutionY = tCurrentExecutionY;
	// Set the variable for the new execution window of the thread for Axis Y
    posLastExecutionZ = posCurrentExecutionZ;
    tLastExecutionZ = tCurrentExecutionZ;
}
Attachments
count-inch.png
Axis 2.png
Axis 1.png
Axis 0.png

User avatar
TomKerekes
Posts: 2546
Joined: Mon Dec 04, 2017 1:49 am

Re: Are threads stopped during step response tests?

Post by TomKerekes » Wed Jan 11, 2023 12:12 am

Hi Roamin,
I included the raw data for all 3 axes
I don't see any raw data files attached. You might need to Zip it if too big.

It doesn't seem you addressed many of the points I made in my last post. So I'm not sure what to do.

Errors are probably around 300 counts. 300/33898 = 0.009inch. Not very good.

I'd prefer to work on only one axis. Once it is tuned you can do the others an a similar manner.
the code that generates the tach (it constantly loops in thread 1 after setting up the axes. Within that loop I also check a few bits to make sure all switches are in the correct state.)
The code doesn't have a main function. I assume you aren't showing it? Is there any delay in the loop? as shown it is looping as fast as it can. If looping too fast (small dt) its output may be very erratic because of encoder quantization errors (sometimes you might measure a change of 1 and sometimes 2).
I believe the machine (Fanuc 5m) originally has the following capabilities : 400IPM , 0.0001 inch precision, unsure about acceleration?
You have KMotionCNC set to 10ips (600IPM). With 33898 counts/inch and V=40000 that is 1.18ips (70.8ipm). Testing slow is ok while tuning but you will eventually want to test at max velocity you will be using. I see only about 200 counts of Output is being used which is about 10%. So you should be able go about 10X that velocity before saturating the velocity command at 10V.
I am unsure how you plan to plot errors ? By looking at the graph or the raw data?
One of the Plot types is "Position Error, Output vs Time"

It occurred to me you could perform the Tach Function with 3 dummy Axes with only D Gain. This would then sample faster (every 90us), more consistently, and would allow a Low Pass filer to be used to smooth noise in the output caused by encoder quantization. See the diagram below with everything zero except D (and IIR Filter #2)

ServoFlowDiagram.PNG
Regards,

Tom Kerekes
Dynomotion, Inc.

Roamin
Posts: 25
Joined: Tue Dec 13, 2022 10:34 pm

Re: Are threads stopped during step response tests?

Post by Roamin » Wed Jan 11, 2023 1:27 pm

Ooops, silly me I forgot to attach the raw data files.. Classic.

Here they are. I will look deeper into what you told me sometimes this morning and discuss it with the person who operated the machine in its original state.

Thanks for your time, once again.

I only included the X axis.

Roamin
Posts: 25
Joined: Tue Dec 13, 2022 10:34 pm

Re: Are threads stopped during step response tests?

Post by Roamin » Wed Jan 11, 2023 1:28 pm

Here is the entire init function, including main.

Code: Select all

#include "KMotionDef.h"
#define TMP 25 // which spare persist to use to transfer data
#include "KflopToKMotionCNCFunctions.c"

// Constantes a modifier
#define POSITION_PER_ROTATION 8000
#define RESOLUTION_OUTPUT_PWM 2048
#define VOLT_PER_RPM 0.003 // 1000 PRM for 3v
#define OUTPUT_PIN 0

// Constante utilise durant le programme
#define INCREMENT_PER_VOLT (RESOLUTION_OUTPUT_PWM/10) // e.g. 2048 increments divise sur 10v

int *PowerUp = &persist.UserData[49];
int *PowerUp = &persist.UserData[49];
int *PowerUpRelays = &persist.UserData[48];
int *Error = &persist.UserData[47];
double *StartDelay = &persist.UserData[45];
int *RADYCheckDelay = &persist.UserData[41];		// Used to delay the check on RADY relay because check happens before relay is closed
int RADYTimerRunOnce = 1;							// Used to run the timer delay for RADY relay only once



main()
{
	double InitStartTime = Time_sec();	// Used to track minimum time to wait before starting RADY check
	printf("Value before func: %d\n", CheckDone(0));
		
	InitAux();
	AddKonnect(0,&VirtualBits,VirtualBitsEx);
	if (CheckDone(0) == -1)		// If Axis X is disabled , proceed with initilization Prevents Init button from being pressed more than once. Re-Enable button should be used
								// to continue after E-Stop
	{		
		printf("Value : %d\n", CheckDone(0));	
		*PowerUp = 0;
		*PowerUpRelays = 0;
		*Error = 0;
		
		ch0->InputMode=ENCODER_MODE;
		ch0->OutputMode=DAC_SERVO_MODE;
		ch0->Vel=40000;
		ch0->Accel=400000;
		ch0->Jerk=4e+06;
		ch0->P=0.6;
		ch0->I=0;
		ch0->D=0;
		ch0->FFAccel=0;
		ch0->FFVel=0;
		ch0->MaxI=2000;
		ch0->MaxErr=1e+08;
		ch0->MaxOutput=2000;
		ch0->DeadBandGain=1;
		ch0->DeadBandRange=0;
		ch0->InputChan0=0;
		ch0->InputChan1=1;
		ch0->OutputChan0=0;
		ch0->OutputChan1=1;
		ch0->MasterAxis=-1;
		ch0->LimitSwitchOptions=0x100;
		ch0->LimitSwitchNegBit=0;
		ch0->LimitSwitchPosBit=0;
		ch0->SoftLimitPos=1e+09;
		ch0->SoftLimitNeg=-1e+09;
		ch0->InputGain0=-1;
		ch0->InputGain1=1;
		ch0->InputOffset0=0;
		ch0->InputOffset1=0;
		ch0->OutputGain=-1;
		ch0->OutputOffset=0;
		ch0->SlaveGain=1;
		ch0->BacklashMode=BACKLASH_OFF;
		ch0->BacklashAmount=0;
		ch0->BacklashRate=0;
		ch0->invDistPerCycle=1;
		ch0->Lead=0;
		ch0->MaxFollowingError=10000000;
		ch0->StepperAmplitude=250;

		ch0->iir[0].B0=1;
		ch0->iir[0].B1=0;
		ch0->iir[0].B2=0;
		ch0->iir[0].A1=0;
		ch0->iir[0].A2=0;

		ch0->iir[1].B0=1;
		ch0->iir[1].B1=0;
		ch0->iir[1].B2=0;
		ch0->iir[1].A1=0;
		ch0->iir[1].A2=0;

		ch0->iir[2].B0=1;
		ch0->iir[2].B1=0;
		ch0->iir[2].B2=0;
		ch0->iir[2].A1=0;
		ch0->iir[2].A2=0;
		
		ch1->InputMode=ENCODER_MODE;
		ch1->OutputMode=DAC_SERVO_MODE;
		ch1->Vel=80000;
		ch1->Accel=400000;
		ch1->Jerk=4e+06;
		ch1->P=0.6;
		ch1->I=0;
		ch1->D=0;
		ch1->FFAccel=0;
		ch1->FFVel=0;
		ch1->MaxI=2000;
		ch1->MaxErr=1e+08;
		ch1->MaxOutput=2000;
		ch1->DeadBandGain=1;
		ch1->DeadBandRange=0;
		ch1->InputChan0=1;
		ch1->InputChan1=1;
		ch1->OutputChan0=1;
		ch1->OutputChan1=1;
		ch1->MasterAxis=-1;
		ch1->LimitSwitchOptions=0x100;
		ch1->LimitSwitchNegBit=0;
		ch1->LimitSwitchPosBit=0;
		ch1->SoftLimitPos=1e+09;
		ch1->SoftLimitNeg=-1e+09;
		ch1->InputGain0=-1;
		ch1->InputGain1=1;
		ch1->InputOffset0=0;
		ch1->InputOffset1=0;
		ch1->OutputGain=-1;
		ch1->OutputOffset=0;
		ch1->SlaveGain=1;
		ch1->BacklashMode=BACKLASH_OFF;
		ch1->BacklashAmount=0;
		ch1->BacklashRate=0;
		ch1->invDistPerCycle=1;
		ch1->Lead=0;
		ch1->MaxFollowingError=10000000;
		ch1->StepperAmplitude=250;

		ch1->iir[0].B0=1;
		ch1->iir[0].B1=0;
		ch1->iir[0].B2=0;
		ch1->iir[0].A1=0;
		ch1->iir[0].A2=0;

		ch1->iir[1].B0=1;
		ch1->iir[1].B1=0;
		ch1->iir[1].B2=0;
		ch1->iir[1].A1=0;
		ch1->iir[1].A2=0;

		ch1->iir[2].B0=1;
		ch1->iir[2].B1=0;
		ch1->iir[2].B2=0;
		ch1->iir[2].A1=0;
		ch1->iir[2].A2=0;
		
		ch2->InputMode=ENCODER_MODE;
		ch2->OutputMode=DAC_SERVO_MODE;
		ch2->Vel=40000;
		ch2->Accel=400000;
		ch2->Jerk=4e+06;
		ch2->P=0.6;
		ch2->I=0;
		ch2->D=0;
		ch2->FFAccel=0;
		ch2->FFVel=0;
		ch2->MaxI=2000;
		ch2->MaxErr=1e+08;
		ch2->MaxOutput=2000;
		ch2->DeadBandGain=1;
		ch2->DeadBandRange=0;
		ch2->InputChan0=2;
		ch2->InputChan1=1;
		ch2->OutputChan0=2;
		ch2->OutputChan1=1;
		ch2->MasterAxis=-1;
		ch2->LimitSwitchOptions=0x100;
		ch2->LimitSwitchNegBit=0;
		ch2->LimitSwitchPosBit=0;
		ch2->SoftLimitPos=1e+09;
		ch2->SoftLimitNeg=-1e+09;
		ch2->InputGain0=-1;
		ch2->InputGain1=1;
		ch2->InputOffset0=0;
		ch2->InputOffset1=0;
		ch2->OutputGain=-1;
		ch2->OutputOffset=0;
		ch2->SlaveGain=1;
		ch2->BacklashMode=BACKLASH_OFF;
		ch2->BacklashAmount=0;
		ch2->BacklashRate=0;
		ch2->invDistPerCycle=1;
		ch2->Lead=0;
		ch2->MaxFollowingError=10000000;
		ch2->StepperAmplitude=250;

		ch2->iir[0].B0=1;
		ch2->iir[0].B1=0;
		ch2->iir[0].B2=0;
		ch2->iir[0].A1=0;
		ch2->iir[0].A2=0;

		ch2->iir[1].B0=1;
		ch2->iir[1].B1=0;
		ch2->iir[1].B2=0;
		ch2->iir[1].A1=0;
		ch2->iir[1].A2=0;

		ch2->iir[2].B0=1;
		ch2->iir[2].B1=0;
		ch2->iir[2].B2=0;
		ch2->iir[2].A1=0;
		ch2->iir[2].A2=0;
		
		EnableAxis(0);
		EnableAxis(1);
		EnableAxis(2);

		DefineCoordSystem(0,1,2,-1);

	}			// End of if (CheckDone(0) == -1)
	
	

////////////		initialize the variables of the tachometer generator
////////////
	
	if (CheckDone(0) != -1)
	{
		printf("Power up relays : %d\n", *PowerUpRelays);

		
		if (!*PowerUpRelays)
		{
			printf("Enable X Axis\n");
			SetBit(63);				// Set X Position Ready
			SetBit(62);				// Enable X
			while(!ReadBit(1025));	// Wait for ready signal from X

			printf("Enable Y Axis\n");
			SetBit(61);				// Set Y Position Ready
			SetBit(60);				// Enable Y
			while(!ReadBit(1027));	// Wait for ready signal from Y

			printf("Enable Z Axis\n");
			SetBit(59);				// Set Z Position Ready
			SetBit(58);				// Enable Z
			while(!ReadBit(1029));	// Wait for ready signal from Z

			printf("Enable Spindle\n");
			SetBit(57);				// Spindle Emergency Stop
			SetBit(56);				// Spindle Ready
			
			printf("Set RADY relay on for operation\n");
			SetBit(52);				// Machine Ready (RADY Relay)
			while(!ReadBit(1052));	// Machine ready signal from RADY relay
			*PowerUpRelays = 1;
		} 

		// Get the position of the encoder of channel 0 (Axis X)
		double initPosX = ch0->Position;
		// Get the time since startup in seconds (chiffre a virgule, ex. 0.009 secondes)
		double initTimeX = Time_sec();
		  
		// Get the position of the encoder of channel 1 (Axis Y)
		double initPosY = ch1->Position;
		// Get the time since startup in seconds (chiffre a virgule, ex. 0.009 secondes)
		double initTimeY = Time_sec();
			  
		// Get the position of the encoder of channel 1 (Axis Z)
		double initPosZ = ch2->Position;
		// Get the time since startup in seconds (chiffre a virgule, ex. 0.009 secondes)
		double initTimeZ = Time_sec();


		// Initiliaze the variable used in the main loop for Axis X
		double tLastExecutionX = initTimeX, tCurrentExecutionX = initTimeX;
		double posLastExecutionX = initPosX, posCurrentExecutionX = initPosX;
		// Initiliaze the variable used in the main loop for Axis Y
		double tLastExecutionY = initTimeY, tCurrentExecutionY = initTimeY;
		double posLastExecutionY = initPosY, posCurrentExecutionY = initPosY;
		// Initiliaze the variable used in the main loop for Axis Z
		double tLastExecutionZ = initTimeZ, tCurrentExecutionZ = initTimeZ;
		double posLastExecutionZ = initPosZ, posCurrentExecutionZ = initPosZ;


////////////		Start the tachometer generator & alarm detection loop and loop forever
////////////


		while(1)
		{
				// Get the time and the position at the start of the execution window of the thread for Axis X
				posCurrentExecutionX = ch0->Position;
				tCurrentExecutionX = Time_sec();
				// Get the time and the position at the start of the execution window of the thread for Axis Y
				posCurrentExecutionY = ch1->Position;
				tCurrentExecutionY = Time_sec();
				// Get the time and the position at the start of the execution window of the thread for Axis Z
				posCurrentExecutionZ = ch2->Position;
				tCurrentExecutionZ = Time_sec();
				
				// Compute the current RPM of the encoder for Axis X
				// RPM = step/sec * R/step * sec/min
				double stepPerSecX = (posCurrentExecutionX-posLastExecutionX)/(tCurrentExecutionX - tLastExecutionX);
				double encoderRPMX = (stepPerSecX / POSITION_PER_ROTATION) * 60; 
				// Compute the current RPM of the encoder for Axis Y
				// RPM = step/sec * R/step * sec/min
				double stepPerSecY = (posCurrentExecutionY-posLastExecutionY)/(tCurrentExecutionY - tLastExecutionY);
				double encoderRPMY = (stepPerSecY / POSITION_PER_ROTATION) * 60; 
				// Compute the current RPM of the encoder for Axis Z
				// RPM = step/sec * R/step * sec/min
				double stepPerSecZ = (posCurrentExecutionZ-posLastExecutionZ)/(tCurrentExecutionZ - tLastExecutionZ);
				double encoderRPMZ = (stepPerSecZ / POSITION_PER_ROTATION) * 60; 
				
				// Compute the required dacValue for Axis X
				// voltage = RPM * v/RPM * increment/v
				int dacValueX = (int)(encoderRPMX * VOLT_PER_RPM * INCREMENT_PER_VOLT);
				// Compute the required dacValue for Axis Y
				// voltage = RPM * v/RPM * increment/v
				int dacValueY = (int)(encoderRPMY * VOLT_PER_RPM * INCREMENT_PER_VOLT);
				// Compute the required dacValue for Axis Z
				// voltage = RPM * v/RPM * increment/v
				int dacValueZ = (int)(encoderRPMZ * VOLT_PER_RPM * INCREMENT_PER_VOLT);


				
				// Write the dac value in the required output for Axis X
				DAC(3, dacValueX); 
				// Write the dac value in the required output for Axis Y
				DAC(4, dacValueY); 
				// Write the dac value in the required output for Axis Z
				DAC(5, dacValueZ);
				
				// Set the variable for the new execution window of the thread for Axis X
				posLastExecutionX = posCurrentExecutionX;
				tLastExecutionX = tCurrentExecutionX;
				// Set the variable for the new execution window of the thread for Axis Y
				posLastExecutionY = posCurrentExecutionY;
				tLastExecutionY = tCurrentExecutionY;
				// Set the variable for the new execution window of the thread for Axis Y
				posLastExecutionZ = posCurrentExecutionZ;
				tLastExecutionZ = tCurrentExecutionZ;
				

				if (((Time_sec() - InitStartTime) > 3) && (!*Error))  // Starts with !0 = 1
				{
					if (ReadBit(1051))
					{
						*Error = 1;
						DoPC(PC_COMM_ESTOP);
						MsgBoxNoWait("Air Pressure Missing",MB_ICONEXCLAMATION);
					}
					if (!ReadBit(1041))
					{
						*Error = 1;
						DoPC(PC_COMM_ESTOP);
						MsgBox("Tool Changer not in LEFT position",MB_OK|MB_ICONEXCLAMATION);
					}
					if (!ReadBit(1042))
					{
						*Error = 1;
						DoPC(PC_COMM_ESTOP);
						MsgBox("Tool Changer not in UP position",MB_OK|MB_ICONEXCLAMATION);
					}
					if (!ReadBit(1044))
					{
						*Error = 1;
						DoPC(PC_COMM_ESTOP);
						MsgBox("Tool clamp is not closed",MB_OK|MB_ICONEXCLAMATION);
					}
					if (!ReadBit(1047))
					{
						*Error = 1;
						DoPC(PC_COMM_ESTOP);
						MsgBox("Oiler out of oil",MB_OK|MB_ICONEXCLAMATION);
					}
					if (!ReadBit(1052))	// Checks if RADY relay is still closed
					{
						*Error = 1;
						DoPC(PC_COMM_ESTOP);			// Software E-stop command
						MsgBoxNoWait("RADY Relay is open",MB_OK|MB_ICONEXCLAMATION); // Sends error message to user 
					}
				}	


				WaitNextTimeSlice();
	

		}		// End of while(1)
	}			// End of if (CheckDone(0) == 1)
}				// End of main


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*

Relays:


Bit / Function

48 / Unused
49 / Unused
50 / Unused
51 / Unused

52 / Machine ready (RADY)
53 / Spindle Tool Change command (ORCM)
54 / Spindle Reverse (SRV) 
55 / Spindle Forware (SFR)

56 / Spindle Ready (MRDY)
57 / Spindle Emergency Stop (ESP)
58 / Enable (ENBL-Z)
59 / Position Ready (PRDY-Z)
60 / Enable (ENBL-Y)
61 / Position Ready (PRDY-Y)
62 / Enable (ENBL-X)
63 / Position Ready (PRDY-X)

Magnectic Board Relays :

Bit / Function

144 / MGAD		Tool changer right when active
145 / MGDN    Tool changer down when active
146 / UNCLP
147 / SCM
148 / TCCW
149 / TCST
150 / COL1
151 / Unused

Signal Monitoring : 

1024 / X OVL / Axis is overloaded when signal is LOW.  Reset with red thermostat button.
1025 / X VRDY / Velocity control Ready
1026 / Y OVL / Axis is overloaded when signal is LOW.  Reset with red thermostat button.
1027 / Y VRDY / Velocity control Ready
1028 / Z OVL / Axis is overloaded when signal is LOW.  Reset with red thermostat button.
1029 / Z VRDY / Velocity control Ready
1030 / Spindle Alarm , see on-board LEDs and alarm table.
1031 / Spindle tool change ready

1032 / X encoder overheat
1033 / Y encoder overheat
1034 / Z encoder overheat
1035 /
1036 / 
1037 / 
1038 / 
1039 / 

1040 / 167 - Tool changer all the way right (ready to change tool) (high when in position)
1041 / 168 - Tool changer all the way left (back to home position)  (high when in position)
1042 / 169 - Tool changer all the way up (high when in position)
1043 / 170 - Tool changer all the way down (high when in position)
1044 / 171 - Spindle ready to hold tool (high when in position) relay UNCLP
1045 / 172 - Spindle holding tool (high when in position)
1046 / 173 - Tool changer CAM sensor position   High when cam is ready
1047 / 165 - Oiler - pin high right now.

1048 / X Home 150
1049 / Y Home 151
1050 / Z Home 152
1051 / 166 - Compressed air sensor - Low when pressure is present.
1052 / 174 - RADY relay closed
1053 / 
1054 / 
1055 / 

*/

Roamin
Posts: 25
Joined: Tue Dec 13, 2022 10:34 pm

Re: Are threads stopped during step response tests?

Post by Roamin » Wed Jan 11, 2023 1:43 pm

You are correct that the tach loop runs as fast as possible without delays. If the tach generation pauses for too long the axes go into tach alarm. I have not measured the minimum speed at which it needs to run to avoid tach alarms.


As for the missing information you asked , I thought I had answered everything .. I will look into it again really soon and try and provide the missing information.

Post Reply