一起玩儿平衡车(ESP32)——06 平衡车控制类的编写及驱动死区的确定

本文详细介绍了如何在平衡车软件开发中实现车辆的站立和基本控制,包括PID参数调节、系统初始化、电机控制的PWM使用,以及如何测量并处理驱动死区。通过实例展示了小车类的创建和关键方法,如motor()函数的实现与死区补偿。
摘要由CSDN通过智能技术生成

摘要:本文介绍如何编写平衡车控制类以及确定驱动死区

从本篇文章开始,将介绍平衡车软件的开发及调试方法。平衡车开发中,最关键的就是要让小车能够稳定的站立起来,这是最基础也是最核心的一步,除了软件开发工作,这一步的关键就是PID参数的调节。

下面就来逐步的实现,让平衡车先站立起来的目标。为了实现这个目标的软件结构并不复杂,大致流程如下图所示:

系统初始化部分包括了对蓝牙串口(主要用于PID调试和反馈小车状态)、LEDC控制器初始化和姿态控制器MPU6050初始化这三个部分。

平衡车处理器进入正常运行阶段之后,就是重复在做一件事情,根据姿态控制器的姿态信息来改变对小车电机的控制,通过这种不停的动态调整,来达到一种动态平衡的状态,使平衡车保持在平衡位置附近。

接下来就来实现一个小车类,这个类定义了与驱动小车电机相关的各种配置信息和方法。先来看一下类的声明,以及在头文件中定义的一些配置信息。如下所示:

/*********************************

    car.h

    by 一起玩儿科技   2024/02/23

 ********************************/

#pragma once

#ifndef _YQWKJ_BALANCE_CAR_

#define _YQWKJ_BALANCE_CAR_

#include <Arduino.h>

// 默认控制引脚

#define R_IN1 25

#define R_IN2 27

#define L_IN1 32

#define L_IN2 17

// PWM通道占用

#define L_CH1 0

#define L_CH2 1

#define R_CH1 2

#define R_CH2 3

//左右轮死区pwm值

#define LEFT_MOTOR_PWM_OFFSET 41

#define RIGHT_MOTOR_PWM_OFFSET 41

class Car {

public:

  // 构造函数

  Car();

  // 初始化方法

  void begin();

  void motor(int leftPWM, int rightPWM);

private:

  // 私有属性

  // 两轮的驱动引脚

  uint8_t l1 = L_IN1, l2 = L_IN2, r1 = R_IN1, r2 = R_IN2;

  // 处理偏移量

  int calcPwmOffset(int pwm, int offset);

  // 输出PWM

  void outputPWM(int pwm, int ch0, int ch1);

};

#endif

关于类的一些基本知识和编写方法,在前面已经介绍了,如有疑问,请参考之前的文章。

在这个头文件中,定义了驱动电机所适用的引脚,以及所占用的PWM通道的情况。所定义的两个方法,其中begin()方法用于完成与小车相关的各种初始化工作,motor()方法用于实现对小车的驱动。关于PWM的相关知识可以参考之前的文章。

motor()方法的两个参数就是PID算法所计算出来的控制输入值,该值为整型,可正可负。符号表示车轮需要转动的方向,而绝对值则表示需要车轮转动的快慢,绝对值越大,要求车轮转动的越快。

接下来就看一下这个类的实现方法,如下所示:

/*********************************

    car.c

    by 一起玩儿科技   2024/02/23

 ********************************/

#include "car.h"

// 无参构造函数

Car::Car() {}

// 初始化方法

void Car::begin() {

  uint8_t pins[] = { L_IN1, L_IN2, R_IN1, R_IN2 };

  uint8_t chs[] = { L_CH1, L_CH2, R_CH1, R_CH2 };

  for (int i = 0; i < 4; i++) {

    ledcSetup(chs[i], 1000, 8);

    ledcAttachPin(pins[i], chs[i]);

  }

}

// 补偿函数

int Car::calcPwmOffset(int pwm, int offset) {

  if(pwm>0){

    pwm += offset;

  } else if(pwm<0) {

    pwm -= offset;

  }

  return constrain(pwm, -255, 255);

}

// 输出PWM

void Car::outputPWM(int pwm, int ch0, int ch1) {

    if (pwm == 0) {

    ledcWrite(ch0, 0);

    ledcWrite(ch1, 0);

  } else if (pwm < 0) {

    ledcWrite(ch0, 0);

    ledcWrite(ch1, 0 - pwm);

  } else {

    ledcWrite(ch0, pwm);

    ledcWrite(ch1, 0);

  }

}

// 驱动轮子运动

