Vision board电动滑板移植

详述了一个使用Vision board和蓝牙模块控制的电动滑板车设计,包括机械结构、电机选择与控制、主控板遥控模块的搭建。设计亮点在于自动紧急刹车系统和无线遥控功能,采用N5065无刷电机和HC-05蓝牙模块,实现了安全高效的个人代步和取快递工具。

Vision board电动滑板移植封面
VOR2342025-11-05 09:17:44MIT License
881
Star2
4

PCBA

设计文件

KiCad图标Electric skateboard.zip2.27MB

其他附件

EDA查看器

复制嵌入代码

详细介绍

1. 前言

🛹在2018年我本科在万宇杰老师的带领下和小伙伴们一起立了校级重点科研立项——《DIY电动滑板》,非常怀恋那时候的干劲,闲暇时分傍晚和枚金江一起玩滑板飞驰绿道,白天和刘姣一起画工程图,晚上和叶俊吉万金华他们做舞蹈机器人,时光就是这样一点点缓缓流淌过😜😜。

🛹不知不觉中双元班的同窗们都各自高飞,而我还在艰难的求学中(2025年10月被OE、11月CEP拒稿)。恰巧也让我有了新的想法,之前的滑板是用Arduino Uno加拓展板设计的硬件,现在运输小船做实验就由我的滑板小小的身体承受着,每次都要走公里才能到达海边。滑板载着小船漫步过海滨小路,载着我跑过乡村小路,载着打印机溜过校园大道弯儿,值得高兴的是我的滑板已经累计有200公里左右,也没有出现什么故障,🤣🤣🤣嘻嘻。

🛹接着这样的机会尝试用本次《2025RTT硬件设计大赛》中的Vision board来移植原来的Arduino Uno方案,以此来纪念我的青春创作,保留原先Uno拓展板的电气与硬件。这次比赛首先是绘制类似Arduino Uno原先的端口封装与Vision board之间的拓展板,其次移植蓝牙控制程序和改进主动刹车功能。

screenshot_image.png

2. 设计方案

本设计采用长板滑板版面,选用直流无刷电机,购买绘制特殊规格长板滑板的皮带轮及电机架,以航模电池3S5200MAH电池供电。配以专用直流无刷电机电调。主控电路与遥控电路均以Vision Board为核心,使用蓝牙数据通讯协议进行无线数据透传,采用PWM脉冲宽度调制方式,采用TOF激光距离传感器进行紧急制动判断。以下为个模块设计简介。

2.1 机械结构模块

机械模块主要解决传动系统搭建及电机的安装。车架架设计为高64MM,长248MM7.25英寸的支架。传动系统采用同步轮传动,使用皮带连接。齿轮比是大轮36齿,小轮12齿,用5M*270,11带宽齿的齿轮带连接,传动比为3:1,如图电机支架同步轮及配件。

screenshot_传动部分.png

2.2 电机模块

电机的KV值,决定着电机的转速增加量,KV值越大转速越快。电动滑板在启动时,由于静摩擦的原因,启动的阶段阻力非常大,之后阻力会突然变小。所以,电动滑板在启动之后常常有顿挫感。电机的选择对滑板的安全性的提升显得尤为重要。根据滑板的启动的特性,应选用KV值越小的电机。

2.2.1 电机选择

本设计电机选用N5065外转子无刷电机270KV,其各项参数指标如图左边为无刷电机N5065,右边为C5065

screenshot_电机参数.jpg

首先从电机型号上来说n5065表示电机尺寸为:直径50mm,长度65mm。前面的n和c表示系列号,其中n系列做工和工作效率高于c系,N电机比C电机功率大,发热小,扭力大。N电机比C电机的磁铁长度和定子长度稍微长一些,n系尾部为平面,c系尾部为锥形。详细参数如下: 可以看到,n5065的功率要比c5065大,一个是1820W,一个是1665W。电压比c5065高,重量也比c5065重50g。(注:这里只说了KV值400得,你也可以选择270的) 根据公式:转速=KV值X电压; n5065电机转速=KV (400)x22(v)=8800rpm ; c5065电机转速=KV (400) x20(v)=8000rpm ;

2.2.2 电机控制

单片机的控制信号,可以轻松输出0或1,如果需要控制电机信号就需要PWM脉冲宽度调制,也可参考SG90舵机控制。在本设计中使用KV值为270KV的N5065无刷电机,无刷电机和有刷电机有相似之处,也有转子和定子,只不过和有刷电机的结构相反;有刷电机的转子是线圈绕组,和动力输出轴相连,定子是永磁磁钢;无刷电机的转子是永磁磁钢,连同外壳一起和输出轴相连,定子是绕组线圈,去掉了有刷电机用来交替变换电磁场的换向电刷,故称之为无刷电机。依靠改变输入到无刷电机定子线圈上的电流波交变频率和波形,在绕组线圈周围形成一个绕电机几何轴心旋转的磁场,这个磁场驱动转子上的永磁磁钢转动,电机就转起来了,电机的性能和磁钢数量、磁钢磁通强度、电机输入电压大小等因素有关,更与无刷电机的控制性能有很大关系,因为输入的是直流电,电流需要电子调速器将其变成3相交流电,还需要从遥控器接收机那里接收控制信号,控制电机的转速,以满足模型使用需要。相比于传统直流有刷电机相比,无刷电机能量密度高,力矩大,重量轻,性能好等优点。增强了电机的可靠性,但是无刷电机的驱动比有刷电机要复杂得多,需要通过专门的电子驱动器才能正常工作,为降低开发难度的目的,该部分采用了车模用的无刷电机调速器,该调速器可根据输入的PWM信号占空比的大小来控制无刷电机的转速。

