CiA 402 -- 伺服驱动器辅助
CiA 402 (IEC 61800-7-204) 是基于 CoE 的伺服驱动器设备协议。SDK 提供两个层级的 CiA 402 支持:
CiA402-- 基础接口,通过slave.cia402访问,提供使能/禁用/故障复位等基本操作CiA402Advanced-- 高级接口,独立创建,支持 PDO 映射扫描与类型化位置/速度/转矩访问
CiA 402 运行在 CoE (SDO) 之上,仅当从站支持 CoE 时可用。
从站是否支持 CiA 402 由设备能力决定(可读 0x6502 / 0x60E3 等对象确认);slave.cia402 按需创建,始终返回 CiA402 实例。
本页示例中出现的 CiA402Advanced(master._dll, master.master_index, slave_index) 写法属于内部高级 API, 仅在需要 PDO 映射扫描或类型化位置/速度/转矩访问时使用。常规使能/禁用/故障复位/状态查询走 slave.cia402 (基础接口) 即可。
高级 API 的句柄参数后续可能调整, 请勿在生产代码中长期固化该构造形式。
PDO 初始化
scan_pdo_mapping()
def scan_pdo_mapping(self) -> bool
初始化所有 PDO 偏移缓存。按方向查询主站库给出的各信号对象在过程映像中的字节偏移(0x1C12 RxPDO / 0x1C13 TxPDO 分配,偏移由 native GetPdoOffsets 计算,本层不自扫分配表)。返回 bool — True 表示至少找到一个已知信号 (扫描成功),False 表示未找到任何映射。
本方法不会自动调用:不调用则所有访问走 SDO 回退。要启用 PDO 快速路径须先手动 scan_pdo_mapping()(通常在进入 OP 前调一次;状态降级后可用 reset_pdo_offsets() 清缓存并重扫)。
示例:
from darra_ethercat import CiA402Advanced
drv = CiA402Advanced(master._dll, master.master_index, 1)
drv.scan_pdo_mapping()
状态控制
enable(max_retries=10)
def enable(self, max_retries: int = 10) -> bool
使能驱动器:SDK 自动推进 SwitchOnDisabled → ReadyToSwitchOn → SwitchedOn → OperationEnabled 状态时序。Fault / FaultReaction 态不自动 Fault Reset,此时直接返回 False,须先显式 fault_reset() 再使能;QuickStopActive 走 DisableVoltage → Shutdown → SwitchOn → EnableOperation 恢复,不读 0x605A、不做 T16。
参数:
max_retries(int) — 最大重试次数 (默认 10)
返回值:
bool—True= 已使能到 OperationEnabled,False= 失败/超时/当前处于 Fault 或 FaultReaction
disable()
def disable(self) -> bool
禁用伺服。基础 CiA402 写控制字 0x07 (Disable Operation, bit0-3 = 0x07);CiA402Advanced 写 Disable Voltage (0x00),→ SwitchOnDisabled,完全断电。返回 bool — True 表示命令已下发。
disable_operation()
def disable_operation(self) -> bool
禁用运行 (Transition 5: OperationEnabled -> SwitchedOn)。电机仍通电,可快速重新 enable()。返回 bool — True 表示命令已下发。
返回 bool 的声明仅适用于基础 CiA402 (slave.cia402)。CiA402Advanced 的 disable() / disable_operation() / quick_stop() / fault_reset() 写控制字后返回 None;其中 CiA402Advanced.disable() 写的是 Disable Voltage (完全断电),与基础接口的 Disable Operation 不同语义。
quick_stop()
def quick_stop(self) -> bool
快速停止 (-> QuickStopActive)。返回 bool — True 表示命令已下发。
fault_reset()
def fault_reset(self) -> bool
清除故障。发送 Fault Reset (bit7) 上升沿清除驱动器故障。基础 CiA402 走 native CiA402_FaultReset;CiA402Advanced 的实现为先写 Shutdown (0x06,bit2=1 不顺带 QuickStop) 再写 0x86 (bit7 上升沿 + bit2=1),禁止单独写 0x0080,内含 1ms 间隔 — PDO 实时回调中改用 fault_reset_step() 分两个周期下发。返回 bool — True 表示复位成功。
示例:
cia = slave.cia402
# 一次性使能 (内部完成状态机)
if not cia.enable():
print(f"使能失败, 当前状态: {cia.state}")
# 禁用驱动器
cia.disable()
# 故障复位
cia.fault_reset()
# 快速停止
cia.quick_stop()
状态读取
state / state_drive
# 基础 CiA402 (slave.cia402)
@property
def state(self) -> CiA402State
# CiA402Advanced
@property
def state_drive(self) -> StateCiA402
解析当前驱动器状态。基础 CiA402.state 返回 CiA402State(native CiA402_ParseState 解析);CiA402Advanced.state_drive 返回 StateCiA402(由状态字 0x6041 经 StateCiA402.from_statusword 实时解析)。基础接口的 state_drive 是 state 的别名(返回 CiA402State)。
StateCiA402 枚举 (slave/cia402.py, CiA402Advanced.state_drive 用):
class StateCiA402(IntEnum):
NOT_READY_TO_SWITCH_ON = 0 # 初始化中
SWITCH_ON_DISABLED = 1 # 驱动禁用
READY_TO_SWITCH_ON = 2 # 准备就绪
SWITCHED_ON = 3 # 已开启
OPERATION_ENABLED = 4 # 运行使能
QUICK_STOP_ACTIVE = 5 # 快速停止
FAULT_REACTION_ACTIVE = 6 # 故障反应中
FAULT = 7 # 故障
UNKNOWN = 99 # 未知状态
CiA402State 枚举 (darra_ethercat 顶层, CiA402.state 用):
class CiA402State(IntEnum):
NOT_READY = 0 # 未就绪
SWITCH_ON_DISABLED = 1 # 使能关闭
READY_TO_SWITCH_ON = 2 # 准备使能
SWITCHED_ON = 3 # 已使能
OPERATION_ENABLED = 4 # 运行
QUICK_STOP_ACTIVE = 5 # 快速停止
FAULT_REACTION = 6 # 故障反应中
FAULT = 7 # 故障
UNKNOWN = 99 # 未知
示例:
state = slave.cia402.state # CiA402State 枚举
print(f"驱动器状态: {state.name}")
status_word
@property
def status_word(self) -> int
读取状态字 (0x6041)。CiA402Advanced 上另有 statusword 别名,PDO 优先读取。
control_word
@property
def control_word(self) -> int
@control_word.setter
def control_word(self, value: int) -> None
写入控制字 (0x6040)。注意读侧语义:读取不回读 0x6040,而是按当前状态字计算出的使能命令 (CiA402_GetEnableCommand)。要回读 0x6040 实际内容,用 CiA402Advanced 的 controlword / control_word_pdo(PDO 优先)。写入失败抛 RuntimeError 的情形见「绑定到 PDO 后的写入语义」。
相关属性:
target_reached(bool) — 目标已到达 (Bit 10)has_fault(bool) — 存在故障 (Bit 3)has_warning(bool) — 存在警告 (Bit 7)is_remote(bool) — 远程模式已激活 (Bit 9)
示例:
cia = slave.cia402
sw = cia.status_word
print(f"StatusWord: 0x{sw:04X}")
if cia.target_reached:
print("目标已到达")
if cia.has_fault:
print("存在故障")
操作模式
操作模式属性
操作模式通过属性读写, 不是 set_mode()/get_mode() 方法:
# 基础 CiA402 (slave.cia402)
@property
def mode(self) -> int
@mode.setter
def mode(self, value: int) -> None
# CiA402Advanced
@property
def operation_mode(self) -> int
@operation_mode.setter
def operation_mode(self, value: int) -> None
设置或读取操作模式。读走 0x6061 (Modes of Operation Display),写走 0x6060;CiA402Advanced.operation_mode 读侧 PDO 优先。基础 mode 写失败抛 RuntimeError;CiA402Advanced.operation_mode 写失败(过程映像与 SDO 双败)抛 RuntimeError,见「绑定到 PDO 后的写入语义」。CiA402Advanced 另有只读属性 modes_of_operation_display,语义同 operation_mode 读侧。
ModeCiA402 枚举:
class ModeCiA402(IntEnum):
PP = 1 # 轮廓位置模式
VL = 2 # 速度模式
PV = 3 # 轮廓速度模式
PT = 4 # 轮廓转矩模式
HM = 6 # 回零模式
IP = 7 # 插补位置模式
CSP = 8 # 周期同步位置模式
CSV = 9 # 周期同步速度模式
CST = 10 # 周期同步转矩模式
CSTCA = 11 # 周期同步转矩加速度模式
示例:
from darra_ethercat import ModeCiA402
cia = slave.cia402
cia.mode = ModeCiA402.CSP # 基础 CiA402: mode 属性
print(f"操作模式: {cia.mode}")
# CiA402Advanced 用 operation_mode 属性
# drv.operation_mode = ModeCiA402.CSP
运动参数
| 类别 | 属性 | 类型 | 读写 | 说明 |
|---|---|---|---|---|
| 位置 | actual_position | int | 只读 | 实际位置 (0x6064) |
| 速度 | velocity_actual | int | 只读 | 实际速度 (0x606C) |
| 转矩 | torque_actual | int | 只读 | 实际转矩,千分之额定 (0x6077) |
| 位置 | target_position | int | 读写 | 目标位置 (0x607A) |
| 速度 | target_velocity | int | 读写 | 目标速度 (0x60FF) |
| 转矩 | target_torque | int | 读写 | 目标转矩,千分之额定 (0x6071) |
表内属性读取侧实际返回 Optional[int]:CiA402Advanced PDO 命中读失败折叠为 0,无映射回退 SDO 读失败返回 None;SDO 包装属性(轮廓/回零/扩展/限位/IO 各表同)读失败一律返回 None,可与真值 0 区分。失败态的可分辨读取见「可观测只读」。
示例:
drv = CiA402Advanced(master._dll, master.master_index, 1)
drv.scan_pdo_mapping()
# 读取实际值
print(f"位置: {drv.actual_position}")
print(f"速度: {drv.velocity_actual}")
# 写入目标值
drv.target_position = 100000
drv.target_velocity = 5000
轮廓参数
| 类别 | 属性 | 类型 | 读写 | 说明 |
|---|---|---|---|---|
| 轮廓 | profile_velocity | int | 读写 | 轮廓速度 (0x6081) |
| 轮廓 | profile_acceleration | int | 读写 | 轮廓加速度 (0x6083) |
| 轮廓 | profile_deceleration | int | 读写 | 轮廓减速度 (0x6084) |
示例:
drv.profile_velocity = 10000
drv.profile_acceleration = 50000
drv.profile_deceleration = 50000
极性与轮廓配置
| 类别 | 属性 | 类型 | 读写 | 说明 |
|---|---|---|---|---|
| 极性 | polarity | int | 读写 | 极性配置 (0x607E)。位 6 = 速度极性反转,位 7 = 位置极性反转 |
| 轮廓 | motion_profile_type | int | 读写 | 运动轮廓类型 (0x6086)。0 = 梯形 (线性斜坡),1 = 正弦,2 = S 形 (抛物线) |
| 停止 | quick_stop_deceleration | int | 读写 | 快速停止减速度 (0x6085) |
| 选项码 | quick_stop_option_code | int | 读写 | 快速停止选项码 (0x605A, SDO 路径)。读失败返回 None(可与真值 0 区分);哨兵口径见同对象 quick_stop_option(「选项码属性 (0x605A-0x605E)」一节) |
示例:
# 反转位置方向
drv.polarity = 0x80 # 位 7 = 位置极性反转
# 设置 S 形轮廓
drv.motion_profile_type = 1
# 设置快速停止减速度
drv.quick_stop_deceleration = 100000
触探功能 (Touch Probe)
configure_touch_probe()
def configure_touch_probe(self, function: int) -> None
配置触探功能 (0x60B8)。通过设置功能位启用/禁用触探及其触发条件。
参数:
function(int) — 触探功能字。位 0 = 启用探针 1,位 1 = 正边沿触发 (1=正边沿),位 2 = 仅第一个事件;位 8 起为探针 2 对应位
相关属性:
touch_probe_status(int, 只读) — 触探状态 (0x60B9)touch_probe_positive_value(int, 只读) — 上升沿捕获位置 (0x60BA)touch_probe_negative_value(int, 只读) — 下降沿捕获位置 (0x60BB)
示例:
# 启用触探 1,正边沿触发 (位 0 + 位 1)
drv.configure_touch_probe(0x0003)
# 或用封装好的开关
# drv.enable_touch_probe(positive_edge=True, first_event_only=True)
# 读取触探结果
if (drv.touch_probe_status & 0x0002) != 0: # 正边沿已捕获
captured_pos = drv.touch_probe_positive_value
print(f"触探捕获位置: {captured_pos}")
回零速度与加速度
| 类别 | 属性 | 类型 | 读写 | 说明 |
|---|---|---|---|---|
| 回零 | homing_method | int | 读写 | 回零方法 (0x6098) |
| 回零 | home_offset | int | 读写 | 回零偏移 (0x607C) |
| 回零 | homing_speed_search | int | 读写 | 回零搜索速度 (0x6099:01),快速搜索参考信号 |
| 回零 | homing_speed_zero | int | 读写 | 回零零位速度 (0x6099:02),慢速精确定位零位 |
| 回零 | homing_acceleration | int | 读写 | 回零加速度 (0x609A) |
start_homing()
def start_homing(self) -> None
HM 模式启动回零。需先设置 homing_method。CiA402Advanced 实现要求当前为 OperationEnabled,否则抛 RuntimeError(Fault / FaultReaction 态一律拒绝,不把故障当使能发运动命令)。
相关属性:
homing_attained(bool) — 回零完成 (Bit 12)homing_error(bool) — 回零错误 (Bit 13)
示例:
drv.operation_mode = ModeCiA402.HM
drv.homing_method = 35 # 当前位置回零
# 设置回零速度和加速度
drv.homing_speed_search = 5000 # 搜索速度
drv.homing_speed_zero = 500 # 零位速度
drv.homing_acceleration = 10000 # 回零加速度
# 使能后启动回零
drv.enable()
drv.start_homing()
# 后续检查 drv.homing_attained / drv.homing_error
扩展运动参数
| 类别 | 属性 | 类型 | 读写 | 说明 |
|---|---|---|---|---|
| 转矩 | max_torque | int | 读写 | 最大转矩 (0x6072),千分之额定转矩 |
| 转矩 | motor_rated_torque | int | 读写 | 电机额定转矩 (0x6076),单位 mNm |
| 偏移 | position_offset | int | 读写 | 位置偏移 (0x60B0),CSP 模式下叠加到目标位置 |
| 偏移 | velocity_offset | int | 读写 | 速度偏移 (0x60B1),CSV 模式下叠加到目标速度 |
| 插补 | interpolation_time_period_value | int | 读写 | 插补时间周期值 (0x60C2:01) |
| 插补 | interpolation_time_period_index | int | 读写 | 插补时间周期指数 (0x60C2:02),实际周期 = Value x 10^Index 秒 |
示例:
# 转矩配置
drv.max_torque = 1000 # 最大转矩 = 100% 额定
rated = drv.motor_rated_torque
# CSP 模式偏移叠加
drv.position_offset = 100 # 位置附加偏移
drv.velocity_offset = 50 # 速度附加偏移
# 插补时间周期 (1ms = 1 x 10^(-3) 秒)
drv.interpolation_time_period_value = 1
drv.interpolation_time_period_index = -3
力矩限制
| 类别 | 属性 | 类型 | 读写 | 说明 |
|---|---|---|---|---|
| 限制 | positive_torque_limit | int | 读写 | 正方向力矩限制 (0x60E0),千分之额定转矩 |
| 限制 | negative_torque_limit | int | 读写 | 负方向力矩限制 (0x60E1),千分之额定转矩 |
示例:
# 限制正负方向转矩为额定的 50%
drv.positive_torque_limit = 500
drv.negative_torque_limit = 500
数字 IO
| 类别 | 属性 | 类型 | 读写 | 说明 |
|---|---|---|---|---|
| 输入 | digital_inputs | int | 只读 | 数字输入 (0x60FD),32 位位图 |
| 输出 | digital_outputs | int | 读写 | 数字输出 (0x60FE:01),32 位位图 |
示例:
# 读取数字输入
inputs = drv.digital_inputs
limit_switch = (inputs & 0x01) != 0 # 位 0
# 设置数字输出
drv.digital_outputs = 0x03 # 输出位 0 和位 1
软件位置限位
| 类别 | 属性 | 类型 | 读写 | 说明 |
|---|---|---|---|---|
| 限位 | software_position_limit_min | int | 读写 | 软件位置限位下限 (0x607D:01) |
| 限位 | software_position_limit_max | int | 读写 | 软件位置限位上限 (0x607D:02) |
示例:
# 设置运动范围限制
drv.software_position_limit_min = -1000000
drv.software_position_limit_max = 1000000
快速运动控制
new_setpoint()
def new_setpoint(self, position: int, relative: bool = False) -> None
PP 模式发送新定位命令。设置目标位置并触发 Controlword Bit 4 (New Setpoint);relative=True 时置 Bit 6 (Relative)。CiA402Advanced 实现要求当前为 OperationEnabled,否则抛 RuntimeError(仅 PP 模式有效,CSP 下 bit4-6 无意义)。
参数:
position(int) — 目标位置relative(bool) —True= 相对定位,False= 绝对定位(默认)
示例:
drv.operation_mode = ModeCiA402.PP
drv.profile_velocity = 10000
drv.profile_acceleration = 50000
drv.enable()
# 发送定位命令
drv.new_setpoint(100000) # 绝对定位到 100000
# drv.new_setpoint(5000, True) # 或相对移动 5000
clear_new_setpoint()
def clear_new_setpoint(self) -> None
PP 模式清除 NewSetpoint 标志(Controlword Bit4=0)。在 target_reached 后调用,完成 SetPointAck 握手。CiA402Advanced 实现要求当前为 OperationEnabled,否则抛 RuntimeError。
示例:
if drv.target_reached:
drv.clear_new_setpoint()
驱动器信息
supported_drive_modes
@property
def supported_drive_modes(self) -> Optional[int]
支持的驱动模式位掩码 (0x6502, 只读)。Bit 0=PP, Bit 1=VL, Bit 2=PV, Bit 3=PT, Bit 5=HM, Bit 6=IP, Bit 7=CSP, Bit 8=CSV, Bit 9=CST, Bit 10=CSTCA。CiA402Advanced 读失败返回 None;基础 CiA402 把读失败折叠为 0。
is_mode_supported()
def is_mode_supported(self, mode: ModeCiA402) -> bool
检查驱动器是否支持指定操作模式。
示例:
if drv.is_mode_supported(ModeCiA402.CSP):
drv.operation_mode = ModeCiA402.CSP
supported_homing_methods
@property
def supported_homing_methods(self) -> List[int]
读取驱动器支持的回零方法编号列表 (0x60E3)。读失败同样返回空列表,调用方应按失败处理。
参数:
- 无
返回值:
List[int]— 回零方法编号列表 (sbyte 列表,负值为厂商自定义方法)
三种情形各返什么:
- 失败(SDO 超时 / 通讯异常 / 其它 abort)— 返空列表
- 空集(SI0=0 且读成功,从站真返回 0 条)— 返空列表
- 不支持(从站无 0x60E3 或 0x60E3:00,SDO abort 0x06020000 对象不存在 / 0x06090011 子索引不存在)— 返空列表
本属性把三种情形折叠成同一种返回形状(空列表),因此空列表不能读成「支持 0 种回零方法」。底层 CiA402_GetSupportedHomingMethods 失败返 -1、真返回 0 条返 0(fail-closed,在底层可区分),封装层把两者都映射为空列表;本属性自身的读取原语 sdo_read_value 读失败返 None,同样在本属性内被折叠。本页可区分三态的入口是 try_supported_homing_methods():一次拿到完整列表,由 CiA402ReadStatus 给出 SUCCESS(value == [] 即真空集,是真值)/ NOT_SUPPORTED(不支持)/ READ_FAILED(失败)。try_read_object() 仍是通用途径。
示例:
methods = drv.supported_homing_methods
if not methods:
print("回零方法列表为空: 读失败 / 空集 / 不支持,不能当作支持 0 种方法")
else:
print(f"支持 {len(methods)} 种回零方法: {methods}")
# 需要区分三态时用专用入口
result = drv.try_supported_homing_methods()
print(result.status.name, result.value)
try_supported_homing_methods()
def try_supported_homing_methods(self) -> CiA402ReadResult
读取驱动器支持的回零方法编号列表 (0x60E3),真值 / 读失败 / 不支持三态可分。与 try_read_error_code() 同载体 CiA402ReadResult。
参数:
- 无
返回值:
CiA402ReadResult— 带状态结果,value为回零方法编号列表 (仅status==SUCCESS时有意义,否则为None)
三种情形各返什么:
- 真值(status = SUCCESS)— 拿到完整列表 value;空列表 = 从站真返回 0 条回零方法(真不支持任何回零方法),这是真值不是失败
- 读失败(status = READ_FAILED,SDO 超时 / 通讯异常 / 其它 abort)— value 无意义(None)
- 不支持(status = NOT_SUPPORTED,0x60E3 对象/子索引不存在,SDO abort 0x06020000 / 0x06090011 / 0x06090012)— value 无意义(None)
子项读失败绝不许造条目:只要有一个子项没读到,整体如实报该状态(取首个失败子项的状态与 abort code),value 不作真值用,不许把失败当 0 号方法推进列表。abort_code 照常携带 SDO Abort Code(成功为 NO_ERROR)。方法编号是 sbyte (List[int],-128..-1 为厂商自定义方法),是真实取值不是哨兵,别用 is_read_fail_sentinel 判别去过滤。
旧入口 supported_homing_methods 与 get_supported_homing_methods_dll() 把「失败 / 真空集 / 不支持」折叠成同一个形状(空列表),失败也会得到空集,要可分辨请用 try_supported_homing_methods();旧入口对读不到的子项是「跳过、不造条目」。
示例:
r = drv.try_supported_homing_methods()
if r.ok:
if not r.value:
print("从站确认不支持任何回零方法")
else:
print(f"支持 {len(r.value)} 种回零方法: {r.value}")
elif r.not_supported:
print("从站不支持 0x60E3")
else:
print(f"读取失败: {r.status.name}, abort={r.abort_code}")
可观测只读
只读返回值的读失败语义按接口区分,公开哨兵常量共三档:
READ_FAIL_SENTINEL_I16= -32768 (INT16_MIN) — 有符号 16 位选项码 (0x605A-0x605E) 读失败READ_FAIL_SENTINEL_U16= 0xFFFF — 无符号 16 位error_code(0x603F) 读失败READ_FAIL_SENTINEL_INT= -1 — 返回 int 的计数形读取接口 (get_error_history/get_supported_homing_methods_dll等) 读失败整体返回 -1 (fail-closed)
判别按返回形取同档谓词:is_read_fail_sentinel_i16(v) 只认 -32768,is_read_fail_sentinel_u16(v) 只认 0xFFFF,is_read_fail_sentinel_int(v) 只认 -1。合并入口 is_read_fail_sentinel(v) 是 legacy 跨域判别,有文档化假阳性,保留给旧调用点;新代码一律用上面三个分域谓词。三档哨兵都不是合法取值(合法选项码为小值含 0;error_code 0x0000 表无错误;计数形接口的合法返回为条数 >= 0),请勿当作真实数值使用,判别后按失败处理。
按返回形选谓词,不要跨档混用。合并入口 is_read_fail_sentinel 一次覆盖三档取值域,其文档化假阳性为:-1 是 Fault Reaction 选项码 (0x605E) 的合法厂商取值,65535 也是合法的计数值,两者经合并入口都会被判成读失败。int8 方法编号(0x60E3 列表项,-128..-1 为厂商自定义方法)是真实取值,不属哨兵,同样请勿拿来判别。
三类失败口径须区分:① 哨兵路径 = 五个选项码属性 (quick_stop_option / shutdown_option / disable_operation_option / halt_option / fault_reaction_option) 与 error_code 六个 native 直读属性(读失败返回 -32768 / 0xFFFF 两档哨兵);② 计数形哨兵路径 = 返回 int 的计数形读取接口 (get_error_history / get_supported_homing_methods_dll 等),底层读失败整体返回 -1 (fail-closed,READ_FAIL_SENTINEL_INT),封装层映射为空列表,调用方应按失败处理;③ 折叠失败路径 = actual_position / torque_actual 等 PDO 优先数值 getter(PDO 命中时读失败折叠为 0,与真值 0 不可分;无映射回退 SDO 时读失败返回 None)与 SDO 包装属性(如 quick_stop_option_code,读失败返回 None)。try_read_* 系列方法真去读一次 SDO,把结果如实分成三态 (成功 / 不支持 / 读失败),可完整区分"从站真返 0"与读失败。
try_read_* 强制走 SDO 只读 (即使该对象已 PDO 映射),以便拿到 SDO Abort Code 区分状态。实时高频读仍请用对应 PDO 优先属性 (如 actual_position、statusword)。
CiA402ReadStatus 枚举
class CiA402ReadStatus(IntEnum):
NOT_ATTEMPTED = 0 # 尚未尝试读取
SUCCESS = 1 # 读取成功, 返回的是从站真值
NOT_SUPPORTED = 2 # 从站不支持该对象 (SDO abort 0x06020000/0x06090011)
READ_FAILED = 3 # SDO 读取失败 (超时 / 通讯异常 / 其它 abort)
CiA402ReadResult
try_read_* 方法返回的带状态结果 (NamedTuple)。
value(Optional[Union[int, List[int]]]) — 字段 — 只读 — 读到的值 (仅status==SUCCESS时有意义, 否则为None;标量对象为 int,0x60E3 列表对象为 List[int])status(CiA402ReadStatus) — 字段 — 只读 — 读取状态枚举abort_code(SDOError) — 字段 — 只读 — SDO Abort Code (NO_ERROR= 无 abort)ok(bool) — 属性 — 只读 — 是否读取成功且值可信 (status == SUCCESS)not_supported(bool) — 属性 — 只读 — 从站是否不支持该对象 (status == NOT_SUPPORTED)
try_read_object()
def try_read_object(self, index: int, subindex: int = 0,
dtype: str = 'u32') -> CiA402ReadResult
通用可观测只读 — 对任意 CiA402 对象做一次 SDO 只读尝试,返回带状态的结果。纯只读无副作用。dtype 取 'u8'/'i8'/'u16'/'i16'/'u32'/'i32'/'u64'/'i64'。
专用 try_read_* 方法
try_read_operation_mode_display()— 对象0x6061— 返回值宽度i8— 操作模式显示 (强制 SDO, 不取 PDO)try_read_supported_drive_modes()— 对象0x6502— 返回值宽度u32— 支持的驱动模式位掩码 (ETG.6010 mandatory)try_read_error_code()— 对象0x603F— 返回值宽度u16— 当前错误代码 (三态返回,与error_code属性的 0xFFFF 哨兵口径相互独立)try_read_torque_actual()— 对象0x6077— 返回值宽度i16— 实际转矩 (千分之额定)try_read_digital_inputs()— 对象0x60FD— 返回值宽度u32— 数字输入try_read_position_actual()— 对象0x6064— 返回值宽度i32— 实际位置 (SDO 路径)try_read_velocity_actual()— 对象0x606C— 返回值宽度i32— 实际速度 (SDO 路径)try_read_statusword()— 对象0x6041— 返回值宽度u16— 状态字 (SDO 路径)try_read_drive_data(subindex)— 对象0x6510— 返回值宽度u32— 厂商特定驱动数据 (子索引由 ESI 定义)
每个方法签名均为 def try_read_xxx(self) -> CiA402ReadResult (除 try_read_drive_data(self, subindex: int))。
可观测状态属性
last_read_status(CiA402ReadStatus) — 状态 — 只读 — 最近一次 SDO 只读尝试的状态last_read_abort_code(SDOError) — 状态 — 只读 — 最近一次失败/不支持时的 Abort Code (NO_ERROR=成功)last_read_object(Tuple[int, int]) — 状态 — 只读 — 最近一次尝试的 (对象索引, 子索引)supported_drive_modes_readable(bool) — 状态 — 只读 — 从站是否真支持 0x6502 对象 (据只读尝试结果)
示例:
from darra_ethercat import CiA402ReadStatus, is_read_fail_sentinel_u16
# 三态区分: 真值 / 不支持 / 读失败 (try_read_* 系列)
result = drv.try_read_supported_drive_modes()
if result.ok:
print(f"支持的驱动模式位掩码: 0x{result.value:08X}")
elif result.not_supported:
print("从站不支持 0x6502 对象")
else:
print(f"读取失败, abort=0x{int(result.abort_code):08X}")
# 查询最近一次只读尝试的诊断信息
idx, sub = drv.last_read_object
print(f"最近读取 0x{idx:04X}:{sub:02X}, 状态={drv.last_read_status.name}")
# 选项码读失败 = 哨兵 -32768, 用 is_read_fail_sentinel_i16 判别; error_code 读失败 = 0xFFFF, 用 is_read_fail_sentinel_u16 判别
if is_read_fail_sentinel_u16(drv.error_code):
print("error_code 读取失败, 按失败处理")
TxPDO 数据有效性
txpdo_data_invalid
@property
def txpdo_data_invalid(self) -> bool
TxPDO 数据是否无效 (0x603E)。非零表示驱动器 TxPDO 数据不可信,例如编码器未就绪时位置值无意义。
示例:
if not drv.txpdo_data_invalid:
pos = drv.actual_position # 数据有效,可安全使用
同步功能
| 类别 | 属性 | 类型 | 读写 | 说明 |
|---|---|---|---|---|
| 同步 | synchronization_settings | int | 读写 | 同步设置 (0x60D9:01),同步使能位掩码 |
| 同步 | drive_sync_status | int | 只读 | 驱动同步状态 (0x60DA),指示驱动器是否已同步到主站时钟 |
示例:
# 读取驱动器同步状态
sync_status = drv.drive_sync_status
print(f"同步状态: 0x{sync_status:04X}")
# 配置同步设置
drv.synchronization_settings = 0x0001
深度合规 API (ETG.6010 + CiA 402-2)
以下方法/属性把状态机推进、选项码判断、错误历史等逻辑下沉到 native DLL,Python 层仅做类型转换与友好包装。适用于需要严格 CiA 402-2 状态转换校验、选项码 (0x605A-0x605E) 读写、驱动器内部错误历史 (0x1003) 的场景。
request_transition()
def request_transition(self, target: StateCiA402) -> bool
请求指定状态转换,合法性由 DLL 按 CiA 402-2 Fig.3 T1-T16 校验。非法转换 (如 FAULT → OPERATION_ENABLED 直跳) 返回 False 且不发命令。
参数:
target(StateCiA402) — 目标状态
返回值:
bool—True= 转换命令已下发,False= 非法转换 / 失败
get_transition_command()
@staticmethod
def get_transition_command(dll: DarraCoreDLL, current: StateCiA402,
target: StateCiA402) -> int
查询 (当前状态 → 目标状态) 的合法控制字,0xFFFF = 非法转换。纯计算,故为静态方法,需传入 dll 实例。
fault_reset_step()
def fault_reset_step(self, step: int) -> bool
非阻塞 Fault Reset 单步 (PDO 回调两周期分别调 step=0/step=1,避免 fault_reset() 内的 sleep 阻塞 RT)。step=0 写 CW_SHUTDOWN (0x06,bit2=1 不顺带 QuickStop),step=1 写 CW_FAULT_RESET (0x86,产生 bit7 上升沿)。
supported_drive_modes_dll / is_mode_supported_dll()
@property
def supported_drive_modes_dll(self) -> int
def is_mode_supported_dll(self, mode: int) -> bool
通过 DLL 查询支持的驱动模式位掩码 (0x6502) 与模式支持判断,由 native 做 PDO/SDO 分流与位运算,与 supported_drive_modes / is_mode_supported() 等价。
选项码属性 (0x605A-0x605E)
quick_stop_option(int) — 读写 — Quick Stop 选项码 (0x605A)。0=直接切断, 1=斜坡减速, 2=Quick Stop 减速, 3=转矩停止;5/6/7/8=减速后保持 QuickStopActive(允许 T16 直接 → OperationEnabled)shutdown_option(int) — 读写 — Shutdown 选项码 (0x605B): OperationEnabled → ReadyToSwitchOn 减速行为disable_operation_option(int) — 读写 — Disable Operation 选项码 (0x605C): OperationEnabled → SwitchedOn 行为halt_option(int) — 读写 — Halt 选项码 (0x605D): CW Bit8 Halt 行为 (1=轮廓斜坡, 2=QS 减速, 3=转矩停止)fault_reaction_option(int) — 读写 — Fault Reaction 选项码 (0x605E): 故障检测后行为 (-1=厂商, 0=直接切断, 1/2/3=减速)
返回值与读失败哨兵:正常 = 真值;读失败 = -32768 (INT16_MIN, READ_FAIL_SENTINEL_I16)。-32768 不是合法取值(合法选项码为小值含 0),请勿当作真实数值使用;用 is_read_fail_sentinel_i16(v) 判别后按失败处理(公开常量 READ_FAIL_SENTINEL_I16 = -32768 / READ_FAIL_SENTINEL_U16 = 0xFFFF / READ_FAIL_SENTINEL_INT = -1,分域判别函数 is_read_fail_sentinel_i16 / is_read_fail_sentinel_u16 / is_read_fail_sentinel_int 各自只认本档哨兵;合并入口 is_read_fail_sentinel 是 legacy 跨域判别,有文档化假阳性)。
error_code / 错误历史
@property
def error_code(self) -> int
def get_error_history(self, max_count: int = 32) -> List[int]
def clear_error_history(self) -> bool
error_code(int) — 只读 — 当前 Error Code (0x603F, CiA 402 附录)。正常 = 真值;读失败 = 0xFFFF 哨兵 (READ_FAIL_SENTINEL_U16)。0xFFFF 不是合法取值 (0x0000 表无错误), 请勿当作真实数值使用, 判别用is_read_fail_sentinel_u16(v)get_error_history(max_count=32)(List[int]) — 只读 — 读取 Pre-defined Error Field (0x1003), 返回驱动器内部持久错误历史 (Error Code 低 16 位)。底层读失败整体返回 -1 (fail-closed), 封装层映射为空列表, 调用方应按失败处理 (空列表不代表确认无历史)clear_error_history()(bool) — 只写 — 清除驱动器内部错误历史 (0x1003:00 = 0)。返回bool表示清除命令是否成功
回零方法 DLL 接口
@property
def homing_method_dll(self) -> int
@homing_method_dll.setter
def homing_method_dll(self, value: int) -> None
def get_supported_homing_methods_dll(self, max_count: int = 64) -> List[int]
@staticmethod
def is_standard_homing_method(dll: DarraCoreDLL, method: int) -> bool
homing_method_dll— 经 native 直读/直写回零方法 (0x6098, sbyte),与homing_method属性等价。get_supported_homing_methods_dll()— 读取支持的回零方法列表 (0x60E3, sbyte 列表, 可含负值厂商扩展)。底层读失败整体返回 -1 (fail-closed), 封装层映射为空列表, 调用方应按失败处理 (空列表不代表确认无回零方法)。要可分辨请用try_supported_homing_methods()。is_standard_homing_method()— 校验回零方法是否属于 ETG.6010 标准 35 方法 (1..35 或 37)。纯计算静态方法,需传入dll。
get_supported_homing_methods_dll() 三种情形各返什么:
- 失败(SDO 超时 / 通讯异常 / 其它 abort)— 底层返 -1(fail-closed),本封装映射为空列表,按失败处理
- 空集(SI0=0 且读成功,从站真返回 0 条)— 返空列表
- 不支持(从站无 0x60E3 或 0x60E3:00,SDO abort 0x06020000 / 0x6090011)— 底层返 -1,与失败同档,本封装映射为空列表
示例:
from darra_ethercat import StateCiA402
# 按 CiA 402-2 校验的状态转换 (非法转换返回 False, 不发命令)
if not drv.request_transition(StateCiA402.OPERATION_ENABLED):
print("非法转换 / 失败")
# 读取驱动器内部错误历史 (空列表 = 无历史或读失败, 应按失败处理)
history = drv.get_error_history()
print(f"错误历史 ({len(history)} 条): {[hex(e) for e in history]}")
# 当前错误码 (读失败 = 0xFFFF 哨兵, 不是真实错误码)
from darra_ethercat import is_read_fail_sentinel_u16
if is_read_fail_sentinel_u16(drv.error_code):
print("error_code 读取失败, 按失败处理")
else:
print(f"当前错误码: 0x{drv.error_code:04X}")
# 配置 Quick Stop 选项码 (6 = 减速后保持 QuickStopActive); 读返回 -32768 哨兵即失败
drv.quick_stop_option = 6
高级接口 (CiA402Advanced)
CiA402Advanced 继承自 CiA402,额外支持 PDO 映射扫描与类型化访问。
from darra_ethercat import CiA402Advanced
drv = CiA402Advanced(master._dll, master.master_index, 1)
PDO 映射扫描
# 扫描 PDO 映射 (0x1C12 / 0x1C13)
drv.scan_pdo_mapping()
print(f"PDO 映射完成: {drv.is_pdo_mapped}")
print(f"映射信号: {drv.pdo_offsets}")
扫描完成后,读写运动参数会优先通过 PDO(零延迟),无映射时回退到 SDO。
扫描到输出映射后写控制字 / 操作模式 / 目标位置 / 目标速度 / 目标转矩:对象已映射 → 走过程映像版本化读改写事务直接写 PDO;过程映像写入失败或对象未映射 → 回退 SDO。两条路径都失败时抛 RuntimeError(含从站号、输出偏移与 SDO Abort Code),不会静默丢弃写入。
相关属性:
status_word_pdo(int, 只读) — 通过 PDO 读取状态字 (0x6041)control_word_pdo(int, 读写) — 通过 PDO 读写控制字 (0x6040)
标准对象索引常量
| 常量 | 值 | 说明 |
|---|---|---|
| OD_CONTROLWORD | 0x6040 | 控制字 |
| OD_STATUSWORD | 0x6041 | 状态字 |
| OD_QUICK_STOP_OPTION_CODE | 0x605A | 快速停止选项码 |
| OD_MODES_OF_OPERATION | 0x6060 | 操作模式设置 |
| OD_MODES_DISPLAY | 0x6061 | 操作模式显示 |
| OD_POSITION_ACTUAL | 0x6064 | 实际位置 |
| OD_TARGET_TORQUE | 0x6071 | 目标转矩 |
| OD_MAX_TORQUE | 0x6072 | 最大转矩 |
| OD_MOTOR_RATED_TORQUE | 0x6076 | 电机额定转矩 |
| OD_TORQUE_ACTUAL | 0x6077 | 实际转矩 |
| OD_TARGET_POSITION | 0x607A | 目标位置 |
| OD_HOME_OFFSET | 0x607C | 回零偏移 |
| OD_SOFTWARE_POSITION_LIMIT | 0x607D | 软件位置限制 |
| OD_POLARITY | 0x607E | 极性 |
| OD_MAX_PROFILE_VELOCITY | 0x6080 | 最大轮廓速度 |
| OD_PROFILE_VELOCITY | 0x6081 | 轮廓速度 |
| OD_PROFILE_ACCELERATION | 0x6083 | 轮廓加速度 |
| OD_PROFILE_DECELERATION | 0x6084 | 轮廓减速度 |
| OD_QUICK_STOP_DECEL | 0x6085 | 快速停止减速度 |
| OD_MOTION_PROFILE_TYPE | 0x6086 | 运动轮廓类型 |
| OD_HOMING_METHOD | 0x6098 | 回零方法 |
| OD_HOMING_SPEEDS | 0x6099 | 回零速度 |
| OD_HOMING_ACCELERATION | 0x609A | 回零加速度 |
| OD_POSITION_OFFSET | 0x60B0 | 位置偏移 (CSP) |
| OD_VELOCITY_OFFSET | 0x60B1 | 速度偏移 (CSV) |
| OD_TORQUE_OFFSET | 0x60B2 | 转矩偏移 |
| OD_TOUCH_PROBE_FUNCTION | 0x60B8 | Touch Probe 功能控制 |
| OD_TOUCH_PROBE_STATUS | 0x60B9 | Touch Probe 状态 |
| OD_TOUCH_PROBE_POS_EDGE | 0x60BA | Touch Probe 正边沿位置 |
| OD_TOUCH_PROBE_NEG_EDGE | 0x60BB | Touch Probe 负边沿位置 |
| OD_INTERPOLATION_TIME_PERIOD | 0x60C2 | 插补时间周期 |
| OD_VELOCITY_ACTUAL | 0x606C | 实际速度 |
| OD_DIGITAL_INPUTS | 0x60FD | 数字输入 |
| OD_DIGITAL_OUTPUTS | 0x60FE | 数字输出 |
| OD_TARGET_VELOCITY | 0x60FF | 目标速度 |
| OD_TXPDO_DATA_INVALID | 0x603E | TxPDO 数据无效标志 |
| OD_ERROR_CODE | 0x603F | 当前错误代码 (只读) |
| OD_SYNCHRONIZATION_SETTINGS | 0x60D9 | 同步设置 |
| OD_DRIVE_SYNC_STATUS | 0x60DA | 驱动同步状态 (只读) |
| OD_POSITIVE_TORQUE_LIMIT | 0x60E0 | 正方向力矩限制 |
| OD_NEGATIVE_TORQUE_LIMIT | 0x60E1 | 负方向力矩限制 |
| OD_SUPPORTED_HOMING_METHODS | 0x60E3 | 支持的回零方法列表 |
| OD_SUPPORTED_MODES | 0x6502 | 支持的驱动模式位掩码 |
| OD_DRIVE_DATA | 0x6510 | 厂商特定驱动数据 (RECORD, 只读) |
控制字命令常量
| 常量 | 值 | 说明 |
|---|---|---|
| CW_SHUTDOWN | 0x06 | 关机命令 |
| CW_SWITCH_ON | 0x07 | 开启命令 |
| CW_ENABLE_OPERATION | 0x0F | 使能运行 |
| CW_DISABLE_VOLTAGE | 0x00 | 禁用电压 |
| CW_QUICK_STOP | 0x02 | 快速停止 |
| CW_FAULT_RESET | 0x86 | 故障复位 (bit7 上升沿且 bit2=1,禁止单独写 0x80) |
| CW_HALT | 0x0100 | 暂停位 (Bit 8, 暂停运动但不禁用) |
状态字位掩码常量
| 常量 | 值 | 说明 |
|---|---|---|
| SW_READY_TO_SWITCH_ON | 0x0001 (Bit 0) | 准备就绪 |
| SW_SWITCHED_ON | 0x0002 (Bit 1) | 已开启 |
| SW_OPERATION_ENABLED | 0x0004 (Bit 2) | 运行使能 |
| SW_FAULT | 0x0008 (Bit 3) | 故障 |
| SW_VOLTAGE_ENABLED | 0x0010 (Bit 4) | 电压已使能 |
| SW_QUICK_STOP | 0x0020 (Bit 5) | 快速停止 |
| SW_SWITCH_ON_DISABLED | 0x0040 (Bit 6) | 开启已禁用 |
| SW_WARNING | 0x0080 (Bit 7) | 警告 |
| SW_REMOTE | 0x0200 (Bit 9) | 远程模式 |
| SW_TARGET_REACHED | 0x0400 (Bit 10) | 目标已到达 |
| SW_INTERNAL_LIMIT | 0x0800 (Bit 11) | 内部限位激活 |
| SW_OP_MODE_SPECIFIC_1 | 0x1000 (Bit 12) | 模式相关位 1 (PP: Set-point acknowledge / HM: Homing attained) |
| SW_OP_MODE_SPECIFIC_2 | 0x2000 (Bit 13) | 模式相关位 2 (CSP: Following error / HM: Homing error) |
完整示例
CSP 模式 -- 周期同步位置控制
from darra_ethercat import EtherCATMaster, EcState, CiA402Advanced, ModeCiA402
with EtherCATMaster() as master:
master.set_network(r"\Device\NPF_{GUID}")
master.set_state(EcState.OP)
master.start()
# 创建 CiA402 高级接口
drv = CiA402Advanced(master._dll, master.master_index, 1)
# 扫描 PDO 映射
drv.scan_pdo_mapping()
# 设置 CSP 模式
drv.operation_mode = ModeCiA402.CSP
# 一次性使能 (内部完成完整状态机)
if not drv.enable():
print(f"使能失败, 当前状态: {drv.state}")
raise SystemExit(1)
# PDO 周期回调中控制
def on_pdo(mi):
drv.target_position = calculate_next_position()
master.on_pdo_cycle(on_pdo)
import time
time.sleep(10)
drv.disable()
master.stop()
PP 模式 -- 轮廓位置控制
drv = CiA402Advanced(master._dll, master.master_index, 1)
drv.scan_pdo_mapping()
drv.operation_mode = ModeCiA402.PP
drv.profile_velocity = 10000
drv.profile_acceleration = 50000
drv.profile_deceleration = 50000
drv.enable()
# 发送定位命令
drv.new_setpoint(100000)
# 等待到达
import time
while not drv.target_reached:
time.sleep(0.01)
drv.clear_new_setpoint()
print("定位完成")
通过 SDO 直接控制
slave = master[1]
# 设置操作模式 (CSP)
slave.coe.sdo_write_value(0x6060, 0, 8, dtype='i8')
# 写入控制字: Shutdown
slave.coe.sdo_write_value(0x6040, 0, 0x06, dtype='u16')
# 写入控制字: Switch On
slave.coe.sdo_write_value(0x6040, 0, 0x07, dtype='u16')
# 写入控制字: Enable Operation
slave.coe.sdo_write_value(0x6040, 0, 0x0F, dtype='u16')
# 读取状态字
sw = slave.coe.sdo_read_value(0x6041, 0, dtype='u16')
print(f"StatusWord: 0x{sw:04X}")
# 写入目标位置
slave.coe.sdo_write_value(0x607A, 0, 100000, dtype='i32')