Commit ee4b6a90 authored by Wenxi XU's avatar Wenxi XU
Browse files

添加基本电机驱动

parent 22ec970b
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(dji_m3508_demo)

target_sources(app PRIVATE src/main.c) 
 No newline at end of file
+153 −0
Original line number Diff line number Diff line
# DJI M3508电机控制示例 - 简化版

这个示例演示了DJI M3508电机的基本控制功能,通过自动循环执行简单的测试序列。

## 功能特性

- **自动循环测试**: 无需人工干预,自动执行测试序列
- **双环控制**: 实现角度环->速度环的串联控制
- **实时监控**: 监控电机状态包括温度、电流、在线状态
- **简单可靠**: 去除复杂交互,专注于电机控制演示
- **安全保护**: 包含温度、电流过载保护

## 测试序列

程序会自动循环执行以下测试序列:

1. **设置零点1**: 将当前位置设为零点
2. **速度测试**: 以100 RPM恒定速度转动10秒
3. **停止**: 停止电机运行2秒
4. **设置零点2**: 重新将当前位置设为零点  
5. **角度测试**: 正转180度
6. **循环**: 回到步骤1继续循环

## 硬件要求

### 必需硬件
- RoboMaster Board C (或兼容的STM32F4开发板)
- DJI M3508电机 + C620电调
- CAN收发器模块
- 24V电源供应

### 连接说明
```
开发板          电调/电机
CAN1_RX (PD0) -> CAN_H (通过收发器)
CAN1_TX (PD1) -> CAN_L (通过收发器)
GND           -> GND
```

### CAN总线配置
- 波特率: 1Mbps
- 电机ID: 1 (0x201)
- 控制帧ID: 0x200

## PID参数配置

### 速度环PID
- Kp: 8.00
- Ki: 0.50  
- Kd: 0.10
- 输出限制: ±16384 (电流值)
- 积分限制: ±5000

### 角度环PID
- Kp: 12.00
- Ki: 0.00 (纯PD控制)
- Kd: 2.00
- 输出限制: ±8000 (速度目标值)

## 构建和运行

```bash
# 进入示例目录
cd samples/dji_m3508_demo

# 构建项目
west build -b robomaster_board_c

# 烧录程序
west flash
```

## 日志输出示例

```
[00:00:00.100,000] <inf> dji_m3508_demo: === DJI M3508电机控制示例 - 简化版 ===
[00:00:00.150,000] <inf> dji_m3508_demo: 测试序列:设置零点 -> 100rpm转10s -> 停下 -> 设置零点 -> 正转180度 -> 循环
[00:00:00.200,000] <inf> dji_m3508_demo: 电机已启用
[00:00:00.250,000] <inf> dji_m3508_demo: === 步骤1: 设置零点1 ===
[00:00:00.800,000] <inf> dji_m3508_demo: === 步骤2: 开始速度测试 (100 RPM, 10秒) ===
[00:00:01.850,000] <inf> dji_m3508_demo: 速度测试 - 目标: 100.0 RPM, 实际: 98.5 RPM, 剩余: 9秒
[00:00:02.850,000] <inf> dji_m3508_demo: 速度测试 - 目标: 100.0 RPM, 实际: 99.8 RPM, 剩余: 8秒
...
[00:00:10.800,000] <inf> dji_m3508_demo: === 步骤3: 停止电机 ===
[00:00:12.800,000] <inf> dji_m3508_demo: === 步骤4: 设置零点2 ===
[00:00:13.300,000] <inf> dji_m3508_demo: === 步骤5: 开始角度测试 (正转180度) ===
[00:00:13.800,000] <inf> dji_m3508_demo: 角度测试 - 目标: 180.0°, 实际: 15.2°, 速度: 245.6 RPM
[00:00:14.300,000] <inf> dji_m3508_demo: 角度测试 - 目标: 180.0°, 实际: 45.8°, 速度: 198.3 RPM
...
[00:00:18.500,000] <inf> dji_m3508_demo: 角度测试完成!实际角度: 179.2°
[00:00:19.500,000] <inf> dji_m3508_demo: === 循环完成,重新开始 ===
```

## 测试预期结果

### 速度控制测试
- 电机应在约1-2秒内达到目标速度100 RPM
- 速度误差应控制在±5 RPM以内
- 转动应平稳无明显振动

### 角度控制测试
- 电机应在5-8秒内转动到180度位置
- 角度误差应控制在±5度以内
- 到达目标位置后应稳定保持

## 安全注意事项