2.3 主控板及其遥控模块

本设计选用Vision board作为控制芯片,手机app收发作为上位机遥控。app使用参考

screenshot_image.png

2.3.1 主控芯片选择

Vision Board搭载全球首颗 480 MHz Arm Cortex-M85芯片,拥有Helium和TrustZone技术的加持。SDK包里集成了OpenMV机器视觉例程,配合MicroPython 解释器,使其可以流畅地开发机器视觉应用。Vision Board搭载了全球首款基于 ARM Cortex-M85 架构的瑞萨电子RA8 MCU,6.39 CoreMark/MHz,可以快速而高效地运行机器视觉算法,实现图像处理、等功能。虽然在这个电动滑板大材小用,但是给代码框架和使用安全上提高了不少。🛹👍👍👍

screenshot_image.png

参考资料见:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/tutorial/make-bsp/renesas-ra/%E7%91%9E%E8%90%A8VisionBoard%E5%BC%80%E5%8F%91%E5%AE%9E%E8%B7%B5%E6%8C%87%E5%8D%97

2.3.2 无线通信模块的选取

本设计的无线通信模块选择经典HC-05主从机一体蓝牙模块,该模块能耗低,稳定性强,具有很好的抗干扰能力。选用蓝牙模块以后,可以实现双向通讯,加入显示模块及其他传感器模块后,遥控器可显示电池电量,行驶速度,行驶里程和载重等。为之后功能的增加和改进,提供了便利,同时大量搭载蓝牙的设备可以用于控制滑板,例如开发手机APP控制端,将手机作为遥控端使用。

screenshot_HC05.jpg

本设计的遥控器和主控板上,分别有一块Vision board和一个HC-05蓝牙模块。蓝牙模块设置了自动配对之后,上电之后它们就进入“传输”状态,这时,使用函数串口8编写读取解析程序,便可实现遥控器与主控板之间的数据传输。

2.3.3 自动紧急刹车控制

防追尾系统对于驾驶员来说是一大必要辅助驾驶利器。此次设计的防追尾系统主要利用超声波在空气中的传播速度和关系进行测量。超声波具有指向性强、能量消耗慢且在介质中传播距离较远特点。其实说到超声波,我们就会想到蝙蝠,是的,它的工作原理就是模仿蝙蝠的。先发出一个声音,然后在接收返回的声音,通过发出和返回的时间差来可以计算出距离,就这么简单。所以我们就要有一个机制,发出多长的光信号,回收采集的理论上应该是发出的同时就要采样收集了。

原先的方案是超声波测距,之前实测发现会频繁误触,而且采样时间非常长(两次连续判断1.5m障碍物大约需要0.5s,如果车速在2m/s,会明显刹车来不及,主要是反应时间有点长,发生碰撞危险),这次采用I2c距离TOF传感器,看看效果应该要好一点点😊😊😊

screenshot_image.png

3. 实验步骤

3.1 实验材料

主控板:Vision board开发板,sensor shield拓展板,USB数据线 传感器:TOF传感器,光照传感器 执行器:N5056无刷电机,LED灯,一路高电平触发继电器,有源蜂鸣器 通讯:蓝牙HC05 辅助硬件:滑板,同步带结构,盒子,泡沫,热熔胶,公母线若干 软件:一台安装RT-Thread开发环境的电脑

3.2 根据原理图搭建电路

实验原理图: screenshot_image.png

  • 蓝牙RX接uart8的TX,蓝牙TX接uart8的RX

  • 有源蜂鸣器引脚2

  • 继电器引脚4,然后单独接led灯与外接电源串联

  • ESC定义无刷电机引脚9,电调提供电源

  • TOF测距SDA接A4,SCL接A5

  • 光照检测接A0

3.4 PCB及三维图

screenshot_image.png

整体元器件非常少,就2202.54排母下面连接Vision board,然后6/2*8/10排母在上方连接Arduino Uno拓展板

下面是硬件细节图 拓展板焊接效果 screenshot_a5557127dfe6e68a910915a51f192358.jpg

控制器电源电调都安装在防水盒中 screenshot_d0e43b8c9293c7a0348cc791898ba567.jpg控制盒整体外观

screenshot_5623af0c0a4c6732196d41cc65c79ec0.jpg

3.5 源码分享

配置串口2

screenshot_image.png

