news 2026/7/29 16:22:03

基于Intel Curie Nano与SPI通信的智能机器人底盘闭环控制实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Intel Curie Nano与SPI通信的智能机器人底盘闭环控制实战

1. 项目缘起:从一块“古董”开发板开始的智能硬件之旅

最近在整理工作室的物料箱,翻出来一块几年前入手的 Intel Curie Nano 开发板。这板子当年也算是 Intel 进军物联网和可穿戴领域的一个标志性产品,内置了 Intel Curie 模组,集成了低功耗的 Quark SE 微控制器和一个 32 位的 ARC 架构传感器协处理器,还有 6 轴 IMU 和蓝牙。不过后来 Intel 在物联网硬件上战略收缩,这板子也就渐渐淡出了主流视野,成了我们这些硬件爱好者手里的“收藏品”。看着它静静地躺在防静电袋里,总觉得有点可惜,毕竟硬件底子还是不错的。正好手头有个一直想做的项目——一个能自动分类、压缩甚至提醒你倒垃圾的“智能垃圾桶”,不如就用它来当主控,也算是让这块板子发挥一下余热。

这个“智能垃圾桶”项目我打算分成几期来做,从易到难,从基础到复杂。第一期,我们就从最根本的“底盘”开始。为什么是底盘?因为一个好的移动平台是整个项目稳定运行的基础。想象一下,你的垃圾桶需要自主移动到指定位置,或者根据指令调整朝向,如果底盘不稳、控制不灵,后面加上再多的传感器和智能算法都是白搭。这就像盖房子,地基没打好,上面装修得再漂亮也住不踏实。

本期“底盘篇”的核心目标很明确:利用 Intel Curie Nano 作为主控,搭配常见的电机驱动模块和轮子,搭建一个稳定、可控的双轮差分底盘。我们会深入探讨如何通过 Arduino 环境为这块相对小众的板子编程,如何通过 SPI 总线与外围设备高效通信(比如读取更高精度的编码器数据),并解决在驱动和控制中可能遇到的实际问题。无论你是对 Intel Curie 这块板子感兴趣,还是想学习如何从零搭建一个机器人底盘,亦或是想了解 SPI 通信在电机控制中的实际应用,这篇内容都会给你带来一次扎实的实战体验。

2. 硬件选型与核心组件解析

搭建一个差分底盘,硬件选型是第一步,它直接决定了后续开发的难度和最终性能的上限。我的选型原则是在满足基本功能的前提下,尽量利用手头已有的物料,同时兼顾学习的价值。

2.1 主控:Intel Curie Nano 的再审视

首先是我们今天的主角,Intel Curie Nano。它本质上是一个基于 Intel Quark SE 微控制器的 Arduino 兼容板。其核心优势在于极低的功耗和内置的传感器融合引擎,非常适合可穿戴设备。但对于机器人底盘来说,我们更关注以下几点:

  • GPIO 与 PWM:它提供了足够数量的数字 IO 和 PWM 输出,足以驱动两个电机并连接必要的传感器。
  • 通信接口:具备 I2C 和 SPI 接口,这对于连接现代传感器(如 SPI 接口的磁编码器)至关重要。
  • 开发环境:兼容 Arduino IDE,极大地降低了开发门槛。虽然 Intel 官方对其的支持已减弱,但社区的基础库和示例仍然可用。

它的局限性也很明显:主频较低(32MHz),内存有限(SRAM 仅 24KB),处理复杂的控制算法(如 PID 调参过程)或大量数据时会有些吃力。但这对于本期实现底盘的基础移动和速度控制来说,是完全足够的。选择它,正是为了在资源受限的环境下实践高效编程。

