跳转至

异常处理

异常类总览

所有异常继承自 OpenArmXError,便于统一捕获处理。

OpenArmXError
├── CANError(CAN通信异常)
│   ├── CANInitializationError
│   ├── CANTimeoutError
│   └── CANTransmissionError
├── MotorError(电机异常)
│   ├── MotorNotEnabledError
│   ├── MotorFaultError
│   ├── MotorTimeoutError
│   └── MotorCalibrationError
├── ConfigurationError(配置异常)
│   ├── InvalidMotorIDError
│   ├── InvalidModeError
│   ├── InvalidParameterError
│   └── ConfigFileError
├── LimitExceededError(限制异常)
│   ├── PositionLimitError
│   ├── VelocityLimitError
│   ├── TorqueLimitError
│   ├── KpLimitError
│   └── KdLimitError
└── ConnectionError(连接异常)
    └── ConnectionLostError

CAN通信异常

CANInitializationError

CAN总线初始化失败。

抛出场景:

  • CAN接口不存在
  • 权限不足
  • 驱动未加载
  • 硬件故障
try:
    arm = Arm('can9')  # can9不存在
except CANInitializationError as e:
    print(f"CAN初始化失败: {e}")

CANTimeoutError

CANTimeoutError(motor_id=None, timeout=None)

CAN通信超时。

参数名 类型 默认值 说明
motor_id int None 超时的电机ID
timeout float None 超时时间(秒)

抛出场景:

  • 电机未连接或断电
  • CAN总线故障
  • 电机ID配置错误
  • 网络拥堵
try:
    arm.move_joint_mit(5, position=1.0)
except CANTimeoutError as e:
    print(f"电机{e.motor_id}通信超时: {e.timeout}s")

CANTransmissionError

CAN消息发送失败。

抛出场景:

  • CAN总线错误
  • 总线关闭状态
  • 硬件故障

电机异常

MotorNotEnabledError

MotorNotEnabledError(motor_id)

电机未使能。

参数名 类型 说明
motor_id int 未使能的电机ID
# 在未使能的情况下尝试移动电机
arm.move_joint_position(motor_id=5, position=1.0)
# 抛出 MotorNotEnabledError

MotorFaultError

MotorFaultError(motor_id, fault_code=None, fault_description=None)

电机故障。

参数名 类型 默认值 说明
motor_id int - 故障的电机ID
fault_code int None 故障代码(16进制)
fault_description str None 故障描述

故障类型:

故障类型
bit 0 欠压故障
bit 1 驱动故障
bit 2 过温
bit 3 磁编码故障
bit 4 堵转过载故障
bit 5 未标定

MotorTimeoutError

MotorTimeoutError(motor_id, command=None, timeout=None)

电机响应超时。

参数名 类型 默认值 说明
motor_id int - 超时的电机ID
command str None 超时的命令类型
timeout float None 超时时间(秒)

MotorCalibrationError

MotorCalibrationError(motor_id)

电机未标定。

参数名 类型 说明
motor_id int 未标定的电机ID

配置异常

InvalidMotorIDError

InvalidMotorIDError(motor_id, valid_range=(1, 8))

无效的电机ID。

参数名 类型 默认值 说明
motor_id int - 无效的电机ID
valid_range tuple (1, 8) 有效范围
try:
    arm.enable(motor_id=10)  # ID超出范围
except InvalidMotorIDError as e:
    print(f"电机ID错误: {e}")

InvalidModeError

InvalidModeError(mode, valid_modes=['mit', 'pp', 'speed', 'current', 'csp'])

无效的控制模式。

参数名 类型 默认值 说明
mode str/int - 无效的模式
valid_modes list ['mit', 'pp', 'speed', 'current', 'csp'] 有效模式列表

InvalidParameterError

InvalidParameterError(param_name, param_value, reason=None)

无效的参数。

参数名 类型 默认值 说明
param_name str - 参数名称
param_value any - 参数值
reason str None 无效原因

ConfigFileError

ConfigFileError(config_file, reason=None)

配置文件错误。

参数名 类型 默认值 说明
config_file str - 配置文件路径
reason str None 错误原因

限制异常

PositionLimitError

PositionLimitError(motor_id, position, min_limit, max_limit)

