本文面向初次接触 roboto_motors 的开发者,目标是在 10 分钟内完成环境验证、编译安装,并运行第一段能实际控制电机的代码。阅读本文前,建议先浏览 项目概览与适用场景 以确认本项目是否匹配你的硬件组合。整体流程遵循「环境检查 → 编译安装 → 最小可运行示例 → 验证反馈」四步闭环,确保每一步都有明确的判断标准。
Sources: README_CN.md
前置条件检查
在编译代码前,请确认以下两项硬件与软件条件已经满足。若 CAN 接口尚未配置或依赖包缺失,请先跳转到 CAN总线接口配置 和 编译依赖与安装 完成准备。
| 检查项 | 最低要求 | 验证命令 |
|---|---|---|
| 操作系统 | Linux (内核支持 SocketCAN) | uname -r |
| CAN 接口 | 已启用 can0 并匹配电机波特率 |
ip link show can0 |
| 依赖库 | spdlog, fmt, Boost, Eigen3, pybind11 |
apt list --installed | grep spdlog |
| 编译工具 | cmake ≥ 3.12, colcon (ROS 2 方式) 或 g++ ≥ 9 |
cmake --version |
本项目通过抽象接口屏蔽底层总线差异,目前生产环境仅支持 SocketCAN 后端。所有电机驱动共享同一套 CAN 抽象层,因此无论你使用达妙 DM、EVO 还是 LeadRobot,上层初始化与调用范式完全一致。
Sources: src/protocol/can_iso.hpp, src/protocol/can/socket_can.hpp
编译与安装
仓库提供两条编译路径:ROS 2 colcon 工作流(推荐,自动处理 ament 依赖与 Python 模块安装路径)和独立 CMake 工作流(适合无 ROS 2 环境的服务器或嵌入式场景)。
方式一:ROS 2 工作流(推荐)
将本仓库放入 ROS 2 工作空间的 src 目录后,执行标准 colcon 构建命令。ament_cmake 会自动识别 package.xml 中的依赖声明,并将静态库、头文件和 Python 扩展模块安装到对应的目标目录。
# 假设工作空间为 ~/roboto_ws
cd ~/roboto_ws
colcon build --packages-select roboto_motors --cmake-args -DCMAKE_BUILD_TYPE=Release
source install/setup.bash
构建成功后,Python 模块 motors_py 可通过 from motors_py import MotorDriver 直接导入,无需手动设置 PYTHONPATH。
Sources: CMakeLists.txt, package.xml
方式二:独立 CMake 工作流
若你仅在裸机 Linux 上使用 C++ SDK,或需要手动控制安装前缀,可直接使用 CMake:
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)
sudo make install
顶层 CMakeLists.txt 中声明了 C++17 标准、-O3 优化、ccache 加速以及 POSITION_INDEPENDENT_CODE,确保静态库既能被可执行文件链接,也能被 Python 扩展安全加载。
Sources: CMakeLists.txt
电机型号速查
通过工厂方法创建实例时,motor_model 参数为整数枚举值。下表列出当前已适配的全部型号及其对应数值,避免你在初始化时因型号错误导致工厂抛出 runtime_error。
| 品牌 | 型号 | motor_type |
motor_model 值 |
|---|---|---|---|
| DM(达妙) | DM4340P-48V | "DM" |
0 |
| DM(达妙) | DM10010L-48V | "DM" |
1 |
| EVO | EVO431040 | "EVO" |
0 |
| EVO | EVO811825 | "EVO" |
1 |
| EVO | EVO811832 | "EVO" |
2 |
| LeadRobot | LRO-5550 | "LRO" |
0 |
| LeadRobot | LRO-6562 | "LRO" |
1 |
| LeadRobot | LRO-8462 | "LRO" |
2 |
| LeadRobot | LRO-10062 | "LRO" |
3 |
工厂方法 MotorDriver::create_motor 的完整签名为:(motor_id, interface_type, interface, motor_type, motor_model, master_id_offset=0, motor_zero_offset=0.0)。其中 interface_type 当前固定填 "can",interface 填实际网卡名(如 "can0")。master_id_offset 仅对 DM 电机有意义,用于区分主从 ID;motor_zero_offset 以弧度为单位,用于统一多关节坐标系。
Sources: src/motor_driver.cpp, src/drivers/dm/dm_motor_driver.hpp, src/drivers/evo/evo_motor_driver.hpp, src/drivers/lro/lro_motor_driver.hpp
运行第一个程序
以下给出两段最小可运行示例:Python 版适合算法验证与快速调试,C++ 版适合部署到实时控制循环。两段代码均执行「创建实例 → 初始化使能 → MIT 阻抗控制 → 读取反馈 → 安全失能」的完整生命周期。
Python 示例
from motors_py import MotorDriver, MotorControlMode
import time
# 1. 创建电机实例:以 DM4340P-48V 为例,CAN ID 为 0x01
motor = MotorDriver.create_motor(
motor_id=0x01,
interface_type="can",
interface="can0",
motor_type="DM",
motor_model=0
)
# 2. 初始化并使能电机
motor.init_motor()
# 3. 切换到 MIT 模式并发送控制指令
motor.set_motor_control_mode(MotorControlMode.MIT)
motor.motor_mit_cmd(0.0, 0.0, 10.0, 1.0, 0.0)
# 4. 读取实时反馈
time.sleep(0.1)
motor.refresh_motor_status()
print(f"pos={motor.get_motor_pos():.3f} rad, "
f"spd={motor.get_motor_spd():.3f} rad/s, "
f"temp={motor.get_motor_temperature():.1f} °C")
# 5. 安全关闭
motor.deinit_motor()
C++ 示例
#include "motor_driver.hpp"
#include <iostream>
#include <memory>
#include <unistd.h>
int main() {
// 1. 创建电机实例
auto motor = MotorDriver::create_motor(
0x01, "can", "can0", "DM", 0);
// 2. 初始化并使能
motor->init_motor();
// 3. MIT 阻抗控制
motor->set_motor_control_mode(MotorDriver::MIT);
motor->motor_mit_cmd(0.0f, 0.0f, 10.0f, 1.0f, 0.0f);
// 4. 读取反馈
usleep(100000);
motor->refresh_motor_status();
std::cout << "pos=" << motor->get_motor_pos()
<< " spd=" << motor->get_motor_spd()
<< " temp=" << motor->get_motor_temperature() << "\n";
// 5. 安全关闭
motor->deinit_motor();
return 0;
}
两段代码的共同要点:
init_motor()是进入可控状态的唯一入口,它会发送使能帧并配置内部状态机。refresh_motor_status()主动向电机查询最新状态;若省略此调用,get_motor_pos()等 getter 返回的是上一次异步回调缓存的值。deinit_motor()必须调用,否则电机可能保持在带电锁定状态,造成安全隐患。
Sources: src/pybind_module.cpp, include/motor_driver.hpp
控制模式速查
roboto_motors 抽象基类定义了四种统一控制模式。不同品牌电机对模式的支持存在差异,下表给出快速对照,帮助你在首次上电时选择正确的控制策略。
| 模式枚举 | 数值 | 适用品牌 | 调用方法 | 典型场景 |
|---|---|---|---|---|
NONE |
0 |
全部 | — | 初始状态,未使能 |
MIT |
1 |
DM、EVO、LRO | motor_mit_cmd(pos, vel, kp, kd, torque) |
阻抗控制、腿足机器人 |
POS |
2 |
DM、LRO | motor_pos_cmd(pos, spd, ignore_limit) |
关节定位、机械臂 |
SPD |
3 |
DM、LRO | motor_spd_cmd(spd) |
轮式驱动、传送带 |
切换模式前务必调用 set_motor_control_mode(mode);部分电机(如 DM)在收到与当前模式不匹配的控制帧时会静默丢弃或报协议错误。MIT 模式下的五个参数物理意义为:目标位置(rad)、目标速度(rad/s)、位置刚度 Kp、速度阻尼 Kd、前馈力矩(Nm),其数学原理将在 MIT阻抗控制原理与实现 中详细推导。
Sources: include/motor_driver.hpp
验证通信与常见排查
完成首次编译并运行示例后,可通过以下清单验证通信链路是否真正打通。
| 现象 | 期望结果 | 若不符合,检查项 |
|---|---|---|
init_motor() 返回值 |
非零(成功)或布尔真 | CAN 接口是否 up;波特率是否 1M;电机供电是否正常 |
get_response_count() |
持续递增 | 电机 ID 是否与 motor_id 一致;总线终端电阻 120Ω 是否接入 |
get_motor_pos() |
数值合理,非固定死值 | 是否调用了 refresh_motor_status();回调是否被正确注册 |
deinit_motor() 后电机 |
可手动自由转动 | 指令是否真正发送到总线;电机是否进入刹车状态 |
若 create_motor 抛出 "Motor type not supported" 异常,请核对 motor_type 字符串大小写(必须全大写 "DM" / "EVO" / "LRO")以及 motor_model 数值是否在枚举范围内。
Sources: src/motor_driver.cpp
下一步学习路径
完成本文的「最小闭环」后,建议按以下顺序继续深入:
- 环境深化 — 若你需要配置多路 CAN 或 CAN-FD,阅读 CAN总线接口配置;若依赖安装遇到问题,阅读 编译依赖与安装。
- SDK 深入 — 根据你的主力语言选择 Python SDK快速上手 或 C++ SDK快速上手,其中包含批量多电机控制、实时循环模板和线程安全注意事项。
- 架构理解 — 当你需要扩展新品牌电机或自定义协议时,架构总览与模块划分 和 MotorDriver抽象基类与接口设计 将帮助你定位修改点。
下图汇总了从「首次克隆仓库」到「稳定运行控制循环」的完整决策流程:
flowchart TD
A[克隆仓库] --> B{CAN接口已配置?}
B -->|否| C[CAN总线接口配置]
B -->|是| D{依赖已安装?}
D -->|否| E[编译依赖与安装]
D -->|是| F[colcon build / cmake]
F --> G[运行Python/C++最小示例]
G --> H{反馈正常?}
H -->|否| I[检查ID/波特率/终端电阻]
H -->|是| J[进入Python/C++ SDK深入指南]
C --> B
E --> D
I --> G