ROS 2 FishBot PID控制电机代码

news2025/5/15 16:51:49
#include <Arduino.h>
#include <Wire.h>
#include <MPU6050_light.h>
#include <Esp32McpwmMotor.h>
#include <Esp32PcntEncoder.h>

Esp32McpwmMotor motor;        // 创建一个名为motor的对象,用于控制电机
Esp32PcntEncoder encoders[2]; // 创建一个数组用于存储两个编码器

MPU6050 mpu(Wire);

int64_t last_ticks[2] = {0, 0}; // 用于存储上一次编码器的计数
int64_t delta_ticks[2] = {0, 0}; // 用于存储两次编码器计数之间的差值
int64_t last_update_time = 0;    // 用于存储上一次更新时间
float current_speed[2] = {0, 0}; // 用于存储当前速度

// put function declarations here:
int myFunction(int, int);

#define TRIG 27
#define ECHO 21
#define LED_BUILTIN 2

unsigned long previousMillis = 0; // 用于记录上一次电平切换的时间
const long interval = 500;        // 定时器间隔,单位为毫秒

void setup()
{
  // put your setup code here, to run once:
  int result = myFunction(2, 3);
  Serial.begin(115200);
  pinMode(TRIG, OUTPUT);
  pinMode(ECHO, INPUT);
  pinMode(LED_BUILTIN, OUTPUT);

  Wire.begin(18, 19); // SDA, SCL
  byte status = mpu.begin();
  Serial.print(F("MPU6050 status: "));
  Serial.println(status);
  while (status != 0)
  {
    // stop everything if could not connect to MPU6050
  }
  mpu.calcOffsets(true, true); // gyro and accelero
  Serial.println("Done!");
  Serial.println("===========================\n");
  motor.attachMotor(0, 22, 23); // 将电机0连接到引脚22和引脚23
  motor.attachMotor(1, 12, 13); // 将电机1连接到引脚12和引脚13
  Serial.println("Motor Done!");
  // 编码器
  encoders[0].init(0, 32, 33); // 初始化第一个编码器,使用GPIO 32和33连接
  encoders[1].init(1, 26, 25); // 初始化第二个编码器,使用GPIO 26和25连接
  Serial.println("Encoder Done!");

  // 控制电机
  motor.updateMotorSpeed(0, 100); // 设置电机0的速度为负70
  motor.updateMotorSpeed(1, 0);  // 设置电机1的速度为正70
}

unsigned long timer = 0;
void loop()
{
  // put your main code here, to run repeatedly:
  digitalWrite(TRIG, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG, LOW);
  double duration = pulseIn(ECHO, HIGH);
  double distance = duration * 0.034 / 2;
  // Serial.printf("Distance: %lf cm\n", distance);
  /*
  mpu.update();
  if ((millis() - timer) > 1000)
  {
    // 温度
    Serial.printf("Temp: %lf\n", mpu.getTemp());
    // 加速度计数据
    Serial.printf("Accel: %lf %lf %lf\n", mpu.getAccX(), mpu.getAccY(), mpu.getAccZ());
    // 加速度计角度
    Serial.printf("Angle: %lf %lf %lf\n", mpu.getAngleX(), mpu.getAngleY(), mpu.getAngleZ());
    // 陀螺仪数据
    Serial.printf("Gyro: %lf %lf %lf\n", mpu.getGyroX(), mpu.getGyroY(), mpu.getGyroZ());
    // 综合角度
    Serial.printf("Angle: %lf %lf %lf\n", mpu.getAngleX(), mpu.getAngleY(), mpu.getAngleZ());
    Serial.println("===========================\n");
    timer = millis();
  }
  */
  // 获取当前时间
  unsigned long currentMillis = millis();

  // 检查是否达到定时器间隔
  if (currentMillis - previousMillis >= interval)
  {
    // 记录当前时间
    previousMillis = currentMillis;
    // 切换2号脚的电平
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  }

  delay(10);
  // 读取编码器的计数器数值
  int64_t dt = millis() - last_update_time; // 计算时间差
  delta_ticks[0] = encoders[0].getTicks() - last_ticks[0]; // 计算第一个编码器的差值
  delta_ticks[1] = encoders[1].getTicks() - last_ticks[1]; // 计算第二个编码器的差值
  current_speed[0] = delta_ticks[0]*0.105805 / (float)dt; // 计算第一个编码器的速度
  current_speed[1] = delta_ticks[1]*0.105805 / (float)dt; // 计算第二个编码器的速度

  //下次正常计算
  last_ticks[0] = encoders[0].getTicks(); // 保存第一个编码器的计数
  last_ticks[1] = encoders[1].getTicks(); // 保存第二个编码器的计数
  last_update_time = millis(); // 保存当前时间
  // 读取并打印两个编码器的计数器数值 一圈1930个脉冲 直径65mm  一个脉冲前进=0.105805mm
  Serial.printf("speed1=%lf,speed2=%lf\n", current_speed[0], current_speed[1]);
  //Serial.printf("tick1=%d,tick2=%d\n", encoders[0].getTicks(), encoders[1].getTicks());
}