位置超限。

参数名 类型 说明
motor_id int 电机ID
position float 设置的位置值
min_limit float 最小限制
max_limit float 最大限制

VelocityLimitError

VelocityLimitError(motor_id, velocity, min_limit, max_limit)

速度超限。

参数名 类型 说明
motor_id int 电机ID
velocity float 设置的速度值
min_limit float 最小限制
max_limit float 最大限制

TorqueLimitError

TorqueLimitError(motor_id, torque, min_limit, max_limit)

扭矩超限。

参数名 类型 说明
motor_id int 电机ID
torque float 设置的扭矩值
min_limit float 最小限制
max_limit float 最大限制

KpLimitError

KpLimitError(motor_id, kp, min_limit, max_limit)

KP增益超限。

参数名 类型 说明
motor_id int 电机ID
kp float 设置的KP值
min_limit float 最小限制
max_limit float 最大限制

KdLimitError

KdLimitError(motor_id, kd, min_limit, max_limit)

KD增益超限。

参数名 类型 说明
motor_id int 电机ID
kd float 设置的KD值
min_limit float 最小限制
max_limit float 最大限制

连接异常

ConnectionError

连接错误基类。

抛出场景:

  • 与硬件的连接出现问题

ConnectionLostError

ConnectionLostError(device=None)

连接丢失。

参数名 类型 默认值 说明
device str None 丢失连接的设备描述

异常处理示例

from openarmx_arm_driver import Arm
from openarmx_arm_driver.exceptions import *

try:
    # 1. 初始化机械臂
    arm = Arm('can0', side='right')

    # 2. 使能电机
    arm.enable_all()

    # 3. 控制运动
    arm.move_joint_mit(motor_id=5, position=1.0)

    # 4. 状态查询
    status = arm.get_status(5)

except CANInitializationError as e:
    print(f"CAN初始化失败: {e}")
    print("请检查:")
    print("  1. CAN接口是否存在")
    print("  2. 是否有sudo权限")
    print("  3. CAN驱动是否已加载")

except MotorNotEnabledError as e:
    print(f"电机未使能: {e}")
    print("请先调用 enable() 或 enable_all() 方法")

except InvalidMotorIDError as e:
    print(f"无效的电机ID: {e}")
    print(f"有效范围: {e.valid_range[0]}-{e.valid_range[1]}")

except MotorFaultError as e:
    print(f"电机故障: {e}")
    if e.fault_code:
        print(f"故障代码: 0x{e.fault_code:02X}")

except CANTimeoutError as e:
    print(f"通信超时: {e}")
    print("可能的原因:")
    print("  1. 电机电源未开")
    print("  2. CAN线连接不良")
    print("  3. 网络拥堵")

except Exception as e:
    print(f"未知错误: {e}")

finally:
    # 确保连接关闭
    if 'arm' in locals():
        arm.close()

异常处理建议

  • 从具体到一般: 先捕获具体异常,再捕获基类异常
  • 资源清理: 在finally块中确保资源释放
  • 错误恢复: 根据异常类型尝试恢复操作
  • 日志记录: 记录异常信息便于调试
  • 用户提示: 提供明确的错误解决建议

场景化异常处理示例

场景1:机械臂初始化

from openarmx_arm_driver import Arm
from openarmx_arm_driver.exceptions import CANInitializationError

def init_arm_safely(can_channel, side='right', max_retries=3):
    """安全初始化机械臂,支持重试"""
    for attempt in range(max_retries):
        try:
            arm = Arm(can_channel, side=side)
            print(f"机械臂初始化成功: {can_channel}")
            return arm
        except CANInitializationError as e:
            print(f"初始化失败 (尝试 {attempt + 1}/{max_retries}): {e}")
            if attempt < max_retries - 1:
                print("检查CAN接口后重试...")
                import time
                time.sleep(2)

    print("初始化失败,请检查:")
    print("  1. CAN接口是否存在: ip link show")
    print("  2. CAN接口是否启用: sudo ip link set can0 up type can bitrate 1000000")
    print("  3. 是否有足够权限")
    return None

# 使用
arm = init_arm_safely('can0', side='right')
if arm:
    # 继续操作...
    pass

