FORUM CTRLX AUTOMATION
ctrlX World Partner Apps for ctrlX AUTOMATION
Dear Community User! We are updating our platform to a new
system.
Read more: Important
information on the platform change.
10-14-2022 08:06 AM - edited 10-14-2022 08:08 AM
We are now using C++ single axis motion command(such as axsPosCmd) to achieve the position control with SDK in ctrlX core by Motion APP.
For example, when there are three position movement with three velocity in single axis, we know it can be done by sending three motion command (axsPosCmd) to motion APP in buffer mode. But in this mode, the velocity will reduce to 0 from one positioning to next positioning like the graph showed below:
We want to achieve blending smooth velocity change from one positioning to next positioning,like the below graph:
And if we use axsPosCmd in non-buffer mode, the velocity will directly change to current positioning command without last command finished, this is not what we want to achieve either.
Also, if we send the non-buffer axsPosCmd by self-design program which is cyclic reading the Ipo position, the position accuracy is not as good as we expect, there will be position error in velocity change timing due to program scan delay.
So how can we achieve the accurate continuous positioning with velocity smooth changing in ctrlX motion APP?
Solved! Go to Solution.
10-14-2022 11:14 AM - edited 10-17-2022 07:21 AM
The motion in general does not bring the the velocity to 0 when commands are send as buffered to the kernel. It does e.g. if waiting for the command to be ended. See here a trace of a PLC example. Same behaviour is expected for the C++ interface:
See comment below. Buffered commands are at the moment always bringing the axis to velocity = 0.
Used program code from the CXA_MotionInterface:
3: // move absolute - send three commands in one cycle
arAxisCtrl_gb[uiAxisIndex].PosMode.Position := 25;
arAxisCtrl_gb[uiAxisIndex].PosMode.Velocity := 10;
aruliReturnedCmdId[0] := arAxisCtrl_gb[uiAxisIndex].Admin.mTriggerMoveCmd(_OpMode:= ModePosAbs, Buffered:=TRUE, UserID:=CONCAT('my own id ', TO_STRING(uiMyOwnId)) );
uiMyOwnId := uiMyOwnId + 1;
arAxisCtrl_gb[uiAxisIndex].PosMode.Position := 15;
arAxisCtrl_gb[uiAxisIndex].PosMode.Velocity := 7.5;
aruliReturnedCmdId[1] := arAxisCtrl_gb[uiAxisIndex].Admin.mTriggerMoveCmd(_OpMode:= ModePosAbs, Buffered:=TRUE, UserID:=CONCAT('my own id ', TO_STRING(uiMyOwnId)) );
uiMyOwnId := uiMyOwnId + 1;
arAxisCtrl_gb[uiAxisIndex].PosMode.Position := 10;
arAxisCtrl_gb[uiAxisIndex].PosMode.Velocity := 5;
aruliReturnedCmdId[1] := arAxisCtrl_gb[uiAxisIndex].Admin.mTriggerMoveCmd(_OpMode:= ModePosAbs, Buffered:=TRUE, UserID:=CONCAT('my own id ', TO_STRING(uiMyOwnId)) );
uiMyOwnId := uiMyOwnId + 1;
arAxisCtrl_gb[uiAxisIndex].PosMode.Position := 5;
arAxisCtrl_gb[uiAxisIndex].PosMode.Velocity := 2.5;
aruliReturnedCmdId[2] := arAxisCtrl_gb[uiAxisIndex].Admin.mTriggerMoveCmd(_OpMode:= ModePosAbs, Buffered:=TRUE, UserID:=CONCAT('my own id ', TO_STRING(uiMyOwnId)) );
uiMyOwnId := uiMyOwnId + 1;
arAxisCtrl_gb[uiAxisIndex].PosMode.Position := 0;
arAxisCtrl_gb[uiAxisIndex].PosMode.Velocity := 1;
aruliReturnedCmdId[2] := arAxisCtrl_gb[uiAxisIndex].Admin.mTriggerMoveCmd(_OpMode:= ModePosAbs, Buffered:=TRUE, UserID:=CONCAT('my own id ', TO_STRING(uiMyOwnId)) );
uiMyOwnId := uiMyOwnId + 1;
uiState := uiState + 1; // go direct to next state to avoid entering the commands again
4: // wait for CmdDone
IF arAxisStatus_gb[uiAxisIndex].Admin.CmdDone THEN
uiState := uiState + 1;
END_IF
5: // move relative - send three commands in one cycle
arAxisCtrl_gb[uiAxisIndex].PosMode.Distance := 10;
arAxisCtrl_gb[uiAxisIndex].PosMode.Velocity := 3.0;
aruliReturnedCmdId[3] := arAxisCtrl_gb[uiAxisIndex].Admin.mTriggerMoveCmd(_OpMode:= ModePosRel, Buffered:=TRUE, UserID:=CONCAT('my own id ', TO_STRING(uiMyOwnId)) );
uiMyOwnId := uiMyOwnId + 1;
arAxisCtrl_gb[uiAxisIndex].PosMode.Distance := 7.5;
arAxisCtrl_gb[uiAxisIndex].PosMode.Velocity := 2.0;
aruliReturnedCmdId[4] := arAxisCtrl_gb[uiAxisIndex].Admin.mTriggerMoveCmd(_OpMode:= ModePosRel, Buffered:=TRUE, UserID:=CONCAT('my own id ', TO_STRING(uiMyOwnId)) );
uiMyOwnId := uiMyOwnId + 1;
arAxisCtrl_gb[uiAxisIndex].PosMode.Distance := 5;
arAxisCtrl_gb[uiAxisIndex].PosMode.Velocity := 1.0;
aruliReturnedCmdId[5] := arAxisCtrl_gb[uiAxisIndex].Admin.mTriggerMoveCmd(_OpMode:= ModePosRel, Buffered:=TRUE, UserID:=CONCAT('my own id ', TO_STRING(uiMyOwnId)) );
uiMyOwnId := uiMyOwnId + 1;
uiState := uiState + 1; // go direct to next state to avoid entering the commands again
6: // // wait for CmdDone
IF arAxisStatus_gb[uiAxisIndex].Admin.CmdDone THEN
uiState := uiState + 1;
END_IF
To get maximum accuracy for reading values, you would have to create a realtime bundle to be able to be called deterministic with the motion. More information how to get RT see FAQ for the SDK.
10-14-2022 12:00 PM
Hello, I can see your graph with PLC trace shows that velocity does not reduce to 0.
But I wonder if your trace or program cycle is set too long and acceleration and deceleration of the axis is too high, so the trace does not get the 0 velocity timing;
So I test the program in PLC agian, I set the velocity is 1000mm/min and 500mm/min, the acc/dec is 0.01 m/s2, position is 100mm and 200mm. Trace and PLC program cyclic interval is 2ms.
My program is as below:
And I get the below graph, the velocity is reduce to 0 when position cmd changed:
I do not know if I have the wrong setting, pls help me.
10-14-2022 03:44 PM
You are totally right. Sorry for that, I just used some standard values for my tests and can confirm your behavior.
It seems that blending of buffered commands for single axis is not possible at the moment. I will have an discussion with our R&D and suggest to read out the position cyclically and send new movement command unbuffered accordingly.
10-15-2022 09:19 AM - edited 10-15-2022 09:20 AM
Hello,
here are some links to the documentation describing the functionality of the different commands:
General information about buffered and unbuffered commands
Axis commands:
- Absolute positioning of the axes (description of the functionality of buffered and non-buffered commands)
Kinematic commands:
- Absolute traversing of kinematics (description of the functionality of buffered and non-buffered commands)
- Direct asynchronous absolute traversing of kinematics (description of the functionality of buffered and non-buffered commands)
- Blending/permanent kinematic option (If a motion in Y follows a motion in X, blending results in a continuous motion, whereas motion 2 starts before motion 1 has been completed.) (You can use blending or continuous motion)
- Continuous motion across several commands (You can use blending or continuous motion)
- Path rounding with polynomial (You can use "PolyTrans or Blending)
Hopefully these description helps a little bit.
Best regards,
Michael
10-17-2022 04:22 AM
But there is some problem to read out the position cyclically and send new movement command unbuffered , the reading cycle is 2 ms, we have tested it and there will cause some position error(position accuracy is not good) in the velocity changing time due to program scan delay. Also, the program logic is more difficult in this way. So I am seeking the better way to achieve this kind of function.
10-17-2022 04:35 AM - edited 10-17-2022 09:51 AM
And I have learned that blending movement can be achieved in Kinematic command, but I want the same function in single axis command(better in buffer mode command). If there is any other way to achieve the function, pls let me know. Thanks.