// put function definitions here:
int myFunction(int x, int y)
{
  return x + y;
}

Lib

PidController.cpp

#include "PidController.h"
#include <Arduino.h>

PidController::PidController() = default;
// 默认构造函数
PidController::PidController(float kp, float ki, float kd) {
    _kp = kp;
    _ki = ki;
    _kd = kd;

}
/*
        float update(float current);// 更新PID控制器,提供当前值,返回控制量
        void update_target(float target); // 更新目标值
        void update_pid(float kp, float ki, float kd); // 更新PID参数
        void reset(); // 重置PID控制器
        void out_limit(float); // 输出限制
*/
float PidController::update(float current){
    error_ = target_ - current; // 计算误差
    error_sum_ += error_; // 累加误差
    if(error_sum_ > intergral_up_) error_sum_ = intergral_up_; // 积分限幅
    if(error_sum_ < -1*intergral_up_) error_sum_ = -1*intergral_up_; // 积分限幅

    derror_ = error_ - prev_error_; // 计算误差变化率
    prev_error_ = error_; // 保存上一次误差
    float output = _kp * error_ + _ki * error_sum_ + _kd * derror_; // 计算输出
    if(output > out_max_) output = out_max_; // 输出限幅
    if(output < out_min_) output = out_min_; // 输出限幅
    return output;
}
void PidController::update_target(float target){
    target_ = target;
}
void PidController::update_pid(float kp, float ki, float kd){
    _kp = kp;
    _ki = ki;
    _kd = kd; 
}
void PidController::reset(){
    error_ = 0;
    error_sum_ = 0;
    derror_ = 0; 
    prev_error_ = 0;
    _kp = 0;
    _ki = 0;
    _kd = 0;
    intergral_up_ = 2500; 
    out_max_ = 0;
    out_min_ = 0;
}
void PidController::out_limit(float out_min, float out_max){
    out_max_ = out_max;
    out_min_ = out_min; 
}

PidController.h

#ifndef _PID_CONTROLLER_H_
#define _PID_CONTROLLER_H_

class PidController{
    public:
        PidController() = default;
        PidController(float kp, float ki, float kd);

    private:
        // pid 控制器参数
        float target_;// 目标值
        float out_min_;// 输出最小值
        float out_max_;// 输出最大值
        float _kp; // 比例系数
        float _ki; // 积分系数
        float _kd; // 微分系数
        float intergral_up_ = 2500; // 积分上限
        //pid 中间
        float error_; // 误差
        float error_sum_; // 误差和
        float derror_; // 误差差分
        float prev_error_; // 上次误差
        

    public:
        float update(float current);// 更新PID控制器,提供当前值,返回控制量
        void update_target(float target); // 更新目标值
        void update_pid(float kp, float ki, float kd); // 更新PID参数
        void reset(); // 重置PID控制器
        void out_limit(float out_min, float out_max); // 输出限制
        
};


#endif
// _PID_CONTROLLER_H_;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2373923.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCV定位地板上的书