参考:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/tutorial/make-bsp/renesas-ra/%E7%91%9E%E8%90%A8VisionBoard%E5%BC%80%E5%8F%91%E5%AE%9E%E8%B7%B5%E6%8C%87%E5%8D%97?id=%e4%b8%80%e3%80%81ra8d1-vision-board%e4%b8%8a%e7%9a%84uart%e5%ae%9e%e8%b7%b5%ef%bc%88%e5%88%98%e5%bb%ba%e5%8d%8eou%ef%bc%89 配置PWM12

screenshot_image.png

参考:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/tutorial/make-bsp/renesas-ra/%E7%91%9E%E8%90%A8VisionBoard%E5%BC%80%E5%8F%91%E5%AE%9E%E8%B7%B5%E6%8C%87%E5%8D%97?id=%e4%b9%9d%e3%80%81ra8d1-vision-board%e4%b8%8a%e7%9a%84pwm%e5%ae%9e%e8%b7%b5%ef%bc%88%e4%b8%81%e6%8c%af%e5%af%8c%ef%bc%89 配置软件I2C

screenshot_image.png

screenshot_image.png

screenshot_image.png

参考:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/tutorial/make-bsp/renesas-ra/%E7%91%9E%E8%90%A8VisionBoard%E5%BC%80%E5%8F%91%E5%AE%9E%E8%B7%B5%E6%8C%87%E5%8D%97?id=%e5%8d%81%e4%b8%89%e3%80%81ra8d1-vision-board%e4%b8%8a%e7%9a%84iic%e5%ae%9e%e8%b7%b5%ef%bc%88%e6%ac%a7%e5%b0%8f%e9%be%99%ef%bc%89 配置ADC

screenshot_image.png参考:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/tutorial/make-bsp/renesas-ra/%E7%91%9E%E8%90%A8VisionBoard%E5%BC%80%E5%8F%91%E5%AE%9E%E8%B7%B5%E6%8C%87%E5%8D%97?id=%e5%85%ad%e3%80%81ra8d1-vision-board%e4%b8%8a%e7%9a%84adc%e5%ae%9e%e8%b7%b5%ef%bc%88%e4%be%af%e6%b3%bd%e5%8d%8e%ef%bc%89 rt-thread设备驱动勾选

screenshot_image.png

下面是程序控制源码

/*
 • Copyright (c) 2024, YourName

 • 项目:基于 RT-Thread 的电机 ESC 控制(移植自 Arduino)

 • 功能:通过串口接收指令控制电机转速、LED、蜂鸣器,集成VL53L0X主动刹车和光线检测自动夜灯

 • 已移除:超声波、光敏电阻、自动急停

 • 新增:所有速度档位切换必须平滑加减速(逐步 ±1,每次延时 50ms)
 • 修复:ESC电调上电初始化问题
 • 新增:VL53L0X激光测距主动刹车功能
 • 新增:光线检测自动夜灯功能
 */

#include <rtthread.h>
#include <rtdevice.h>
#include <string.h>
#include "hal_data.h"

// ====================== 引脚 / 设备 宏定义 ======================
#define PWM_DEV_NAME       "pwm12"       // PWM设备名,请根据实际连接修改
#define PWM_CHANNEL        0            // PWM通道

#define LED_PIN            BSP_IO_PORT_00_PIN_01 // LED引脚(根据实际修改)
#define BUZZER_PIN         BSP_IO_PORT_05_PIN_05 // 蜂鸣器引脚(根据实际修改)

#define SERIAL_DEVICE_NAME "uart2"      // 串口设备名,如USB转串口 / 蓝牙模块

// ====================== VL53L0X 配置 ======================
#define TOF_DEVICE_NAME    "tof_vl53l0x" // VL53L0X设备名
#define BRAKE_DISTANCE     1000          // 刹车距离阈值:1000mm = 1m
#define BRAKE_SAMPLE_COUNT 2             // 连续检测次数

// ====================== ADC 光线检测配置 ======================
#define ADC_DEV_NAME        "adc1"      // ADC 设备名称
#define ADC_DEV_CHANNEL     4           // ADC 通道
#define REFER_VOLTAGE       330         // 参考电压 3.3V,数据精度乘以100保留2位小数
#define CONVERT_BITS        (1 << 12)   // 转换位数为12位
#define LIGHT_THRESHOLD     200         // 光线阈值,小于200为夜间
#define LIGHT_SAMPLE_DELAY  2000        // 光线检测间隔:2秒

// ====================== 速度档位定义 ======================
volatile int item = 1000;               // 当前速度值(扩大10倍以支持小数计算)
const int speed_level = 1000;           // 启动速度(对应1.0ms)
const int speed_min = 950;              // 最低速度(对应0.95ms)
const int speed_one = 1150;             // 档位1(对应1.15ms)
const int speed_two = 1250;             // 档位2(对应1.25ms)
const int speed_three = 1300;           // 档位3(对应1.3ms)
const int speed_max = 1400;             // 档位4(最高速,对应1.4ms)
const int speed_add = 50;               // 每次调整步进(对应0.05ms)

