Hi…
As @lemster68 commented, the charge must be calculated in all possible cases and must be charged as you alternate between products and quantities.
In the case of TCP, as far as I know, it is unique for each tool, regardless of whether it has 1 or n grip positions. What you can do in this case is move the TCP to the center of the object you are currently manipulating.
I needed to do something similar and I created a method for this, which I share with you, I hope it helps.
MODULE Mod_DoubleGripper
! 1. BASE TOOL (Reference)
! tframe: Defined at the adapter center (or at Gripper 1 as zero reference)
! tload: Total mass (Adapter + Gripper 1 + Gripper 2) and COG of the entire assembly
PERS tooldata tMasterGripper := [TRUE, [[0,0,150],[1,0,0,0]], [8.5, [10,0,80], [1,0,0,0], 0, 0, 0]];
! 2. WORKING TOOL (The one the robot actually uses in MoveL/J commands)
VAR tooldata tActiveGripper;
! ====================================================================================================
! Author:
! ------
! - SLSaibel - slsaibel@gmail.com
!
! Description: PROC Main - Main execution block
! ====================================================================================================
PROC Main()
! --- OPERATION WITH PART A ---
! Assuming Gripper A is offset by +100mm in Y and has a 90° rotation in Z
EditOffsTCP_v2_1_1 tActiveGripper, tMasterGripper \offsTCPP:=[0,100,50] \rotP:=[0,0,90];
MoveL pPickPointA, v500, z10, tActiveGripper;
! Pick part...
! --- OPERATION WITH PART B ---
! Assuming Gripper B is offset by -100mm in Y (opposite side)
EditOffsTCP_v2_1_1 tActiveGripper, tMasterGripper \offsTCPP:=[0,-100,50] \rotP:=[0,0,-90];
MoveL pPickPointB, v500, z10, tActiveGripper;
! Pick part...
ENDPROC
! ====================================================================================================
! Author:
! ------
! - SLSaibel - slsaibel@gmail.com
!
! Description: PROC EditOffsTCP_v2_1_1 - Modifies TCP and Recalculates COG Correctly
! ====================================================================================================
!
! Objective:
! ---------
! Change the position (`.trans`) and, optionally, the orientation (`.rot`) of a `tooldata` TCP,
! based on a reference and applying offsets. The routine recalculates the Center of Gravity (COG)
! in a mathematically robust way so that its physical position in space is preserved.
!
! Version History:
! ---------------------
! - v2.1.1 (2026-02-20) : Removed mandatory offset parameter 'offsP'. Changed input parameter sequence. - SLS
! - v2.1.0 (2026-02-17) : Included TCP offset parameters for the output tool. If the tool TCP is not exactly at the center, this parameter must be informed. - SLS
! - v2.0.0 (2025-06-12) : [!] Critical Correction - COG calculation logic completely rewritten using the `pose` data type to ensure mathematical correctness in any translation or rotation.
! The routine is now robust and predictable. - SLS
! - v1.2.1 (2025-05-28) : Previous version with non-standard COG calculation logic.
!
! Parameters:
! -----------
! setToolR {INOUT tooldata} : Tool (`tooldata`) to be modified.
! globalToolP {IN tooldata} : Reference tool, used as the base for calculations.
! \offsTCPP {IN \pos} : Optional. TCP offset for the output tool.
! offsP {IN pos} : Translational offset (X, Y, Z) to be applied to the TCP.
! \rotP {IN \pos} : Optional. Rotation (Euler angles Rx, Ry, Rz in degrees) to be applied to the TCP orientation.
! \delayP {IN \num} : Optional. Wait time in seconds (default: 0.1s).
!
! Dependencies:
! -------------
! - PoseMult(), PoseInv(), OrientZYX() (RAPID System)
!
! Return:
! --------------------------
! [None] - Procedure.
!
! Usage Example:
! ----------------------
! MODULE MOD_Test
! ! Necessary attributes
! PERS tooldata tBaseG := [TRUE, [[0,0,200],[1,0,0,0]], [1, [0,0,1], [1,0,0,0], 0, 0, 0]];
! VAR tooldata tNew;
! ! #####################
!
! PROC main()
! ! Modifies the TCP applying offset and rotation, preserving the physical position of the COG
! EditOffsTCP_v2_1_1 tNew, tBaseG \offsTCPP:=[0,0,50] \rotP:=[0,0,180];
! ENDPROC
! ENDMODULE
!
! Limitations and Observations:
! -------------------------
! - Depends on SystemAutenicate_v2_1_0().
! - From v2.0.0 onwards, the logic uses the `pose` type to ensure mathematical correctness in any translation or rotation.
! - It is necessary to check the path if migrating from versions 1.x.x.
!
! ####################################################################################################
! # IMPORTANT #
! ####################################################################################################
! # - The behavior of this version (v2.0.0) is DIFFERENT and CORRECT compared to versions 1.x.x. #
! # Test and validate the new path and the dynamic behavior of the robot after the update. #
! ####################################################################################################
!
! ====================================================================================================
PROC EditOffsTCP_v2_1_1(INOUT tooldata setToolR,tooldata globalToolP\pos offsTCPP\pos rotP\num delayP)
! ## ATTRIBUTES ##############################################################################
VAR pose poseTcpOriginalL;
VAR pose poseCogTcpOriginalL;
VAR pose poseCogGlobalL;
VAR pose poseOffsetL;
VAR pose poseNewTcpL;
VAR pose poseCogNewTcpL;
VAR pos offsAdjL:=[0,0,0];
! ## ATTRIBUTES ##############################################################################
! Verifying system authenticity ...
!IF NOT SystemAutenicate_v2_1_0() EXIT;
! 1. CONVERT INPUT DATA TO 'POSE' FORMAT
!'pose' groups position and orientation, ideal for transformations.
! Original TCP Pose relative to the robot flange ...
poseTcpOriginalL.trans:=globalToolP.tframe.trans;
poseTcpOriginalL.rot:=globalToolP.tframe.rot;
! COG Pose relative to the original TCP (orientation is null as it is a position vector) ...
poseCogTcpOriginalL.trans:=globalToolP.tload.cog;
poseCogTcpOriginalL.rot:=[1,0,0,0];
! 2. FIND THE PHYSICAL AND ABSOLUTE POSITION OF THE COG
! This position in space should not change when the TCP is altered.
poseCogGlobalL:=PoseMult(poseTcpOriginalL,poseCogTcpOriginalL);
! 3. CALCULATE THE NEW TCP POSE
! Adds the TCP offset for the gripper ...
IF Present(offsTCPP) THEN
Add offsAdjL.x,offsTCPP.x;
Add offsAdjL.y,offsTCPP.y;
Add offsAdjL.z,offsTCPP.z;
ENDIF
IF Present(rotP) THEN
IF Abs(rotP.z)=180 THEN
offsAdjL.z:=-offsAdjL.z;
! X remains the same
! Y remains the same
ENDIF
ENDIF
! Define the offset pose to be applied.
poseOffsetL.trans:=offsAdjL;
IF Present(rotP) THEN
! Converts Euler angles (stored in 'pos') to a quaternion ('orient') ...
poseOffsetL.rot:=OrientZYX(rotP.z,rotP.y,rotP.x);
ELSE
! No rotation ...
poseOffsetL.rot:=[1,0,0,0];
ENDIF
! Applies the offset to the original TCP pose to obtain the new TCP pose ...
poseNewTcpL:=PoseMult(poseTcpOriginalL,poseOffsetL);
! 4. CALCULATE THE NEW COG VECTOR (RELATIVE TO THE NEW TCP)
! The formula is: NewVector_COG = Inverse(NewPose_TCP) * GlobalPose_COG.
poseCogNewTcpL:=PoseMult(PoseInv(poseNewTcpL),poseCogGlobalL);
! 5. UPDATE THE OUTPUT TOOL
! Copies all data from the reference tool (mass, inertia, etc.).
setToolR:=globalToolP;
! Assigns the new calculated TCP frame ...
setToolR.tframe.trans:=poseNewTcpL.trans;
setToolR.tframe.rot:=poseNewTcpL.rot;
! Assigns the new COG position, now relative to the new TCP ...
setToolR.tload.cog:=poseCogNewTcpL.trans;
! Waits for a period for the settings to be applied ...
IF Present(delayP) THEN
WaitTime delayP;
ELSE
WaitTime 0.1;
ENDIF
RETURN ;
ERROR
! #############################################################################################
! HANDLING OF POSSIBLE ERRORS
! #############################################################################################
! Clears the screen ...
TPErase;
ErrWrite "UNHANDLED",EDIT_OFFS_TCP.versionedName
\RL2:="Unhandled error in TCP/COG calculation."
\RL3:="Please contact the automation team."
\RL4:="RAPID Error No: "+ValToStr(ERRNO);
IF OpMode()<>OP_AUTO THEN
Stop;
TRYNEXT;
ELSE
! In AUTO mode, stops the robot immediately to avoid collisions with the wrong TCP.
Stop;
Stop;
! Propagates the error clearly to the main routine if necessary, or exits the program.
EXIT;
ENDIF
ENDPROC
ENDMODULE
Adapt your reality
Good Job.