一起玩儿物联网人工智能小车(ESP32)——56. 利用光敏电阻实现追光小车(二)

摘要:本文介绍使用光敏电阻实现追光小车

追光小车的基本功能就是可以向着光强更强的地方行驶。在这一基本目标的指引下,是有很多种方案可以选择的。至于哪种方案更合适,则取决于你要实现什么样的追光功能。例如对于一个普通小车,可以实现跟随光源左右摆动,这是最简单的追光动作了。复杂一点儿的可以追随光源向前、向后运动。如果是麦克纳姆轮小车,那就还可以做左右移动的动作,甚至各个方向的平移都可以通过光源来控制实现。

在本文所实现的追光小车,其功能主要是可以在光的指引下向左行驶、向右行驶或者执行,当指引光线消失的时候,小车停止运行。先看一下光敏电阻传感器的安装方法,如下图所示:

一起玩儿物联网人工智能小车(ESP32)——56. 利用光敏电阻实现追光小车(二)_第1张图片

下面来看一下追光小车的具体接线方法:

模块

引脚

连接对象

L298N模块

IN1

ESP32的P18

IN2

ESP32的P23

IN3

ESP32的P32

IN4

ESP32的P33

OUT1、OUT2

右前轮TT电动机

OUT3、OUT4

左前轮TT电动机

+12V

电池的正极

GND

电池的负极和ESP32的GND(分别连接)

+5V

ESP32的+5V(开发时不需要连接,运行时连接)

左一光敏电阻模块

AO输出

ESP32的P34

VCC

ESP32的+3.3V

GND

ESP32的GND

左二光敏电阻模块

AO输出

ESP32的P35

VCC

ESP32的+3.3V

GND

ESP32的GND

右一光敏电阻模块

AO输出

ESP32的P15

VCC

ESP32的+3.3V

GND

ESP32的GND

右二光敏电阻模块

AO输出

ESP32的P25

VCC

ESP32的+3.3V

GND

ESP32的GND

在这里需要提醒的是,四个光敏电阻使用的电源电压为3.3V,这样AO输出的电压才不会超过3.3V。ESP32扩展板上面有个5V和3V3的跳线,可以将那两排排针的电压通过跳线设置成3.3V,在开发这个实验的时候,需要改变一下跳线的位置。

根据前面光敏电阻模块的安装布局,追光小车的实现的功能大致如下:

  1. 借用之前循迹小车实现的Car类,在这里就不进一步的解释了,直接拿过来用就可以了。
  2. 实现一个环境检测功能,让小车可以原地转动一定的角度,并且多次进行数据的采样,然后计算出采样数据的平均值,作为光敏模块的初值。

在这里提醒一下,光敏电阻模块对光的反馈还是很灵敏的,因此测试环境一方面是不能太亮,不能是太阳直射的地方,另一方面是光要比较均匀,小车放置在地上,不能有明显的影子。

  1. 根据实时采集的光敏电阻数值的变化量,来判断小车该往哪个方向行走。
  2. 利用ESP32的蓝牙功能,实现远程调试,并且可以通过远程发送命令来控制小车。

下面就来分段看一下追光小车的实现方法。首先还是定义一系列的常量,其中包括光敏电阻模块所使用的引脚,光敏电阻的数量,光强测试的间隔以及次数等。如下所示:

// 光敏电阻GPIO

#define PHOTORESISTER_LEFT_1 34

#define PHOTORESISTER_LEFT_2 35

#define PHOTORESISTER_RIGHT_1 15

#define PHOTORESISTER_RIGHT_2 25

// GPIO config end

#define PHOTORESISTER_NUMBERS 4

#define TEST_TIME_INTERVAL 200  //测试时间间隔

#define TEST_TIMES 40           //测试次数

接下来就是实现几个全局变量了。具体使用的全局变量如下所示:

// 全局常量

// 光敏电阻引脚数组

const uint8_t photoresister_pin[] = { PHOTORESISTER_LEFT_1, PHOTORESISTER_LEFT_2, PHOTORESISTER_RIGHT_1, PHOTORESISTER_RIGHT_2 };

// 记录每一个光敏电阻的初值

uint16_t envir_values[PHOTORESISTER_NUMBERS] = { 0, 0, 0, 0 };

// 所有光敏电阻总的初值

uint16_t total_value = 0;

// 小车对象

Car car;

// 蓝牙串口对象