2.2 动力与驱动:电机、驱动模块与电源

  • 电机:我选择了两个常见的 N20 微型减速电机。它们体积小、扭矩足,自带减速箱,非常适合小型底盘。关键是,我手头有配套的增量式光电编码器(AB 相输出),可以用于测量电机转速,实现闭环速度控制。
  • 电机驱动模块:这是连接弱电(主控)和强电(电机)的关键。我选用的是 TB6612FNG 双路电机驱动芯片模块。相比于经典的 L298N,TB6612FNG 效率更高、发热更少,支持 PWM 调速和正反转控制,逻辑电压与 Curie Nano 的 3.3V 完美兼容,无需电平转换。
  • 电源:整个系统需要两组电源:一是为 Curie Nano 和逻辑电路供电的 3.3V/5V;二是为电机供电的功率电源。我使用一块 7.4V 的 2S 锂聚合物电池。通过一个降压模块(如 MP1584EN)将电池电压降至 5V 给 Curie Nano 的 VIN 引脚供电,同时电池电压直接接入 TB6612FNG 的 VMOT 引脚驱动电机。这里有个重要细节:必须确保逻辑部分(Curie Nano, TB6612)和电机部分的地(GND)是共用的,即整个系统只有一个共同的参考地,否则控制信号会紊乱。

2.3 感知与反馈:编码器的选择与 SPI 的引入

要让底盘走直线、转特定的角度,开环控制(只给 PWM,不管实际转速)是绝对不行的,漂移会非常严重。因此,我们需要电机编码器来提供速度反馈。

我手头的光电编码器是增量式的,通过 AB 相输出脉冲。Curie Nano 可以利用外部中断引脚来捕获这些脉冲,计算转速。但这里我想引入本期的一个技术重点:SPI 通信

为什么考虑 SPI?增量式编码器在高速或需要绝对位置时,对主控的中断响应和计数能力要求高,容易丢失脉冲。而一些更先进的磁编码器(如 AS5047P、MA730)则通过 SPI 接口直接输出一个绝对角度值(比如 14 位,0-16383)。主控只需定期通过 SPI 读取这个值,通过计算角度差就能得到精确的速度和位置信息,无需担心中断丢失,抗干扰能力也更强。这为后续实现更精准的位置控制打下了基础。

因此,在硬件连接上,我预留了 SPI 接口(Curie Nano 的引脚 10-SS, 11-MOSI, 12-MISO, 13-SCK),为未来升级为 SPI 编码器做好准备。本期我们先使用中断方式读取增量编码器,但会详细讲解 SPI 的配置,为“第二期”做好铺垫。

2.4 结构件与杂项

底盘结构我用的是激光切割的亚克力板,轻便且易于加工。轮子、万向轮(或从动球)、电机固定支架、电池仓等都是模型用的标准件。连接线建议使用杜邦线进行原型搭建,但为了可靠性,后期最好焊接或使用更可靠的连接器。

下表总结了本期核心硬件清单及连接要点:

组件型号/规格关键接口/参数与 Curie Nano 连接要点
主控Intel Curie Nano3.3V 逻辑, Arduino 兼容核心控制中心
电机驱动TB6612FNG 模块双路,逻辑电压 3.3V-5VPWMA/B接 PWM 引脚,AIN1/AIN2, BIN1/BIN2接数字 IO
电机N20 减速电机 (带编码器)工作电压 6V,减速比 1:30电机线接驱动模块输出,编码器线接中断引脚
编码器增量式光电编码器 (AB相)每转脉冲数 (PPR)A相接外部中断引脚,B相接普通数字输入引脚
电源7.4V 2S 锂聚合物电池-经降压模块至 5V 接 Curie VIN,电池直连驱动 VMOT
结构亚克力底盘、轮子、万向轮-确保电机安装牢固,底盘重心平衡

3. 软件环境搭建与 Curie Nano 的 Arduino 配置

虽然 Curie Nano 相对小众,但在 Arduino IDE 中配置它并不复杂。关键在于找到正确的板卡支持包。

3.1 安装 Arduino IDE 与板卡支持

首先,确保你安装了最新版的 Arduino IDE(1.8.x 或 2.0+均可)。打开 IDE,进入“文件”->“首选项”。在“附加开发板管理器网址”中,添加以下 URL:

https://github.com/01org/corelibs-arduino101/raw/master/package_index.json

这个链接包含了 Intel Curie 系列开发板的支持包。添加后,点击“确定”。

接着,打开“工具”->“开发板”->“开发板管理器”。在搜索框中输入“Curie”。你应该能看到一个名为“Intel Curie Boards by Intel”的包。选择它并点击“安装”。安装过程可能需要几分钟,请保持网络通畅。

