🤖 roboto_origin_03 Wiki
首页 / 电机子模块 / C++ SDK快速上手

本文面向已具备 C++17 基础和 CMake 项目经验的开发者,提供从工程集成到完成首条电机控制指令的完整路径。阅读完本页,你将能够在自己的 C++ 项目中实例化电机、切换控制模式、发送指令并读取实时反馈,而无需深入协议或驱动实现细节。

Sources: motor_driver.hpp, motor_driver.cpp

前置条件

在开始编码前,请确保已完成以下两步:

本页示例假设你使用标准 CAN 接口 can0,并链接已安装的 roboto_motors 库。

Sources: CMakeLists.txt, roboto_motorsConfig.cmake

工程集成

roboto_motors 对外暴露单一公共头文件 motor_driver.hpp,并通过 CMake 导出 roboto_motors::roboto_motors 接口目标。在你的 CMakeLists.txt 中只需三行即可集成:

find_package(roboto_motors REQUIRED)
add_executable(my_robot_app main.cpp)
target_link_libraries(my_robot_app PRIVATE roboto_motors::roboto_motors)

该接口目标会自动拉取头文件路径、静态库(motorsdm_motorsevo_motorslro_motorsmotors_canmotors_canfd)以及第三方依赖(fmtspdlogEigen3)。若你是在 ROS 2 工作空间内通过 colcon build 编译,则通过 ament_target_dependencies 或直接用 target_link_libraries 链接即可,因为 ament_cmake 会同步导出库和包含目录。

Sources: CMakeLists.txt, roboto_motorsConfig.cmake

核心工作流

使用 C++ SDK 的典型流程遵循“创建 → 初始化 → 控制循环 → 反初始化”四段式结构。下图展示了单电机标准控制循环的数据流与调用顺序:

flowchart TD
    A[1. create_motor<br/>工厂方法实例化] --> B[2. init_motor<br/>解锁并设置模式]
    B --> C{控制循环<br/>1kHz 典型频率}
    C --> D[set_motor_control_mode<br/>MIT / POS / SPD]
    D --> E[发送指令<br/>motor_mit_cmd / motor_pos_cmd / motor_spd_cmd]
    E --> F[refresh_motor_status<br/>请求最新反馈]
    F --> G[读取状态<br/>get_motor_pos / get_motor_spd / get_motor_current]
    G --> C
    C --> H[3. deinit_motor<br/>失能并释放资源]
    H --> I[电机实例析构<br/>自动注销 CAN 回调]

注意 create_motor 返回 std::shared_ptr<MotorDriver>,因此建议用 auto 或智能指针接收,避免所有权管理错误。进入控制循环前必须先调用 init_motor(),它会执行电机解锁、模式设置、状态刷新并返回错误码;退出前必须调用 deinit_motor(),否则电机可能停留在带电状态。

Sources: motor_driver.hpp, motor_driver.hpp, dm_motor_driver.cpp

创建电机实例

库内部采用工厂模式屏蔽不同品牌电机的构造差异。你只需提供统一参数,即可在运行时生成 DmMotorDriverEvoMotorDriverLroMotorDriver 实例。

auto motor = MotorDriver::create_motor(
    0x01,           // motor_id: CAN 节点 ID
    "can",          // interface_type: "can" | "canfd" | "ethercanfd"
    "can0",         // interface: SocketCAN 接口名
    "DM",           // motor_type: "DM" | "EVO" | "LRO"
    0,              // motor_model: 型号枚举值
    0,              // master_id_offset: 主站偏移(仅 DM 常用)
    0.0             // motor_zero_offset: 零位偏移,单位 rad
);

下表汇总了当前支持电机的型号枚举与常用参数。motor_model 传入的是对应枚举的整数值。

品牌 motor_type 型号枚举 默认值索引 支持接口 可用控制模式
DM(达妙) "DM" DM4340P_48V = 0, DM10010L_48V = 1 0 CAN / CAN-FD MIT, 位置, 速度
EVO "EVO" EVO431040 = 0, EVO811825 = 1, EVO811832 = 2 0 CAN / CAN-FD MIT
LeadRobot "LRO" LRO_5550 = 0, LRO_6562 = 1, LRO_8462 = 2, LRO_10062 = 3 0 CAN-FD MIT, 位置, 速度, 电流

工厂方法会根据 motor_type 字符串分发到具体实现类,并在构造函数中自动注册 SocketCAN 接收回调。若传入不支持的类型,将抛出 std::runtime_error

Sources: motor_driver.cpp, dm_motor_driver.hpp, evo_motor_driver.hpp, lro_motor_driver.hpp

电机生命周期管理

电机实例创建后处于“未激活”状态,必须通过生命周期方法切换硬件运行状态。

// 初始化:解锁 → 设置默认模式 → 上锁使能 → 读取状态
uint8_t err = motor->init_motor();
if (err != 0) {
    // 根据品牌错误码判断故障类型
    std::cerr << "Init failed, error code: " << (int)err << std::endl;
}

// ... 控制循环 ...

// 反初始化:发送失能命令,电机进入自由状态
motor->deinit_motor();

init_motor() 的返回值由各品牌驱动自行定义。例如 DM 电机返回 DMError 枚举,若返回 0x01DM_UP)表示正常上线,返回 0x0DDM_LOST_CONN)则表示通信未建立。建议在生产代码中对返回值做分支处理。deinit_motor() 无返回值,但会触发失能帧,释放电机力矩。

Sources: motor_driver.hpp, dm_motor_driver.hpp

控制模式与指令发送

MotorDriver 抽象基类定义了三种控制模式,对应 MotorControlMode_e 枚举:MIT = 1POS = 2SPD = 3。在发送任何运动指令前,必须先调用 set_motor_control_mode() 显式切换模式,否则底层驱动可能忽略指令或按旧模式解析数据。