场景2:电机使能与故障检测

from openarmx_arm_driver import Arm
from openarmx_arm_driver.exceptions import (
    MotorNotEnabledError,
    MotorFaultError,
    CANTimeoutError
)

def enable_motors_with_check(arm):
    """使能电机并检测故障"""
    failed_motors = []
    fault_motors = []

    for motor_id in arm.motor_ids:
        try:
            result = arm.enable(motor_id, timeout=2.0)
            if result != 0:
                failed_motors.append(motor_id)
                print(f"电机{motor_id}: 使能失败")
            else:
                # 检查电机状态
                status = arm.get_status(motor_id)
                if status and status.get('fault_status') != '正常':
                    fault_motors.append((motor_id, status.get('fault_status')))
                    print(f"电机{motor_id}: 存在故障 - {status.get('fault_status')}")
                else:
                    print(f"电机{motor_id}: 使能成功 ✓")

        except CANTimeoutError as e:
            failed_motors.append(motor_id)
            print(f"电机{motor_id}: 通信超时")
        except MotorFaultError as e:
            fault_motors.append((motor_id, e.fault_description))
            print(f"电机{motor_id}: 故障 - {e.fault_description}")

    return {
        'success': len(failed_motors) == 0 and len(fault_motors) == 0,
        'failed': failed_motors,
        'faults': fault_motors
    }

# 使用
with Arm('can0', side='right') as arm:
    result = enable_motors_with_check(arm)
    if not result['success']:
        print(f"警告: {len(result['failed'])}个电机使能失败, {len(result['faults'])}个电机有故障")

场景3:参数限制检查

from openarmx_arm_driver import Arm
from openarmx_arm_driver.exceptions import (
    PositionLimitError,
    VelocityLimitError,
    TorqueLimitError,
    KpLimitError,
    KdLimitError
)

def safe_move_joint(arm, motor_id, position, kp=10.0, kd=1.0):
    """安全移动关节,自动处理限制异常"""
    try:
        result = arm.move_joint_mit(
            motor_id=motor_id,
            position=position,
            kp=kp,
            kd=kd
        )
        return result == 0

    except PositionLimitError as e:
        print(f"位置超限: {e.position:.3f} rad")
        print(f"允许范围: [{e.min_limit:.3f}, {e.max_limit:.3f}] rad")
        # 自动裁剪到安全范围
        safe_pos = max(e.min_limit, min(e.max_limit, position))
        print(f"自动调整到: {safe_pos:.3f} rad")
        return arm.move_joint_mit(motor_id, position=safe_pos, kp=kp, kd=kd) == 0

    except KpLimitError as e:
        print(f"KP增益超限: {e.kp:.3f}")
        print(f"允许范围: [{e.min_limit:.3f}, {e.max_limit:.3f}]")
        safe_kp = max(e.min_limit, min(e.max_limit, kp))
        return arm.move_joint_mit(motor_id, position=position, kp=safe_kp, kd=kd) == 0

    except KdLimitError as e:
        print(f"KD增益超限: {e.kd:.3f}")
        safe_kd = max(e.min_limit, min(e.max_limit, kd))
        return arm.move_joint_mit(motor_id, position=position, kp=kp, kd=safe_kd) == 0

# 使用
with Arm('can0', side='right') as arm:
    arm.enable_all()
    arm.set_mode('mit')

    # 即使参数超限也能安全执行
    safe_move_joint(arm, motor_id=5, position=100.0, kp=10000.0)  # 会自动裁剪

场景4:双臂协调控制异常处理

from openarmx_arm_driver import Robot
from openarmx_arm_driver.exceptions import (
    CANInitializationError,
    CANTimeoutError,
    OpenArmXError
)