安装完成后,你就可以在“工具”->“开发板”列表中看到“Intel Curie Nano”的选项了,选择它。

3.2 关键设置与端口选择

选择好开发板后,还需要注意“工具”菜单下的几个选项:

  • 端口:将 Curie Nano 通过 Micro-USB 线连接到电脑,这里会出现对应的串口(如 COM3 或 /dev/ttyACM0),选择它。
  • 编程器:保持默认的“AVRISP mkII”即可。
  • 其他选项:如 CPU 频率等,本期项目保持默认设置。

3.3 验证与基础程序上传

为了测试环境是否搭建成功,我们可以上传一个最简单的 Blink 程序。但 Curie Nano 的板载 LED 连接在引脚 13,而这个引脚同时也是 SPI 的 SCK 引脚。为了避免冲突,我们可以用另一个数字引脚(比如引脚 4)接一个外置 LED 来测试。

void setup() { pinMode(4, OUTPUT); // 设置引脚4为输出 } void loop() { digitalWrite(4, HIGH); // 点亮LED delay(1000); // 等待1秒 digitalWrite(4, LOW); // 熄灭LED delay(1000); // 等待1秒 }

点击上传按钮,如果一切顺利,程序将被编译并上传到 Curie Nano,外接在引脚 4 的 LED 会开始闪烁。这证明你的开发环境已经就绪。

注意:有时在上传过程中可能会遇到错误,比如“下载超时”或“找不到设备”。可以尝试以下步骤:1) 按一下板子上的“MASTER RESET”按钮再重新上传;2) 更换一条质量好的 USB 数据线;3) 检查电脑的端口是否被其他软件占用。

4. 底盘基础驱动:PWM 与电机控制逻辑

底盘要动起来,最核心的就是控制两个电机的转速和方向。我们将通过 PWM 信号来控制电机速度,通过两个 GPIO 的电平组合来控制方向。

4.1 TB6612FNG 驱动逻辑详解

TB6612FNG 每一路电机控制需要三个信号:AIN1/AIN2(或BIN1/BIN2)用于方向,PWMA/PWMB用于速度。

其真值表如下:

AIN1AIN2PWMA电机A状态
00X停止(刹车)
10PWM正转(速度由 PWM 占空比决定)
01PWM反转(速度由 PWM 占空比决定)
11X停止(刹车)

“刹车”状态相当于将电机两端短路,能产生一定的制动效果,比单纯断开电源停止得更快。

4.2 Curie Nano 的 PWM 配置与代码实现

在 Arduino 中,我们使用analogWrite(pin, value)函数来输出 PWM。其中value范围是 0-255,对应占空比 0%-100%。Curie Nano 的特定引脚支持 PWM,我们需要查看其引脚定义图。通常数字引脚 3, 5, 6, 9, 10, 11 是支持 PWM 的。

假设我们将电机 A 的PWMA接 Curie Nano 的引脚 5,AIN1接引脚 7,AIN2接引脚 8。电机 B 同理接在引脚 6, 9, 10 上。

下面是一个基础的电机控制函数封装:

// 电机引脚定义 #define MOTOR_A_PWM 5 #define MOTOR_A_IN1 7 #define MOTOR_A_IN2 8 #define MOTOR_B_PWM 6 #define MOTOR_B_IN1 9 #define MOTOR_B_IN2 10 void setup() { // 初始化所有电机控制引脚为输出模式 pinMode(MOTOR_A_PWM, OUTPUT); pinMode(MOTOR_A_IN1, OUTPUT); pinMode(MOTOR_A_IN2, OUTPUT); pinMode(MOTOR_B_PWM, OUTPUT); pinMode(MOTOR_B_IN1, OUTPUT); pinMode(MOTOR_B_IN2, OUTPUT); } // 控制单个电机的函数 // motor: 0 代表电机A, 1 代表电机B // speed: -255 到 255,负数为反转 void setMotor(int motor, int speed) { speed = constrain(speed, -255, 255); // 限制速度范围 int pwmPin, in1Pin, in2Pin; if (motor == 0) { pwmPin = MOTOR_A_PWM; in1Pin = MOTOR_A_IN1; in2Pin = MOTOR_A_IN2; } else { pwmPin = MOTOR_B_PWM; in1Pin = MOTOR_B_IN1; in2Pin = MOTOR_B_IN2; } if (speed > 0) { // 正转 digitalWrite(in1Pin, HIGH); digitalWrite(in2Pin, LOW); analogWrite(pwmPin, speed); } else if (speed < 0) { // 反转 digitalWrite(in1Pin, LOW); digitalWrite(in2Pin, HIGH); analogWrite(pwmPin, -speed); // 取绝对值 } else { // 停止(刹车) digitalWrite(in1Pin, LOW); digitalWrite(in2Pin, LOW); analogWrite(pwmPin, 0); } } void loop() { // 测试:让底盘前进2秒,原地右转2秒,停止1秒 setMotor(0, 200); // 电机A正转,速度200 setMotor(1, 200); // 电机B正转,速度200 delay(2000); setMotor(0, 150); // 电机A正转,速度150 setMotor(1, -150); // 电机B反转,速度150 -> 差分转向 delay(2000); setMotor(0, 0); // 停止 setMotor(1, 0); delay(1000); }

上传这段代码,你的底盘应该能执行一个简单的前进-右转-停止的动作序列。如果电机转向与预期相反,只需交换对应电机的IN1IN2接线即可。

5. 速度闭环控制:增量式编码器与中断计数

开环控制下,即使给两个电机相同的 PWM 值,由于电机特性、电池电压波动、轮子摩擦等因素,底盘也几乎不可能走直线。我们必须引入编码器反馈,构成闭环。

5.1 编码器计数原理与接口

增量式编码器输出两路相位差 90 度的方波(A 相和 B 相)。通过检测 A 相的上升沿和下降沿,并结合 B 相的电平状态,可以在一个脉冲周期内进行 4 次计数(即 4 倍频),大大提高分辨率。例如,我的编码器电机标称是每转 11 个脉冲(PPR),经过 4 倍频后,每转的计数就是 44。

我们将编码器 A 相接 Curie Nano 的外部中断引脚(如引脚 2 或 3),B 相接普通数字输入引脚(如引脚 4)。

5.2 使用外部中断实现高速计数

Curie Nano 的 Arduino 核心支持attachInterrupt()函数。我们需要在中断服务函数中,根据 A、B 相的状态判断是加计数还是减计数。

// 编码器引脚定义 #define ENCODER_A_A 2 // 中断引脚,接编码器A相 #define ENCODER_A_B 4 // 普通输入,接编码器B相 #define ENCODER_B_A 3 // 中断引脚,接另一个编码器A相 #define ENCODER_B_B 12 // 普通输入,接另一个编码器B相 // 编码器计数变量,使用 volatile 修饰,确保在中断内修改能被主循环正确读取 volatile long encoderCountA = 0; volatile long encoderCountB = 0; // 电机A的编码器中断服务函数 void handleEncoderA() { int a = digitalRead(ENCODER_A_A); int b = digitalRead(ENCODER_A_B); // 根据A、B相状态判断方向。此逻辑取决于你的编码器相位和接线,可能需要调整 if (a == b) { encoderCountA--; } else { encoderCountA++; } } // 电机B的编码器中断服务函数 void handleEncoderB() { int a = digitalRead(ENCODER_B_A); int b = digitalRead(ENCODER_B_B); if (a == b) { encoderCountB++; } else { encoderCountB--; } } void setup() { Serial.begin(115200); // 初始化串口用于调试输出 // 初始化编码器引脚 pinMode(ENCODER_A_A, INPUT_PULLUP); pinMode(ENCODER_A_B, INPUT_PULLUP); pinMode(ENCODER_B_A, INPUT_PULLUP); pinMode(ENCODER_B_B, INPUT_PULLUP); // 附着中断,在ENCODER_A_A引脚变化(上升沿或下降沿)时触发中断函数 attachInterrupt(digitalPinToInterrupt(ENCODER_A_A), handleEncoderA, CHANGE); attachInterrupt(digitalPinToInterrupt(ENCODER_B_A), handleEncoderB, CHANGE); // ... 电机引脚初始化代码(同上) } long lastPrintTime = 0; long lastCountA = 0, lastCountB = 0; void loop() { // 每100毫秒计算一次速度(脉冲数/时间) if (millis() - lastPrintTime > 100) { long currentCountA = encoderCountA; long currentCountB = encoderCountB; long deltaA = currentCountA - lastCountA; long deltaB = currentCountB - lastCountB; // 计算转速(脉冲数/100ms),你可以将其转换为 RPM(转/分钟) float speedA = deltaA; // 这里只是脉冲数,需要根据PPR和倍频换算成实际转速 float speedB = deltaB; Serial.print("Speed A: "); Serial.print(speedA); Serial.print(" | Speed B: "); Serial.println(speedB); lastCountA = currentCountA; lastCountB = currentCountB; lastPrintTime = millis(); } // 这里可以加入PID控制逻辑,根据目标速度和当前速度(speedA/B)调整PWM输出 // setMotor(0, pidA.update(targetSpeed, speedA)); // setMotor(1, pidB.update(targetSpeed, speedB)); }