// ====================== 全局变量 ======================
static struct rt_device_pwm *pwm_dev;
static rt_device_t serial_dev;
static rt_device_t tof_dev = RT_NULL;   // VL53L0X设备句柄
static rt_adc_device_t adc_dev = RT_NULL; // ADC设备句柄
char rx_buffer[64];
int rx_len = 0;

static bool is_started = false;         // 系统是否已启动
static bool esc_initialized = false;    // ESC是否已完成初始化
static bool auto_brake_enabled = true;  // 自动刹车功能是否启用
static bool auto_light_enabled = true;  // 自动夜灯功能是否启用
static bool manual_light_control = false; // 手动灯光控制标志
static int brake_counter = 0;           // 连续刹车检测计数器
static int current_light_level = 0;     // 当前光线强度

// ====================== PWM脉冲宽度计算 ======================
// 将速度值转换为PWM脉冲宽度(纳秒)
static int speed_to_pulse(int speed_val)
{
    // 将速度值映射到安全范围内
    int pulse_ns = 950000 + (speed_val - 950) * (450000 / (1400 - 950));

    // 确保在安全范围内
    if (pulse_ns < 950000) pulse_ns = 950000;
    if (pulse_ns > 1400000) pulse_ns = 1400000;

    return pulse_ns;
}

// ====================== 蜂鸣器控制 ======================
void buzzer_beep(int count, int duration_ms)
{
    for (int i = 0; i < count; i++)
    {
        rt_pin_write(BUZZER_PIN, PIN_HIGH);
        rt_thread_mdelay(duration_ms);
        rt_pin_write(BUZZER_PIN, PIN_LOW);
        rt_thread_mdelay(duration_ms);
    }
}

// ====================== ESC初始化序列 ======================
static void esc_init_sequence(void)
{
    rt_kprintf("[ESC] Starting initialization sequence...\r\n");

    // 发送安全启动信号
    rt_kprintf("[ESC] Setting to safe start position (1.0ms)...\r\n");
    rt_pwm_set(pwm_dev, PWM_CHANNEL, 20000000, 1000000); // 1.0ms
    rt_thread_mdelay(1000);

    esc_initialized = true;
    rt_kprintf("[ESC] Initialization completed successfully!\r\n");
    buzzer_beep(3, 100); // 3声短鸣表示初始化完成
}

// ====================== LED 控制 ======================
void led_on(void)
{
    rt_pin_write(LED_PIN, PIN_HIGH);
}

void led_off(void)
{
    rt_pin_write(LED_PIN, PIN_LOW);
}

// ====================== ADC 光线检测函数 ======================
static int read_light_level(void)
{
    rt_uint32_t value, vol;
    rt_err_t ret = RT_EOK;

    if (adc_dev == RT_NULL)
    {
        rt_kprintf("[LIGHT] ADC device not initialized!\r\n");
        return -1;
    }

    // 使能设备
    ret = rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);
    if (ret != RT_EOK)
    {
        rt_kprintf("[LIGHT] Failed to enable ADC channel!\r\n");
        return -1;
    }

    // 读取采样值
    value = rt_adc_read(adc_dev, ADC_DEV_CHANNEL);

    // 转换为对应电压值(乘以100保留2位小数)
    vol = value * REFER_VOLTAGE / CONVERT_BITS;

    // 关闭通道
    ret = rt_adc_disable(adc_dev, ADC_DEV_CHANNEL);

    // 返回原始ADC值(0-4095),值越小表示光线越暗
    return (int)value;
}

// ====================== 自动灯光控制 ======================
static void auto_light_control(void)
{
    if (!auto_light_enabled || manual_light_control)
    {
        return;
    }

    int light_level = read_light_level();
    if (light_level < 0)
    {
        return; // 读取失败
    }

    current_light_level = light_level;

    // 光线阈值判断:小于200为夜间
    if (light_level < LIGHT_THRESHOLD)
    {
        // 夜间模式,开启LED
        led_on();
        static bool night_mode_reported = false;
        if (!night_mode_reported)
        {
            rt_kprintf("[LIGHT] Night mode detected. Light level: %d, LED ON\r\n", light_level);
            night_mode_reported = true;
        }
    }
    else
    {
        // 白天模式,关闭LED
        led_off();
        static bool day_mode_reported = false;
        if (!day_mode_reported)
        {
            rt_kprintf("[LIGHT] Day mode detected. Light level: %d, LED OFF\r\n", light_level);
            day_mode_reported = true;
        }
    }
}

// ====================== 紧急刹车函数 ======================
static void emergency_brake(void)
{
    rt_kprintf("[BRAKE] EMERGENCY BRAKE ACTIVATED! Distance too close.\r\n");

    // 快速减速到最小值(比平滑减速更快)
    int current_speed = item;
    rt_kprintf("[BRAKE] Rapid deceleration from %d to %d\r\n", current_speed, speed_min);

    while (item > speed_min)
    {
        item -= 2; // 每次减2,更快减速

        // 防止低于最小值
        if (item < speed_min)
        {
            item = speed_min;
        }

        int pulse = speed_to_pulse(item);
        rt_pwm_set(pwm_dev, PWM_CHANNEL, 20000000, pulse);
        rt_thread_mdelay(20); // 更短的延时,快速刹车
    }

    // 刹车提示
    buzzer_beep(3, 100);
    led_on(); // LED亮起表示刹车状态

    rt_kprintf("[BRAKE] Emergency brake completed. Speed: %d\r\n", item);
}

