Skip to content

Commit a92d8b7

Browse files
authored
Merge 1dc8d65 into d075045
2 parents d075045 + 1dc8d65 commit a92d8b7

File tree

2 files changed

+102
-15
lines changed

2 files changed

+102
-15
lines changed
+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
from ABBRobotEGM import EGM
2+
import numpy as np
3+
import time
4+
5+
# Ensure the robot is configured correctly by setting up "EGMPathCorr" with "Path" level in the Motion configuration.
6+
# This can be done in the robot's controller under: Motion configuration -> External Motion Interface Data.
7+
# MODULE MainModule
8+
# VAR egmident egmID1;
9+
# CONST robtarget Target_10:=[[0.771953305,2.548198209,204.864360938],[-0.000000176,-0.000000006,1,-0.000000002],[-1,0,-1,0],[9E+09,9E+09,9E+09,9E+09,9E+09,9E+09]];
10+
# TASK PERS wobjdata Workobject_1:=[FALSE,TRUE,"",[[525,-125,308],[1,0,0,0]],[[0,0,0],[1,0,0,0]]];
11+
# PERS tooldata Pen_TCP:=[TRUE,[[110,0,140],[0.707106781,0,0.707106781,0]],[1,[10.7435139,0,140],[1,0,0,0],0,0,0]];
12+
13+
# PROC main()
14+
# EGMReset egmID1;
15+
# EGMGetId egmID1;
16+
# EGMSetupUC ROB_1,egmId1,"EGMPathCorr","UCdevice"\PathCorr\APTR;
17+
# EGMActMove EGMid1,Pen_TCP.tframe\SampleRate:=48;
18+
# MoveL Target_10,vmax,fine,Pen_TCP\WObj:=Workobject_1;
19+
# EGMMoveL egmID1,Offs(Target_10,200,0,0),v10,fine,Pen_TCP\WObj:=Workobject_1;
20+
# MoveL Offs(Target_10,0,0,200),vmax,fine,Pen_TCP\WObj:=Workobject_1;
21+
# EGMStop egmID1,EGM_STOP_HOLD;
22+
# ENDPROC
23+
# ENDMODULE
24+
25+
26+
def main() -> None:
27+
"""
28+
Example showing how to apply path corrections during robot movement.
29+
This will apply a sinusoidal correction in the Y direction while the robot
30+
moves along a straight line in the X direction (using EGMMoveL in RAPID).
31+
32+
The sinusoidal pattern:
33+
- Amplitude: 100mm
34+
- Frequency: 2.0 Hz
35+
36+
This creates a wavy pattern perpendicular to the robot's movement direction. Run the python script before
37+
running the RAPID program on the robot.
38+
"""
39+
with EGM() as egm:
40+
print("Waiting for initial message from robot...")
41+
# Wait for first message from robot to establish connection
42+
while True:
43+
success, _ = egm.receive_from_robot(timeout=1.0)
44+
if success:
45+
print("Connected to robot!")
46+
break
47+
# Parameters for sinusoidal correction
48+
amplitude = 100.0 # mm
49+
frequency = 2.0 # Hz
50+
t_start = time.time()
51+
print("Sending Y-axis path corrections...")
52+
53+
while True:
54+
# Always receive from robot first to maintain connection
55+
success, _ = egm.receive_from_robot(timeout=0.1)
56+
if not success:
57+
print("Lost connection to robot")
58+
break
59+
60+
# Calculate Y correction using sine wave
61+
t = time.time() - t_start
62+
y_correction = amplitude * np.sin(2 * np.pi * frequency * t)
63+
64+
correction = np.array([0.0, y_correction, 0.0])
65+
egm.send_to_robot_path_corr(correction, age=1)
66+
67+
# Match robot's sensor refresh rate of 48ms
68+
time.sleep(0.048)
69+
70+
71+
if __name__ == "__main__":
72+
main()

src/ABBRobotEGM/egm.py

+30-15
Original file line numberDiff line numberDiff line change
@@ -466,34 +466,49 @@ def _create_sensor_message_cart(self, pos: np.ndarray, orient: np.ndarray, speed
466466

467467
def send_to_robot_path_corr(self, pos: np.ndarray, age: float = 1) -> bool:
468468
"""
469-
Send a path correction command. Returns False if no data has been received from the robot yet. The path
470-
correction is a displacement [x,y,z] in millimeters in **path coordinates**. The displacement uses
471-
"path coordinates", which relate the direction of movement of the end effector. See `CorrConn` command in
472-
*Technical reference manual RAPID Instructions, Functions and Data types* for a detailed description of path
473-
coordinates. The EGM operation must have been started with EGMActMove, and use EGMMoveL and EGMMoveC commands.
469+
Send a path correction command to the robot. The path correction is a displacement [x,y,z]
470+
in millimeters in path coordinates. Path coordinates relate to the direction of movement
471+
of the end effector.
472+
473+
The EGM operation must have been started with EGMActMove, and use EGMMoveL or EGMMoveC commands.
474+
See CorrConn command in Technical reference manual for details about path coordinates.
474475
475476
:param pos: The displacement in path coordinates in millimeters [x,y,z]
477+
:param age: Age of the correction in seconds (must be positive). Defaults to 1
476478
:return: True if successful, False if no data received from robot yet
479+
:raises ValueError: If pos is not a 3-element array or age is not positive
477480
"""
478-
self.send_sequence_number += 1
479-
sensor_message = self._create_sensor_message_path_corr(pos, age)
480-
return self._send_message(sensor_message)
481+
if not self.egm_addr:
482+
return False
481483

482-
def _create_sensor_message_path_corr(self, pos: np.ndarray, age: float) -> Any:
483-
"""Create the sensor message with path correction data to be sent to the robot."""
484+
# Input validation
485+
try:
486+
pos = np.asarray(pos, dtype=np.float64).flatten()
487+
if pos.size != 3:
488+
raise ValueError("pos must be a 3-element array [x,y,z]")
489+
except (ValueError, TypeError):
490+
raise ValueError("pos must be convertible to a numpy array")
491+
492+
if age <= 0:
493+
raise ValueError("age must be positive")
494+
495+
# Create path correction message
484496
sensor_message = egm_pb2.EgmSensorPathCorr()
497+
498+
# Set header with path correction message type
485499
header = sensor_message.header
486500
header.mtype = egm_pb2.EgmHeader.MessageType.Value('MSGTYPE_PATH_CORRECTION')
487501
header.seqno = self.send_sequence_number
488502
self.send_sequence_number += 1
489503

504+
# Set path correction data
490505
path_corr = sensor_message.pathCorr
491-
path_corr.pos.x = pos[0]
492-
path_corr.pos.y = pos[1]
493-
path_corr.pos.z = pos[2]
494-
path_corr.age = age
506+
path_corr.pos.x = float(pos[0])
507+
path_corr.pos.y = float(pos[1])
508+
path_corr.pos.z = float(pos[2])
509+
path_corr.age = int(age) # Protocol expects unsigned integer
495510

496-
return sensor_message
511+
return self._send_message(sensor_message)
497512

498513
def _get_timestamps(self, robot_message: Any) -> Tuple[Optional[EGMClock], Optional[EGMTimeStamp]]:
499514
clock = None

0 commit comments

Comments
 (0)