这段代码实现了编码器脉冲的计数和速度的粗略计算。打开 Arduino IDE 的串口监视器,设置波特率为 115200,手动转动电机轮子,你应该能看到速度值的变化。

重要经验:中断服务函数handleEncoder必须尽可能短小,只做最简单的计数操作。避免在中断中使用Serial.print()delay()等耗时函数,否则会导致系统响应异常甚至崩溃。所有复杂计算(如 PID)和输出都应放在主循环loop()中。

5.3 差分底盘的运动学模型

要控制底盘走直线或旋转,需要理解差分底盘的运动学。两个轮子的速度差决定了底盘的角速度。假设两个轮子间距为L,轮子半径为r,左轮转速为v_left,右轮转速为v_right(单位:脉冲数/时间)。

  • 线速度V = (v_right + v_left) * r / 2
  • 角速度ω = (v_right - v_left) * r / L

我们的速度闭环控制目标,就是通过 PID 等算法,让两个轮子的实际转速v_leftv_right精确地跟踪我们根据上述模型计算出的目标转速。由于篇幅所限,完整的 PID 实现和调参将是一个独立的话题,但有了精确的编码器反馈数据,我们已经迈出了最关键的一步。

6. SPI 通信基础与为未来升级做准备

尽管本期我们使用的是中断读取增量编码器,但为了项目的可扩展性,以及应对更高精度的需求,预先配置好 SPI 接口是很有必要的。未来我们可以无缝地将编码器更换为 SPI 接口的绝对式磁编码器。

6.1 SPI 在 Arduino 中的使用

Arduino 提供了SPI库来简化 SPI 操作。Curie Nano 的 SPI 引脚是固定的:

  • SS (Slave Select):引脚 10 (但可以作为普通数字 IO 使用)
  • MOSI (Master Out Slave In):引脚 11
  • MISO (Master In Slave Out):引脚 12
  • SCK (Serial Clock):引脚 13

我们需要在代码中引入SPI.h头文件,并进行初始化。

#include <SPI.h> // 假设我们未来连接一个 SPI 编码器,其片选引脚接 Curie Nano 的引脚 1 #define ENCODER_SPI_CS 1 void setup() { Serial.begin(115200); SPI.begin(); // 初始化 SPI 总线 pinMode(ENCODER_SPI_CS, OUTPUT); digitalWrite(ENCODER_SPI_CS, HIGH); // 默认不选中从设备 // ... 其他初始化代码 } // 一个模拟读取 SPI 编码器的函数(以 AS5047P 为例) unsigned int readSPIEncoder() { unsigned int data = 0; digitalWrite(ENCODER_SPI_CS, LOW); // 选中编码器 delayMicroseconds(1); // 等待片选稳定 // SPI 传输:先发送一个读取命令(例如 0xFFFF),同时接收高字节 data = SPI.transfer16(0xFFFF); // transfer16 用于16位数据交换 digitalWrite(ENCODER_SPI_CS, HIGH); // 取消选中 // AS5047P 返回的14位数据在最高位,需要处理 data = (data & 0x3FFF); // 取低14位为角度值 return data; } void loop() { // 定期读取 SPI 编码器 unsigned int angle = readSPIEncoder(); Serial.print("SPI Encoder Angle: "); Serial.println(angle); delay(100); // 每100ms读取一次 }