MIT 阻抗控制

MIT 模式同时指定目标位置、速度、刚度、阻尼和前馈力矩,适用于腿式机器人、机械臂力控等场景。

motor->set_motor_control_mode(MotorDriver::MIT);
motor->motor_mit_cmd(
    0.0f,   // f_p: 目标位置 (rad)
    0.0f,   // f_v: 目标速度 (rad/s)
    10.0f,  // f_kp: 位置刚度 / 比例增益
    1.0f,   // f_kd: 速度阻尼 / 微分增益
    0.0f    // f_t: 前馈力矩 (Nm)
);
参数 类型 单位 说明
f_p float rad 目标关节位置
f_v float rad/s 目标关节速度
f_kp float 外环位置刚度增益,越大越“硬”
f_kd float 外环速度阻尼增益,越大越“粘滞”
f_t float Nm 前馈力矩,用于补偿重力或接触力

位置控制

位置模式让电机以指定速度逼近目标位置,内部由驱动器完成轨迹规划或伺服跟踪。

motor->set_motor_control_mode(MotorDriver::POS);
motor->motor_pos_cmd(1.57f, 3.14f, false);  // 目标 90°,最大速度 ~180°/s
参数 类型 单位 说明
pos float rad 目标位置
spd float rad/s 运动速度上限
ignore_limit bool 是否忽略内部位置限位,默认 false

速度控制

速度模式使电机持续旋转,适用于轮子、传送带等无需位置闭环的场景。

motor->set_motor_control_mode(MotorDriver::SPD);
motor->motor_spd_cmd(3.14f);  // ~180°/s

此外,基类还提供指针重载版本的 motor_mit_cmd(float*, float*, float*, float*, float*),供批量多电机控制或推理引擎直接传递连续内存块。若你仅需控制单电机,使用标量版本即可。

Sources: motor_driver.hpp, motor_driver.hpp

状态反馈读取

控制循环中,应在发送指令后调用 refresh_motor_status() 触发一次状态查询,随后通过原子 getter 读取最新数据。所有反馈变量均为 std::atomic 类型,可直接在多线程环境中安全读取。

motor->refresh_motor_status();
float pos = motor->get_motor_pos();           // rad
float spd = motor->get_motor_spd();           // rad/s
float cur = motor->get_motor_current();       // A
float temp = motor->get_motor_temperature();  // °C
uint8_t err = motor->get_error_id();          // 品牌相关错误码
方法 返回值 单位 数据源说明
get_motor_pos() float rad 编码器反馈经齿轮比和零位偏移换算后的关节位置
get_motor_spd() float rad/s 关节速度,部分电机由驱动器微分输出
get_motor_current() float A 相电流或母线电流(品牌定义不同)
get_motor_temperature() float °C 绕组或功率板温度
get_error_id() uint8_t 实时错误标志位,非零时需关注
get_response_count() int 累计接收到的 CAN 回复帧数,可用于诊断丢包

refresh_motor_status() 的具体实现因品牌而异:部分电机发送显式查询帧,部分则依赖周期性广播帧更新内部缓存。在高频控制循环中(如 1kHz),应确认目标电机的总线负载能力,避免过度查询。

Sources: motor_driver.hpp

错误处理与状态恢复

get_error_id() 返回非零值,或 init_motor() 异常返回时,应优先排查硬件过压、过流、过温或通信断开等根本原因。排除故障后,调用 clear_motor_error() 向电机 MCU 发送错误清除指令,使其从保护态恢复到待机态。

if (motor->get_error_id() != 0) {
    motor->clear_motor_error();
    Timer::sleep_for(10);  // 等待电机处理,单位 ms
    // 重新初始化
    motor->init_motor();
}

注意:clear_motor_error() 只重置软件故障标志,无法修复物理损坏。若错误反复出现,应检查供电、接线及机械负载。utils.hpp 中提供的 Timer::sleep_for() 可作为跨平台延时工具使用。

Sources: motor_driver.hpp, utils.hpp

完整最小示例

下面是一个从实例化到控制再到安全关闭的完整 main.cpp,可直接作为模板复制到你的工程中:

#include "motor_driver.hpp"
#include <iostream>
#include <thread>

int main() {
    try {
        // 1. 创建 DM 电机实例
        auto motor = MotorDriver::create_motor(
            0x01, "can", "can0", "DM", 0, 0, 0.0);

        // 2. 初始化并使能
        uint8_t err = motor->init_motor();
        if (err != 0) {
            std::cerr << "Motor init error: " << (int)err << std::endl;
            return 1;
        }

        // 3. MIT 控制循环(示例:1kHz,持续 2 秒)
        motor->set_motor_control_mode(MotorDriver::MIT);
        auto start = std::chrono::steady_clock::now();
        while (true) {
            auto now = std::chrono::steady_clock::now();
            auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start).count();
            if (elapsed > 2000) break;

            // 目标:回到零位,中等刚度
            motor->motor_mit_cmd(0.0f, 0.0f, 10.0f, 1.0f, 0.0f);
            
            // 读取反馈
            motor->refresh_motor_status();
            std::cout << "pos: " << motor->get_motor_pos()
                      << " spd: " << motor->get_motor_spd()
                      << " err: " << (int)motor->get_error_id()
                      << std::endl;

            std::this_thread::sleep_for(std::chrono::milliseconds(1));
        }

        // 4. 安全关闭
        motor->deinit_motor();
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
        return 1;
    }
    return 0;
}

Sources: README_CN.md, motor_driver.hpp

下一步

掌握上述基础后,你可以根据实际电机品牌和控制需求,深入阅读以下页面: