# EtherCAT 电机接入教程 这份文档只说明新增一种 EtherCAT 电机需要改哪里、怎么写。 ## 1. 增加 vendor 修改 `protos/cmvr/config/motor_config/motor_config.proto`: ```proto enum MotorVendor { MOTOR_VENDOR_UNKNOWN = 0; MOTOR_VENDOR_TI5 = 1; MOTOR_VENDOR_MUJOCO = 2; MOTOR_VENDOR_XXX = 3; } ``` `MOTOR_VENDOR_XXX` 改成真实厂商名,例如 `MOTOR_VENDOR_FOO`。不要复用 `TI5`。 ## 2. 写电机配置 新增配置文件: ```text cmvr-es/config/devices/motor/ethercat_motors.pb.txt ``` 示例: ```proto motor { id: "ethercat_motors" motor_groups { id: "right_arm_ethercat" bus_type: MOTOR_BUS_ETHERCAT vendor: MOTOR_VENDOR_XXX protocol: MOTOR_PROTOCOL_ETHERCAT_CIA402 tool_frame: "R_FINGER_TIP" ethercat { master_id: "eth0" cycle_us: 1000 slaves { motor_id: 1 slave_index: 0 vendor_id: 0x00000000 product_code: 0x00000000 } slaves { motor_id: 2 slave_index: 1 vendor_id: 0x00000000 product_code: 0x00000000 } slaves { motor_id: 3 slave_index: 2 vendor_id: 0x00000000 product_code: 0x00000000 } slaves { motor_id: 4 slave_index: 3 vendor_id: 0x00000000 product_code: 0x00000000 } slaves { motor_id: 5 slave_index: 4 vendor_id: 0x00000000 product_code: 0x00000000 } slaves { motor_id: 6 slave_index: 5 vendor_id: 0x00000000 product_code: 0x00000000 } slaves { motor_id: 7 slave_index: 6 vendor_id: 0x00000000 product_code: 0x00000000 } } joint_limits { enable: true source: JOINT_LIMIT_SOURCE_CUSTOM joints { joint_name: "R_SHOULDER_P" q_lb: -3.14 q_ub: 3.14 qd: 5.0 qdd: 10.0 } joints { joint_name: "R_SHOULDER_R" q_lb: -0.78 q_ub: 1.57 qd: 5.0 qdd: 10.0 } joints { joint_name: "R_SHOULDER_Y" q_lb: -3.14 q_ub: 3.14 qd: 5.0 qdd: 10.0 } joints { joint_name: "R_ELBOW_R" q_lb: 0 q_ub: 2.05 qd: 5.0 qdd: 10.0 } joints { joint_name: "R_WRIST_P" q_lb: -3.14 q_ub: 3.14 qd: 5.0 qdd: 10.0 } joints { joint_name: "R_WRIST_Y" q_lb: -0.78 q_ub: 0.78 qd: 5.0 qdd: 10.0 } joints { joint_name: "R_WRIST_R" q_lb: -0.57 q_ub: 1.57 qd: 5.0 qdd: 10.0 } } motors { motors { id: 1 joint_name: "R_SHOULDER_P" } motors { id: 2 joint_name: "R_SHOULDER_R" } motors { id: 3 joint_name: "R_SHOULDER_Y" } motors { id: 4 joint_name: "R_ELBOW_R" } motors { id: 5 joint_name: "R_WRIST_P" } motors { id: 6 joint_name: "R_WRIST_Y" } motors { id: 7 joint_name: "R_WRIST_R" } } } } ``` `motors.motors.id` 是系统内的电机逻辑 id。`ethercat.slaves.motor_id` 必须和它对应。 `joint_limits` 按 `joint_name` 读取,和 CAN、MuJoCo 电机保持同一个风格。 ## 3. 注册设备 修改: ```text cmvr-es/config/manager/device_manager.pb.txt ``` 加入: ```proto devices { id: "ethercat_motors" type: MOTOR_SYSTEM enable: true config_file: "devices/motor/ethercat_motors.pb.txt" } ``` ## 4. 机械臂使用 EtherCAT group 修改机械臂配置,例如: ```text cmvr-es/config/devices/arm/arm.pb.txt ``` 把 motor backend 改成: ```proto motor { motor_system_id: "ethercat_motors" motor_group_ids: "right_arm_ethercat" joint_names: "R_SHOULDER_P" joint_names: "R_SHOULDER_R" joint_names: "R_SHOULDER_Y" joint_names: "R_ELBOW_R" joint_names: "R_WRIST_P" joint_names: "R_WRIST_Y" joint_names: "R_WRIST_R" } ``` ## 5. 实现 bus runtime EtherCAT 总线资源放在: ```text cmvr-es/devices/motor/bus_runtime/ethercat/include/ethercat_motor_bus_runtime.h cmvr-es/devices/motor/bus_runtime/ethercat/src/ethercat_motor_bus_runtime.cpp ``` `EthercatMotorBusRuntime` 负责: ```text 读取 ethercat 配置 初始化 EtherCAT master 扫描/校验 slave_index、vendor_id、product_code 启动 cyclic loop 保存 command/feedback buffer 停止 cyclic loop ``` bus runtime 不创建具体电机,也不关心厂商;它只保存总线连接、线程和数据缓存。 ## 6. 增加具体电机 driver 新增目录: ```text cmvr-es/devices/motor/drivers/xxx_ethercat/ CMakeLists.txt include/xxx_ethercat_motor.h include/xxx_ethercat_motor_protocol.h src/xxx_ethercat_motor.cpp src/xxx_ethercat_motor_protocol.cpp ``` `XxxEthercatMotor` 继承 `AbstractMotor`。 `XxxEthercatMotorProtocol` 继承 `MotorProtocolInterface`,把 `setTarget`、`setQd`、`getQ` 等接口转换成 EtherCAT command/feedback。 ## 7. 在 MotorManager 里创建 EtherCAT 电机 修改: ```text cmvr-es/devices/motor/manager/src/motor_manager.cpp ``` 在 `MotorManager::createEthercatMotors_()` 里按 `vendor + protocol` 创建具体电机: ```cpp auto ethercat_bus_runtime = std::dynamic_pointer_cast(bus_runtime); if (group_cfg.vendor() == config::MOTOR_VENDOR_XXX && group_cfg.protocol() == config::MOTOR_PROTOCOL_ETHERCAT_CIA402) { auto protocol = std::make_shared(ethercat_bus_runtime); std::vector> motors; motors.reserve(motor_cfgs.size()); for (const auto& cfg : motor_cfgs) { auto motor = std::make_shared(cfg); motor->setProtocol(protocol); if (!motor->init()) { return {}; } motors.push_back(std::move(motor)); } return motors; } ``` ## 8. 加入 CMake 修改: ```text cmvr-es/devices/motor/manager/CMakeLists.txt ``` 给 `motor_manager` 链接新增的具体 EtherCAT 电机 target。 修改: ```text cmvr-es/devices/motor/CMakeLists.txt ``` 加入: ```cmake add_subdirectory(drivers/xxx_ethercat) ``` ## 9. 验证配置 先验证 proto: ```bash ./output/bin/protoc \ --encode=cmvr.config.MotorRootConfig \ -I protos \ protos/cmvr/config/motor_config/motor_config.proto \ < cmvr-es/config/devices/motor/ethercat_motors.pb.txt \ > /tmp/ethercat_motors.pb.bin ``` 再编译: ```bash cmake --build cmake-build-debug --target mujoco_manual_ui_test ``` 真机联调时先只验证初始化日志:master 打开、slave 数量、vendor/product 校验、cyclic loop 启动、每个 motor 注册成功。然后再下发运动命令。