Claude 这类 AI 助手开始接管物理世界,真正落到工程里,并不是让大模型去搬箱子,而是一条由软件决策、AI 编排、机械臂执行组成的链路:AI Agent 发现一个异常业务事件,判断需要物理干预,然后通过机械臂在真实空间完成一个动作。被反复提到的“机械臂阻拦 5000 万美元打款”场景,本质上是一个安全演示模型——风控系统识别到超大额可疑转账后触发升级,机械臂移动到确认闸机位置按下急停按钮,让放行流程无法继续。它不复杂,也不神秘,前提是把软件层和硬件层的边界分清楚。
这篇文章会带读者做一个可以实际跑起来的最小案例:使用 Claude Code 作为 AI 编排工具,用 Python 写一个风控决策服务和一个机械臂控制服务,再用仿真环境模拟机械臂执行“按急停”动作。读完以后,你会知道 Claude Code 在这个场景里到底负责什么,机械臂控制里的 DH 参数、轨迹规划、扭矩计算分别解决什么问题,以及从仿真到真实硬件落地还需要补哪些安全措施。
1. 先理解这条链路:“检测—决策—执行”如何落到物理世界
1.1 不要把“AI 接管”理解成“AI 决定一切”
“Claude 开始接管物理世界”这个标题容易让人产生两个误解:一是 Claude 像人一样直接操作机械设备,二是 Claude 拥有最终决策权。实际都不是。
在可落地的工程结构里,Claude Code 的角色更像是“编排者”。它是一个运行在终端里的 AI 编程助手,能读文件、写代码、执行命令、调用其他工具,因此可以完成多步任务编排。比如读取风控结果、生成机械臂控制脚本、运行脚本并检查日志。真正决定“这笔打款要不要拦截”的,仍然应该是确定性风控规则,而不是大模型的概率输出。
这里的核心原则是:AI 负责把复杂任务拆解成可执行步骤,并把步骤串联起来;涉及资金、物理设备、人身安全的最终动作,必须经过可审计、可回退的确定性判断。
1.2 一条真实可搭建的链路包含四个模块
“检测—决策—执行”落到物理世界后,至少包含四个模块。各模块职责不同,出错后的表现也不同,先拆清楚边界再写代码,后面排查时会省很多时间。
| 模块 | 职责 | 典型实现 | 出错时的表现 |
|---|---|---|---|
| 风控决策服务 | 分析支付事件,输出风险等级和是否升级物理拦截 | Python 规则引擎 | 漏报、误报、拦截过频 |
| 指令编排层 | 把风控结果翻译成机械臂控制指令 | Claude Code 加 Python 脚本 | 指令与动作不匹配 |
| 机械臂控制服务 | 管理关节角、速度、轨迹,执行移动 | Python SDK 或 ROS 工具链 | 运动超限、抖动、碰撞 |
| 执行器 | 真正完成物理动作 | 仿真环境或真实机械臂 | 末端位姿偏差、响应延迟 |
这四个模块之间通过事件和指令传递信息:风控服务输出“是否拦截”,编排层把“拦截”翻译成“机械臂移动到急停按钮位置”,控制服务计算关节角并执行轨迹,执行器完成物理动作。
1.3 为什么需要“物理拦截”而不是只发一条告警
告警的缺点是容易被忽略。告警堆积、值班人员不在场、连续误报后警惕性下降,都会让告警失效。物理拦截的价值在于:它在时间上造成一个不可忽略的停顿。机械臂按下急停按钮后,确认闸机被锁住,后续打款流程必须由人工介入才能恢复。
典型的物理拦截场景包括:按急停按钮、推动隔离闸门、切断物理电路、锁住确认踏板。但也要注意,物理干预的成本比软件告警高得多。它需要机械臂本身安全可靠,需要故障后能复位,还需要有清晰的升级条件和人工确认流程。不是每个异常都值得用机械臂去拦,只有“误放行的代价极高”且“拦截失败的损失可控”的场景才值得。
2. 环境准备:Claude Code、仿真环境和目录结构一次到位
2.1 安装 Claude Code 并确认命令可用
Claude Code 是 Anthropic 旗下的 AI 编程助手,常见安装方式是通过 npm 全局安装,也可以参考官方文档选择其他安装方式。安装前先确认本机已有 Node.js 18 或更高版本。
node -v npm install -g @anthropic-ai/claude-code claude --version安装完成后,在终端输入claude启动交互界面,并按提示完成账号鉴权。这里有两个高频问题需要提前注意。
第一个是 Windows 下提示“无法将‘claude’项识别为 cmdlet、函数、脚本文件或可运行程序的名称”,或者“claude 不是内部或外部命令”。原因通常是 npm 全局安装目录没有加入 PATH。可以用npm prefix -g查看全局目录,把它加入系统 PATH,然后重新打开终端。
第二个是启动后提示类似“xxx is not a model this version of claude code recognizes”。这类提示一般说明 CLI 版本与后端或自定义模型配置不匹配。处理顺序是:先升级 CLI,再检查是否配置过自定义模型名,确认模型名和接口协议与当前版本兼容。如果账号鉴权提示当前不可用,先确认账号已通过正常渠道完成开通和有效鉴权,不要使用任何非官方方式绕过限制。
在演示项目里,还可以把常用的机械臂操作封装成可复用的提示词模板或脚本,让 Claude Code 在后续对话中快速复用。这部分能力不同版本表现有差异,落地前以官方文档为准。
2.2 机械臂环境:先仿真,再谈真机
学习机械臂控制,第一步一定是在仿真环境里跑通,而不是直接买真机。仿真环境能复现运动学、轨迹规划、碰撞检测等核心问题,成本低、风险小,也更容易观察内部状态。
| 方案 | 适用场景 | 优点 | 注意点 |
|---|---|---|---|
| MuJoCo | 强化学习、快速验证控制算法 | 轻量、仿真速度快 | 需要编写或获取 MJCF 模型 |
| Gazebo 加 MoveIt | ROS 工具链、运动规划 | 传感器、控制器工具链完整 | 环境较重,版本匹配繁琐 |
| 真机厂商 SDK | 实机部署前调试 | 最接近真实硬件 | 有碰撞风险,需要急停和围栏 |
本文示例使用仿真模式实现,即ArmController在仿真模式下不真正连接硬件,而是打印执行轨迹并模拟执行时间。这样不依赖具体机械臂品牌,任何读者都能复现。
2.3 项目目录和依赖配置
建议按下面的目录结构组织代码,把配置、数据、源码分开,后续接入真实机械臂时只需要改配置和控制器实现。
claude-arm-guard/ ├── config/ │ └── arm.yaml ├── data/ │ └── payment_50m.json ├── src/ │ ├── risk_engine.py │ ├── arm_controller.py │ └── guard_demo.py ├── requirements.txt └── README.mdrequirements.txt只需要最基础的依赖,示例使用PyYAML读取配置,用numpy做运动学计算。真实项目按机械臂 SDK 补充即可。
numpy>=1.24 PyYAML>=6.0机械臂相关参数放在config/arm.yaml里,包括执行模式、通信地址、运动速度和急停按钮对应的目标关节角。
arm: mode: simulation connection: host: 127.0.0.1 port: 5000 motion: joint_speed: 30 acceleration: 15 blocking: true target: estop_joints: [-15.2, -30.0, 45.0, 10.0, 20.0, 0.0, 0.0]这里的estop_joints是机械臂按下急停按钮时的目标关节角数组。这个值不是随便写的,它需要由逆运动学计算得到:先确定急停按钮在机械臂基坐标系下的位置和姿态,再反解出每个关节的角度。在最小案例里,可以先固定一个能到达按钮位置的关节角组合,后面再替换成 IK 计算的动态结果。
2.4 环境检查清单
写代码前先按清单确认一遍环境,避免把时间花在依赖问题上。
| 检查项 | 命令或验证方式 | 通过标准 |
|---|---|---|
| Node.js 版本 | node -v | 18 或更高 |
| claude 命令可用 | claude --version | 显示版本号 |
| Python 版本 | python --version | 3.10 或更高 |
| Python 依赖 | pip install -r requirements.txt | 安装无报错 |
| 仿真模式可写日志 | 运行一次控制服务 | 生成轨迹日志 |
| 数据文件存在 | cat data/payment_50m.json | JSON 可正常解析 |
注意:不要只验证程序能启动,还要验证输入数据结构、配置读取结果和输出日志是否符合预期。
3. 最小可运行案例:风控事件触发机械臂拦截动作
3.1 定义支付事件和风险升级规则
先准备一个模拟的大额转账事件data/payment_50m.json。这里的金额字段用来演示“超过阈值才升级为物理拦截”,与任何真实交易无关。
{ "event_id": "pay_20250607_001", "amount": 51800000, "currency": "USD", "beneficiary": "UNKNOWN_COMPANY_LTD", "variance": 7.3, "status": "pending_confirm" }风险升级规则可以设计成:金额超过 5000 万美元,并且收款方在黑名单中,或者金额波动明显异常时,触发物理拦截。这里把阈值定义清楚,后面写进风控引擎。
3.2 风控决策服务:用确定性规则做最终判断
src/risk_engine.py实现一个简单的规则引擎。规则引擎的优点是确定性和可审计性:同样的输入,永远得到同样的输出,便于测试和复盘。
# src/risk_engine.py BLACKLIST = {"UNKNOWN_COMPANY_LTD", "SUSPECT_FINANCE_01"} class RiskEngine: def __init__(self, thresholds=None): self.thresholds = thresholds or { "high_amount": 10_000_000, "physical_escalation": 50_000_000, "variance_limit": 5.0, } def evaluate(self, payment): decisions = [] if payment["amount"] > self.thresholds["high_amount"]: decisions.append("high_amount") if payment["beneficiary"] in BLACKLIST: decisions.append("blacklist") if payment.get("variance", 0) > self.thresholds["variance_limit"]: decisions.append("abnormal_variance") if payment["amount"] > self.thresholds["physical_escalation"]: decisions.append("physical_escalation") return decisions关键点是physical_escalation这个决策。它只由金额阈值这类确定性规则产生,不由模型生成。这样即使 AI 编排层出错,最终是否启动物理拦截也由可审计的逻辑决定。
3.3 机械臂控制服务:把“拦截”翻译成关节运动
src/arm_controller.py实现机械臂控制服务的抽象。仿真模式下,它不连接硬件,而是打印轨迹、模拟执行时间并返回结果;接入真机时,只需要把_send方法替换为厂商 SDK 调用。
# src/arm_controller.py import time class ArmController: def __init__(self, config): self.config = config self.mode = config.get("arm", {}).get("mode", "simulation") def _simulate(self, command): joints = command["joints"] speed = command.get("speed", 30) print(f"[simulation] move_joints -> target={joints}, speed={speed}, blocking={command.get('blocking', True)}") print("[simulation] plan 5 waypoints, estimated 2.1s") time.sleep(0.2) print("[simulation] endpoint reached, estop pressed") def _send(self, command): # 真机模式下,这里替换为机械臂 SDK 的 TCP/串口发送逻辑 raise NotImplementedError("real arm connection is not configured") def move_joints(self, joints, speed=30, blocking=True): command = { "action": "move_joints", "joints": joints, "speed": speed, "blocking": blocking, } if self.mode == "simulation": self._simulate(command) return {"ok": True, "trace": "simulated_move", "joints": joints} return self._send(command) def press_estop(self): target = self.config["arm"]["target"]["estop_joints"] speed = self.config["arm"]["motion"]["joint_speed"] return self.move_joints(target, speed=speed)press_estop的名字说明意图:移动到急停按钮位置并按下。在这个抽象层,控制器不关心为什么拦截,只负责安全稳定地把机械臂移动到目标关节角。职责分离后,风控规则变化不需要改控制器,机械臂型号变化也不需要改风控服务。
3.4 让 Claude Code 生成并运行编排脚本
现在我们进入编排层。在终端启动 Claude Code:
cd claude-arm-guard claude给 Claude Code 的提示词可以这样写:
“阅读src/risk_engine.py和src/arm_controller.py。请新增src/guard_demo.py:接收data/payment_50m.json,用 RiskEngine 判断是否需要物理拦截;如果需要,调用 ArmController.press_estop 执行拦截,并把结果打印出来。要求:脚本可以重复运行;拦截动作前输出风险决策列表;非拦截场景不触碰机械臂。”
这段话实际上把模块边界和要求说清楚了。Claude Code 生成代码后,先人工审查再运行,这是使用 Agent 工具的基本习惯。生成的编排脚本大致如下:
# src/guard_demo.py import json import sys import yaml from arm_controller import ArmController from risk_engine import RiskEngine def load_config(): with open("config/arm.yaml", "r", encoding="utf-8") as f: return yaml.safe_load(f) def main(event_path): with open(event_path, "r", encoding="utf-8") as f: payment = json.load(f) engine = RiskEngine() decisions = engine.evaluate(payment) print("risk decisions:", decisions) if "physical_escalation" not in decisions: print("no physical intervention needed") return arm = ArmController(load_config()) result = arm.press_estop() print("arm result:", result) if result.get("ok"): print("payment blocked:", payment["event_id"]) else: print("intervention failed") if __name__ == "__main__": main(sys.argv[1])这段脚本的关键点有三个:先打印风险决策列表,作为审计记录;只有明确出现physical_escalation才调用机械臂;非拦截场景直接返回,不触碰机械臂。这样设计避免了“每次运行都会动一下机械臂”的误操作。
3.5 运行和预期输出
执行编排脚本:
cd claude-arm-guard python src/guard_demo.py data/payment_50m.json预期输出:
risk decisions: ['high_amount', 'blacklist', 'abnormal_variance', 'physical_escalation'] [simulation] move_joints -> target=[-15.2, -30.0, 45.0, 10.0, 20.0, 0.0, 0.0], speed=30, blocking=True [simulation] plan 5 waypoints, estimated 2.1s [simulation] endpoint reached, estop pressed arm result: {'ok': True, 'trace': 'simulated_move', 'joints': [-15.2, -30.0, 45.0, 10.0, 20.0, 0.0, 0.0]} payment blocked: pay_20250607_001验证点有三个:风险决策列表包含physical_escalation;机械臂控制服务正确输出目标关节角和执行轨迹;最终结果返回ok: True,并打印被拦截的事件编号。
4. 关键参数和原理:机械臂凭什么“准”
4.1 DH 参数:机械臂数学模型的坐标系
机械臂控制的起点是建立数学模型。最常用的是 DH(Denavit-Hartenberg)参数表,它用四个参数描述相邻连杆坐标系之间的变换关系。
| 关节 i | θi(关节角,度) | di(连杆偏置,mm) | ai(连杆长度,mm) | αi(连杆扭转,度) |
|---|---|---|---|---|
| 1 | θ1 | 333 | 0 | -90 |
| 2 | θ2 | 0 | 316 | 0 |
| 3 | θ3 | 0 | 0 | 90 |
| 4 | θ4 | 384 | 0 | -90 |
| 5 | θ5 | 0 | 0 | 90 |
| 6 | θ6 | 0 | 0 | 0 |
上表是示例值,具体数值必须以机械臂官方手册或 URDF 文件为准。很多初学者问“如何获取 DH 参数”,推荐三个途径:读机械臂官方技术手册;解析 URDF 文件中的关节和连杆定义;在仿真环境里用工具直接加载模型并打印关节信息。拿到 DH 参数后,才能计算正运动学和逆运动学。
4.2 正运动学、逆运动学和轨迹规划
正运动学解决“已知关节角,求末端位置和姿态”,逆运动学解决“已知末端目标位姿,求各关节角”。在最小案例里,press_estop使用的是事先配置好的目标关节角,实际项目中这个角度应该由逆运动学动态计算:先确定急停按钮在基坐标系下的位姿,再通过 IK 求解,并检查是否存在多解、是否超出关节限位。
轨迹规划解决的是“从当前位姿到目标位姿怎么走”。直接一步到位容易造成关节速度突变和机械振动,所以控制服务会生成一系列中间路点,并对速度和加速度做限制。这也是为什么仿真输出里会出现“plan 5 waypoints, estimated 2.1s”。
轨迹规划的常见指标包括:
- 路径长度:末端走过的距离,越短效率越高。
- 执行时间:受速度和加速度限制影响。
- 平滑度:速度、加速度是否连续,影响机械臂寿命和稳定性。
- 碰撞风险:真实项目中需要结合环境模型做碰撞检测。
4.3 电机扭矩和选型:能不能推动负载
很多机械臂项目做到运动学这一步就停了,忽略电机负载能力。实际上,机械臂能不能把急停按钮按下去,取决于关节电机扭矩是否足够。基础计算公式是:
T = m * g * L / (2 * eta)其中m是负载质量(kg),g是重力加速度(约 9.81 m/s²),L是负载重心到关节的力臂长度(m),eta是传动效率。计算得到理论扭矩后,还要乘以安全系数。
| 参数 | 含义 | 示例值 | 影响 |
|---|---|---|---|
| m | 负载质量 | 0.5 kg | 质量越大需要的扭矩越大 |
| L | 力臂长度 | 0.3 m | 离关节越远扭矩需求越高 |
| eta | 传动效率 | 0.85 | 效率越低实际需求越高 |
| n | 安全系数 | 1.5 到 2.0 | 覆盖启停冲击和磨损 |
代入示例:T = 0.5 * 9.81 * 0.3 / (2 * 0.85) = 0.866 N·m,考虑 1.5 倍安全系数后约1.3 N·m。选电机时至少要看额定扭矩、峰值扭矩、转速和减速比几项参数,不能只看功率。
4.4 运动参数速查表
config/arm.yaml里的运动参数直接影响拦截动作是否安全。
| 参数 | 默认建议 | 调大 | 调小 |
|---|---|---|---|
| joint_speed | 10 到 30 度/秒 | 执行快,但冲击和振动风险高 | 更稳,但响应慢 |
| acceleration | 5 到 15 度/秒² | 启停响应快 | 避免末端抖动 |
| blocking | true | 同步等待动作完成 | 异步不等待,适合连续动作序列 |
真实项目中,机械臂按急停按钮的速度不宜过快。速度过快会导致末端冲击力过大,轻则定位不准,重则损坏按钮或机械结构。建议在仿真环境中先测试多组速度,记录末端到位误差,再选择在误差范围内最慢且最稳定的速度。
5. 运行验证与生产环境差距
5.1 从日志、轨迹和结果确认拦截成功
仿真环境里打印“endpoint reached”不代表真实场景拦截成功。验证要覆盖四个层次。
| 验证项 | 预期结果 | 检查方式 |
|---|---|---|
| 决策日志 | 风险决策列表包含 physical_escalation | 查看运行日志 |
| 运动轨迹 | 路径无突变、无超限 | 查看路点和速度曲线 |
| 末端位姿 | 末端到达按钮上方并按下 | 仿真中读取末端坐标 |
| 拦截结果 | 返回 ok=True 且事件编号正确 | 检查最终打印结果 |
如果只有最后一行payment blocked,没有中间轨迹,无法确认机械臂真的到达了按钮位置,也无法确认按压力度是否足够。
5.2 仿真、测试、生产环境的差异
学习环境能跑通,只是起点。从仿真到生产,每一层都要补能力。
| 维度 | 学习环境 | 测试环境 | 生产环境 |
|---|---|---|---|
| 执行器 | MuJoCo 或 Gazebo 仿真 | 仿真加真机空载 | 真机带负载 |
| 决策来源 | 固定规则 | 规则加模型 | 规则加模型加人工审批 |
| 日志 | 终端打印 | 结构化日志 | 审计日志加监控告警 |
| 安全 | 无碰撞风险 | 碰撞检测加急停 | 双急停、围栏、故障回滚 |
| 权限 | 本地直接运行 | 受控环境 | 最小权限加操作审批 |
生产环境里,机械臂拦截大额转账这类动作,必须满足可审计、可回滚、可人工接管三个条件。机械臂动作前要有二次确认,动作后要有状态回读,确认失败时要能触发人工介入流程。
5.3 需要重点留意的三个验证盲区
第一个盲区是只验证脚本能跑,不验证末端真的到达按钮位置。仿真环境里要读取末端坐标,真机环境要依靠编码器反馈或视觉确认。
第二个盲区是只验证正常拦截,不验证失败回退。如果机械臂运动到一半超时、断电或撞到障碍物,系统如何恢复?最小案例里至少要设计一个“拦截失败则告警转人工”的分支。
第三个盲区是只验证决策正确,不验证指令链路。风控服务输出到编排层,再到控制服务,中间每一层都可能丢字段、改类型、延迟超时。联调时要对每一层的输入输出做校验,而不是只看最终结果。
6. 常见问题排查:Claude Code 和机械臂联调
6.1 Claude Code 安装和命令不可用
联调阶段最先遇到的问题通常不在机械臂,而在 Claude Code 本身。
| 现象 | 可能原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| claude 不是内部或外部命令 | npm 全局目录不在 PATH | npm prefix -g | 把全局目录加入 PATH |
| 无法将“claude”项识别为 cmdlet | Windows 环境变量未刷新 | 新开终端 | 重新加载 PATH 或重启终端 |
| 提示某模型名不被当前版本识别 | CLI 与模型配置不匹配 | claude --version | 升级 CLI,检查自定义模型名 |
| 账号鉴权不可用 | 账号未完成开通或鉴权失效 | 查看官方状态页和账号信息 | 按官方流程完成鉴权和重试 |
这里要特别强调:遇到账号鉴权或服务不可用提示时,走官方注册、登录和鉴权流程,不要使用任何非官方渠道绕过限制。
6.2 机械臂仿真常见问题
| 问题现象 | 常见原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| 连接仿真环境失败 |