Can't get SMC_SmoothPath to work

bertus
2025-12-17
2026-01-11
  • bertus - 2025-12-17

    Hello all,

    I am trying to run a XY trajectory from a static G-Code file.
    Everything works except the smoothing/blending of the corners.

    My setup:

    • Codesys v3.5sp18
    • Raspberry Pi 4
    • CODESYS Control for Raspberry Pi SL, using demo license.
    • Zero axes (only using the interpolator).

    The order of processing for my decoding is:

    1. SMC_NCDecoder (ok)
    2. SMC_SmoothMerge (ok)
    3. SMC_SmoothPath (not ok)
    4. SMC_LimitDynamics
    5. SMC_CheckVelocities

    I created following instances and buffers:

    fbNCDecoder: SMC_NCDecoder;
    fbSmoothMerge: SMC_SmoothMerge;
    fbSmoothPath: SMC_SmoothPath;
    fbLimitDynamics: SMC_LimitDynamics;
    fbCheckVelocities: SMC_CheckVelocities;
    aNCDecoderBuffer: ARRAY[0..49] OF SMC_GeoInfo; // buffer stage 1 (fbNCDecoder)
    aSmoothMergeBuffer: ARRAY[0..19] OF SMC_GeoInfo; // buffer stage 2 (fbSmoothMerge)
    aSmoothPathBuffer: ARRAY[0..99] OF SMC_GeoInfo; // buffer stage 3 (fbSmoothPath)
    aLimitDynamicsBuffer: ARRAY[0..39] OF SMC_GeoInfo; // buffer stage 4 (fbLimitDynamics)
    

    My original G-Code file has a lot of short G1 elements but this example, I reduced it to a simple square:

    N000 G51 D10
    N010 G38 O1 
    N020 G00 X0 Y0 F50 E1000 E-1000 
    N030 G01 X0 Y-37.5 
    N040 G01 X-75 Y-37.5 
    N050 G01 X-75 Y37.5 
    N060 G01 X0 Y37.5 
    N070 G01 X0 Y0 
    N080 G50 
    N090 G39 O1 
    

    In the Codesys CNC settings, I have these instances activated:

    • SMC_SmoothMerge
    • SMC_SmoothPath
    • SMC_LimitDynamics
    • SMC_CheckVelocities

    And with those settings (and the path-preprocessing button actived), the G-Code viewer shows the square with nicely blended corners (see attached picture).
    However, when running it on the PLC, it doesn't blend the corners at all.

    Below the relevant part of my code. The process is started with bDecode.

    // stage 1: decoding G-code
    fbNCDecoder(
      ncprog := square,
      bExecute:= bDecode,
      bAbort:= NOT bDecode,
      nSizeOutQueue := SIZEOF(aNCDecoderBuffer),
      pbyBufferOutQueue := ADR(aNCDecoderBuffer)
      );
    
    // stage 2: merge short linear segments
    fbSmoothMerge(
      bExecute := bDecode,
      poqDataIn := fbNCDecoder.poqDataOut,
      nSizeOutQueue := SIZEOF(aSmoothMergeBuffer),
      pbyBufferOutQueue := ADR(aSmoothMergeBuffer),
      piMaxDifference := PI_MAX_DIFFERENCE,
      usiMaxDegree := 5,
      wFeatureFlag := 1,
      wAdditionalParamNumber := 0,
      dMinimumCurvatureRadius := D_MIN_CURVATURE_RADIUS
      );
    
    // stage 3: smooth corners
    fbSmoothPath(
      bExecute := bDecode,
      bAbort := NOT bDecode,
      poqDataIn := fbSmoothMerge.poqDataOut,
      eMode := SMC_SMOOTHPATHMODE.SP_SPLINE5_MIN_CURVATURE,
      eAddAxMode := SMC_SMOOTHPATHADDAXMODE.SPAA_NONE,
      nSizeOutQueue := SIZEOF(aSmoothPathBuffer),
      pbyBufferOutQueue := ADR(aSmoothPathBuffer),
      dAngleTol := D_ANGLE_TOL,
      bSymmetricalDistances := TRUE,
      bImprovedSymmetricCuts := TRUE
      );
    
    // stage 4: keep acc/dec and velocity within limits
    fbLimitDynamics(
      bExecute := bDecode,
      bAbort := NOT bDecode,
      poqDataIn := fbSmoothPath.poqDataOut,
      wAxis := 16#07,
      nSizeOutQueue := SIZEOF(aLimitDynamicsBuffer),
      pbyBufferOutQueue := ADR(aLimitDynamicsBuffer),
      bIncludePathSettings := TRUE,
      dMaxVel := 2500,
      dMaxAccDec := 10000
      );
    
    // stage 5: check the path speed
    fbCheckVelocities(
      bExecute := bDecode,
      bAbort := NOT bDecode,
      poqDataIn := fbLimitDynamics.poqDataOut,
      dAngleTol := D_ANGLE_TOL);
    
    pathQueue := fbCheckVelocities.poqDataOut;
    
    // repeat until MaxDuration 
    ...
    

    Any idea what I'm doing wrong? Is smooth path supposed to work at all when using a demo license?

    Thanks.

     
  • gseidel - 2025-12-17

    Hi bertus,

    I cannot see any problem in the code you posted. Just some ideas:

    • What is the value of D_ANGLE_TOL?
      -
    • Do you need the assignment bAbort := NOT bDecoder? Does it help to leave it out?

    • Any errors (e.g. SMC_SmoothPath.Error/ErrorID) or PLC log messages?

    • What if you comment out SMC_SmoothMerge and SMC_LimtiDynamics, is corner smoothing still not working?

    • Is bDecode written from a different task (e.g. visu)? Then I would recommend to assign to a local variable (bDecodeLocal := bDecode) and use that for all FBs. Otherwise, FBs might see different values of bDecode in a single cycle, which will cause problems.

    Regards,

    Georg

     
  • bertus - 2025-12-18

    Hi Georg, thanks for your time.

    What is the value of D_ANGLE_TOL?

    It is set to 0.01.

    Do you need the assignment bAbort := NOT bDecoder? Does it help to leave it out?

    This seems to make no difference.

    Any errors (e.g. SMC_SmoothPath.Error/ErrorID) or PLC log messages?

    No errors or log messages.

    What if you comment out SMC_SmoothMerge and SMC_LimtiDynamics, is corner smoothing still not working?

    Indeed.

    Is bDecode written from a different task (e.g. visu)? Then I would recommend to assign to a local variable (bDecodeLocal := bDecode) and use that for all FBs. Otherwise, FBs might see different values of bDecode in a single cycle, which will cause problems.

    It is a local variable, written from a state machine inside this program.

    But there is new information:

    During some trial and error, I tried skipping "stage 2: merge short linear segments" (SMC_SmoothMerge). And suddenly it rounds the corners, see attached image.

    • Looking a bit closer, I noticed that (with the original program) after a download (cold start?), it in fact did round the corners, but only once. Ever next cycle in which the NC program was executed, the corners where not rounded.
    • Not sure if there is any relation, but SMC_SmoothMerge is also the only FB which has no bAbort input.
    • Perhaps I need to reset it (or any buffers) by other means?
     
  • gseidel - 2025-12-23

    Hi bertus,

    thanks for the additional information. Setting bAbort on a restart is not needed. Calling all FBs starting from the decoder up to checkvelocities with bExecute = FALSE and then giving a new rising edge on bExecute is what should be done.

    Do you also restart the Interpolator? (I don't think that has anything to do with the issue, but still...)

    Best regards,

    Georg

     
  • bertus - 2026-01-02

    Thanks. I will do more investigation and testing next week.

     
  • blitz - 2026-01-07

    Hi @bertus

    I have a similar situation:

    • just like in your case I can see that rounding works correctly only for the first element,

    • additionally in my case on a path made of lines where I am testing this every second line is skipped. The interpolator jumps to every second iActObjectSourceNo.

     
  • blitz - 2026-01-09

    This code works well:

    PROGRAM PLC_PRG
    VAR
        execute : BOOL;
        abort : BOOL;
        doMDI : BOOL;
    
        manualDataInput : STRING(1024) := 'N5 G01 X0.00 Y200.00 N6 G01 X200.00 Y200.00 N7 G01 X200.00 Y0.00 N8 G01 X0.00 Y0.00';
    
        stringStreamArray: ARRAY[0..NUMBER_OF_CHAINS-1] OF SMC_StringStream2 ;
        i : UDINT;  
    
        readNCFromStream  : SMC_ReadNCFromStream;
        sentencesFromStringSteam : SMC_GSentenceQueue ;
    
        interpreter : SMC_NCInterpreter;
        interpreterGeoInfoBuffer: ARRAY[1..GEO_INFO_BUFFERS_SIZE] OF SMC_GeoInfo;
    
        roundPath : SMC_RoundPath;
        roundPathGeoInfoBuffer: ARRAY[1..GEO_INFO_BUFFERS_SIZE] OF SMC_GeoInfo;
    
    END_VAR
    VAR CONSTANT
        MDI_FILE_NAME : STRING := 'MDI.nc';
        NUMBER_OF_CHAINS : UDINT := 15;
        GEO_INFO_BUFFERS_SIZE : DINT := 500;
    END_VAR
    
    readNCFromStream.bExecute := interpreter.bExecute := roundPath.bExecute := execute;
    readNCFromStream.bAbort := interpreter.bAbort := roundPath.bAbort := abort;
    
    
    IF doMDI THEN
            stringStreamArray[0].Init(MDI_FILE_NAME);
            stringStreamArray[0].AppendData(manualDataInput);
            stringStreamArray[0].SetEndOfData();
    
            i := 0 ;
    
            WHILE i < NUMBER_OF_CHAINS DO
                readNCFromStream.aStream[i] := stringStreamArray[i];
                i := i + 1 ;
            END_WHILE
    
    
    
    
            readNCFromStream.bExecute := TRUE;
    
            IF
                readNCFromStream.bBusy
            THEN
                    ;
            END_IF
    END_IF
    
    
        readNCFromStream(
            fDefaultVel := 10,
            fDefaultAccel := 10,
            fDefaultDecel := 10,
            fDefaultVelFF := 10,
            fDefaultAccelFF := 10,
            fDefaultDecelFF := 10,
            sentences := sentencesFromStringSteam
        );
    
            interpreter(
                sentences:= sentencesFromStringSteam,
                nSizeOutQueue:= SIZEOF(interpreterGeoInfoBuffer), 
                pbyBufferOutQueue:= ADR(interpreterGeoInfoBuffer));
    
        roundPath(
            poqDataIn := interpreter.poqDataOut,
            dRadius := ,
            dAngleTol := ,
            nSizeOutQueue := SIZEOF(roundPathGeoInfoBuffer),
            pbyBufferOutQueue := ADR(roundPathGeoInfoBuffer));
    
     
  • blitz - 2026-01-11

    So in my case the solution was that I had both smooth path and round path in the code. Both blocks had their own buffer and seemed completely separate but still triggering Execute on both caused the mentioned problems. When I blocked Execute the problems disappeared.

     
  • blitz - 2026-01-11

    @bertus try to test using only SMC_SmoothMerge and SMC_SmoothPath separately. Block setting bExecute on both of this FBs.

     

Log in to post a comment.