⚠️ **重要提醒**:
- 确保电机安装牢固,避免高速旋转时脱落
- 检查CAN总线连接是否正确
- 确保24V电源供应充足
- 首次运行时建议降低PID参数进行测试
- 出现异常时立即断电

## 故障排除

### 电机不响应
1. 检查CAN总线连接和波特率设置
2. 确认电机ID配置正确 (应为1,CAN ID 0x201)
3. 检查24V电源供应是否充足
4. 查看日志中的错误信息

### 速度控制不稳定
1. 适当增加速度环的Kp值
2. 检查机械负载是否过大
3. 确认电机温度是否正常
4. 检查电源电压是否稳定

### 角度控制超调或振荡
1. 适当减少角度环的Kp值
2. 增加角度环的Kd值以提高阻尼
3. 检查机械间隙和刚性

### 程序运行异常
1. 检查CAN总线是否正常工作
2. 确认电机驱动程序是否正确加载
3. 查看系统日志中的错误信息
4. 重启系统重新初始化

## 扩展功能

可以基于此示例扩展的功能:
- 调整测试参数(速度、角度、时间)
- 添加更多测试步骤
- 记录和分析性能数据
- 多电机协调控制
- 远程控制接口

## 参考资料

- [DJI M3508电机手册](https://www.dji.com/cn/robomaster-s1/info#specs)
- [Zephyr电机驱动API](https://docs.zephyrproject.org/latest/hardware/peripherals/motor.html)
- [CAN总线配置指南](https://docs.zephyrproject.org/latest/connectivity/networking/api/can.html) 
 No newline at end of file
+35 −0
Original line number Diff line number Diff line
// DJI M3508电机示例 - RoboMaster Board C设备树配置

/ {
    speed_pid_m3508: speed_pid_m3508 {
        compatible = "pid,single";
        #controller-cells = <0>;
        k_p = <800>;
        k_i = <50>;
        k_d = <10>;
        out_max = <2>;
    };

    angle_pid_m3508: angle_pid_m3508 {
        compatible = "pid,single";
        #controller-cells = <0>;
        k_p = <1200>;
        k_i = <0>;
        k_d = <200>;
        out_max = <50>;
    };

    m3508_motor: m3508_motor {
        compatible = "dji,motor";
        is_m3508;
        id = <1>;
        rx_id = <0x201>;
        tx_id = <0x200>;
        gear_ratio = "19.20";
        status = "okay";
        can_channel = <&can1>;
        // 应当保持外环在前,内环在后
        controllers = <&angle_pid_m3508 &speed_pid_m3508>;
        capabilities = "angle", "speed";
    };
};
 No newline at end of file
+16 −0
Original line number Diff line number Diff line
# DJI M3508电机控制示例配置 - 简化版

# 基础配置
CONFIG_MAIN_THREAD_PRIORITY=7
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048

# 电机驱动支持
CONFIG_MOTOR=y
CONFIG_MOTOR_DJI=y

# CAN总线支持
CONFIG_CAN=y
CONFIG_CAN_INIT_PRIORITY=80

# PID控制器支持
CONFIG_PID=y
 No newline at end of file
+182 −0
Original line number Diff line number Diff line
/*
 * DJI M3508电机控制示例 - 简化版
 * 自动循环:设置零点 -> 100rpm转10s -> 停下 -> 设置零点 -> 正转180度 -> 循环
 *
 * Copyright (c) 2024 ttwards
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/motor.h>
#include <zephyr/logging/log.h>
#include <math.h>

LOG_MODULE_REGISTER(dji_m3508_demo, LOG_LEVEL_INF);

// 电机设备定义
#define M3508_NODE DT_NODELABEL(m3508_motor)
static const struct device *m3508_motor = DEVICE_DT_GET(M3508_NODE);

// 测试序列状态
typedef enum {
	STATE_SET_ZERO_1 = 0, // 设置零点1
	STATE_SPEED_TEST,     // 100rpm转10秒
	STATE_STOP,           // 停下
	STATE_SET_ZERO_2,     // 设置零点2
	STATE_ANGLE_TEST,     // 正转180度
	STATE_MAX
} test_state_t;

static test_state_t current_state = STATE_SET_ZERO_1;
static uint32_t state_start_time = 0;

/**
 * @brief 电机状态监控线程
 */
void motor_monitor_thread(void *arg1, void *arg2, void *arg3)
{
	ARG_UNUSED(arg1);
	ARG_UNUSED(arg2);
	ARG_UNUSED(arg3);

	while (1) {
		k_msleep(500); // 每秒检查一次

		motor_status_t status;
		int ret = motor_get(m3508_motor, &status);

		if (ret != 0) {
			LOG_ERR("读取电机状态失败: %d", ret);
			continue;
		}

		LOG_INF("motor torque: %.2f, speed: %.2f, angle: %.2f", status.torque, status.rpm,
			status.angle);
	}
}

K_THREAD_DEFINE(motor_monitor, 1024, motor_monitor_thread, NULL, NULL, NULL, 5, 0, 0);

/**
 * @brief 执行测试序列
 */
void execute_test_sequence(void)
{
	uint32_t elapsed_time = k_uptime_get_32() - state_start_time;
	motor_status_t status;

	switch (current_state) {
	case STATE_SET_ZERO_1:
		LOG_INF("=== 步骤1: 设置零点1 ===");
		motor_control(m3508_motor, SET_ZERO);
		k_msleep(500); // 等待零点设置完成

		// 切换到下一状态
		current_state = STATE_SPEED_TEST;
		state_start_time = k_uptime_get_32();
		LOG_INF("=== 步骤2: 开始速度测试 (100 RPM, 10秒) ===");
		break;

	case STATE_SPEED_TEST:
		motor_set_speed(m3508_motor, 100.0f);

		// 每秒显示状态
		if (elapsed_time % 1000 < 50) {
			motor_get(m3508_motor, &status);
			LOG_INF("速度测试 - 目标: 100.0 RPM, 实际: %.1f RPM, 剩余: %d秒",
				status.rpm, (10000 - elapsed_time) / 1000);
		}

		// 10秒后停止
		if (elapsed_time >= 10000) {
			current_state = STATE_STOP;
			state_start_time = k_uptime_get_32();
			LOG_INF("=== 步骤3: 停止电机 ===");
		}
		break;

	case STATE_STOP:
		motor_set_torque(m3508_motor, 0.0f);

		// 停止2秒
		if (elapsed_time >= 2000) {
			current_state = STATE_SET_ZERO_2;
			state_start_time = k_uptime_get_32();
			LOG_INF("=== 步骤4: 设置零点2 ===");
		}
		break;

	case STATE_SET_ZERO_2:
		motor_control(m3508_motor, SET_ZERO);
		k_msleep(500); // 等待零点设置完成

		// 切换到角度测试
		current_state = STATE_ANGLE_TEST;
		state_start_time = k_uptime_get_32();
		LOG_INF("=== 步骤5: 开始角度测试 (正转180度) ===");
		break;

	case STATE_ANGLE_TEST:
		motor_set_angle(m3508_motor, 180.0f);

		// 每500ms显示状态
		if (elapsed_time % 500 < 50) {
			motor_get(m3508_motor, &status);
			LOG_INF("角度测试 - 目标: 180.0°, 实际: %.1f°, 速度: %.1f RPM",
				status.angle, status.rpm);
		}

		// 检查是否到达目标角度或超时
		motor_get(m3508_motor, &status);
		if (fabsf(status.angle - 180.0f) < 5.0f || elapsed_time >= 8000) {
			if (fabsf(status.angle - 180.0f) < 5.0f) {
				LOG_INF("角度测试完成!实际角度: %.1f°", status.angle);
			} else {
				LOG_WRN("角度测试超时!当前角度: %.1f°", status.angle);
			}

			// 等待1秒后重新开始循环
			k_msleep(1000);
			current_state = STATE_SET_ZERO_1;
			state_start_time = k_uptime_get_32();
			LOG_INF("=== 循环完成,重新开始 ===");
		}
		break;

	default:
		current_state = STATE_SET_ZERO_1;
		state_start_time = k_uptime_get_32();
		break;
	}
}

/**
 * @brief 主函数
 */
int main(void)
{
	LOG_INF("=== DJI M3508电机控制示例 - 简化版 ===");
	LOG_INF("测试序列:设置零点 -> 100rpm转10s -> 停下 -> 设置零点 -> 正转180度 -> 循环");

	// 检查电机设备是否就绪
	if (!device_is_ready(m3508_motor)) {
		LOG_ERR("M3508电机设备未就绪");
		return -ENODEV;
	}

	// 启用电机
	motor_control(m3508_motor, ENABLE_MOTOR);
	LOG_INF("电机已启用");

	// 初始化状态
	state_start_time = k_uptime_get_32();

	// 主控制循环
	while (1) {
		k_msleep(50); // 20Hz控制频率
		execute_test_sequence();
	}

	return 0;
}
 No newline at end of file
Loading