任务目标是将下面的图片中的书本找出来&#xff1a; 使用到的技术包括&#xff1a;转灰度图、提取颜色分量、二值化、形态学、轮廓提取等。 我们尝试先把图片转为灰度图&#xff0c;然后二值化&#xff0c;看看效果&#xff1a; 可以看到&#xff0c;二值化后&#xff0c;书的…

NHANES稀有指标推荐:MedHi

文章题目&#xff1a;Association of dietary live microbe intake with frailty in US adults: evidence from NHANES DOI&#xff1a;10.1016/j.jnha.2024.100171 中文标题&#xff1a;美国成人膳食活微生物摄入量与虚弱的相关性&#xff1a;来自 NHANES 的证据 发表杂志&…

关于我在实现用户头像更换时遇到的图片上传和保存的问题

目录 前言 前端更换头像 后端处理 文件系统存储图片 数据库存储图片 处理图片文件 生成图片名 保存图片 将图片路径存储到数据库 完整代码 总结 前言 最近在实现一个用户头像更换的功能&#xff0c;但是因为之前并没有处理过图片的上传和保存&#xff0c;所以就开始…

10.二叉搜索树中第k小的元素(medium)

1.题目链接&#xff1a; 230. 二叉搜索树中第 K 小的元素 - 力扣&#xff08;LeetCode&#xff09;230. 二叉搜索树中第 K 小的元素 - 给定一个二叉搜索树的根节点 root &#xff0c;和一个整数 k &#xff0c;请你设计一个算法查找其中第 k 小的元素&#xff08;从 1 开始计数…

AlimaLinux设置静态IP

通过nmcli命令来操作 步骤 1&#xff1a;确认当前活动的网络接口名称 首先&#xff0c;需要确认当前系统中可用的网络接口名称。可以使用以下命令查看&#xff1a; nmcli device步骤 2&#xff1a;修改配置以匹配正确的接口名称 sudo nmcli connection modify ens160 ipv4.…

滑动窗口——将x减到0的最小操作数

题目&#xff1a; 这个题如果我们直接去思考方法是很困难的&#xff0c;因为我们不知道下一步是在数组的左还是右操作才能使其最小。正难则反&#xff0c;思考一下&#xff0c;无论是怎么样的&#xff0c;最终这个数组都会分成三个部分左中右&#xff0c;而左右的组合就是我们…

基于SpringBoot的抽奖系统测试报告

一、编写目的 本报告为抽奖系统测试报告&#xff0c;本项目可用于团体抽奖活动&#xff0c;包括了用户注册&#xff0c;用户登录&#xff0c;修改奖项以及抽奖等功能。 二、项目背景 抽奖系统采用前后端分离的方法来实现&#xff0c;同时使用了数据库来存储相关的数据&…

服务器mysql连接我碰到的错误

搞了2个下午&#xff0c;总算成功了 我在服务器上使用docker部署了java项目与mysql&#xff0c;但mysql连接一直出现问题 1.首先&#xff0c;我使用的是localhost连接&#xff0c;心想反正都在服务器上吧。 jdbc:mysql://localhost:3306/fly-bird?useSSLfalse&serverTime…

【Part 2安卓原生360°VR播放器开发实战】第四节|安卓VR播放器性能优化与设备适配

《VR 360全景视频开发》专栏 将带你深入探索从全景视频制作到Unity眼镜端应用开发的全流程技术。专栏内容涵盖安卓原生VR播放器开发、Unity VR视频渲染与手势交互、360全景视频制作与优化&#xff0c;以及高分辨率视频性能优化等实战技巧。 &#x1f4dd; 希望通过这个专栏&am…

从知识图谱到精准决策:基于MCP的招投标货物比对溯源系统实践

前言 从最初对人工智能的懵懂认知&#xff0c;到逐渐踏入Prompt工程的世界&#xff0c;我们一路探索&#xff0c;从私有化部署的实际场景&#xff0c;到对DeepSeek技术的全面解读&#xff0c;再逐步深入到NL2SQL、知识图谱构建、RAG知识库设计&#xff0c;以及ChatBI这些高阶应…

图形化编程革命:iVX携手AI 原生开发范式