这段代码展示了 SPI 通信的基本流程:初始化总线、控制片选引脚、使用SPI.transfer()SPI.transfer16()进行数据交换。不同的 SPI 设备(编码器)其通信协议(命令字、数据格式、时钟极性 CPHA 和相位 CPOL)可能不同,需要查阅具体的数据手册。在SPI.begin()后,有时还需要用SPI.setBitOrder(MSBFIRST);SPI.setDataMode(SPI_MODE1);等函数来配置通信模式,这必须与从设备严格匹配。

6.2 中断与 SPI 的权衡

使用中断方式读取增量编码器,优点是响应实时性极高,几乎无延迟,适合高速计数。缺点是需要占用两个中断引脚,并且在高转速或代码复杂时,频繁中断可能影响主程序运行。

使用 SPI 读取绝对编码器,优点是不占用中断资源,通过轮询方式获取绝对位置,数据更可靠,抗噪声能力强,且能获得绝对角度信息。缺点是读取速度受 SPI 时钟频率和轮询间隔限制,存在一定的延迟。

对于智能垃圾桶底盘这种对实时性要求不是极端高的应用,SPI 轮询方式通常足够可靠,且为未来增加更多 SPI 设备(如 SPI 接口的显示屏、Flash 存储等)提供了便利。这也是我倾向于为未来升级预留 SPI 接口的原因。

7. 系统集成、调试与常见问题排查

将电机驱动、编码器计数、电源管理集成到一起,并让底盘稳定受控地运行,是本期最后也是最考验耐心的一步。

7.1 完整系统接线与供电检查

在将所有模块焊接或牢固连接之前,强烈建议先在面包板上搭建整个系统,并逐一测试每个功能。完整的接线示意图如下(文字描述):

  1. 电源部分:7.4V 电池正极接降压模块输入正,输出 5V 接 Curie Nano 的 VIN 和 TB6612 的 VCC(逻辑电源)。电池正极同时接 TB6612 的 VMOT(电机电源)。所有模块的 GND 连接到一起,最后接到电池负极。
  2. 控制部分:Curie Nano 的 PWM 引脚 (5,6) 接 TB6612 的 PWMA/B;数字 IO (7,8,9,10) 接 TB6612 的 AIN1, AIN2, BIN1, BIN2。
  3. 反馈部分:编码器 A 相接中断引脚 (2,3),B 相接普通数字输入引脚 (4,12)。编码器电源接 3.3V 或 5V(需确认编码器工作电压),地接共同 GND。

上电前,务必用万用表检查电源线路,确保无短路。先单独给 Curie Nano 上电,测试程序能否下载和运行。然后再连接电机驱动和电机。

7.2 典型问题与排查思路

  • 问题一:电机不转或只振动。

    • 排查:首先检查电机驱动模块的 VMOT 电压是否正常(接近电池电压)。然后检查 STBY(待机)引脚是否已接高电平(如果模块有的话)。用analogWrite给一个较小的 PWM 值(如 100),同时用digitalWrite设置正确的方向引脚,用万用表测量驱动模块输出端是否有变化的电压。如果驱动模块输出正常,则检查电机本身和接线。
  • 问题二:编码器计数不准或为0。

    • 排查:首先确认编码器供电正常。在中断服务函数中,除了计数,可以尝试简单翻转一个 LED 的状态,观察在转动电机时 LED 是否闪烁,以确认中断是否被触发。如果触发但计数方向错误,调整中断服务函数中的方向判断逻辑(交换a==b的判断结果)。如果完全不触发,检查接线是否正确,特别是中断引脚是否连接到了编码器的 A 相。另外,机械编码器有时需要上拉电阻,如果模块没有内置,需要在信号线和电源之间接一个 10kΩ 的上拉电阻。Curie Nano 的INPUT_PULLUP模式内部提供了上拉,通常可以省去外接电阻。
  • 问题三:底盘严重跑偏或无法走直线。

    • 排查:这是开环控制的必然结果。首先,即使不给 PWM,用手推动底盘,检查两个轮子转动是否一样顺畅,机械结构是否对称。然后,在开环下,给两个电机相同的 PWM 值,用编码器计数功能查看两个轮子的实际转速是否一致。通常会发现差异。这就是我们需要闭环控制的原因。在引入 PID 之前,可以尝试手动微调两个电机的 PWM 补偿值,形成一个简单的“校准表”。
  • 问题四:系统运行时 Curie Nano 意外复位。

    • 排查:最可能的原因是电源问题。电机启动瞬间电流很大,可能导致电压骤降,使得微控制器复位。确保电池电量充足,连接线足够粗。可以在 Curie Nano 的 VIN 和 GND 之间并联一个较大容量的电解电容(如 100uF-470uF)作为储能缓冲。另外,检查代码中是否有数组越界、死循环或堆栈溢出等问题。

