tag 标签: 平衡小车

相关博文
  • 热度 1
    2024-2-1 05:07
    719 次阅读|
    0 个评论
    在PID控制器中,抗饱和机制(也称为积分限幅或积分饱和预防)是一种保护措施,用于防止当系统输出达到其操作范围的极限时,积分项累积过大导致控制量无法及时响应系统的实际需求。这种情况通常发生在控制系统启动时或者设定值发生较大变化时。 抗饱和机制的一种常见实现是限制积分项的增长。一旦检测到输出已经达到其最大或最小值,就停止积分项的累加,直到输出离开饱和区。这可以防止在输出停留在极限值时积分项无限制地增长。 更复杂的控制算法可能包括: 1. 非线性PID: 传统的PID控制器使用线性控制法则,但在某些系统中,可能需要根据误差大小调整PID参数,即非线性PID。 2. 自适应PID: 这种控制器可以根据系统的动态特性自动调整PID参数。 3. 模糊逻辑控制器: 利用模糊逻辑来决定控制规则,它不依赖于精确的数学模型。 4. 模型预测控制 (MPC): MPC使用系统的数学模型来预测未来的输出,并优化控制动作以最小化某个性能指标。 5. 神经网络或深度学习: 这些方法可以利用数据驱动的方式学习最优的控制策略。 下面是一个包含抗饱和机制的简单PID控制器C语言代码示例: //```c #define Kp 1.0 // 比例系数 #define Ki 0.5 // 积分系数 #define Kd 0.1 // 微分系数 #define OUTPUT_LIMIT 100.0 // 输出限幅 #define INTEGRAL_LIMIT 1000.0 // 积分项限幅 float setpoint, input, error, Pout, Iout, Dout, last_error, output; int integral = 0; bool is_saturated = false; // 标志位,表示是否饱和 void PID_update(float setpoint, float input) { error = setpoint - input; Pout = Kp * error; // 检查饱和情况,如果输出大于等于上限或小于等于下限,则停止积分 = OUTPUT_LIMIT || output <= -OUTPUT_LIMIT) { is_saturated = true; } else { is_saturated = false; } if (!is_saturated) { integral += Ki * error; // 限制积分项增长 INTEGRAL_LIMIT) { integral = INTEGRAL_LIMIT; } else if (integral < -INTEGRAL_LIMIT) { integral = -INTEGRAL_LIMIT; } } Dout = Kd * (error - last_error); last_error = error; output = Pout + Iout + Dout; // 应用输出限幅 OUTPUT_LIMIT) { output = OUTPUT_LIMIT; } else if (output < -OUTPUT_LIMIT) { output = -OUTPUT_LIMIT; } } //``` 这段代码引入了`OUTPUT_LIMIT`和`INTEGRAL_LIMIT`两个常数来定义输出和积分项的限制。`is_saturated`变量用于标识输出是否处于饱和状态,从而决定是否继续积分。最后,对最终的`output`进行限幅处理,确保其不会超出定义的范围。
  • 热度 3
    2024-2-1 04:58
    886 次阅读|
    1 个评论
    MPU6050是一种集成了3轴陀螺仪和3轴加速度计的微型电子运动传感器,广泛应用于飞行控制系统、游戏控制器的运动检测等领域。它通过I2C接口与微控制器进行通信,可以测量设备在空间中的加速度和角速度。 在控制理论中,PID(比例-积分-微分)控制器是一种常用的反馈控制器。它的工作原理是通过比较期望值和实际值之间的误差,然后通过比例、积分和微分三个环节来调整控制量,使系统的实际输出逐渐接近期望输出。 以下是一个简单的C语言实现的PID控制器代码: //```c #define Kp 1.0 // 比例系数 #define Ki 0.5 // 积分系数 #define Kd 0.1 // 微分系数 float setpoint, input, error, Pout, Iout, Dout, last_error, output; int integral = 0; void PID_update(float setpoint, float input) { error = setpoint - input; Pout = Kp * error; integral += Ki * error; Dout = Kd * (error - last_error); last_error = error; output = Pout + Iout + Dout; } //``` 在这个代码中,`setpoint`是期望的输出值,`input`是实际的输入值。`Pout`、`Iout`、`Dout`分别代表比例、积分和微分环节的输出。`last_error`用于存储上一次的误差,以便计算微分环节的输出。`output`是最终的控制量输出。 注意,这只是一个简单的PID控制器实现,实际应用中可能需要根据系统的具体情况进行调整和优化。例如,可能需要添加抗饱和机制,或者使用更复杂的控制算法。
  • 热度 3
    2016-4-9 12:38
    3075 次阅读|
    1 个评论
    废话不说,先上代码 #include "main.h" /* PA7——TIM3_CH2 PA6——TIM3_CH1 PB7——TIM4_CH2 PB6——TIM4_CH1 */   int Timer3_Overflow; int Timer4_Overflow;   void wheel_encoder_configuration(void) {          GPIO_InitTypeDef                                        ENCODER;          TIM_TimeBaseInitTypeDef    TIMER3,  TIMER4;          TIM_ICInitTypeDef                                      IC_ENCODER;          NVIC_InitTypeDef                                        nvic;            RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB, ENABLE);          RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3|RCC_APB1Periph_TIM4, ENABLE);                   //PA6、PA7的复用功能一定要分开配置才行          GPIO_PinAFConfig(GPIOA,GPIO_PinSource6, GPIO_AF_TIM3);          GPIO_PinAFConfig(GPIOA,GPIO_PinSource7, GPIO_AF_TIM3);          ENCODER.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;          ENCODER.GPIO_Mode = GPIO_Mode_AF;          ENCODER.GPIO_Speed = GPIO_Speed_100MHz;          ENCODER.GPIO_PuPd = GPIO_PuPd_DOWN;          GPIO_Init(GPIOA, ENCODER);          GPIO_PinAFConfig(GPIOB,GPIO_PinSource6, GPIO_AF_TIM4);          GPIO_PinAFConfig(GPIOB,GPIO_PinSource7, GPIO_AF_TIM4);          ENCODER.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;          ENCODER.GPIO_Mode = GPIO_Mode_AF;          ENCODER.GPIO_Speed = GPIO_Speed_100MHz;          ENCODER.GPIO_PuPd = GPIO_PuPd_DOWN;          GPIO_Init(GPIOB, ENCODER);            /***********configure the TIM3**********/          TIMER3.TIM_Prescaler = 0;          TIMER3.TIM_Period = 0xffff;          TIMER3.TIM_ClockDivision = TIM_CKD_DIV1;          TIMER3.TIM_CounterMode = TIM_CounterMode_Up;            TIM_TimeBaseInit(TIM3, TIMER3);          /***********configure the TIM4***********/          TIMER4.TIM_Prescaler = 0;          TIMER4.TIM_Period = 0xffff;          TIMER4.TIM_ClockDivision = TIM_CKD_DIV1;          TIMER4.TIM_CounterMode = TIM_CounterMode_Up;            TIM_TimeBaseInit(TIM4, TIMER4);                   /**********Set TIM3 and TIM4 to the ecoder mode***************/          TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);          TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);                   /**********Set TIM3 and TIM4 to the input capture mode***************/          IC_ENCODER.TIM_Channel    =       TIM_Channel_1;          IC_ENCODER.TIM_ICSelection        =       TIM_ICSelection_DirectTI;          TIM_ICInit(TIM3, IC_ENCODER);          IC_ENCODER.TIM_Channel    =       TIM_Channel_2;          IC_ENCODER.TIM_ICSelection        =       TIM_ICSelection_DirectTI;          TIM_ICInit(TIM3, IC_ENCODER);          IC_ENCODER.TIM_Channel    =       TIM_Channel_1;          IC_ENCODER.TIM_ICSelection        =       TIM_ICSelection_DirectTI;          TIM_ICInit(TIM4, IC_ENCODER);          IC_ENCODER.TIM_Channel    =       TIM_Channel_2;          IC_ENCODER.TIM_ICSelection        =       TIM_ICSelection_DirectTI;          TIM_ICInit(TIM4, IC_ENCODER);                   /*************Configurate interrupts of TIM3 and TIM4*********/          nvic.NVIC_IRQChannel = TIM3_IRQn;          nvic.NVIC_IRQChannelPreemptionPriority = 2;          nvic.NVIC_IRQChannelSubPriority = 2;          nvic.NVIC_IRQChannelCmd = ENABLE;          NVIC_Init(nvic);          nvic.NVIC_IRQChannel = TIM4_IRQn;          nvic.NVIC_IRQChannelPreemptionPriority = 2;          nvic.NVIC_IRQChannelSubPriority = 2;          nvic.NVIC_IRQChannelCmd = ENABLE;          NVIC_Init(nvic);                   /*Clear the interruput flag and then enable the interrupt*/          TIM_ClearFlag(TIM3, TIM_FLAG_Update);          TIM_ClearFlag(TIM4, TIM_FLAG_Update);          TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);          TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);                   /*Set TIM3 and TIM4 CNT to 0x7fff*/          TIM_SetCounter(TIM3,0x7fff);          TIM_SetCounter(TIM4,0x7fff);          /*Enable TIM3 and TIM4 */          TIM_Cmd(TIM3, ENABLE);          TIM_Cmd(TIM4, ENABLE); }   int encoderR_get_cnt(void) {          /*get the speed of right wheel */          int cnt = 0;          cnt = (TIM3 - CNT) - 0x7fff;          TIM3 - CNT = 0x7fff;          return cnt; }   int encoderL_get_cnt(void) {          /*get the speed of left wheel*/          int cnt = 0;          cnt = (TIM4 - CNT) - 0x7fff;          TIM4 - CNT = 0x7fff;          return cnt; }   void TIM3_IRQHandler(void) {          TIM_ClearFlag(TIM3, TIM_FLAG_Update);          if(Timer3_Overflow != 0xffffffff)          {                    Timer3_Overflow++;          } }   void TIM4_IRQHandler(void) {          TIM_ClearFlag(TIM4, TIM_FLAG_Update);          if(Timer4_Overflow != 0xffffffff)          {                    Timer4_Overflow++;          } } 刚开始在调试的时候遇到了问题,读出来的cnt值只有0,1,-1,而且根本没有什么规律,后来发现是gpio的配置有问题,相应的GPIO口应该配制成GPIO_Mode_AF,而不是GPIO_Mode_IN,而且配置复用功能的时候,GPIO_PinAFConfig()函数的参数一定要注意,GPIO_PinSource参数只能写一个,不要讲多个GPIO_PinSource与在一起。比如,要配置PA6和PA7,应写成这样: GPIO_PinAFConfig(GPIOA,GPIO_PinSource6, GPIO_AF_TIM3);          GPIO_PinAFConfig(GPIOA,GPIO_PinSource7, GPIO_AF_TIM3); 而不要写出这样: GPIO_PinAFConfig(GPIOA,GPIO_PinSource6| GPIO_PinSource7,  GPIO_AF_TIM3);          程序中CNT寄存器的初值设置为0X7fff,这样的话无论一开始轮子是正转还是反转,都基本不会溢出,但是为了防止溢出的情况,又在两个定时器的中断函数中对溢出次数进行计数,在后续的应用中就可以结合溢出次数和CNT计数器的值,得到实际的计数值。
  • 热度 14
    2016-3-18 19:29
    4538 次阅读|
    10 个评论
    学习一种单片机最快的方式莫过于用它实际制作一个作品了,前些天看到有人在玩平衡小车,感觉非常有趣,于是就决定自己动手制作一个基于 stm32 的两轮平衡小车。从电路板设计,到程序编写,一步一步的,希望自己在这个过程中有一定的收获。 这篇博客先写最开始的电路设计,之后会更新后续的代码编写和小车调试。 电路设计的基本过程一般是这样的:需求分析 —— 元件选型 —— 原理图设计 ——PCB 设计 —— 焊接调试。 需求分析:在这里,主要是确定小车需要哪些模块、外设或接口。首先, stm32 最小系统是必须的,这是小车控制的核心。然后小车的两个轮子需要两个 H 桥驱动和编码器接口。需要陀螺仪感知小车的姿态(包括倾角,转向角,角速度等)。需要一些调试和指示用的外设(蜂鸣器, LED 等)。需要电源电路为系统供电。需要电池电压采集电路来实时采集电池电压,做低压报警,防止电池过放。需要下载接口和调试用的串口。基本就是根据自己想要实现功能,然后确定需要那些部分的电路。 元件选型:为需要的各个部分电路选择元件,一般来说,主要是选择何种型号、什么封装的芯片,对于一些比较特殊的电路,即使是电阻电容这样常见的无源器件也需要认真选择,然而平衡小车的电路中并不需要一些高精度或很高速的电路部分,所以,下面主要介绍如何选择各个电路部分的主要芯片。 1. 最小系统:考虑到小车的主控板可以作为今后做四轴或其他更高级的机器人的基础电 路,所以主控选择了 stm32F405RGT6 ,这款主控是 M3 内核的,主频可以达到 168M , 有丰富的外设资源可以使用,性能非常强悍,非常适合以后的开发和拓展。 2. 电机驱动:小车的两个轮子电机需要两个 H 桥驱动。驱动的选择和电机的参数紧密相 关。我选用的电机的正常工作电流 360mA ,并且考虑到小车在正常情况下基本不会出 现赌转的情况,并且为了尽量减小 PCB 板的面积,所以选择了东芝的电机驱动芯片 TB6612 ,该芯片有两个集成的 H 桥,可以同时驱动两个电机,每个 H 桥可以持续输出 1.2A 的电流, PWM 频率可以到 100kHz ,芯片的供电电压最大可以到 15V ,适合用 3S 电池供电,并且芯片封装很小,节省 PCB 面积。 3. 电源电路:主要考虑输入电压、输出电压、输出电流等。小车电池采用 3S 锂聚合物电 池,放电率 25C ,以后做四轴还能用。电池的满电电压 12.6V 左右,电路中需要 5V 和 3.3V 的电源,因此需要选择两款款芯片将 12V 电压降压到 5V ,然后再将 5V 降压到 3.3V 。 12V-5V 的芯片可以选择 MPS 公司的 DCDC 降压芯片 MP2482 ,该芯片支持最大 5A 电流输出,最大 28V 电压输入, 0.8 至 25V 可调电压输出,符合需求。 5V-3.3V 选用 常见的 AMS1117-3.3 。 4. 陀螺仪:选用 MPU6050 模块,模块自带了软件滤波算法,采用串口对外输出数据, 在 115200 波特率的情况下,每秒钟可以输出 100 帧的数据。 原理图设计: 1. 最小系统:包括 stm32f405rgt6 ,晶振电路,复位电路。 R2 和 R3 用于配置启动方式。 C9 和 C12 是单片机内部电源变换部分的滤波电容。最小系统基本都是一样的,按照常用的电路设计一般没有错。那几个电容式芯片的去耦电容,注意一下,模拟电压、模拟地和数字电源、数字地之间用磁珠隔离,防止数字电路的高频噪声影响到模拟电路的精度。 2. 电源电路: R22 和 R23 将电池电压分压到 1.2V ,接到单片机内部的 ADC 引脚,监控电池电压,防止过放。 3. 电机驱动和编码器电路:基本就是按照芯片 datasheet 上的参考电路设计的,注意做好电源去耦,因为电机的电压较高、电流较大,容易通过电源网络对其它电路产生影响。 4. 陀螺仪:集成的模块,没啥好说的,把引脚连到单片机上就好了,简单在电源脚上并一个电容做一下电源去耦(不做也无妨,因为模块内部已经做了)。 5.CAN 通信:在小车上并没有用到 CAN 通信,但是 CAN 通信在现在的机器人设计中应用的非常广泛。并且我们做小车本来就是要学东西的,加上调一下,多学习些知识,何乐而不为呢? CAN 通信用的芯片是飞利浦的 TJA1050 ,这是一个 CAN 收发器, stm32 内部集成了 CAN 控制器。 R4 是阻抗匹配的电阻,在整个 CAN 通信网络中有且只有两个节点焊接此电阻,电阻阻值为 120ohm 。现在也还没有深入学习 CAN 通信相关的知识,在以后调试 CAN 通信的时候再详细学习吧。 (^.^) 6. 调试电路和其他的接口: SWD 下载接口, USART 调试串口,蓝牙接口(根据选择的蓝牙模块不同确定接口不同引脚的用途,比如使能脚、状态输入脚等,后面写蓝牙的时候详细描述)。蜂鸣器和双色 LED 灯用于调试和指示。另外又引出了几个定时器的引脚,可以留作拓展使用。 PCB 设计:不详细说了吧,工程文件在附件里有。注意事项主要有电源去耦(具体做法可以参考我之前关于电源去耦的博文)、线宽控制(大电流的走线要尽量宽)、开关电源的布局布线(一般来说主要记住三点就好了:一是芯片的 SW 的信号是电压高,频率高的开关信号,会对其他电路产生严重的干扰,要尽量远离敏感的电路。二是反馈网络是很敏感的网络,尽量避免受干扰。三是电源的电流较大,注意线宽和过孔的数量、孔径等,保证有能力通过这么大的电流。注意了这三点,集成的 DCDC 电路一般没有问题)等。这里贴一下板子正反两面的 3D 效果图 焊接调试:也不多说了,平时多练练手, 0603 QFP 这些封装的元件就能轻松搞定了。焊接的时候先焊接电源部分,上电测试电源没有问题的话再焊接其他部分,否则一旦电源有问题,整板全白焊了。。下面贴一个焊接调试好了的实物图。   嗯,就先写这么些吧。上面写的比较啰嗦,也没有什么高大上的技术,差不多都是硬件电路设计需要的基础知识吧。只是希望将自己设计这块平衡小车电路板的过程比较系统和通俗的介绍一下,能将自己这实践过程中学到的只是和经验分享出来,没怎么接触过这方面的朋友可以将其当作参考,快速入门。同时希望各位高手和达人能多多指教。