BluetoothSerial SerialBT;

// 小车运行状态

uint8_t status_run = 0;

每个变量我都增加了注释,可以结合下面的程序来理解。下面就是环境初值的测试方法了。这个方法的执行过程大致是让首先让小车朝一个方向原地转动起来,然后按照预先设计的间隔,依次采集4个光敏电阻模块的测量值,并计算求和。最后,再算出测量的平均值以及总值。这两个值将作为后边进行判断的阈值使用。具体的实现代码如下:

// 测试环境初值

void test_environment() {

  // 原地测试光线强度

  car.turnLeft();

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

    delay(TEST_TIME_INTERVAL);

    for (int j = 0; j < PHOTORESISTER_NUMBERS; j++) {

      envir_values[j] += analogRead(photoresister_pin[j]);

    }

  }

  car.stop();

  // 计算求平均数

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

    envir_values[i] = envir_values[i] / TEST_TIMES;

    total_value += envir_values[i];

    SerialBT.print(envir_values[i]);

    SerialBT.print(" ");

  }

  SerialBT.println();

}

接下来就是追光行走方法了。这个方法就是采集光敏电阻的输出值,然后与初始阈值进行比较,如果总体光强明显变强,那么就说明有光照在小车前,小车需要运动,然后再判断运动的方向,如果左侧光强明显强于右侧,则向左转弯行驶,如果右侧光强明显强于左侧,则小车向右转弯行驶,否则小车就执行。具体代码如下所示:

// 采集每一个光敏电阻的输出值,并返回总值

uint16_t test_photoresister(uint16_t* vals) {

  uint16_t total = 0;

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

    vals[i] = analogRead(photoresister_pin[i]);

    total += vals[i];

  }

  return total;

}

// 追光行走

void follow_spot() {

  uint16_t test_values[PHOTORESISTER_NUMBERS];

  uint16_t total = test_photoresister(test_values);

  SerialBT.print("O:");

  SerialBT.print(total_value);

  SerialBT.print("\tc:");

  SerialBT.print(total);

  if (total * 1.1 <= total_value) {     // 是否明显光线变强

    uint16_t l_total = test_values[0] + test_values[1] - envir_values[0] - envir_values[1];

    uint16_t r_total = test_values[2] + test_values[3] - envir_values[2] - envir_values[3];

    if (fabs(l_total) * 1.05 < fabs(r_total)) {     // 是否右侧变化强于左侧

      car.turnRight();

    } else if (fabs(l_total) > fabs(r_total) * 1.05) {      // 是否左侧变化强于右侧

      car.turnLeft();

    } else {

      car.run();

    }

  } else {

    car.stop();

  }

}

好了,追光小车的主要方法都实现了。可以看到,有了前面的Car类,小车的各种行驶就变得异常的简单了,都是调用一个方法就实现了。这就是封装的好处,可以让我们专注于追光有关的业务逻辑,而不用再关心小车控制的问题。

最后再介绍一下初始化方法和主程序loop循环。初始化的代码如下:

void setup() {

  // 初始化串口

  Serial.begin(115200);

  SerialBT.begin("ESP32");

  car.begin();

  SerialBT.println("Setup finished!");

}

这个初始化的方法也是异常的简单。就是初始化了串口、蓝牙串口和小车。

在主程序中,主要是实现了通过蓝牙接收的命令来控制小车的运行。当蓝牙收到字符“r”(run的简称)时,表示小车开始追光运行。当蓝牙收到字符“t”(test的简称)时,表示小车需要对环境光线进行测试。当蓝牙收到字符“s”(stop的简称)时,表示小车停止运行。具体的实现单模如下所示:

void loop() {

  while (SerialBT.available()) {

    char c = SerialBT.read();

    if ('r' == c) {

      status_run = 1;

    } else if ('t' == c) {

      test_environment();

    } else if ('s'==c) {

      status_run = 0;

      car.stop();

    }

    SerialBT.print(c);

  }

  if(status_run==1) {

    follow_spot();

  }

  delay(100);

}

好了,整个追光小车的实现方法就介绍完了。之前有人私信问我怎么用手机控制小车,相信通过这个例子,已经为你展示了一种最简单的方法。你可以把之前的代码改进一下,就能在手机或者电脑上通过蓝牙来控制小车的行驶了。

你可能感兴趣的:(物联网,ESP32,单片机,自动追光)