// ====================== 平滑加减速函数 =======================
static void smooth_set_speed(int target)
{
    if (!esc_initialized)
    {
        rt_kprintf("[ERROR] ESC not initialized! Cannot set speed.\r\n");
        return;
    }

    int step = (target > item) ? 1 : -1;

    rt_kprintf("[Smooth] Changing speed from %d to %d...\r\n", item, target);

    while (item != target)
    {
        item += step;

        // 防止超过目标
        if ((step > 0 && item > target) || (step < 0 && item < target))
        {
            item = target;
        }

        // 设置 PWM
        int pulse = speed_to_pulse(item);
        rt_pwm_set(pwm_dev, PWM_CHANNEL, 20000000, pulse);
        rt_thread_mdelay(30);

        rt_kprintf("[Smooth] Now at: %d (Pulse: %dns)\r\n", item, pulse);
    }

    rt_kprintf("[Smooth] Reached target: %d\r\n", item);
}

// ====================== VL53L0X 距离监测线程 ======================
static void distance_monitor_thread_entry(void *parameter)
{
    struct rt_sensor_data sensor_data;
    rt_size_t res;

    rt_kprintf("[VL53L0X] Distance monitoring thread started.\r\n");

    // 查找VL53L0X设备
    tof_dev = rt_device_find(TOF_DEVICE_NAME);
    if (tof_dev == RT_NULL)
    {
        rt_kprintf("[VL53L0X] ERROR: Cannot find VL53L0X device: %s\r\n", TOF_DEVICE_NAME);
        return;
    }

    // 打开设备
    if (rt_device_open(tof_dev, RT_DEVICE_FLAG_RDONLY) != RT_EOK)
    {
        rt_kprintf("[VL53L0X] ERROR: Failed to open VL53L0X device.\r\n");
        return;
    }

    rt_kprintf("[VL53L0X] Device opened successfully. Starting distance monitoring...\r\n");

    while (1)
    {
        // 只有系统已启动且自动刹车启用时才进行距离检测
        if (is_started && auto_brake_enabled && esc_initialized)
        {
            res = rt_device_read(tof_dev, 0, &sensor_data, 1);
            if (res == 1)
            {
                int distance = sensor_data.data.proximity;

                // 调试输出(可选,避免输出过于频繁)
                static int debug_counter = 0;
                if (debug_counter++ % 10 == 0) // 每10次输出一次
                {
                    rt_kprintf("[VL53L0X] Distance: %d mm\r\n", distance);
                }

                // 检测到障碍物距离小于阈值
                if (distance > 0 && distance < BRAKE_DISTANCE)
                {
                    brake_counter++;
                    rt_kprintf("[VL53L0X] Obstacle detected: %d mm (counter: %d/%d)\r\n",
                              distance, brake_counter, BRAKE_SAMPLE_COUNT);

                    // 连续2次检测到障碍物,触发紧急刹车
                    if (brake_counter >= BRAKE_SAMPLE_COUNT)
                    {
                        emergency_brake();
                        brake_counter = 0; // 重置计数器
                    }
                }
                else
                {
                    // 距离安全,重置计数器
                    if (brake_counter > 0)
                    {
                        rt_kprintf("[VL53L0X] Distance safe: %d mm. Reset brake counter.\r\n", distance);
                        brake_counter = 0;
                        // 注意:这里不关闭LED,因为LED可能由光线控制
                    }
                }
            }
            else
            {
                rt_kprintf("[VL53L0X] ERROR: Failed to read distance data.\r\n");
            }
        }
        else
        {
            // 系统未启动或刹车禁用,重置计数器
            brake_counter = 0;
        }

        rt_thread_mdelay(50); // 50ms检测周期
    }
}

// ====================== 光线检测线程 ======================
static void light_monitor_thread_entry(void *parameter)
{
    rt_kprintf("[LIGHT] Light monitoring thread started.\r\n");

    // 初始化ADC设备
    adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
    if (adc_dev == RT_NULL)
    {
        rt_kprintf("[LIGHT] ERROR: Cannot find ADC device: %s\r\n", ADC_DEV_NAME);
        return;
    }

    rt_kprintf("[LIGHT] ADC device initialized successfully.\r\n");

    while (1)
    {
        // 只有系统已启动时才进行光线检测
        if (is_started)
        {
            auto_light_control();
        }

        rt_thread_mdelay(LIGHT_SAMPLE_DELAY); // 2秒检测一次
    }
}

// ====================== 指令动作处理函数 ======================
void action(const char *throttle)
{
    rt_kprintf("[Action] Received: %s\r\n", throttle);

    if (strcmp(throttle, "stop") == 0)
    {
        smooth_set_speed(speed_level);
        // 注意:这里不控制LED关闭,因为可能由光线控制
        buzzer_beep(1, 500);
        rt_kprintf("[Action] STOPPED. Speed set to level.\r\n");
        is_started = false;
        brake_counter = 0; // 重置刹车计数器
    }
    else if (strcmp(throttle, "decrease") == 0)
    {
        int target = item - speed_add;
        if (target < speed_min)
            target = speed_min;
        smooth_set_speed(target);
        buzzer_beep(1, 100);
        rt_kprintf("[Action] DECREASED (smooth). Speed: %d\r\n", item);
    }
    else if (strcmp(throttle, "increase") == 0)
    {
        int target = item + speed_add;
        if (target > speed_max)
            target = speed_max;
        smooth_set_speed(target);
        buzzer_beep(1, 200);
        rt_kprintf("[Action] INCREASED (smooth). Speed: %d\r\n", item);
    }
    else if (strcmp(throttle, "one") == 0)
    {
        smooth_set_speed(speed_one);
        buzzer_beep(1, 150);
        rt_kprintf("[Action] SPEED 1 (smooth). Speed: %d\r\n", item);
    }
    else if (strcmp(throttle, "two") == 0)
    {
        smooth_set_speed(speed_two);
        buzzer_beep(2, 150);
        rt_kprintf("[Action] SPEED 2 (smooth). Speed: %d\r\n", item);
    }
    else if (strcmp(throttle, "three") == 0)
    {
        smooth_set_speed(speed_three);
        buzzer_beep(3, 150);
        rt_kprintf("[Action] SPEED 3 (smooth). Speed: %d\r\n", item);
    }
    else if (strcmp(throttle, "four") == 0)
    {
        smooth_set_speed(speed_max);
        buzzer_beep(4, 150);
        rt_kprintf("[Action] SPEED 4 (MAX, smooth). Speed: %d\r\n", item);
    }
    else if (strcmp(throttle, "turn on") == 0)
    {
        manual_light_control = true; // 进入手动控制模式
        led_on();
        buzzer_beep(1, 200);
        rt_kprintf("[Action] LED ON (Manual control)\r\n");
    }
    else if (strcmp(throttle, "turn off") == 0)
    {
        manual_light_control = true; // 进入手动控制模式
        led_off();
        buzzer_beep(1, 200);
        rt_kprintf("[Action] LED OFF (Manual control)\r\n");
    }
    else if (strcmp(throttle, "auto light") == 0)
    {
        manual_light_control = false; // 退出手动控制,返回自动模式
        rt_kprintf("[Action] Auto light control ENABLED\r\n");
        buzzer_beep(1, 200);
    }
    else if (strcmp(throttle, "buzzer on") == 0)
    {
        buzzer_beep(2, 300);
        rt_kprintf("[Action] BUZZER ON\r\n");
    }
    else if (strcmp(throttle, "buzzer off") == 0)
    {
        buzzer_beep(2, 100);
        rt_kprintf("[Action] BUZZER OFF\r\n");
    }
    else if (strcmp(throttle, "init esc") == 0)
    {
        esc_init_sequence();
    }
    else if (strcmp(throttle, "brake on") == 0)
    {
        auto_brake_enabled = true;
        rt_kprintf("[Action] Auto brake ENABLED\r\n");
        buzzer_beep(1, 200);
    }
    else if (strcmp(throttle, "brake off") == 0)
    {
        auto_brake_enabled = false;
        rt_kprintf("[Action] Auto brake DISABLED\r\n");
        buzzer_beep(2, 200);
    }
    else if (strcmp(throttle, "light on") == 0)
    {
        auto_light_enabled = true;
        rt_kprintf("[Action] Auto light ENABLED\r\n");
        buzzer_beep(1, 200);
    }
    else if (strcmp(throttle, "light off") == 0)
    {
        auto_light_enabled = false;
        rt_kprintf("[Action] Auto light DISABLED\r\n");
        buzzer_beep(2, 200);
    }
    else if (strcmp(throttle, "test brake") == 0)
    {
        rt_kprintf("[Action] Testing emergency brake...\r\n");
        emergency_brake();
    }
    else if (strcmp(throttle, "light status") == 0)
    {
        rt_kprintf("[LIGHT] Current light level: %d, Threshold: %d, Auto: %s, Manual: %s\r\n",
                  current_light_level, LIGHT_THRESHOLD,
                  auto_light_enabled ? "ON" : "OFF",
                  manual_light_control ? "ON" : "OFF");
    }
    else
    {
        rt_kprintf("[Action] Unknown command: %s\r\n", throttle);
    }
}