相关资源
  • 所需E币: 5
    时间: 2024-2-7 13:15
    大小: 373.24KB
    上传者: 丙丁先生
    平衡小车.rar
  • 所需E币: 1
    时间: 2023-6-28 11:15
    大小: 138.14KB
    上传者: 张红川
    dyuRobot_平衡小车原理图.pdf
  • 所需E币: 5
    时间: 2023-6-6 21:04
    大小: 137.98KB
    上传者: 小宫
    STM32智能平衡小车详细电路原理图.pdf
  • 所需E币: 5
    时间: 2023-2-20 20:43
    大小: 4.35MB
    上传者: ZHUANG
    基于单片机的智能平衡小车设计.
  • 所需E币: 1
    时间: 2022-2-26 22:01
    大小: 1.56MB
    上传者: czd886
    基于图像信息采集的自动寻迹平衡小车设计
  • 所需E币: 0
    时间: 2020-5-20 18:08
    大小: 1.24MB
    上传者: Argent
    模电数电是电路的基础,掌握基础才能更好的理解智能芯片的工作原理,万物互联,没有电路的支持,一切都是废墟。射频无线,创造无限可能,5G的兴起,FPGA的火爆,快来下载基础性的资料吧,让你从零开始掌握底层电路的实现原理。
  • 所需E币: 0
    时间: 2020-5-20 18:11
    大小: 699.79KB
    上传者: Argent
    模电数电是电路的基础,掌握基础才能更好的理解智能芯片的工作原理,万物互联,没有电路的支持,一切都是废墟。射频无线,创造无限可能,5G的兴起,FPGA的火爆,快来下载基础性的资料吧,让你从零开始掌握底层电路的实现原理。
  • 所需E币: 4
    时间: 2020-1-4 10:20
    大小: 1.81MB
    上传者: rdg1993
    说明书……
  • 所需E币: 4
    时间: 2020-1-9 14:09
    大小: 20.07MB
    上传者: 16245458_qq.com
    文档包括:1.PCB文件2.小车程序3.小车原理图4.答辩ppt5.毕业论文本文首先以传统PC机为例研究了人机交互方式的发展,然后介绍了各种新型的人机交互方式,最终确定了体感识别作为本文研究的出发点。然后介绍了基于PrimeSense公司的新型景深成像原理以及通过深度图像区域特征值和随机森林算法获得骨骼坐标信息方法。最后通过微软Kinect摄像头实现了景深图像的采集和骨骼跟踪算法的实现,并完成了一套控制系统,以体感识别的方式控制下位机——两轮自平衡小车。……
  • 所需E币: 5
    时间: 2019-12-19 15:15
    大小: 28.84KB
    上传者: 238112554_qq
    平衡小车HEX……
  • 所需E币: 5
    时间: 2019-12-19 15:11
    大小: 5.36MB
    上传者: 978461154_qq
    基于3.5固件库的空工程模版……