3. 动作生成API:动作生成接口设计、参数配置与数据校验、动作序列化与反序列化、动作库管理
各位好,我是老张。今天咱们聊聊动作生成API的设计。说实话,这个模块是整个系统的“心脏”。你想想看,动作怎么定义?怎么传参?怎么存?怎么取?搞不清楚这些,后面执行阶段全是坑。
我在好几个项目里都见过这种情况:动作定义得乱七八糟,参数校验形同虚设,序列化格式换来换去。结果呢?调试两天,上线三天就崩。嗯,咱们今天就把这块彻底理清楚。
3.1 动作生成接口设计
接口设计,说白了就是定规矩。你给外面的人(或者别的模块)一个入口,让他们能“告诉”系统:我要生成一个什么动作。
我个人习惯用RESTful风格。为什么?简单、直观、好调试。一个典型的动作生成接口长这样:
POST /api/v1/actions/generate
Content-Type: application/json
{
"action_name": "pick_and_place",
"action_type": "compound",
"parameters": {
"target_position": [120.5, 340.2, 85.0],
"gripper_force": 15.0,
"speed_profile": "smooth"
},
"constraints": {
"max_velocity": 1.5,
"max_acceleration": 0.8,
"collision_check": true
}
}
这里要注意几个点:
- action_name:动作名称,必须唯一。我建议用下划线命名法,别整驼峰,工业场景下划线更清晰。
- action_type:动作类型。我一般分三类——
primitive(基础动作,比如直线移动)、compound(复合动作,比如抓取+放置)、sequence(序列动作,多个动作按顺序执行)。 - parameters:动作参数。这是最灵活的部分,不同动作参数完全不同。
- constraints:约束条件。安全相关的参数,我建议单独拎出来,别混在parameters里。
3.2 参数配置与数据校验
参数配置这块,我踩过的坑比走过的路还多。你想想看,一个动作可能有几十个参数,每个参数的类型、范围、必填性都不一样。怎么管?
我的做法是:用Schema定义参数模板。每个动作类型对应一个Schema,里面写明每个参数的规则。
{
"action_type": "pick_and_place",
"schema": {
"target_position": {
"type": "array",
"items": {"type": "number"},
"minItems": 3,
"maxItems": 3,
"description": "目标位置 [x, y, z]"
},
"gripper_force": {
"type": "number",
"minimum": 5.0,
"maximum": 50.0,
"default": 15.0,
"description": "夹爪力度 (N)"
},
"speed_profile": {
"type": "string",
"enum": ["slow", "normal", "smooth", "fast"],
"default": "normal",
"description": "速度曲线"
}
}
}
数据校验怎么做?我推荐用JSON Schema校验库。前端传过来的参数,先过一遍Schema,不合格的直接返回错误信息。
我曾经在一个项目里,因为校验不够严格,允许了超出范围的夹爪力度。结果呢?夹爪直接把一个精密零件夹变形了。从那以后,我的校验逻辑里必加一条:所有数值参数必须有明确的上下界。
3.3 动作序列化与反序列化
序列化,就是把内存里的动作对象转成可以存储或传输的格式。反序列化,就是反过来。工业场景里,最常用的格式是JSON和Protobuf。
我个人偏好JSON——可读性强,调试方便。但如果你对性能有极致要求,比如每秒要处理上千个动作,那Protobuf更合适。
一个典型的序列化示例:
// 动作对象 (Python示例)
class Action:
def __init__(self, name, action_type, params, constraints):
self.name = name
self.action_type = action_type
self.params = params
self.constraints = constraints
self.created_at = datetime.now()
self.version = "1.0"
def to_json(self):
return json.dumps({
"name": self.name,
"type": self.action_type,
"params": self.params,
"constraints": self.constraints,
"metadata": {
"created_at": self.created_at.isoformat(),
"version": self.version
}
}, indent=2)
@classmethod
def from_json(cls, json_str):
data = json.loads(json_str)
# 反序列化时一定要做版本兼容
if data.get("metadata", {}).get("version", "1.0") != "1.0":
# 做版本迁移逻辑
pass
return cls(
name=data["name"],
action_type=data["type"],
params=data["params"],
constraints=data["constraints"]
)
3.4 动作库管理
动作库,说白了就是你的“动作仓库”。里面存着所有定义好的动作模板。我见过最糟糕的做法:把动作定义散落在各个配置文件里,找起来跟大海捞针似的。
我的做法是:用数据库管理动作库。每个动作是一条记录,包含名称、类型、Schema、默认参数、版本号、创建时间等字段。
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | UUID | 唯一标识 |
| name | VARCHAR(128) | 动作名称,唯一索引 |
| type | ENUM | primitive / compound / sequence |
| schema | JSON | 参数模板定义 |
| default_params | JSON | 默认参数值 |
| version | VARCHAR(16) | 语义化版本号 |
| status | ENUM | draft / published / deprecated |
| created_at | DATETIME | 创建时间 |
| updated_at | DATETIME | 最后修改时间 |
动作库管理有几个关键操作:
- 注册动作:新增一个动作模板,状态为draft。
- 发布动作:审核通过后,状态改为published。已发布的动作不能修改,只能创建新版本。
- 废弃动作:旧版本标记为deprecated,但保留数据,方便追溯。
- 版本回滚:如果新版本有问题,可以快速切回旧版本。
知识体系总览
下面这张图,把咱们今天讲的内容串起来了。你一看就明白:动作生成API的各个模块是怎么配合的。
你看,整个流程是串起来的:接口层接收请求 → 校验层检查参数 → 序列化层转换格式 → 存储层入库管理。每一层各司其职,缺一不可。
好了,关于动作生成API的核心内容,今天就聊到这儿。记住:接口设计要清晰,校验要严格,序列化要兼容,库管理要规范。把这四点做好,你的动作生成系统就稳了。