← 返回

基于机器视觉的勺轮播种小车

基于机器视觉的勺轮播种小车

开发时间:2024年5月

使用技术:ESP32, ESP32-CAM, .NET MAUI

项目介绍

这是一款基于ESP32和ESP32-CAM的远程控制视觉扁豆播种机,通过手机APP实现远程遥控播种,并可实时查看播种情况。系统采用双ESP32设计,一个负责图传和播种控制,另一个提供网络服务和运动控制。

核心代码实现

主控制代码
MainPage.xaml.cs
// 消息接收处理
private async void OnMessageReceived(object sender, byte[] message)
{
    try {
        await Task.Run(() => {
            using (MemoryStream ms = new MemoryStream(message)) {
                SKBitmap bitmap = SKBitmap.Decode(ms);
                AndroidSaveClass save = new AndroidSaveClass();
                string folderPath = Path.Combine(Environment.GetFolderPath(
                    Environment.SpecialFolder.Personal), "MAUI_Picture_Save");
                
                // 创建保存目录
                if (!save.DoesFolderExist(folderPath)) {
                    Directory.CreateDirectory(folderPath);
                }

                // 生成唯一文件名并保存图片
                string uniqueFileName = $"sample_{DateTime.Now:yyyyMMddHHmmssfff}.png";
                string filePath = Path.Combine(folderPath, uniqueFileName);

                using (var image = SKImage.FromBitmap(bitmap))
                using (var data = image.Encode(SKEncodedImageFormat.Png, 100))
                using (var stream = File.OpenWrite(filePath)) {
                    data.SaveTo(stream);
                }

                // 在主线程更新UI
                MainThread.BeginInvokeOnMainThread(() => {
                    FFloadPicture.Source = filePath;
                });
            }
        });
    }
    catch (Exception ex) {
        Console.WriteLine(ex.ToString());
    }
}
摇杆控制代码
MainPage.xaml.cs
private void Rocker_PositionChanged(object sender, SKPoint newPosition)
{
    // 限制摇杆范围
    if(newPosition.X>255) newPosition.X = 255;
    if(newPosition.Y>255) newPosition.Y = 255;
    if(newPosition.X<-255) newPosition.X = -255;
    if(newPosition.Y<-255) newPosition.Y = -255;
    
    tt = newPosition.Y;
    message = $"Position: ({newPosition.X}, {newPosition.Y})";
    
    // 更新显示
    if (!RCCFlag) {
        Label.Text = $"扁豆株距: {-newPosition.Y*30/256}cm" + "     ";
    }
    else {
        Label.Text = $"扁豆株距: {YY}cm" + "     ";
    }
}
定时发送控制代码
MainPage.xaml.cs
private void OnTimerElapsed(object? sender, ElapsedEventArgs e)
{
#if __ANDROID__
    if (AndroidWifi.IsWifiConnected && Locked && !RCCFlag) {
        Task.Run(() => {
            AndroidWifi.TCP_Send(message, "192.168.4.1", 8081);
        });
    }
    else {
        Task.Run(() => {
            AndroidWifi.TCP_Send(RCCmessage, "192.168.4.1", 8081);
        });
    }
#endif
}
ESP32运动控制代码
esp32_control.ino
// WiFi配置
const char *ssid = "ESP32_Cam";       // AP名称
const char *password = "lly20030601";  // AP密码
int M1, M2;  // 电机方向控制标志
WiFiServer server(8081);

void setup() {
    Serial.begin(115200);
    
    // 配置WiFi AP模式
    WiFi.mode(WIFI_AP);
    WiFi.softAP(ssid, password);
    server.begin();
    
    // 配置电机控制引脚
    pinMode(5, OUTPUT);
    pinMode(18, OUTPUT);
    digitalWrite(5, LOW);
    digitalWrite(18, LOW);
    
    // 配置PWM通道
    ledcSetup(0, 5000, 8);  // 通道0:5KHz, 8位分辨率
    ledcSetup(1, 5000, 8);
    ledcSetup(2, 5000, 8);
    ledcSetup(3, 5000, 8);
    
    // 关联PWM通道到引脚
    ledcAttachPin(19, 0);
    ledcAttachPin(21, 1);
    ledcAttachPin(18, 2);
    ledcAttachPin(5, 3);
    
    // 初始化PWM输出
    ledcWrite(0, 0);
    ledcWrite(1, 0);
    ledcWrite(2, 0);
    ledcWrite(3, 0);
    
    M1 = M2 = 1;  // 初始化电机方向
}

void loop() {
    WiFiClient client = server.available();
    if (client.available()) {
        String line = client.readStringUntil('A');
        // 解析位置数据
        int startPos = line.indexOf('(');
        int endPos = line.indexOf(')');
        
        if (startPos != -1 && endPos != -1) {
            String positionString = line.substring(startPos + 1, endPos);
            int commaPos = positionString.indexOf(',');
            
            // 提取X和Y坐标
            float x = atof(positionString.substring(0, commaPos).c_str());
            float y = atof(positionString.substring(commaPos + 1).c_str());
            
            y = -y;  // Y轴方向转换
            SetPWMDuty(x, y);  // 设置PWM占空比
        }
    }
}

// PWM占空比控制函数
void SetPWMDuty(float x, float y) {
    float W = abs(y) * 255/300;  // 速度映射
    float R = abs(x) * 255/300;  // 转向映射
    float TT = sqrt(y*y);        // 计算合速度
    int NM1 = y > 0 ? 1 : 0;     // 新的方向状态
    int NM2 = y > 0 ? 1 : 0;
    
    // 方向改变时的处理
    if(NM1 != M1 || NM2 != M2) {
        // 停止所有输出
        ledcWrite(0, 0);
        ledcWrite(1, 0);
        ledcWrite(2, 0);
        ledcWrite(3, 0);
        delay(100);  // 防止反向冲击
        
        M1 = NM1;
        M2 = NM2;
    }
    
    // 根据方向设置PWM输出
    if(y > 0) {
        ledcWrite(0, TT);
        ledcWrite(1, TT);
        ledcWrite(2, 0);
        ledcWrite(3, 0);
    } else {
        ledcWrite(0, 0);
        ledcWrite(1, 0);
        ledcWrite(2, TT);
        ledcWrite(3, TT);
    }
}

系统组成

  • ESP32-CAM:负责图像传输和播种装置控制
  • ESP32:作为网络节点和运动控制器
  • 手机APP:基于.NET MAUI开发的控制界面
  • 机械结构:包含播种装置和施肥装置

主要功能

  • 实时视频图传
  • 手机摇杆控制运动
  • 播种正反转控制
  • 施肥装置控制
  • 照明灯控制

技术特点

  • 双ESP32协同工作
  • WiFi AP模式组网
  • 实时图像传输
  • 多电机独立控制
  • 跨平台APP开发

控制功能

  • A键:播种正转
  • B键:施肥控制
  • C键:照明控制
  • D键:播种反转
  • 摇杆:方向控制

开发环境

  • Arduino IDE:ESP32程序开发
  • Visual Studio:.NET MAUI APP开发
  • Android SDK:安卓平台支持

通信协议

  • WiFi图传协议
  • TCP/IP控制协议
  • 自定义指令集