cancel
Showing results for 
Search instead for 
Did you mean: 
SOLVED

How to achieve blending smooth motion control in single axis control?

How to achieve blending smooth motion control in single axis control?

KangZhihao
Established Member

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:

KangZhihao_0-1665726674691.png

We want to achieve blending smooth velocity change from one positioning to next positioning,like the below graph:

KangZhihao_1-1665726959957.png

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?

 

6 REPLIES 6

CodeShepherd
Community Moderator
Community Moderator

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:

EDIT:

See comment below. Buffered commands are at the moment always bringing the axis to velocity = 0.

velocity and position trace of buffered commandsvelocity and position trace of buffered commands

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.

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:

KangZhihao_0-1665741389409.png

And I get the below graph, the velocity is reduce to 0 when position cmd changed:

KangZhihao_1-1665741500913.png

I do not know if I have the wrong setting, pls help me.

 

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.

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

 

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.

KangZhihao
Established Member

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.

Icon--AD-black-48x48Icon--address-consumer-data-black-48x48Icon--appointment-black-48x48Icon--back-left-black-48x48Icon--calendar-black-48x48Icon--center-alignedIcon--Checkbox-checkIcon--clock-black-48x48Icon--close-black-48x48Icon--compare-black-48x48Icon--confirmation-black-48x48Icon--dealer-details-black-48x48Icon--delete-black-48x48Icon--delivery-black-48x48Icon--down-black-48x48Icon--download-black-48x48Ic-OverlayAlertIcon--externallink-black-48x48Icon-Filledforward-right_adjustedIcon--grid-view-black-48x48IC_gd_Check-Circle170821_Icons_Community170823_Bosch_Icons170823_Bosch_Icons170821_Icons_CommunityIC-logout170821_Icons_Community170825_Bosch_Icons170821_Icons_CommunityIC-shopping-cart2170821_Icons_CommunityIC-upIC_UserIcon--imageIcon--info-i-black-48x48Icon--left-alignedIcon--Less-minimize-black-48x48Icon-FilledIcon--List-Check-grennIcon--List-Check-blackIcon--List-Cross-blackIcon--list-view-mobile-black-48x48Icon--list-view-black-48x48Icon--More-Maximize-black-48x48Icon--my-product-black-48x48Icon--newsletter-black-48x48Icon--payment-black-48x48Icon--print-black-48x48Icon--promotion-black-48x48Icon--registration-black-48x48Icon--Reset-black-48x48Icon--right-alignedshare-circle1Icon--share-black-48x48Icon--shopping-bag-black-48x48Icon-shopping-cartIcon--start-play-black-48x48Icon--store-locator-black-48x48Ic-OverlayAlertIcon--summary-black-48x48tumblrIcon-FilledvineIc-OverlayAlertwhishlist