void Car::motor(int leftPWM, int rightPWM) {

  //-----补偿左右轮差值

  leftPWM = calcPwmOffset( leftPWM, LEFT_MOTOR_PWM_OFFSET);

  rightPWM = calcPwmOffset( rightPWM, RIGHT_MOTOR_PWM_OFFSET);

  // 输出控制PWM

  outputPWM( leftPWM, L_CH1, L_CH2 );

  outputPWM( rightPWM, R_CH1, R_CH2 );

}

初始化方法就不再解释了,之前的文章已经见过多次了。下面重点来看一下motor()方法,这个方法的作用就是用PID算法得到的控制量,来驱动平衡车的车轮。

在现在这个初始阶段,我们对小车车轮的驱动控制是开环控制,注意,这里指的是通过PWM信号控制马达转动这件事情,这是一个开环控制,因为我们给定一个PWM信号之后,并不会去关心车轮实际的转速是多少,反过来,车轮的转速也不会影响到我们输出的PWM信号的占空比。我们只是简单的认为,车轮的转速与PWM信号的占空比是成正比例的,也就是PWM信号占空比越大,小车车轮的转速越大。在后边,我会介绍如何通过霍尔编码器来测量小车的转速,从而实现小车车轮驱动的闭环控制。在当前阶段,实现小车的站立和基本的行走功能,使用开环控制小轮也完全能够实现。

在前面的讲解中已经介绍过了,在Arduino IDE中,可以使用ledcWrite()函数来输出PWM信号,该函数所接受的参数取值范围是0~255,对应着输出PWM信号的占空比为0~100%。理想情况是ledcWrite()的参数在0~255之间从小到大的变动时,车轮转速也会从慢到快的线性转动。但实际情况不是这样的,制作精良的电机,线性度会稍微好一些,而质量差的电机,线性度就没有保证了。但是,无论怎样一个电机,都会存在一个死区,也就是说电机不是驱动信号从0变到1就立刻开始运动,而是当占空比小于某个值的时候,电机根本不会转动。因此我们要剔除这个死区的影响,要找到电机不动的最大占空比,也就是可以保持电机不动的最大ledcWrite()参数值。

我的两个电机测试出来的死区最大驱动值是41,定义在了小车类的头文件中,如下所示:

//左右轮死区pwm值

#define LEFT_MOTOR_PWM_OFFSET 41

#define RIGHT_MOTOR_PWM_OFFSET 41

下面来介绍一些如何测量这个死区的最大值。首先要保证可以用ESP32处理器正确的驱动小车的车轮,这就要求你完成前面所有的组装测试工作。测量的方法就是从0开始,依次增加ledcWrite()的参数值,每次+1,找到轮子刚好能转动的ledcWrite()参数值(后边称此值为最小转动值),再减去1,就是驱动参数死区的最大值。在寻找最小转动值的时候,可以使用用手辅助启动,也就是朝着旋转的方向,转动一下轮子,看看轮子是否能够转动起来。这个测试可以多进行几次,最后取一个平均值。除了从小到大的测试,还可以从大到小进行测试,找到最后转动的值,就是最小转动值。恰好停下来的值就是死区最大值。

可以用之前的程序修改一下来进行这个测试工作,在这里我也给出一个简单的测试程序,需要注意的是,在使用这个测试程序的时候,需要先把car.h文件中定义的死区值修改为0,如下所示:

//左右轮死区pwm值

#define LEFT_MOTOR_PWM_OFFSET 0

#define RIGHT_MOTOR_PWM_OFFSET 0

测试程序的代码如下所示:

#include "BluetoothSerial.h"

#include "car.h"

int pwm = 0;

Car car;

BluetoothSerial SerialBT;

void serialDebug() {

  if (SerialBT.available() > 0 || Serial.available() > 0) {

    //delay(5);

    char CMD = SerialBT.available() > 0 ? SerialBT.read() : Serial.read();

    Serial.println(CMD);

    switch (CMD) {

      case 'r':  // 机械平衡点调整

        pwm++;

        break;

      case 'f':

        pwm--;

        break;

    }

    pwm = constrain(pwm, 0, 255);

    SerialBT.print("当前pwm:");

    SerialBT.println(pwm);

  }

}

void setup() {

  // put your setup code here, to run once:

  SerialBT.begin("Balance CAR");

  Serial.begin(115200);

  // 车初始化

  car.begin();

  car.motor(0, 0);

  Serial.println("Init OK......");

}

void loop() {

  // put your main code here, to run repeatedly:

  serialDebug();

  car.motor(pwm, pwm);  //马达输出

  delay(100);

}

在这个程序中,通过串口或者蓝牙,发送小写的“r”字符,就可以不断的增加pwm的值,“f”字符会减小pwm的值。希望多一些耐心,定会找到这两个车轮的死区最大值。

好了,朝着完成的目标又前进了一步。下期见!

  • 30
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一起玩儿科技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值