一、技术核心&#xff1a;图形化编程的底层架构解析 1. 图形化开发的效率优势&#xff1a;代码量减少 72% 的秘密 传统文本编程存在显著的信息密度瓶颈。以 "按钮点击→条件判断→调用接口→弹窗反馈" 流程为例&#xff0c;Python 实现需定义函数、处理缩进并编写 …

JAVA EE_网络原理_网络层

晨雾散尽&#xff0c;花影清晰。 ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ ----------陳長生. ❀主页&#xff1a;陳長生.-CSDN博客❀ &#x1f4d5;上一篇&#xff1a;数据库Mysql_联…

森林生态学研究深度解析:R语言入门、生物多样性分析、机器学习建模与群落稳定性评估

在生态学研究中&#xff0c;森林生态系统的结构、功能与稳定性是核心研究内容之一。这些方面不仅关系到森林动态变化和物种多样性&#xff0c;还直接影响森林提供的生态服务功能及其应对环境变化的能力。森林生态系统的结构主要包括物种组成、树种多样性、树木的空间分布与密度…

AI大模型学习十八、利用Dify+deepseekR1 +本地部署Stable Diffusion搭建 AI 图片生成应用

一、说明 最近在学习Dify工作流的一些玩法&#xff0c;下面将介绍一下Dify Stable Diffusion实现文生图工作流的应用方法 Dify与Stable Diffusion的协同价值 Dify作为低代码AI开发平台的优势&#xff1a;可视化编排、API快速集成 Stable Diffusion的核心能力&#xff1a;高效…

关于chatshare.xyz激活码使用说明和渠道指南!

chatshare.xyz和chatshare.biz是两个被比较的平台&#xff0c;分别在其功能特性和获取渠道有所不同。 本文旨在探讨它们的差异&#xff0c;以及提供如何获取并使用的平台信息。此外&#xff0c;还提及其他一些相关资源和模板推荐以满足用户需求。 主要区分关键点 1、chatshar…

Qt开发经验 --- 避坑指南(14)

文章目录 [toc]1 linux下使用linuxdeploy打包2 Qt源码下载3 QtCreator配置github copilot实现AI编程4 使用其它编程AI辅助开发Qt5 Qt开源UI库6 QT6.8以后版本安装QtWebEngine7 清除QtCreator配置 更多精彩内容&#x1f449;内容导航 &#x1f448;&#x1f449;Qt开发经验 &…

MIT 6.S081 2020 Lab3 page tables 个人全流程

文章目录 零、写在前面1、关于页表2、RISC-V Rv39页表机制3、虚拟地址设计4、页表项设计5、访存流程6、xv6 的页表切换7、页表遍历 一、Print a page table1.1 说明1.2 实现 二、A kernel page table per process2.1 说明2.2 初始化 / 映射相关2.3 用户内核页表的创建和回收2.4…

【Redis】Redis的主从复制

文章目录 1. 单点问题2. 主从模式2.1 建立复制2.2 断开复制 3. 拓扑结构3.1 三种结构3.2 数据同步3.3 复制流程3.3.1 psync运行流程3.3.2 全量复制3.3.3 部分复制3.3.4 实时复制 1. 单点问题 单点问题&#xff1a;某个服务器程序&#xff0c;只有一个节点&#xff08;只搞一个…

第04章—技术突击篇:如何根据求职意向进行快速提升与复盘

经过上一讲的内容阐述后&#xff0c;咱们定好了一个与自身最匹配的期望薪资&#xff0c;接着又该如何准备呢&#xff1f; 很多人在准备时&#xff0c;通常会选择背面试八股文&#xff0c;这种做法效率的确很高&#xff0c;毕竟能在“八股文”上出现的题&#xff0c;也绝对是面…

Quantum convolutional nerual network

一些问答 1.Convolution: Translationally Invariant Quasilocal Unitaries 理解&#xff1f; Convolution&#xff08;卷积&#xff09;&#xff1a; 在量子信息或量子多体系统中&#xff0c;"卷积"通常指一种分层、局部操作的结构&#xff0c;类似于经典卷积神经网…