智能小车(四):电机模块
星期日, 11月 10, 2024 | 6分钟阅读 | 更新于 星期日, 12月 8, 2024
历史回顾
概要
本节课关注于电机模块的使用。电机模块在小车中控制小车运行(前进、后退、停止)。
安全须知
什么是 PWM(脉宽调制)?
PWM(Pulse Width Modulation,脉宽调制)是一种模拟输出信号的方法,广泛用于控制电机、LED亮度等设备。尽管 Arduino 等微控制器不能直接输出模拟电压信号,但可以通过 PWM 产生模拟效果。
PWM 的原理
PWM 是通过快速开关数字信号(高电平和低电平)来模拟一个中间电压。具体来说,它将信号以一定频率周期性地在高电平(5V)和低电平(0V)之间切换。我们称这个周期内信号为高电平的时间占整个周期时间的比率为占空比。占空比越高,模拟出的电压值就越接近高电平(5V);占空比越低,模拟电压就越接近低电平(0V)。
例如,一个 50% 占空比的 PWM 信号,表示一半时间输出 5V,另一半时间输出 0V,这样模拟出的电压效果接近 2.5V。
PWM 示例
如果 PWM 的周期是 1ms,占空比为不同数值时的模拟效果如下:
- 占空比 0%:总是 0V,相当于输出模拟电压 0V。
- 占空比 25%:在 1ms 周期内,0.25ms 为 5V,0.75ms 为 0V,模拟电压接近 1.25V。
- 占空比 50%:在 1ms 周期内,0.5ms 为 5V,0.5ms 为 0V,模拟电压接近 2.5V。
- 占空比 75%:在 1ms 周期内,0.75ms 为 5V,0.25ms 为 0V,模拟电压接近 3.75V。
- 占空比 100%:总是 5V,相当于输出模拟电压 5V。
PWM 的用途
- 调节 LED 亮度:通过改变占空比,可以控制 LED 的亮度。占空比越高,LED 就越亮;占空比越低,LED 就越暗。
- 控制电机转速:通过 PWM 调节电机的供电时间,可以实现电机转速的精确控制。占空比越高,电机转速越快。
- 产生音频信号:改变 PWM 的频率和占空比,可以产生不同的音调,用于简单的声音生成。
在 Arduino 中使用 PWM
在 Arduino 中,PWM 主要通过以下两种方式实现:
硬件 PWM 引脚:Arduino 的部分引脚具备硬件 PWM 功能(通常标有 ~ 符号,例如 3, 5, 6, 9, 10, 11),可以直接通过
analogWrite()
函数输出 PWM 信号。软件 PWM:使用
SoftPWM.h
库,可以在任意数字引脚上实现 PWM,但这种方式占用更多的处理器资源。
analogWrite()
函数
Arduino 的 analogWrite(pin, value)
函数可以直接在支持 PWM 的引脚上输出 PWM 信号。
- 参数:
pin
:支持 PWM 的引脚(例如 3, 5, 6, 9, 10, 11)。value
:0 到 255 之间的数值,表示占空比。0 表示 0% 占空比(0V),255 表示 100% 占空比(5V)。
示例:使用 analogWrite()
控制 LED 亮度
int ledPin = 9; // 支持 PWM 的引脚
void setup() {
pinMode(ledPin, OUTPUT); // 将引脚设置为输出模式
}
void loop() {
for (int brightness = 0; brightness <= 255; brightness += 5) {
analogWrite(ledPin, brightness); // 设置 PWM 占空比,逐步提高亮度
delay(30); // 延时,便于观察亮度变化
}
for (int brightness = 255; brightness >= 0; brightness -= 5) {
analogWrite(ledPin, brightness); // 设置 PWM 占空比,逐步降低亮度
delay(30); // 延时
}
}
在上面的代码中,analogWrite() 函数会将 LED 的亮度逐渐增大,再逐渐减小,产生“呼吸灯”效果。
使用 SoftPWM.h 实现软件 PWM
SoftPWM.h
库提供了软件 PWM 的功能,允许通过软件控制 PWM 输出,用于驱动电机或控制 LED 亮度等。该库适用于没有硬件 PWM 的引脚,通过软件生成 PWM 信号控制外设。
关键API
SoftPWMBegin();
:初始化软件 PWM 系统。应在 setup() 函数中调用。
SoftPWMBegin(); // 初始化软件 PWM
SoftPWMSet(pin, value);
:设置指定引脚的 PWM 输出值。- 参数:pin:要输出 PWM 信号的引脚。
- value:PWM 信号的占空比,范围为 0-255(0 表示低电平,255 表示高电平)。
SoftPWMSet(4, 128); // 将 4 号引脚的 PWM 输出设置为 50%(128/255)
SoftPWMEnd(pin);
:停止在指定引脚上的 PWM 信号输出。
SoftPWMEnd(4); // 停止 4 号引脚的 PWM 输出
PWM 是一种通过快速切换数字信号模拟模拟电压的方法。Arduino 使用 PWM 来控制 LED 亮度、电机转速等外设,是电子项目中非常常见的控制方式。
示例代码
#include <SoftPWM.h>
void setup() {
SoftPWMBegin(); // 初始化软件 PWM 系统
}
void loop() {
SoftPWMSet(4, 200); // 设置 4 号引脚的 PWM 信号为 200,占空比接近 80%
delay(1000); // 保持 1 秒
SoftPWMSet(4, 0); // 关闭 PWM 输出
delay(1000); // 停止 1 秒
}
电机运用
#include <SoftPWM.h>
int LeftBack = 2; //2电机引脚
int LeftFront = 4; //4电机引脚
int RightBack = 7; //7电机引脚
int RightFront =8; //8电机引脚
void setup() {
pinMode(LeftBack, OUTPUT);
pinMode(LeftFront, OUTPUT);
pinMode(RightBack, OUTPUT);
pinMode(RightFront, OUTPUT);
Serial.begin(9600);
SoftPWMBegin();
}
void loop() {
SoftPWMSet(LeftFront, 200);
SoftPWMSet(RightFront, 200);
SoftPWMSet(LeftBack, 0);
SoftPWMSet(RightBack, 0);
delay(500);
SoftPWMSet(LeftFront, 0);
SoftPWMSet(RightFront, 0);
SoftPWMSet(LeftBack, 0);
SoftPWMSet(RightBack, 0);
delay(500);
SoftPWMSet(LeftFront, 0);
SoftPWMSet(RightFront, 0);
SoftPWMSet(LeftBack, 200);
SoftPWMSet(RightBack, 200);
delay(500);
SoftPWMSet(LeftFront, 0);
SoftPWMSet(RightFront, 0);
SoftPWMSet(LeftBack, 0);
SoftPWMSet(RightBack, 0);
delay(500);
}
系统说明
根据提供的信息,车辆的控制系统实际上是:
- 前轮:由舵机控制,负责转向
- 后轮:由电机控制,负责驱动
这种配置通常被称为"阿克曼转向"(Ackermann steering),常见于汽车和许多机器人平台。
问题与解析
- 这段代码中使用了软件PWM(SoftPWM)而不是硬件PWM来控制电机,这种选择可能带来什么优势和劣势,采用软件PWM如何控制电机的?
提示 考虑硬件PWM引脚数量限制,以及软件PWM在灵活性和资源占用上的特点。
解析:
软件PWM和硬件PWM各有优劣:
优势:
- 灵活性:软件PWM可以在任何数字引脚上实现,不受硬件PWM引脚数量的限制。
- 可扩展性:可以轻松增加PWM通道数量。
- 频率控制:可以更灵活地调整PWM频率。
劣势:
- CPU占用:软件PWM需要持续占用CPU资源。
- 精度:相比硬件PWM,软件PWM的精度可能较低,尤其在高频率时。
- 定时干扰:可能会影响其他依赖精确定时的操作。
在这个项目中,使用软件PWM允许我们在多个引脚上同时实现PWM控制,这对于同时控制多个电机是很有用的。
- 代码中电机控制的序列(前进、停止、后退、停止)形成了一个完整的周期。如果要让车辆实现转弯动作,应该如何修改代码?
提示 考虑舵机控制前轮转向和电机驱动后轮的配合。
解析:
在这种配置下,转弯主要由前轮舵机控制,后轮电机提供动力。实现转弯的步骤:
- 使用舵机控制前轮转向
- 用后轮电机提供前进或后退动力
代码修改示例:
void turnRight() {
myservo.write(120); // 前轮向右转(角度可调)
delay(100); // 给舵机一些时间到达位置
// 后轮提供动力
SoftPWMSet(LeftBack, 200);
SoftPWMSet(RightBack, 200);
delay(500); // 转弯持续时间
// 停止并回正
SoftPWMSet(LeftBack, 0);
SoftPWMSet(RightBack, 0);
myservo.write(90); // 前轮回正
}
模块整合
如何将舵机和电机进行有效结合?
- 代码优化
- 提高代码可读性:通过将功能相似的代码封装成函数,loop()函数变得更加简洁,逻辑更清晰。
- 便于维护和扩展:如果需要修改某个动作的实现,只需修改对应的函数,无需遍历整个代码。
- 代码重用性:封装的函数可以在其他地方重复使用,避免代码冗余。
#include <Servo.h>
#include <SoftPWM.h>
Servo myservo; // 舵机对象
// 电机引脚定义
const int LeftBack = 2;
const int LeftFront = 4;
const int RightBack = 7;
const int RightFront = 8;
// 舵机引脚
const int TurnPin = 9;
// 初始化设置
void setup() {
// 设置电机引脚为输出模式
pinMode(LeftBack, OUTPUT);
pinMode(LeftFront, OUTPUT);
pinMode(RightBack, OUTPUT);
pinMode(RightFront, OUTPUT);
Serial.begin(9600);
// 舵机初始化
myservo.attach(TurnPin);
// 初始化软件PWM
SoftPWMBegin();
}
// 控制舵机转向的函数,增加异常控制
void setServoAngle(int angle, int delayTime) {
// 限制角度范围
if (angle > 160) {
angle = 160;
} else if (angle < 20) {
angle = 20;
}
myservo.write(angle); // 设置舵机角度
delay(delayTime); // 延时
}
// 用于控制四个电机的速度,并在运行后延时一段时间,然后停止电机。
void controlMotors(int leftFrontSpeed, int rightFrontSpeed, int leftBackSpeed, int rightBackSpeed, int delayTime) {
SoftPWMSet(LeftFront, leftFrontSpeed);
SoftPWMSet(RightFront, rightFrontSpeed);
SoftPWMSet(LeftBack, leftBackSpeed);
SoftPWMSet(RightBack, rightBackSpeed);
delay(delayTime);
// 停止电机
SoftPWMSet(LeftFront, 0);
SoftPWMSet(RightFront, 0);
SoftPWMSet(LeftBack, 0);
SoftPWMSet(RightBack, 0);
}
// 主循环函数
void loop() {
// 控制舵机
setServoAngle(90, 1000); // 中间位置
setServoAngle(160, 1000); // 向右转
setServoAngle(40, 1000); // 向左转
setServoAngle(180, 1000); // 超过最大角度,自动设为160
setServoAngle(10, 1000); // 低于最小角度,自动设为40
// 前进
controlMotors(200, 200, 0, 0, 500);
// 后退
controlMotors(0, 0, 200, 200, 500);
}