MENU
- 前言
- 原理
- 硬件电路设计
- 软件程序设计
- LEDC输出PWM信号
- 使用第三方库控制舵机
- 网页控制舵机
前言
舵机在电子产品中非常常见,比如四足机器人、固定翼航模等都有应用,因此学习舵机对电子制作非常有意义。本文章使用Arguino的PWM对SG90舵机旋转角度控制。
原理
舵机是一种位置(角度)伺服的驱动器,适用于那些需要角度不断变化并可以保持的控制系统。舵机只是一种通俗的叫法,其本质是一个伺服电机。
舵机有很多规格,但所有的舵机都有外接三根线,分别用棕、红和橙三种颜色进行区分,由于舵机品牌不同,颜色也会有所差异,棕色为接地线,红色为电源正极线,橙色为信号线。只要通过信号线给予规定的控制信号即可实现舵机码盘的转动。
SG90的主要电气参数
使用电压: 4.8V~6V
尺寸: 221.5mm x 11.8mm x 22.7mm
重量: 9g
角度范围: 0-180°
舵机的工作原理是由接收机或者单片机发出信号给舵机,其内部有一个基准电路,将获得的直流偏置电压与电位器的电压比较,获得电压差输出。经由电路板上的IC判断转动方向,再驱动无核心马达开始转动,透过减速齿轮将动力传至摆臂,同时由位置检测器送回信号,判断是否已经到达定位。当电机转速一定时,通过级联减速齿轮带动电位器旋转,使得电压差为0,电机停止转动。一般舵机旋转的角度范围是0度到180度,当然也有0度到360度。
没有必要了解舵机的内部结构,只需要知道如何通过PWM控制其转动即可。舵机的控制就是通过一个固定的频率,给其不同的占空比的,来控制舵机不同的转角。
舵机的转动的角度是通过调节PWM(脉冲宽度调制)信号的占空比来实现的,标准PWM(脉冲宽度调制)信号的周期固定为20ms(50Hz),理论上脉宽分布应在1ms到2ms之间,但是,事实上脉宽可由0.5ms到2.5ms之间,脉宽和舵机的转角0°~180°相对应。有一点值得注意的地方,由于舵机牌子不同,对于同一信号,不同牌子的舵机旋转的角度也会有所不同。
0.5~2.5ms的PWM高电平部分对应控制180度舵机的0~180度,对应的控制关系。
高电平占整个周期(20ms)的时间 舵机旋转的角度 对应的占空比 0.5ms 0° 0.5//20 1ms 45° 1//20 1.5ms 90° 1.5//20 2ms 135° 2//20 2.5ms 180° 2.5//20
硬件电路设计
材料名称 数量 舵机 1 杜邦线(跳线) 3
注意接线顺序。
软件程序设计
LEDC输出PWM信号
首先,使用LEDC输出PWM信号,根据之前的实验原理,可以确定频率、最大脉宽与最小脉宽。
// 1/20秒,50Hz的频率,20ms的周期,这个变量用来存储时钟基准 #define FREQ 50 // 通道 // 高速通道(0~7)由80MHz时钟驱动 // 低速通道(8~15)由1MHz时钟驱动 #define CHANNEL 0 // 分辨率设置为8,就是2的8次方,用256的数值来映射角度 #define RESOLUTION 8 // 定义舵机PWM控制引脚 #define SERVO 13 // 定义函数用于输出PWM的占空比 int calculatePWM(int degree) { // 20ms周期内,高电平持续时长0.5-2.5ms,对应0-180度舵机角度 // 对应0.5ms(0.5ms/(20ms/256)) float min_width = 0.6 / 20 * pow(2, RESOLUTION); // 对应2.5ms(2.5ms/(20ms/256)) float max_width = 2.5 / 20 * pow(2, RESOLUTION); if (degree < 0) degree = 0; if (degree > 180) degree = 180; // 返回度数对应的高电平的数值 return (int)(((max_width - min_width) / 180) * degree + min_width); } void setup() { // 用于设置LEDC通道的频率和分辨率 ledcSetup(CHANNEL, FREQ, RESOLUTION); // 将通道与对应的引脚连接 ledcAttachPin(SERVO, CHANNEL); } void loop() { for (int i = 0; i <= 180; i += 10) { // 输出PWM,设置LEDC通道的占空比 ledcWrite(CHANNEL, calculatePWM(i)); delay(1000); } }
使用第三方库控制舵机
如果想使用Arduino控制舵机就需要在ESP32Servo库,点击
项目
,选择加载库
中的管理库
。然后输入ESP32Servo,点击安装即可。
可以在VSCode的PlatformIO中,根据案例了解ESP32Servo库的使用方法。
#include <ESP32Servo.h> #define SERVO_PIN 13 #define MAX_WIDTH 2500 #define MIN_WIDTH 500 // 定义servo对象 Servo my_servo; void setup() { // 分配硬件定时器 ESP32PWM::allocateTimer(0); // 设置频率 my_servo.setPeriodHertz(50); // 关联servo对象与GPIO引脚,设置脉宽范围 my_servo.attach(SERVO_PIN, MIN_WIDTH, MAX_WIDTH); } void loop() { my_servo.write(180); delay(1000); my_servo.write(0); delay(1000); }
网页控制舵机
代码
#include <WiFi.h> #include <WebServer.h> #include <math.h> // Wi-Fi 网络名称和密码 const char* ssid = "ShiLai"; const char* password = "MJgn20240904$"; // Web服务器对象 WebServer server(80); // 频率、通道、分辨率和舵机引脚定义 #define FREQ 50 #define CHANNEL 0 #define RESOLUTION 8 #define SERVO 13 // 定义函数用于输出PWM的占空比 int calculatePWM(int degree) { // 20ms周期内,高电平持续时长0.5-2.5ms,对应0-180度舵机角度。 float min_width = 0.6 / 20 * pow(2, RESOLUTION); float max_width = 2.5 / 20 * pow(2, RESOLUTION); if (degree < 0) degree = 0; if (degree > 180) degree = 180; return (int)(((max_width - min_width) / 180) * degree + min_width); } void setup() { // 设置串口波特率 Serial.begin(9600); // 连接到Wi-Fi WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("加载中..."); } Serial.print("\nIP地址:"); Serial.println(WiFi.localIP()); // 用于设置 LEDC 通道的频率和分辨率 ledcSetup(CHANNEL, FREQ, RESOLUTION); // 将通道与对应的引脚连接 ledcAttachPin(SERVO, CHANNEL); // 设置处理网页请求的函数 server.on("/", handleRoot); server.on("/set", handleServo); // 启动Web服务器 server.begin(); } void loop() { // 处理网页请求 server.handleClient(); } // 处理根页面的请求 void handleRoot() { String html = "<!DOCTYPE html>"; html += "<html lang='en'>"; html += "<head>"; html += "<meta charset='UTF-8'>"; html += "<meta name='viewport' content='width=device-width, initial-scale=1.0'>"; html += "<title>网页控制舵机</title>"; html += "</head>"; html += "<body>"; html += "<h1>网页控制舵机</h1>"; html += "<input id='idInpu' type='number' min='0' max='180' value='0' />"; html += "<button style='margin-left: 8px;' οnclick='handleSetAngle()'>设置角度</button>"; html += "<p id='idP'></p>"; html += "<script>"; html += "let idP = document.getElementById('idP');"; html += "handleSetAngle();"; html += "function handleSetAngle() {"; html += "let idInpu = document.getElementById('idInpu').value;"; html += "let xhttp = new XMLHttpRequest();"; html += "idP.innerHTML = idInpu;"; html += "xhttp.open('GET', '/set?angle=' + idInpu, true);"; html += "xhttp.onreadystatechange = function() {"; html += "let isCode = this.readyState === 4 && this.status === 200;"; html += "if (isCode) idP.innerHTML = this.responseText;"; html += "};"; html += "xhttp.send();"; html += "}"; html += "</script>"; html += "</body>"; html += "</html>"; server.send(200, "text/html", html); } // 处理舵机角度设置的请求 void handleServo() { String angleStr = server.arg("angle"); int angle = angleStr.toInt(); // 检查角度是否在0到180度之间 if (angle >= 0 && angle <= 180) { ledcWrite(CHANNEL, calculatePWM(angle)); server.send(200, "text/html", "Angle set to: " + angleStr); } else { server.send(200, "text/html", "Invalid angle. Please enter a value between 0 and 180."); } }
解析
代码段使用ESP32通过Wi-Fi建立一个Web服务器,允许用户通过网页输入角度来控制舵机的旋转角度。
01、引入库和定义常量
#include <WiFi.h> #include <WebServer.h> #include <math.h>
WiFi.h
用于连接Wi-Fi网络。
WebServer.h
用于创建一个HTTP Web服务器。
math.h
包含数学函数库,用于进行角度与PWM信号的计算。
02、Wi-Fi 网络名称和密码
const char* ssid = "ShiLai"; const char* password = "MJgn20240904$";
ssid和password定义需要连接的Wi-Fi网络的名称和密码。
03、Web服务器对象
WebServer server(80);
创建一个WebServer对象,端口号为80,表示HTTP服务器的默认端口。
04、舵机控制的定义
#define FREQ 50 #define CHANNEL 0 #define RESOLUTION 8 #define SERVO 13
FREQ: PWM信号的频率设置为50Hz(标准舵机的工作频率)。
CHANNEL: LEDC通道编号,ESP32上使用硬件PWM控制舵机。
RESOLUTION: PWM信号的分辨率设置为8位。
SERVO: 舵机信号线连接的引脚编号为13。
05、定义函数用于输出PWM的占空比
int calculatePWM(int degree) { float min_width = 0.6 / 20 * pow(2, RESOLUTION); float max_width = 2.5 / 20 * pow(2, RESOLUTION); if (degree < 0) degree = 0; if (degree > 180) degree = 180; return (int)(((max_width - min_width) / 180) * degree + min_width); }
calculatePWM函数用于将舵机的角度(0~180度)转换为对应的PWM占空比。
min_width和max_width分别表示对应于0度和180度时的PWM占空比宽度。
通过线性插值计算出给定角度对应的PWM信号。
06、setup函数
void setup() { Serial.begin(9600); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("加载中..."); } Serial.print("\nIP地址:"); Serial.println(WiFi.localIP()); ledcSetup(CHANNEL, FREQ, RESOLUTION); ledcAttachPin(SERVO, CHANNEL); server.on("/", handleRoot); server.on("/set", handleServo); server.begin(); }
Serial.begin(9600)
初始化串口通信,波特率为9600,用于调试信息输出。
WiFi.begin(ssid, password)
尝试连接Wi-Fi网络,等待连接成功。
ledcSetup(CHANNEL, FREQ, RESOLUTION)
配置LEDC通道,设置PWM频率和分辨率。
ledcAttachPin(SERVO, CHANNEL)
将LEDC通道与舵机信号引脚连接。
server.on("/", handleRoot)
设置根路径的HTTP请求处理函数。
server.on("/set", handleServo)
设置用于调整舵机角度的HTTP请求处理函数。
server.begin()
启动Web服务器。
07、loop函数
void loop() { server.handleClient(); }
loop函数中调用server.handleClient(),用于不断处理来自客户端的HTTP请求。
08、处理根页面的请求
void handleRoot() { String html = "<!DOCTYPE html>..."; server.send(200, "text/html", html); }
handleRoot函数生成一个HTML页面,用户可以在该页面中输入舵机角度,并通过点击按钮发送请求以设置舵机角度。
server.send(200, “text/html”, html)向客户端发送HTML内容。
09、处理舵机角度设置的请求
void handleServo() { String angleStr = server.arg("angle"); int angle = angleStr.toInt(); if (angle >= 0 && angle <= 180) { ledcWrite(CHANNEL, calculatePWM(angle)); server.send(200, "text/html", "Angle set to: " + angleStr); } else { server.send(200, "text/html", "Invalid angle. Please enter a value between 0 and 180."); } }
handleServo函数接收HTTP请求中的angle参数,并将其转换为整数。
检查角度是否在0到180度之间,若在范围内则调用ledcWrite函数设置舵机的PWM信号,否则返回错误信息。
10、总结
代码段在ESP32上实现一个Web服务器,允许用户通过网页界面输入舵机的角度,服务器接收到请求后将角度转换为PWM信号以控制舵机旋转。
效果图