-
Notifications
You must be signed in to change notification settings - Fork 1
Adding new motion program commands
abb_motion_program_exec
works by creating a simple interpreter on the robot. The interpreter is written in RAPID and is installed using the setup instructions provided with the software. The Python module is used to create a list of commands, and these commands are converted to a binary file made up of a flat float32 array. The file is then downloaded to the robot RAM drive. The Python module triggers the robot to begin executing the RAPID program once the file is ready and downloaded to the robot. The RAPID software reads through the binary float32 file and runs the commands.
Each command has the following binary format:
Field | Type | Description |
---|---|---|
command_number | FLOAT32 | The sequence number of the command. Must be strictly increasing |
command_opcode | FLOAT32 | The opcode of the command. The opcode selects which command to execute |
params | x | Variable number of parameters following the opcode. Byte length must be divisible by 4 |
The parameters are variable length containing the data required to execute the command. The parameters are generally FLOAT32, but can also be other data types such as strings. All data must be aligned to 4 bytes. Strings must be padded to maintain this alignment.
The list of commands can also be converted to RAPID code
Adding new motion program commands requires the following steps:
- Review existing commands. See
commands.py
andegm_commands.py
for the existing commands insrc/abb_motion_program_exec/commands
. Each command is implemented as a class with methods to write the binary parameters and to generate RAPID code. Utility functions for writing parameters can be found inutil.py
andrapid_types.py
. - Select an opcode that is not being used by the existing commands.
- Implement the new class for the new command with the selected opcode.
- Add your class to the
MotionProgram
class insrc/abb_motion_program_exec/abb_motion_program_exec_client.py
. For example, the MoveJ command is added to the class usingMoveJ = command_append_method(commands.MoveJCommand)
- Add the decoding function for your command to
robot/HOME/motion_program_exec.mod
. The command should use thetry_motion_program_read_num()
to read FLOAT32 from the file. The function should execute the command if decoded successfully, and return FALSE if decoding failed. - Add your command to the CASE statement in
try_motion_program_run_next_cmd()
. This function implements the interpreter by reading the next opcode and then executing the appropriate decoder function.
At this point the new command should be successfully implemented. It is highly recommended that the software be tested in simulation using Robot Studio before using on a physical robot.
The Robot Raconteur interface provides a service that executes motion programs. The service uses the MotionProgram and MotionProgramExecClient classes to implement the service. The Robot Raconteur types used for the service are defined in the experimental.robotics.motion_program.robdef
and experimental.abb_robot.motion_program.robdef
files. The experimental.robotics.motion_program.robdef
contains vendor-independent data types and objects that are intended to eventually become a Robot Raconteur standard type after further development. The experimental.abb_robot.motion_program.robdef
file contains motion program commands that are specific to ABB robots. The structure type experimental.robotics.motion_programs.MotionProgram
Robot Raconteur type is used to define motion programs. The two fields of interest are motion_setup_commands
and motion_program_commands
. Motion programs are not intended to contain operations such as tool changes are logical branches. It is intended that the motion programs are relatively short sequences that are generated by the client. For this reason, the motion program is broken up into "setup" and "motion" commands. Setup commands contain information such as the tool, payload, and frame specifications. The motion section contains the commands that are to be executed by the robot.
Each command is defined using a structure. The motion_setup_commands
and motion_program_commnds
fields contain a list of structure instances, with each list entry containing a command. The Robot Raconteur varvalue{list}
type is used to store the list of commands, allowing any valid Robot Raconteur structure to be added to the list. Example command motion command structures include experimental.robotics.motion_program.MoveJCommand
, experimental.robotics.motion_program.MoveLCommand
. Example setup instructions include experimental.robotics.motion_program.SetToolCommand
. Example ABB specific command structures include experimental.abb_robot.motion_program.EGMMoveLCommand
for motion and experimental.abb_robot.motion_program.SetWorkObjectCommand
for setup.
The Robot Raconteur motion program types also includes the experimental.robotics.motion_program.FreeformCommand
structure. The freeform command is a catch-all command that allows any command to be specified without using a specialized structure. This allows for greater flexibility for software generating motion program commands. For instance, if a vendor specific command needs to be used but the software is not compiled to use that specific type, the user could use a FreeformCommand
to enter the details of the command without requiring a specialized structure. The FreeformCommand
can also be used to add specialized commands without needing to modify the Robot Raconteur service definition files or the client software. The FreeformCommand
contains three important fields, the command_name
, command_args
, and optional
. The command_name
is the name of the command, for instance MoveL
, MoveLCommand
, or experimental.robotics.motion_program.MoveLCommand
are all valid for command_name
. The command_args
is a dictionary of varvalue
containing the fields that are in the command. For MoveL, the field entries would include tcp_pose
, tcp_velocity
, blend_radius
, and fine_point
. The optional
field, when set to False, means that the command can be ignored if the robot does not understand the command.
The Robot Raconteur experimental.robotics.motion_programs.MotionProgram
structure and its contents must be converted to the Python MotionProgram
types to be executed by the MotionProgramExecClient
. This conversion is implemented in the src/abb_motion_program_exec/robotraconteur/_motion_program_conv.py
file. Each command has a class defined for the conversion. For example, the MoveLCommandConv
class is as follows:
class MoveLCommandConv:
rr_types = ["experimental.robotics.motion_program.MoveLCommand"]
freeform_names = ["MoveL","MoveLCommand","experimental.robotics.motion_program.MoveLCommand"]
def apply_rr_command(self, cmd, mp, cfx_robot, **kwargs):
zd = rr_zone_to_abb(cmd_get_arg(cmd,"fine_point"),cmd_get_arg(cmd,"blend_radius"))
sd = rr_speed_to_abb(cmd_get_arg(cmd,"tcp_velocity"))
rt = rr_robot_pose_to_abb(cmd_get_arg(cmd,"tcp_pose"), cfx_robot, cmd_get_extended(cmd, "confdata"))
mp.MoveL(rt, sd, zd)
The first two fields are rr_types
and freeform_names
. The rr_types
field is a list of the Robot Raconteur types defined in robdef files. In this case, experimental.robotics.motion_program.MoveLCommand
is the fully qualified name of the MoveLCommand
. The freeform_names
field is a list of names that will match command_name
in the FreeformCommand structure. The apply_rr_command()
function is called when the motion program conversion matches a command to this class. The cmd
parameter is the matched command instance, and the mp
parameter is an instance of MotionProgramExecClient
. For MoveLCommand
, the zone data, speed data, and robot target are converted from Robot Raconteur to ABB motion program format using utility functions defined earlier in the _motion_program_conv.py
. These functions are rr_zone_to_abb()
, rr_speed_to_abb()
, and rr_robot_pose_to_abb()
.
Note that ABB types use millimeters and degrees, while Robot Raconteur uses MKS system meters and radians following ROS REP-103!!
Note the use of cmd_get_arg()
to access fields from the command instance cmd
. This function will automatically find the field if it is in a specialized type of a FreeformCommand
structure.
Once the class is inserted into the _motion_program_conv.py
file, add the command to the list conv_types
later in the file. This list is searched by the motion program conversion to match to an incoming command based on rr_types
and freeform_names
. Currently the conv_types
list has the following entries:
conv_types = [
MoveAbsJCommandConv,
MoveJCommandConv,
MoveLCommandConv,
MoveCCommandConv,
WaitTimeCommandConv,
SetToolCommandConv,
SetPayloadCommandConv,
CirPathModeCommandConv,
SyncMoveOnCommandConv,
SyncMoveOffCommandConv,
EGMStreamConfigCommandConv,
EGMJointTargetConfigCommandConv,
EGMPoseTargetConfigCommandConv,
EGMPathCorrectionConfigCommandConv,
EGMRunJointCommandConv,
EGMRunPoseCommandConv,
EGMMoveLCommandConv,
EGMMoveCCommandConv,
SetWorkObjectCommandConv
]
When implementing a new command, it is best to begin with a FreeformCommand
only until the design is complete and verified. Contact the authors before adding a new structure type to any robdef files. Uncoordinated modification of robdef
files can cause compatibility issues. Leave the rr_types
as an empty list if no specialized structure is available.
Some RAPID commands have optional arguments. These can be handled in the following way:
In the Python dataclass for the command, add the parameter as an attribute and set it to None by default. Write a float32 as a flag if the value is set or not.
@dataclass
class WaitDICommand(CommandBase):
command_opcode = 1001
signal: str
value: int
max_time: float = None
def write_params(self, f: io.IOBase):
f.write(util.str_to_bin(signal))
f.write(util.num_to_bin(value))
if max_time is not None:
f.write(util.num_to_bin(1))
f.write(util.num_to_bin(max_time))
else:
f.write(util.num_to_bin(0))
f.write(util.num_to_bin(0))
Now in the RAPID module, check if the flag is true. If not true, use a default value.
FUNC bool try_motion_program_waitdi(num cmd_num)
VAR string name;
VAR signaldi din;
VAR num value;
VAR dionum dval;
VAR num max_time_flag;
VAR num max_time:= 9e9;
IF NOT (try_motion_program_read_string(name)
AND try_motion_program_read_num(value)
AND try_motion_program_read_num(max_time_flag)
AND try_motin_program_read_num(max_time)
) THEN
RETURN FALSE;
ENDIF
GetDataVal name, din;
IF max_time_flag > 0 THEN
max_time:=max_time;
ENDIF
WaitDI din, dval\MaxTime:=max_time;
RETURN TRUE;
ENDFUNC
In Robot Raconteur, the optional parameters are stored in the "extended" field. All commands (and most other Robot Raconteur structures) are expected to have an "extended" field to capture optional data or for future proofing.
The function cmd_get_extended
found in _motion_program_conv.py
is used to read extended fields. This function works the same way as cmd_get_arg
, but but searches the extended
field and returns None
if the value is not found. This function can be used to populate the optional value in the Python class.