// ====================== 串口指令线程 ======================
static void serial_cmd_thread_entry(void *parameter)
{
    rt_size_t len;

    while (1)
    {
        memset(rx_buffer, 0, sizeof(rx_buffer));
        len = rt_device_read(serial_dev, 0, rx_buffer, sizeof(rx_buffer) - 1);
        if (len > 0)
        {
            // 去除换行符和回车符
            for (int i = 0; i < len; i++)
            {
                if (rx_buffer[i] == '\r' || rx_buffer[i] == '\n')
                {
                    rx_buffer[i] = '\0';
                    break;
                }
            }

            rt_kprintf("[Serial] Rx: %s\r\n", rx_buffer);

            if (!is_started)
            {
                if (strcmp(rx_buffer, "start") == 0)
                {
                    if (!esc_initialized)
                    {
                        rt_kprintf("[System] Initializing ESC for first start...\r\n");
                        esc_init_sequence();
                    }

                    is_started = true;
                    rt_kprintf("[System] System STARTED.\r\n");
                    buzzer_beep(2, 300);
                }
                else
                {
                    rt_kprintf("[System] ERROR: System NOT STARTED. Send 'start' first.\r\n");
                }
            }
            else
            {
                action(rx_buffer);
            }

            rx_buffer[0] = '\0';
        }
        rt_thread_mdelay(10);
    }
}

// ====================== VL53L0X 硬件初始化 ======================
static int rt_hw_vl53l0x_port(void)
{
    struct rt_sensor_config cfg;

    cfg.intf.dev_name = "i2c1";         /* i2c bus */
    cfg.intf.user_data = (void *)0x29;  /* i2c slave addr */

    // 注意:这个函数名称需要根据你实际的VL53L0X驱动来调整
    rt_hw_vl53l0x_init("vl53l0x", &cfg, 57); /* xshutdown ctrl pin */

    rt_kprintf("[VL53L0X] Hardware port initialized.\r\n");
    return RT_EOK;
}
INIT_COMPONENT_EXPORT(rt_hw_vl53l0x_port);

// ====================== 初始化函数 ======================
int motor_control_app_init(void)
{
    rt_kprintf("=== Motor Control System Starting ===\r\n");

    // 1. 初始化 PWM
    pwm_dev = (struct rt_device_pwm *) rt_device_find(PWM_DEV_NAME);
    if (pwm_dev == RT_NULL)
    {
        rt_kprintf("Cannot find PWM device: %s\r\n", PWM_DEV_NAME);
        return -1;
    }

    // 启用PWM设备,设置安全启动位置
    rt_pwm_enable(pwm_dev, PWM_CHANNEL);
    rt_pwm_set(pwm_dev, PWM_CHANNEL, 20000000, 1000000); // 1.0ms
    rt_thread_mdelay(1000);
    rt_kprintf("PWM ESC device enabled at safe position.\r\n");

    // 2. 初始化 LED & 蜂鸣器
    rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT);
    rt_pin_mode(BUZZER_PIN, PIN_MODE_OUTPUT);
    led_off();
    rt_pin_write(BUZZER_PIN, PIN_LOW);

    // 3. 打开串口
    serial_dev = rt_device_find(SERIAL_DEVICE_NAME);
    if (serial_dev == RT_NULL)
    {
        rt_kprintf("Cannot find serial device: %s\r\n", SERIAL_DEVICE_NAME);
        return -1;
    }

    rt_device_open(serial_dev, RT_DEVICE_FLAG_INT_RX);

    // 4. 创建串口指令线程
    rt_thread_t tid = rt_thread_create("serial_cmd", serial_cmd_thread_entry, RT_NULL, 1024, 20, 10);
    if (tid != RT_NULL)
    {
        buzzer_beep(1, 500);
        rt_thread_startup(tid);
        rt_kprintf("Serial command thread started.\r\n");
    }
    else
    {
        rt_kprintf("Failed to create serial command thread.\r\n");
    }

    // 5. 创建距离监测线程(优先级较高,确保及时刹车)
    rt_thread_t distance_tid = rt_thread_create("distance_monitor",
                                               distance_monitor_thread_entry,
                                               RT_NULL,
                                               1024,
                                               15,  // 较高优先级
                                               10);
    if (distance_tid != RT_NULL)
    {
        rt_thread_startup(distance_tid);
        rt_kprintf("Distance monitoring thread started.\r\n");
    }
    else
    {
        rt_kprintf("Failed to create distance monitoring thread.\r\n");
    }

    // 6. 创建光线检测线程
    rt_thread_t light_tid = rt_thread_create("light_monitor",
                                           light_monitor_thread_entry,
                                           RT_NULL,
                                           1024,
                                           18,  // 中等优先级
                                           10);
    if (light_tid != RT_NULL)
    {
        rt_thread_startup(light_tid);
        rt_kprintf("Light monitoring thread started.\r\n");
    }
    else
    {
        rt_kprintf("Failed to create light monitoring thread.\r\n");
    }

    rt_kprintf("=== System Initialization Complete ===\r\n");
    rt_kprintf("Commands: start, stop, increase, decrease, one-two-three-four\r\n");
    rt_kprintf("          turn on/off, auto light, light on/off, light status\r\n");
    rt_kprintf("          brake on/off, test brake, init esc\r\n");

    return 0;
}