7.3 初步调试与功能验证

在确保各部分硬件工作正常后,可以编写一个综合测试程序:

  1. 让底盘以固定 PWM 值前进 5 秒,通过编码器数据计算实际走过的脉冲数,估算距离。
  2. 让底盘原地旋转 360 度(通过给两个电机相同大小、相反方向的 PWM),检查编码器计数是否对称,旋转是否准确。
  3. 尝试实现一个简单的 P 控制器(比例控制):设定一个目标速度(脉冲数/100ms),根据当前速度与目标的误差,调整 PWM 输出。newPWM = oldPWM + Kp * (targetSpeed - currentSpeed)。从小Kp值开始试,观察速度是否能稳定在目标值附近。

这个过程可能会反复多次,需要耐心记录数据、调整参数。当你能通过串口指令让底盘以相对稳定的速度前进、后退、转弯时,本期“底盘篇”的核心目标就圆满达成了。我们得到了一个“听话”的、具备基本感知(速度反馈)能力的移动平台,这为下一期集成超声波/红外避障传感器、摄像头进行目标识别,乃至最终实现垃圾桶的智能功能,奠定了无比坚实的物理基础。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/29 16:20:04

【AI大模型】知识库提示:结合外部信息的提示词设计

【AI大模型】知识库提示:结合外部信息的提示词设计(含实操代码) AI大模型存在天然的知识截止、场景盲区、实时信息缺失、专属业务知识空白等问题。模型内置的训练知识库存在时间滞后性,无法获取最新行业政策、实时数据、企业私有业务资料、自定义文档信息,直接提问极易出…

作者头像 李华
网站建设 2026/7/29 16:19:47

AI时代API安全防护:F5方案如何应对微服务与AI应用挑战

1. 项目概述&#xff1a;当AI浪潮撞上API洪流&#xff0c;安全防线如何重构&#xff1f; 最近和几个做企业架构和安全的老朋友聊天&#xff0c;话题总绕不开两件事&#xff1a;一是公司上下都在搞的“数字化转型”&#xff0c;恨不得把所有业务都搬到线上、做成服务&#xff1b…

作者头像 李华
网站建设 2026/7/29 16:14:15

抖音无水印下载终极指南:3分钟学会专业级视频保存技巧

抖音无水印下载终极指南&#xff1a;3分钟学会专业级视频保存技巧 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback suppo…

作者头像 李华
网站建设 2026/7/29 16:11:08

构建高性能虚拟化环境的5个关键技术策略

构建高性能虚拟化环境的5个关键技术策略 【免费下载链接】kvm-guest-drivers-windows Windows paravirtualized drivers for QEMU\KVM 项目地址: https://gitcode.com/gh_mirrors/kv/kvm-guest-drivers-windows virtio-win项目为KVM/QEMU虚拟化平台提供完整的Windows半虚…

作者头像 李华
网站建设 2026/7/29 16:10:18

测试转大模型:从一次踩坑讲到改进

《测试转大模型&#xff0c;真正值钱的为什么不是会调 API&#xff1f;》看起来是个大话题&#xff0c;但真落到项目里&#xff0c;常常就是几个具体选择。下面我尽量按实际开发时会遇到的问题来讲。摘要> 摘要&#xff1a;从一个 Demo 到生产环境&#xff0c;权限与日志才是…

作者头像 李华