def coordinated_move(robot, left_positions, right_positions, kp=10.0, kd=1.0):
    """双臂协调运动,带异常恢复"""
    try:
        robot.move_joints_mit(
            left_positions=left_positions,
            right_positions=right_positions,
            kp=kp,
            kd=kd
        )
        return True

    except CANTimeoutError as e:
        print(f"通信超时: {e}")
        # 尝试单独控制未超时的臂
        print("尝试单独控制...")
        try:
            if 'left' in str(e).lower():
                robot.move_joints_mit(right_positions=right_positions, kp=kp, kd=kd)
                print("右臂控制成功,左臂通信异常")
            else:
                robot.move_joints_mit(left_positions=left_positions, kp=kp, kd=kd)
                print("左臂控制成功,右臂通信异常")
        except Exception:
            print("双臂均无法控制")
        return False

    except OpenArmXError as e:
        print(f"控制异常: {e}")
        # 紧急停止
        try:
            robot.disable_all()
            print("已紧急停止所有电机")
        except Exception:
            pass
        return False

# 使用
try:
    robot = Robot('can0', 'can1')
    robot.enable_all()
    robot.set_mode_all('mit')

    coordinated_move(
        robot,
        left_positions=[0.1, 0.2, 0.3, 0.4, 0, 0, 0, 0],
        right_positions=[0.1, 0.2, 0.3, 0.4, 0, 0, 0, 0]
    )

except CANInitializationError as e:
    print(f"机器人初始化失败: {e}")
finally:
    if 'robot' in locals():
        robot.shutdown()

场景5:异常日志记录

import logging
from datetime import datetime
from openarmx_arm_driver import Arm
from openarmx_arm_driver.exceptions import OpenArmXError

# 配置日志
logging.basicConfig(
    filename=f'openarmx_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log',
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

def logged_operation(arm, operation_name, func, *args, **kwargs):
    """带日志的操作执行器"""
    logger.info(f"开始执行: {operation_name}")
    try:
        result = func(*args, **kwargs)
        logger.info(f"执行成功: {operation_name}")
        return result
    except OpenArmXError as e:
        logger.error(f"执行失败: {operation_name} - {type(e).__name__}: {e}")
        raise
    except Exception as e:
        logger.exception(f"未知错误: {operation_name}")
        raise

# 使用
with Arm('can0', side='right') as arm:
    logged_operation(arm, "使能所有电机", arm.enable_all)
    logged_operation(arm, "设置MIT模式", arm.set_mode, 'mit')
    logged_operation(arm, "移动电机5", arm.move_joint_mit, 5, position=1.0, kp=10.0, kd=1.0)

场景6:重试机制

import time
from functools import wraps
from openarmx_arm_driver.exceptions import CANTimeoutError, OpenArmXError

def retry_on_timeout(max_retries=3, delay=0.5):
    """超时重试装饰器"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            last_exception = None
            for attempt in range(max_retries):
                try:
                    return func(*args, **kwargs)
                except CANTimeoutError as e:
                    last_exception = e
                    print(f"超时重试 ({attempt + 1}/{max_retries}): {e}")
                    time.sleep(delay)
            raise last_exception
        return wrapper
    return decorator

# 使用
@retry_on_timeout(max_retries=3, delay=0.5)
def reliable_move(arm, motor_id, position):
    """可靠的移动操作"""
    return arm.move_joint_mit(motor_id, position=position, kp=10.0, kd=1.0)

with Arm('can0', side='right') as arm:
    arm.enable_all()
    arm.set_mode('mit')

    # 自动重试
    reliable_move(arm, 5, 1.0)

异常速查表

异常类 常见原因 处理建议
CANInitializationError CAN接口不存在/未启用 检查接口配置,使用password参数自动启用
CANTimeoutError 电机断电/线缆故障 检查电源和连接,增加timeout
CANTransmissionError CAN总线错误 检查总线状态,重启接口
MotorNotEnabledError 未调用enable 先调用enable()或enable_all()
MotorFaultError 电机硬件故障 根据fault_code诊断,可能需要重启
MotorTimeoutError 电机响应慢 增加timeout,检查负载
MotorCalibrationError 电机未标定 执行标定流程
InvalidMotorIDError ID超出范围 检查motor_ids配置
InvalidModeError 模式名错误 使用有效模式:mit/csp/pp/speed/current
PositionLimitError 位置超限 裁剪到有效范围
VelocityLimitError 速度超限 降低速度设置
TorqueLimitError 扭矩超限 降低扭矩设置
KpLimitError KP增益超限 使用合理的KP值
KdLimitError KD增益超限 使用合理的KD值
ConnectionLostError 连接断开 重新初始化连接