// ====================== 导出组件初始化 ======================
INIT_APP_EXPORT(motor_control_app_init);

需要修改rt_config.h中adc0为1:#define BSP_USING_ADC1

screenshot_image.png

这是一个基于RT-Thread的智能电机控制系统,通过串口指令控制无刷电机转速,集成了VL53L0X激光测距主动刹车和光线检测自动夜灯功能,具备多线程安全保护和智能决策能力。

screenshot_deepseek_mermaid_20251121_b0637e.png

流程说明:

  1. 多线程并行:三个独立线程分别处理用户指令、障碍物监测和光线检测

  2. 安全保护:距离监测线程具有较高优先级,确保及时刹车

  3. 智能决策:基于连续检测和阈值判断,避免误触发

  4. 模式切换:支持手动/自动模式灵活切换

  5. 平滑控制:电机速度变化采用渐进式调节,提升系统稳定性

4.动手操作

a.首先进行光照检测,如光线较弱则自动打开前灯; b.连接好蓝牙,发送“start”字符串开始遥控,发送“one”,“two”,“three”,“four”即可实现稳步增速,发送“increase”、“dicrease”即可实现精准调速,发送“turn on”、“turn off”即可打开前灯led继电器,发送“buzeer on”、“buzeer off”即可触发有源蜂鸣器声响,app不完全配置

screenshot_image.png

c.发送stop即可实现关闭所有增益,发送“reset”即可实现复位跳出循环,再次发送发送“start”字符串又开始遥控。超声波测距检测前方物体距离小于100cm及时关闭所有增益,起到自动紧急刹车保护,程序中执行动作时有蜂鸣器响应反馈;还有心跳包程序,可判断是否蓝牙断开(若断开及时关闭所有增益,起到失控保护),滑板演示

白天载物测试😂😂😂

溜船模式🤣🤣🤣

夜晚爬坡测试😍😍😍

● 第一次是RT-Thread的【基于RT-Thread+RA6M4的智能鱼缸系统设计之鱼我所欲也】活动,作品是2022年暑假做的获得第六名,还是比较开心!

● 第二次2023年寒假做的是【基于MAX7800羽毛板语音控制ESP8266小车】,成绩还不错第七名,让对小车车的可玩性又近了一步!

● 第三次2023年春做的【基于腾讯云的CH32V307开发板远程机械臂小车】,由于图床引用CSDN导致最后评审没有显示出来,最后获得安慰奖!

● 第四次2023年冬做的【FastBond2阶段2——基于ESP32C3开发的简易IO调试设备 - 电子森林 (eetree.cn) 】 ,最终获得三等奖,再接再厉哦!

● 第五次实现了【基于LicheePi-4A的 人脸识别系统软件设计】,人脸识别系统软件设计和调试全流程,加深了对tkinter GUI设计思路,对LicheePi-4A 国产单板计算机更有信心,最终获得参与奖!

● 第六次实现了2024年寒假练 - 基于xiao ESP32S3 Sense的自动化HA鱼缸设计,探讨如何运用Seeed Xiao ESP32-S3 Cam开发板设计一款自动化、可接入Home Assistant(简称HA)的智能鱼缸系统。最终获得第一名,结实了许多朋友,视野开阔了许多。

● 第七次实现了FastBond3挑战部分-XIAO智能助手,让我关于Platformio C++编程充满期待。搭配国产通义灵码编程插件,编程效率嘎嘎上升,与此同时智能终端有更深刻认识啦!😘😘😘

● 第8次设计基于ESP32-S3双核处理器,构建支持8通道K型热电偶(MAX31856)的智能采集系统,通过多线程架构实现数据采集、通信处理、看门狗监控的并行执行,最终达成**±0.5℃精度**、5Hz采样率🤣🤣🤣

这是一个功能完善、安全机制到位的嵌入式智能控制系统,移植了原先Arduino Uno项目,替换更好地TOF距离传感器,体现了Vision Board多传感器融合与实时控制的良好实践。 建议:

希望RT与华秋国内厂商多多合作定期举办嵌入式开发活动,让更加优质的国产嵌入式生态做大做强; 期待RT平台推出更多有质量有意义持续性的创客活动!希望RT不仅开展线上活动,后期可以尝试线下活动,多多与开发者,企业,高校互动,共同探索内需外患。🤣🤣🤣 非常感谢RT举行活动,大家都为这个国内嵌入式生态出一份力,只要努力认真做了都会有所收获,期盼这些作品在将来某一天为构建美好未来贡献一份微博之力! 我后期会持续更新我测评的一系列国内开发板测评,并且会积极参加有质量的玄铁杯活动🛹🛹🛹每天都一点点结合实际需求联动丰富生活,从而实现对外部世界进行充分的感知,尽最大努力认识这个有机与无机的环境,科学地合理地进行创作和发挥效益,然后为人类社会发展贡献一点微薄之力。🤣🤣🤣 🥳🥳🥳再次非常感谢RT华秋的相关组织者支持等等🥳🥳🥳期待这一次的成绩哟!