46 KiB
通用机器人编程语法规范
版本:0.1
适用范围:离线编程、虚拟控制器、虚拟调试、多品牌后处理
目标品牌:ABB RAPID、FANUC TP/LS/KAREL 语义、KUKA KRL
1. 设计目标
本规范定义一种用于离线编程和虚拟调试的通用机器人程序语言,暂定名为 GRL,Generic Robot Language。GRL 不是复制某一家机器人厂商语言,而是抽象 ABB、FANUC、KUKA 等主流工业机器人共同具备的程序语义,形成稳定、可解析、可仿真、可自动生成、可后处理的中性机器人程序。
核心目标:
- 支持离线编程的完整流程:目标点、路径、工艺、IO、等待、流程控制、报警、仿真执行、报告和后处理。
- 方便由规划点、CAD 曲线、工艺模板、AI 规划器自动生成程序。
- 方便转换为 ABB RAPID、FANUC LS/TP 风格文本、KUKA KRL。
- 方便反向导入品牌程序,恢复为统一 IR、GRL 和 OLP 对象模型。
- 程序文本稳定、可读、可 diff,便于版本管理。
- 语义确定,编译后能生成统一可执行 IR,虚拟控制器不依赖品牌控制器细节。
非目标:
- 不追求完全复刻某一品牌控制器所有系统变量和内部调度细节。
- 不直接作为真实机器人安全运行的唯一依据,后处理输出必须经过品牌控制器验证和现场低速试运行。
- 不把 CAD、碰撞检测、机器人运动学全部写入语言本身;这些由 OLP 模型和仿真内核承担。
2. 对标原则
2.1 品牌能力映射
| 能力 | ABB RAPID | FANUC TP/LS | KUKA KRL | GRL |
|---|---|---|---|---|
| 程序模块 | MODULE |
TP 程序、KAREL 程序 | .src/.dat、DEF |
module |
| 子程序 | PROC |
CALL |
DEF 子程序 |
proc |
| 函数 | FUNC |
KAREL function | DEFFCT |
func |
| 中断/陷阱 | TRAP、interrupt |
条件监控、后台逻辑 | INTERRUPT |
trap、interrupt |
| 关节运动 | MoveJ |
J P[...] |
PTP |
movej |
| 直线运动 | MoveL |
L P[...] |
LIN |
movel |
| 圆弧运动 | MoveC |
C P[...] |
CIRC |
movec |
| 笛卡尔目标点 | robtarget |
P[]、PR[] |
E6POS |
pose_target |
| 关节目标点 | jointtarget |
joint position data | E6AXIS |
joint_target |
| 工具 | tooldata |
UTOOL | $TOOL |
tool |
| 工件/基坐标 | wobjdata |
UFRAME | $BASE |
frame |
| 速度 | speeddata |
%、mm/sec |
$VEL、$ACC |
speed |
| 过渡 | zonedata、fine |
FINE、CNT |
C_DIS、C_PTP、APO |
zone |
| IO | SetDO、WaitDI |
DO[]、DI[]、WAIT |
$OUT[]、$IN[]、WAIT FOR |
io.*、wait |
| 标签跳转 | GOTO |
LBL、JMP |
GOTO |
label、jump |
2.2 中性语法风格
GRL 采用“声明式数据 + 结构化程序 + Path/Operation 对象”的风格:
- 目标点、工具、坐标系、速度、过渡作为数据声明。
- 程序流程使用
proc、if、while、for、switch等结构化控制。 - 短程序和调试程序可直接写
movej/movel/movec。 - 自动离线编程优先生成
path和operation,程序中通过run_path、run_operation调用。 - 所有运动语句显式绑定目标点、速度、过渡、工具、坐标系,允许默认值,但编译到 IR 前必须解析为确定值。
- 品牌特性通过
post_hint和@brand.*元数据保留,不污染通用语义。
3. 文件与工程结构
推荐一个 OLP 项目使用以下结构:
project.json
robots/
robot_1.urdf
robot_1.meta.json
programs/
main.grl
weld.grl
targets/
main.targets.json
paths/
weld_path.path.json
operations/
weld_op.operation.json
io/
io_map.json
post/
abb.profile.json
fanuc.profile.json
kuka.profile.json
generated/
abb/
fanuc/
kuka/
reports/
reachability.json
cycle_time.json
GRL 文件扩展名建议使用 .grl。
4. 词法规则
4.1 编码和大小写
- 文件编码使用 UTF-8。
- 关键字建议小写。
- 标识符大小写敏感,推荐使用
snake_case。 - 后处理器输出到品牌程序时按品牌习惯调整大小写。
4.2 注释
// 单行注释
/*
多行注释
*/
自动生成程序应在关键对象上保留来源注释,但不要为每个采样点生成大量冗余注释。
4.3 标识符
identifier = letter { letter | digit | "_" }
示例:
pick_path
weld_op_01
T_curve_032_000
不建议使用中文标识符。UI 可显示中文别名,但程序内部名称应保持 ASCII,便于后处理和版本管理。
4.4 字面量和单位
支持带单位数值:
100 mm
0.25 m
180 deg
3.14159 rad
300 mm/s
50 %
2 s
200 ms
2.5 kg
编译器内部统一规范化:
| 物理量 | 内部单位 |
|---|---|
| 长度 | meter |
| 角度 | radian |
| 时间 | second |
| 质量 | kilogram |
| 线速度 | meter/second |
| 角速度 | radian/second |
| 加速度 | meter/second^2 或 radian/second^2 |
4.5 保留关键字
language module import end
persistent const var
robot tool frame load target speed zone path operation process
proc func return call
if elseif else switch case default while for to step break continue
label jump
movej movel movec run_path run_operation
set_tool set_frame set_speed set_zone
wait pulse timer
io true false and or not mod all any rising falling changed
trap interrupt enable disable raise alarm try catch finally
task sync
post_hint source defaults point event before after at
joint_target pose_target pose poseq joints robot_config ext_axis
fine continuous cnt z
5. 顶层结构
5.1 最小文件
language grl 0.1
module Main
proc main()
// program entry
end
end
language grl 0.1 用于版本控制。后续语言升级时,编译器可按版本选择兼容规则。
5.2 顶层声明
module ModuleName
import CommonTools
persistent tool gripper = ...
persistent frame fixture = ...
const speed v_pick = linear(300 mm/s)
const zone z_pick = z10
target home = ...
path pick_path { ... }
operation pick_op { ... }
proc main()
...
end
end
顶层可包含:
importpersistentconsttargetpathoperationprocfunctraptaskpost_hint
6. 类型系统
6.1 基础类型
| 类型 | 说明 |
|---|---|
bool |
布尔值 |
int |
整数 |
real |
浮点数 |
string |
字符串 |
time |
时间 |
length |
长度 |
angle |
角度 |
percent |
百分比 |
6.2 机器人类型
| 类型 | 说明 |
|---|---|
pose |
笛卡尔位姿 |
joint_array |
关节数组 |
pose_target |
笛卡尔目标点 |
joint_target |
关节目标点 |
tool |
工具 TCP、负载、惯量 |
frame |
工件坐标或基坐标 |
speed |
运动速度参数 |
zone |
到点/过渡参数 |
load |
负载 |
robot_config |
肩、肘、腕等机器人配置 |
ext_axis |
外部轴 |
path |
路径对象 |
operation |
工艺操作对象 |
6.3 变量声明
const int max_retry = 3
var int retry = 0
var bool part_ok = false
var pose_target p_tmp = pick offset z 100 mm
persistent real counter = 0
规则:
const编译后不可修改。persistent在项目或控制器状态中持久化。var默认为局部变量。- 类型可以显式写出,自动生成程序建议显式写出。
7. 表达式与运算
GRL 表达式是数据声明、运动参数、路径参数、IO 条件和流程控制的公共基础。首版按 ABB RAPID、FANUC LS/KAREL、KUKA KRL 共同具备的数学能力建模,优先保证同一 GRL 程序能稳定编译到统一 IR,再由后处理器输出品牌程序。
7.1 语法范围
表达式必须支持:
- 算术:一元
+、一元-、二元+、-、*、/、mod。 - 比较:
==、!=、<、<=、>、>=。 - 逻辑:
and、or、not;可兼容&&、||、!作为输入别名,IR 中统一为 GRL 关键字。 - 括号:
(...)显式改变优先级。 - 函数调用:
callee(args...),参数内部允许完整表达式。 - 单位后缀:可作用于数字字面量、标识符、函数调用或括号表达式,例如
100 mm、blend_base mm、sqrt(90000) mm、(10 + 5) deg。 - 现有结构表达式:数组、对象、构造器调用、标识符引用、IO 引用和
offset表达式必须继续兼容。
优先级从高到低:
| 层级 | 运算 |
|---|---|
| 1 | 函数调用、对象、数组、括号、构造器、offset |
| 2 | 一元 +、-、not、! |
| 3 | *、/、mod |
| 4 | +、- |
| 5 | <、<=、>、>= |
| 6 | ==、!= |
| 7 | and、&& |
| 8 | or、` |
7.2 内置常量和函数
首版内置常量:
| 名称 | 含义 |
|---|---|
pi |
圆周率 |
e |
自然常数 |
首版内置函数:
| 类别 | 函数 |
|---|---|
| 三角函数 | sin、cos、tan、asin、acos、atan、atan2 |
| 数值函数 | sqrt、abs、pow、min、max、clamp、floor、ceil、round |
函数规则:
sin/cos/tan接收角度量;带deg或rad单位时归一化为弧度,纯数按弧度处理。asin/acos接收无单位数值,定义域为[-1, 1];atan接收无单位数值;atan2(y, x)的两个参数必须同维度或均为无单位数值。asin/acos/atan/atan2返回角度量;在pose(...)姿态项或joint_target.joints等角度上下文中可直接使用。sqrt首版只要求支持无单位数值或编译器明确支持的上下文,负数必须报定义域错误。min/max/clamp的参数必须维度兼容;clamp(value, min, max)的边界顺序错误应给出 diagnostic。- 函数参数数量必须严格检查,例如
atan2为 2 个参数,clamp为 3 个参数。
7.3 单位和求值语义
数值求值采用统一 SI 归一化:
| GRL 输入 | IR/KDL 归一化 |
|---|---|
mm |
m |
m |
m |
mm/s |
m/s |
mm/s2 |
m/s2 |
deg |
rad |
rad |
rad |
% |
0 到 1 的比例 |
s |
s |
ms |
s |
kg |
kg |
规则:
- 同维度数值可以相加、相减和比较,结果保留该维度并归一化。
- 纯数可以参与乘除;维度乘除首版只允许编译器明确支持的上下文,避免产生复杂复合单位。
- 不同维度相加、相减或比较必须报错,例如
10 mm + 2 s。 - 构造器可以提供默认上下文单位:
linear(...)默认mm/s,angular(...)默认deg/s,z(...)默认mm,pose(...)前三项默认mm、后三项默认deg,joint_target.joints默认deg,timeout/duration默认s。 - 因此
linear(100 + 50 mm/s)在linear速度上下文中等价于linear(150 mm/s);自动生成程序推荐输出显式单位,减少人工歧义。
7.4 编译期求值位置
常量表达式必须在编译阶段求值,并把结果写入 Semantic IR / KDL request,而不是把表达式字符串传到运动规划层。
必须支持的求值位置:
- 数据声明 initializer:
const、persistent、需要静态初值的var。 speed:linear(...)、joint(...)、angular(...)、acc ...参数。zone:fine、z(...)、cnt(...)。pose(...)、poseq(...)、robot_config(...)。joint_target { joints: [...] }。pose_target { pose: ... }。tool { tcp: ..., mass: ... }和frame { origin: ... }。- path
defaults、source属性和 path point 内联speed、zone、tool、frame、via、target参数。 - path event 的
distance。 wait ... timeout ...和pulse ... duration ...中的时间常量。- operation
process中需要数值常量的工艺参数。 switch case值。
比较和逻辑表达式也必须进入 AST。纯常量比较/逻辑可在编译期折叠;依赖 IO、变量或运行态状态的条件先保留为结构化控制表达式,供虚拟控制器和后续品牌映射使用。
7.5 示例
const real blend_base = 5 + 5
const speed v_pick = linear(100 + 50 mm/s)
const speed v_safe = linear(max(50 mm/s, 200 mm/s / 2))
const zone z_app = z(clamp(blend_base mm, 1 mm, 50 mm))
target home = joint_target {
joints: [0 deg, (10 + 5) deg, -90 deg, 0 deg, 0 deg, 0 deg]
}
target pick = pose_target {
pose: pose(400 + 50 mm, 20 * 2 mm, sqrt(90000) mm, 0 deg, 0 deg, atan2(1, 1))
}
wait io.ai[2] > sin(30 deg) timeout 1 + 1 s
编译期结果要求:
v_pick进入 IR 后为linear,速度0.15 m/s。z_app进入 IR 后为distance,距离0.01 m。home.joints[1]进入 IR 后为15 deg对应的弧度值。pick.pose.position[0]进入 IR 后为0.45 m。atan2(1, 1)进入姿态上下文后为45 deg对应的弧度值。
7.6 诊断
运算功能必须输出稳定 diagnostic:
| code | 场景 |
|---|---|
GRL_EXPR_PARSE |
表达式语法错误或括号不匹配。 |
GRL_EXPR_UNKNOWN_SYMBOL |
常量求值遇到未知标识符。 |
GRL_EXPR_UNKNOWN_FUNCTION |
未知函数。 |
GRL_EXPR_ARITY |
函数参数数量错误。 |
GRL_EXPR_DOMAIN |
数学定义域错误,例如 sqrt(-1)、acos(2)。 |
GRL_EXPR_DIV_ZERO |
除零。 |
GRL_EXPR_UNIT_MISMATCH |
单位维度不兼容。 |
GRL_EXPR_NON_CONSTANT |
需要常量的位置出现运行时表达式。 |
GRL_EXPR_UNSUPPORTED_RUNTIME |
后处理暂不支持的复杂运行时表达式。 |
每个 diagnostic 必须包含 severity、message、source range,并能通过 source map 定位到 GRL 源文件行列。
7.7 跨品牌边界
首版跨品牌输出不直接翻译复杂表达式字符串,而是优先输出编译期折叠后的常量:
linear(100 + 50 mm/s)输出为各品牌可表达的具体速度值。z(clamp(...))输出为具体 ABB zone、FANUC CNT 或 KUKA 近似 blend。- runtime 条件表达式如果后处理器无法保真映射,必须进入转换报告,不得静默丢失。
- 品牌专用数学函数、控制器系统变量和复杂运行时表达式不作为首版公共能力,后续通过 post/import report 增量支持。
8. 坐标、工具和目标点
8.1 工具
persistent tool gripper = tool {
tcp: pose(0 mm, 0 mm, 180 mm, 0 deg, 0 deg, 0 deg),
mass: 2.5 kg,
cog: [0 mm, 0 mm, 80 mm]
}
8.2 工件坐标
persistent frame fixture = frame {
origin: pose(800 mm, 0 mm, 200 mm, 0 deg, 0 deg, 0 deg)
}
8.3 关节目标点
target home = joint_target {
joints: [0 deg, -30 deg, 60 deg, 0 deg, 60 deg, 0 deg]
}
joint_target 主要用于 movej,后处理可映射到 ABB jointtarget、KUKA E6AXIS 或 FANUC 关节位置数据。
8.4 笛卡尔目标点
target pick = pose_target {
pose: pose(500 mm, 120 mm, 300 mm, 180 deg, 0 deg, 90 deg),
config: robot_config(0, 0, 1),
tool: gripper,
frame: fixture
}
pose() 参数顺序为:
pose(x, y, z, rx, ry, rz)
首版 rx/ry/rz 使用固定约定的欧拉角,建议内部立即转换为四元数。为了避免姿态歧义,也允许使用四元数:
poseq(500 mm, 120 mm, 300 mm, 0, 0, 0.7071068, 0.7071068)
8.5 偏移目标点
var pose_target approach = pick offset z 100 mm
var pose_target p2 = pick offset x 20 mm y -10 mm z 50 mm
偏移默认在目标点所属 frame 下解释。需要明确坐标系时:
var pose_target p3 = pick offset_in tool z -50 mm
var pose_target p4 = pick offset_in frame fixture x 100 mm
9. 速度和过渡
9.1 速度
const speed v_joint_fast = joint(80 %)
const speed v_joint_abs = joint(90 deg/s)
const speed v_pick = linear(300 mm/s)
const speed v_weld = linear(120 mm/s)
const speed v_orient = angular(90 deg/s)
运动语义:
joint(...)用于关节运动速度。linear(...)用于 TCP 线速度。angular(...)可作为姿态插补角速度限制。- 加速度可作为可选参数:
const speed v_slow = linear(100 mm/s, acc 500 mm/s2)
9.2 过渡
const zone z_fine = fine
const zone z10 = z(10 mm)
const zone z50 = z(50 mm)
const zone z_cnt = cnt(30)
const zone z_cont = continuous
语义:
fine:精确到点,不做过渡。z(distance):以距离近似表示过渡半径,便于映射 ABBzonedata和 KUKAAPO。cnt(percent):以百分比表示连续过渡,便于映射 FANUCCNT。continuous:允许连续通过,实际过渡由后处理器或虚拟控制器策略决定。
10. 运动指令
10.1 基本语法
movej TargetExpr [speed Speed] [zone Zone] [tool Tool] [frame Frame]
movel TargetExpr [speed Speed] [zone Zone] [tool Tool] [frame Frame]
movec via ViaTargetExpr target EndTargetExpr [speed Speed] [zone Zone] [tool Tool] [frame Frame]
示例:
movej home speed joint(60 %) zone fine tool gripper frame fixture
movel pick speed linear(300 mm/s) zone z10
movec via arc_mid target arc_end speed linear(150 mm/s) zone fine
10.2 MOVEJ 语义
movej 表示关节空间运动。机器人按各轴关节角度差分运行,关节同起同停,TCP 轨迹不要求是直线。
编译规则:
- 目标为
joint_target时直接使用关节数组。 - 目标为
pose_target时先用 IK 求终点关节。 - 轨迹按关节空间插补。
- 后处理映射:
- ABB:
MoveJ - FANUC:
J P[...] - KUKA:
PTP
- ABB:
10.3 MOVEL 语义
movel 表示 TCP 直线运动。TCP 沿起点到终点的空间直线运行,关节角由每个 TCP 采样点 IK 求解。
编译规则:
- 目标必须能解析为笛卡尔位姿。
- 当前点作为直线起点。
- 姿态按默认策略插补,首版推荐固定姿态或 slerp。
- 后处理映射:
- ABB:
MoveL - FANUC:
L P[...] - KUKA:
LIN
- ABB:
10.4 MOVEC 语义
movec 表示 TCP 圆弧运动。TCP 从当前点出发,经过 via 点,到达 target 点,沿圆弧运行,关节角由每个圆弧采样点 IK 求解。
编译规则:
- 起点为当前 TCP。
via和target必须能解析为笛卡尔位姿。- 三点不能重合或近似共线。
- 首版使用 via 点位置约束圆弧,姿态按起点到终点插补。
- 后处理映射:
- ABB:
MoveC - FANUC:
C P[...] - KUKA:
CIRC
- ABB:
10.5 当前工具和当前坐标
set_tool gripper
set_frame fixture
set_speed linear(300 mm/s)
set_zone z10
参数优先级:
- 运动语句显式参数最高。
path defaults次之。- 目标点自带
tool/frame次之。 - 当前控制器状态最低。
编译到 IR 时,所有运动必须有确定的 tool、frame、speed、zone。
11. Path 语法
Path 是自动离线编程的核心对象。CAD 曲线、规划点、示教点、工艺模板应优先生成 Path,而不是直接生成散乱运动语句。
11.1 基本语法
path pick_path {
defaults {
tool: gripper,
frame: fixture,
speed: linear(300 mm/s),
zone: z10
}
point approach movej home zone fine
point p1 movel pick offset z 100 mm
point p2 movel pick zone fine
}
11.2 圆弧点
path arc_path {
defaults {
tool: torch,
frame: part_frame,
speed: linear(120 mm/s),
zone: fine
}
point p0 movej start
point p1 movec via mid target end
}
11.3 来源元数据
path curve_032_generated {
source {
type: cad_curve
id: "edge_032"
sample_distance: 5 mm
normal_strategy: surface_normal
}
defaults {
tool: spray_gun,
frame: part_frame,
speed: linear(500 mm/s),
zone: z20,
posture: follow_normal
}
point p000 movel T_curve_032_000 zone fine
point p001 movel T_curve_032_001
point p002 movel T_curve_032_002
}
来源元数据用于:
- 路径重采样。
- 目标点重命名。
- 重新生成程序。
- 报告中定位到 CAD 曲线或工艺对象。
11.4 Path event
event before p001 io.do[10] = true
event after p010 io.do[10] = false
event at p005 distance -20 mm pulse io.do[20] duration 100 ms
语义:
before:到达路径点之前执行。after:完成路径点之后执行。at ... distance:相对路径点提前或滞后触发,首版可编译为最近采样点事件。- 后处理到品牌程序时,若品牌不支持精确路径触发,应生成转换报告。
11.5 run_path
proc main()
run_path pick_path
end
编译语义:
- 展开 Path 的 defaults、points、events。
- 每个 point 变为 Motion IR。
- event 变为 IO/Wait/Process IR。
- 保留 source map,调试时可从程序行定位到路径点。
12. Operation 语法
Operation 表达工艺语义,比 Path 更高一层。
operation weld_op_01 {
kind: arc_welding
path: weld_seam_01
process {
weld_id: "WELD_1"
voltage: 24.0
current: 180.0
weave: none
}
start_action:
io.do[20] = true
end_action:
io.do[20] = false
}
首版建议支持的 kind:
| kind | 用途 |
|---|---|
handling |
搬运、上下料 |
arc_welding |
弧焊 |
spot_welding |
点焊 |
dispensing |
涂胶 |
spraying |
喷涂 |
grinding |
打磨 |
cutting |
切割 |
generic_path |
通用路径 |
run_operation:
proc main()
run_operation weld_op_01
end
编译语义:
- 执行
start_action。 - 执行 operation 引用的 path。
- 执行
end_action。 - 保留工艺参数,供仿真 UI、报告和后处理使用。
13. IO 与等待
13.1 IO 地址
io.di[1]
io.do[1]
io.ai[1]
io.ao[1]
io.gi[1]
io.go[1]
io.ri[1]
io.ro[1]
推荐通过 io_map.json 提供别名:
io.alias.clamp_open
io.alias.clamp_closed
io.alias.gripper_close_cmd
13.2 IO 写入
io.do[1] = true
io.go[1] = 16
io.ao[1] = 3.5
13.3 wait
wait io.di[1] == true
wait io.alias.clamp_closed == true timeout 2 s
wait io.ai[2] > 3.5 timeout 1.5 s
wait all(io.di[1] == true, io.di[2] == false)
wait any(io.di[10] == true, timer.done("T_PICK"))
wait rising(io.di[4]) timeout 5 s
wait falling(io.di[5]) timeout 5 s
超时处理:
wait io.di[1] == true timeout 2 s on_timeout alarm "Clamp close timeout"
wait io.di[1] == true timeout 2 s on_timeout call recover_clamp()
13.4 pulse
pulse io.do[3] duration 200 ms
虚拟控制器语义:
- 立即置位输出。
- 虚拟时间到达 duration 后自动复位。
- trace 中必须记录置位和复位事件。
14. 流程控制
流程控制用于表达工艺分支、重试、配方选择、批量工位循环和异常恢复。GRL 新程序应优先使用结构化控制流;label/jump 主要用于兼容 FANUC TP/LS 等已有程序导入。
14.1 条件 if / elseif / else
if part_ok
run_path good_path
elseif retry < 3
call retry_pick()
else
alarm "Pick failed"
end
语义规则:
if条件表达式必须能转换为bool。elseif可以出现零次或多次。else最多出现一次,且必须位于所有elseif之后。- 条件按顺序求值,命中第一条为 true 的分支后,不再执行后续分支。
- 每个
if必须以end结束。 - 条件表达式允许访问变量、IO、函数返回值和比较表达式。
- 运动指令允许出现在任意分支内,但编译器必须保证每条分支中的工具、坐标系、速度和 zone 可解析。
常用条件表达式:
if io.di[1] == true
if count >= 3 and not part_ok
if is_part_ready()
if recipe_id == 2 or recipe_id == 3
14.2 while 循环
while retry < 3
call try_pick()
if part_ok
break
end
retry = retry + 1
end
语义规则:
while条件必须为bool。- 每次进入循环前求值。
break立即退出当前循环。continue跳过本轮剩余语句,进入下一轮条件判断。- 编译器建议对可能无限循环的
while true给出 warning,除非循环体内包含明确的break、return、raise或wait。
14.3 for 循环
for i = 0 to 9 step 1
call process_slot(i)
end
也允许省略 step,默认步长为 1:
for layer = 1 to layer_count
call weld_layer(layer)
end
反向循环:
for i = 10 to 0 step -1
call clear_slot(i)
end
语义规则:
- 循环变量默认是当前
for块局部变量。 - 起点、终点、步长必须是整数或可安全转换为整数。
step不能为0。break和continue只影响当前最近一层循环。- 后处理到不支持结构化
for的品牌时,可展开为标签和跳转,但必须保持 source map。
14.4 switch / case / default
switch recipe_id
case 1
run_operation op_a
case 2
run_operation op_b
default
alarm "Unknown recipe"
end
语义规则:
switch表达式可以是int、bool、string或枚举型。case值必须是常量表达式。- 每个
case默认不向下贯穿,不需要写break。 default最多出现一次。- 若需要多个值进入同一分支,可以使用逗号:
switch recipe_id
case 1, 2, 3
run_operation common_recipe
case 10
run_operation special_recipe
default
alarm "Unknown recipe"
end
- 导出到 FANUC TP/LS 时,
switch可转换为SELECT、条件跳转或LBL/JMP组合。 - 导出到 ABB/KUKA 时,优先转换为品牌结构化分支。
14.5 break 和 continue
while true
call check_part()
if part_ok
break
end
retry = retry + 1
if retry < max_retry
continue
end
alarm "Retry failed"
break
end
规则:
break只能出现在while、for或switch中。continue只能出现在while或for中。- 在
switch中不需要break防止贯穿,因为 GRL 默认不贯穿。
14.6 label 和 jump
label retry
call try_pick()
if not part_ok
jump retry
end
新程序不建议优先使用 label/jump,但必须支持它,因为 FANUC TP/LS 程序常用 LBL/JMP,导入时需要映射。
label/jump 规则:
label名称在当前proc内唯一。jump只能跳转到当前proc内的 label。- 禁止
jump进入另一个if/while/for/switch/try块内部。 - 允许
jump跳出块结构,但编译器应生成 warning。 - 自动生成的新程序不应使用
label/jump,除非目标后处理配置要求 FANUC 风格输出。
15. 子程序和函数
15.1 proc 子程序
proc 是无返回值子程序,用于组织工艺步骤、运动流程、IO 流程和恢复逻辑。
proc pick_part(int slot)
movej home speed joint(60 %) zone fine
run_path pick_path
end
调用:
call pick_part(1)
call pick_part(slot_id)
语义规则:
proc可以执行运动、IO、wait、run_path、run_operation。proc可以调用其他proc。- 默认不允许无限递归。编译器应检测直接递归和明显的间接递归,并给出 warning 或 error。
return可以提前退出proc,但不能携带返回值。proc main()是默认入口。一个模块中最多一个默认入口。
15.2 参数方向
参数支持 in、out、inout 三种方向。未写方向时默认为 in。
proc pick_part(in int slot)
call move_to_slot(slot)
end
proc read_part(out bool ok)
ok = io.di[1]
end
proc increment_retry(inout int retry)
retry = retry + 1
end
规则:
in参数按值传入,子程序内修改不影响调用者。out参数调用前不要求有有效值,子程序必须在所有正常返回路径上赋值。inout参数按引用语义传入,子程序内修改会回写调用者。out和inout实参必须是可赋值左值,不能是字面量或表达式。- 后处理到不支持显式参数方向的品牌时,可生成临时变量、寄存器或转换报告。
调用示例:
var bool ok = false
var int retry = 0
call read_part(ok)
call increment_retry(retry)
15.3 func 函数
func 有返回值,适合计算条件、选择目标点、计算偏移、读取配方参数。函数应尽量无副作用。
func bool is_part_ready()
return io.di[1] == true and io.di[2] == false
end
带参数函数:
func pose_target slot_pose(int slot)
return base_pick offset x (slot * 50 mm)
end
func bool can_retry(int retry, int max_retry)
return retry < max_retry
end
调用方式:
if can_retry(retry, max_retry)
call retry_pick()
end
movel slot_pose(slot_id) speed linear(200 mm/s) zone z10
规则:
func必须声明返回类型。- 所有正常返回路径必须返回兼容类型的值。
func允许调用其他func。func默认不允许执行运动指令、wait、pulse、run_path、run_operation。编译器应把这类用法作为 error 或强 warning。- 如果确实需要带副作用的逻辑,应使用
proc。
15.4 作用域和名称解析
名称解析顺序:
- 当前块局部变量。
- 当前
proc/func参数。 - 当前模块中的
const/var/persistent/target/path/operation/proc/func。 import模块导出的符号。- 内置函数和内置类型。
规则:
- 内层局部变量允许遮蔽外层变量,但编译器应对同名遮蔽给出 warning。
- 不允许局部变量与同一作用域内的
proc/func/target/path/operation重名。 for循环变量只在循环体内有效。label只在当前proc内有效。
15.5 子程序调用和后处理
后处理映射:
| GRL | ABB RAPID | FANUC LS/TP | KUKA KRL |
|---|---|---|---|
proc name() |
PROC name() |
/PROG NAME 或子程序 |
DEF name() |
call sub() |
sub; 或 sub() |
CALL SUB |
sub() |
return |
RETURN |
END/跳转近似 |
RETURN |
out/inout 参数 |
VAR 参数或数据变量 |
寄存器/PR/临时变量近似 | 参数或全局变量近似 |
如果目标品牌或输出格式不支持某种参数模式,后处理器必须生成转换报告,不能静默丢失语义。
16. 异常、报警和中断
16.1 alarm 和 raise
alarm "Part not detected"
raise E_PICK_FAILED
16.2 try/catch
try
run_operation pick_op
catch E_PICK_FAILED
call recover_pick()
finally
io.do[1] = false
end
首版可先实现 alarm、raise 和简单 catch,复杂品牌错误恢复分阶段支持。
16.3 trap 和 interrupt
trap emergency_stop()
stop_motion
io.do[100] = false
alarm "Emergency stop input"
end
interrupt e_stop when io.di[100] == true call emergency_stop()
enable interrupt e_stop
disable interrupt e_stop
后处理映射:
- ABB 可映射到
TRAP和 interrupt 机制。 - KUKA 可映射到
INTERRUPT DECL、INTERRUPT ON/OFF等近似结构。 - FANUC 首版可生成后台逻辑、条件监控或转换报告。
17. 多任务
首版只定义语法,不要求完整实现多任务实时调度:
task background monitor_io cycle 20 ms
if io.di[99] == true
alarm "Safety signal lost"
end
end
虚拟控制器可先按周期任务模拟;后处理时根据品牌能力生成后台任务或转换报告。
18. 品牌扩展和后处理提示
18.1 post_hint
post_hint abb {
module_name: "MainModule"
use_wobj: true
}
post_hint fanuc {
program_name: "MAIN"
default_uframe: 1
default_utool: 1
}
post_hint kuka {
src_name: "MAIN"
dat_name: "MAIN"
advance: 3
}
18.2 brand metadata
@brand.abb {
conf_l: true
}
@brand.fanuc {
group: 1
cnt: 50
}
@brand.kuka {
c_dis: true
}
规则:
@brand.*只影响指定品牌后处理。- 通用虚拟控制器不应依赖品牌 metadata 才能执行。
- 无法支持的品牌扩展必须进入转换报告。
19. 自动编程生成规则
自动生成 GRL 时,应遵循以下规则:
- 优先生成
target、path、operation,不要直接把大量运动语句塞进proc main()。 - 目标点命名稳定,推荐
T_{path}_{index}或语义名称,例如T_PICK_APPROACH。 - 路径点命名稳定,推荐
p000、p001、p002。 - 路径整体参数放入
defaults,单点差异写在 point 上。 - 接近点、工艺开始点、工艺结束点、离开点必须显式标注。
- IO、夹具、焊接开关、喷涂开关应生成 path event 或 operation action。
- CAD 来源、曲线 ID、采样距离、法向策略必须写入
source。 - 自动生成文本必须格式化稳定,同一输入重复生成结果一致。
- 编译器应能把 GRL 再解析回等价 OLP 对象模型。
- 后处理器应能选择
compact或expanded输出风格。
生成风格:
// compact
proc main()
run_operation weld_op_01
end
// expanded
proc main()
movel T001 speed linear(120 mm/s) zone z5 tool torch frame part_frame
movel T002 speed linear(120 mm/s) zone z5 tool torch frame part_frame
end
20. 后处理映射规则
20.1 ABB RAPID
| GRL | ABB RAPID |
|---|---|
module |
MODULE |
proc |
PROC |
movej |
MoveJ |
movel |
MoveL |
movec |
MoveC |
pose_target |
robtarget |
joint_target |
jointtarget |
tool |
tooldata |
frame |
wobjdata |
linear(300 mm/s) |
v300 或自定义 speeddata |
fine、z(10 mm) |
fine、z10 或自定义 zonedata |
io.do[1] = true |
SetDO do1, 1 |
wait io.di[1] == true |
WaitDI di1, 1 |
示例:
movel pick speed linear(300 mm/s) zone z10 tool gripper frame fixture
可后处理为:
MoveL pick, v300, z10, gripper\WObj:=fixture;
20.2 FANUC LS/TP 风格
| GRL | FANUC |
|---|---|
proc main() |
/PROG MAIN |
movej |
J P[...] |
movel |
L P[...] |
movec |
C P[...] |
pose_target |
P[...] 或 PR[...] |
tool |
UTOOL |
frame |
UFRAME |
joint(50 %) |
50% |
linear(300 mm/s) |
300mm/sec |
fine |
FINE |
cnt(50) |
CNT50 |
io.do[1] = true |
DO[1]=ON |
wait io.di[1] == true |
WAIT DI[1]=ON |
示例:
movel P10 speed linear(300 mm/s) zone cnt(10)
可后处理为:
L P[10] 300mm/sec CNT10 ;
20.3 KUKA KRL
| GRL | KUKA KRL |
|---|---|
proc main() |
DEF MAIN() |
movej |
PTP |
movel |
LIN |
movec |
CIRC |
pose_target |
E6POS |
joint_target |
E6AXIS |
tool |
$TOOL |
frame |
$BASE |
linear(300 mm/s) |
$VEL.CP = 0.3 |
fine |
no approximation |
continuous、z(...) |
C_DIS 或 $APO |
io.do[1] = true |
$OUT[1] = TRUE |
wait io.di[1] == true |
WAIT FOR $IN[1] == TRUE |
示例:
movel XPICK speed linear(300 mm/s) zone continuous
可后处理为:
$VEL.CP = 0.3
LIN XPICK C_DIS
21. 编译语义和 IR
编译管线:
GRL Source
-> Lexer
-> Parser
-> AST
-> Symbol Table
-> Semantic Analyzer
-> Executable IR
-> Virtual Controller
-> Post Processor
运动 IR 示例:
interface MotionInstruction {
kind: "motion";
motionType: "joint" | "linear" | "circular";
target: TargetRef | ResolvedTarget;
via?: TargetRef | ResolvedTarget;
speed: SpeedSpec;
zone: ZoneSpec;
tool: ToolRef;
frame: FrameRef;
sourceRange: SourceRange;
sourceObject?: {
pathId?: string;
pointId?: string;
operationId?: string;
};
brandMeta?: Record<string, unknown>;
}
22. 语义检查
编译器必须检查:
- 标识符是否重复或未声明。
- 类型是否匹配。
- 目标点类型是否适合运动指令。
movec是否缺少 via 点。- 圆弧三点是否重合或共线。
- 工具、坐标系、速度、过渡是否可解析。
- 单位是否正确。
- 表达式优先级、括号、函数调用参数是否解析正确。
- 内置函数名称、参数数量、定义域和单位维度是否正确。
- 需要常量的位置是否只使用常量表达式。
- 除零、未知符号、未知函数和复杂运行时表达式是否产生稳定 diagnostic。
- IO 地址是否存在于 IO map 或允许范围。
- 子程序参数数量和类型是否匹配。
out参数是否在所有正常返回路径上赋值。inout和out实参是否为可赋值左值。func是否在所有正常返回路径上返回正确类型。break、continue是否出现在合法结构内。switch case是否为常量表达式,是否存在重复 case。jump是否跳入非法块结构。wait是否有可执行条件。- Path 是否为空。
- Path point 名称是否重复。
- Operation 是否引用不存在的 Path。
- 目标点是否可达。
- 关节是否超限。
- 后处理目标品牌是否支持所用语义。
诊断格式建议:
E1001: target 'pick' is not defined
E2003: movec points are collinear: start=p0, via=p1, target=p2
W3002: zone z50 is larger than segment length
W4001: KUKA post approximates GRL cnt(30) by C_DIS
23. EBNF 语法草案
以下是首版解析器可采用的简化 EBNF:
program = [ language_decl ] module_decl ;
language_decl = "language" "grl" version ;
module_decl = "module" identifier { top_decl } "end" ;
top_decl = import_decl
| data_decl
| target_decl
| path_decl
| operation_decl
| proc_decl
| func_decl
| trap_decl
| task_decl
| post_hint_decl ;
import_decl = "import" identifier ;
data_decl = ( "persistent" | "const" | "var" ) type identifier "=" expr ;
target_decl = "target" identifier "=" ( joint_target | pose_target ) ;
joint_target = "joint_target" "{" "joints" ":" array [ "," ext_axis ] "}" ;
pose_target = "pose_target" "{" "pose" ":" pose_expr
[ "," "config" ":" config_expr ]
[ "," "tool" ":" identifier ]
[ "," "frame" ":" identifier ] "}" ;
path_decl = "path" identifier "{" { path_item } "}" ;
path_item = defaults_block | source_block | path_point | path_event ;
defaults_block = "defaults" "{" { property } "}" ;
source_block = "source" "{" { property } "}" ;
path_point = "point" identifier motion_stmt ;
path_event = "event" ( ( "before" | "after" ) identifier
| "at" identifier "distance" expr ) statement ;
operation_decl = "operation" identifier "{"
"kind" ":" identifier
"path" ":" identifier
[ process_block ]
[ action_block ]
"}" ;
proc_decl = "proc" identifier "(" [ param_list ] ")" { statement } "end" ;
func_decl = "func" type identifier "(" [ param_list ] ")" { statement } "end" ;
trap_decl = "trap" identifier "(" ")" { statement } "end" ;
statement = motion_stmt
| run_stmt
| assign_stmt
| wait_stmt
| pulse_stmt
| if_stmt
| while_stmt
| for_stmt
| switch_stmt
| call_stmt
| return_stmt
| break_stmt
| continue_stmt
| label_stmt
| jump_stmt
| alarm_stmt
| try_stmt ;
motion_stmt = movej_stmt | movel_stmt | movec_stmt ;
target_expr = target_ref [ offset_clause ] | pose_expr ;
offset_clause = "offset" { axis length }
| "offset_in" ( "tool" | "frame" identifier ) { axis length } ;
movej_stmt = "movej" target_expr motion_params ;
movel_stmt = "movel" target_expr motion_params ;
movec_stmt = "movec" "via" target_expr "target" target_expr motion_params ;
motion_params = [ "speed" speed_expr ] [ "zone" zone_expr ]
[ "tool" identifier ] [ "frame" identifier ] ;
run_stmt = "run_path" identifier | "run_operation" identifier ;
wait_stmt = "wait" expr [ "timeout" duration ] [ "on_timeout" timeout_action ] ;
pulse_stmt = "pulse" io_ref "duration" duration ;
assign_stmt = lvalue "=" expr ;
call_stmt = "call" identifier "(" [ arg_list ] ")" ;
return_stmt = "return" [ expr ] ;
break_stmt = "break" ;
continue_stmt = "continue" ;
if_stmt = "if" expr { statement }
{ "elseif" expr { statement } }
[ "else" { statement } ]
"end" ;
while_stmt = "while" expr { statement } "end" ;
for_stmt = "for" identifier "=" expr "to" expr [ "step" expr ] { statement } "end" ;
switch_stmt = "switch" expr { case_clause } [ default_clause ] "end" ;
case_clause = "case" const_expr { "," const_expr } { statement } ;
default_clause = "default" { statement } ;
label_stmt = "label" identifier ;
jump_stmt = "jump" identifier ;
param_list = param { "," param } ;
param = [ "in" | "out" | "inout" ] type identifier ;
arg_list = expr { "," expr } ;
const_expr = expr ;
duration = expr ;
length = expr ;
speed_expr = expr ;
zone_expr = expr ;
expr = or_expr ;
or_expr = and_expr { ( "or" | "||" ) and_expr } ;
and_expr = equality_expr { ( "and" | "&&" ) equality_expr } ;
equality_expr = relation_expr { ( "==" | "!=" ) relation_expr } ;
relation_expr = additive_expr { ( "<" | "<=" | ">" | ">=" ) additive_expr } ;
additive_expr = multiply_expr { ( "+" | "-" ) multiply_expr } ;
multiply_expr = unary_expr { ( "*" | "/" | "mod" ) unary_expr } ;
unary_expr = [ "+" | "-" | "not" | "!" ] postfix_expr ;
postfix_expr = primary_expr { unit_suffix | offset_clause } ;
unit_suffix = unit ;
primary_expr = literal
| identifier
| io_ref
| call_expr
| array
| object
| "(" expr ")" ;
call_expr = identifier "(" [ arg_list ] ")" ;
literal = number | string | "true" | "false" ;
24. 完整示例
language grl 0.1
module Main
persistent tool gripper = tool {
tcp: pose(0 mm, 0 mm, 180 mm, 0 deg, 0 deg, 0 deg),
mass: 2.5 kg
}
persistent frame fixture = frame {
origin: pose(800 mm, 0 mm, 200 mm, 0 deg, 0 deg, 0 deg)
}
const speed v_fast = joint(70 %)
const speed v_pick = linear(300 mm/s)
const zone z_pick = z(10 mm)
target home = joint_target {
joints: [0 deg, -30 deg, 60 deg, 0 deg, 60 deg, 0 deg]
}
target pick = pose_target {
pose: pose(500 mm, 120 mm, 300 mm, 180 deg, 0 deg, 90 deg),
config: robot_config(0, 0, 1),
tool: gripper,
frame: fixture
}
target place = pose_target {
pose: pose(650 mm, -100 mm, 320 mm, 180 deg, 0 deg, 90 deg),
config: robot_config(0, 0, 1),
tool: gripper,
frame: fixture
}
path pick_path {
defaults {
tool: gripper,
frame: fixture,
speed: v_pick,
zone: z_pick
}
point approach movej home speed v_fast zone fine
point above_pick movel pick offset z 100 mm
point at_pick movel pick zone fine
event after at_pick io.do[1] = true
event after at_pick wait io.di[1] == true timeout 2 s on_timeout alarm "Clamp close timeout"
point leave_pick movel pick offset z 100 mm
}
path place_path {
defaults {
tool: gripper,
frame: fixture,
speed: v_pick,
zone: z_pick
}
point above_place movel place offset z 100 mm
point at_place movel place zone fine
event after at_place io.do[1] = false
event after at_place wait io.di[2] == true timeout 2 s on_timeout alarm "Clamp open timeout"
point leave_place movel place offset z 100 mm
}
operation pick_op {
kind: handling
path: pick_path
process {
gripper: "main_clamp"
}
}
operation place_op {
kind: handling
path: place_path
process {
gripper: "main_clamp"
}
}
proc main()
set_tool gripper
set_frame fixture
run_operation pick_op
run_operation place_op
movej home speed v_fast zone fine
end
end
25. 实施优先级
P0 必须实现:
language、module、proc。const、var、persistent。tool、frame、speed、zone。joint_target、pose_target。movej、movel、movec。path、point、event、run_path。operation、run_operation。if、elseif、else、while、for、switch。call、return、break、continue。proc参数方向in/out/inout。func、函数调用表达式和返回值检查。- 表达式 parser、单位后缀、内置数学函数、三角函数和编译期常量求值。
io.do/di、wait、pulse。- AST、语义检查、IR、source map。
- ABB、FANUC、KUKA 后处理原型。
P1 扩展:
label/jump和品牌标签程序导入。trap、interrupt。- 多任务
task。 - 更完整的 IO 类型。
- 工艺模板库。
- 品牌程序导入。
- 复杂品牌扩展和转换报告。
26. 参考资料
-
ABB RAPID Technical Reference Manual,RAPID Instructions, Functions and Data Types:
https://library.e.abb.com/public/b227fcd260204c4dbeb8a58f8002fe64/Rapid_instructions.pdf -
ABB RAPID Overview:
https://search.abb.com/library/Download.aspx?Action=Launch&DocumentID=3HAC050947-001&DocumentPartId=&LanguageCode=en -
KUKA System Software,说明 KSS 支持 InLine forms 和 KRL 专家编程:
https://www.kuka.com/de-de/produkte-leistungen/robotersysteme/software/systemsoftware/kuka_systemsoftware -
KUKA Application and Robot Programming,说明 KRL、Sunrise programming、simulation/offline programming 等工作流:
https://www.kuka.com/en-de/services/service_robots-and-machines/installation-start-up-and-programming-of-robots/application-and-robot-programming -
FANUC PLC Motion Interface,说明 FANUC 可从上位控制发起 linear、joint、circular robot motion,并管理 speeds 与 termination types:
https://www.fanucamerica.com/products/software/plc-motion-interface -
FANUC ROBOGUIDE,说明 FANUC 离线编程可生成机器人程序,并支持 CAD to Path、仿真和路径开发:
https://www.fanucamerica.com/products/software/roboguide -
FANUC ASCII Program Loader,说明 FANUC LS 可读程序可编译为 TP 程序:
https://www.fanucamerica.com/products/controller-series/r-50ia