3766|2

655

帖子

30

TA的资源

版主

楼主
 

21“万里”树莓派小车——电机控制学习(4轮速度控制) [复制链接]

前面介绍了树莓派速度控制,但那是控制一个轮子的速度,结果是速度还是有时慢有时快,但误差还能接受,接下来是控制四个轮子的速度看看控制的效果是否一致。

新建文件

由于要控制四个轮子的速度,那么编写函数就非常有必要了,可以实现代码复用。新建Control.cpp和Control.h两个文件放在根目录,然后在“tasks.json”文件中添加文件。

 "${fileDirname}/Control.cpp",

代码编写

Control.h,结构体和函数定义的编写。

#ifndef _CONTROL_H
#define _CONTROL_H

#include <stdio.h>
#include <iostream>
#include <wiringPi.h>
#include "Emakefun_MotorShield.h"
#define SPEED_INTEGRAL_MAX  1
struct Motor
{
    int Pin_A;//广电编码A引脚
    int Pin_B;//广电编码B引脚
    float Speed_Now;//当前速度
    float Speed_Set;//设置的速度
    uint8_t Direction;//设置的方向
    int Encoder_Cnt;//编码器计数
    float Encoder_Integral;//编码器积分
    Emakefun_DCMotor *Handle;//电机句柄
    float Motor_out_float;//计算出来的电机输出值
    uint8_t Motor_out;//转化为驱动输入的字节格式
};
void Encoder_Init();
void Motor_Init();
void Do_Motor(long Time_Period);
#endif

Control.cpp,对具体函数功能进行实现,以及参数的设置。其次稍微修改了一下电机驱动库文件,原来的驱动库,方向控制和力矩控制是两个函数,我将其合并为了一个。

#include "Control.h"
struct Motor M1={
   .Pin_A=21,
   .Pin_B=22,
};
struct Motor M2={
   .Pin_A=23,
   .Pin_B=24,
};
struct Motor M3={
   .Pin_A=28,
   .Pin_B=29,
};
struct Motor M4={
   .Pin_A=25,
   .Pin_B=27,
};
float Speed_Kp=255,Speed_Ki=100;
float Speed=0;
float Set_PWM_float=0;
uint8_t Set_PWM=0;
Emakefun_MotorShield Pwm ;
//外部中断回调函数,用来计数电机速度
void Interrupt_M1 (void) {
   M1.Encoder_Cnt++;
}
void Interrupt_M2 (void) {
   M2.Encoder_Cnt++;
}
void Interrupt_M3 (void) {
   M3.Encoder_Cnt++;
}
void Interrupt_M4 (void) {
   M4.Encoder_Cnt++;
}
//电机驱动初始化
void Motor_Init(){
  Pwm = Emakefun_MotorShield();
  Pwm.begin(50);
  M1.Handle = Pwm.getMotor(1);
  M2.Handle = Pwm.getMotor(2);
  M3.Handle = Pwm.getMotor(3);
  M4.Handle = Pwm.getMotor(4);
  M1.Speed_Set=0.5;
  M2.Speed_Set=0.5;
  M3.Speed_Set=0.5;
  M4.Speed_Set=0.5;
  M1.Direction=FORWARD;
  M2.Direction=FORWARD;
  M3.Direction=FORWARD;
  M4.Direction=FORWARD;
}
//编码器驱动初始化
void Encoder_Init(){
   wiringPiSetup () ;
   wiringPiISR (M1.Pin_A, INT_EDGE_BOTH, &Interrupt_M1) ;
   wiringPiISR (M1.Pin_B, INT_EDGE_BOTH, &Interrupt_M1) ;
   wiringPiISR (M2.Pin_A, INT_EDGE_BOTH, &Interrupt_M2) ;
   wiringPiISR (M2.Pin_B, INT_EDGE_BOTH, &Interrupt_M2) ;
   wiringPiISR (M3.Pin_A, INT_EDGE_BOTH, &Interrupt_M3) ;
   wiringPiISR (M3.Pin_B, INT_EDGE_BOTH, &Interrupt_M3) ;
   wiringPiISR (M4.Pin_A, INT_EDGE_BOTH, &Interrupt_M4) ;
   wiringPiISR (M4.Pin_B, INT_EDGE_BOTH, &Interrupt_M4) ;
}
void Get_Speed(long Time_Period)
{
   M1.Speed_Now=(float)M1.Encoder_Cnt/Time_Period*50;
   M1.Encoder_Cnt=0;
   M2.Speed_Now=(float)M2.Encoder_Cnt/Time_Period*50;
   M2.Encoder_Cnt=0;
   M3.Speed_Now=(float)M3.Encoder_Cnt/Time_Period*50;
   M3.Encoder_Cnt=0;
   M4.Speed_Now=(float)M4.Encoder_Cnt/Time_Period*50;
   M4.Encoder_Cnt=0;
}

//电机速度PI控制函数
float Speed_PI(float * Encoder_Integral,float Now_Speed,float Set_Speet)
{
 float fP;
 fP=Set_Speet-Now_Speed;
 *Encoder_Integral+=fP;
 if(*Encoder_Integral>SPEED_INTEGRAL_MAX)   *Encoder_Integral=SPEED_INTEGRAL_MAX;           
 return fP*Speed_Kp+(*Encoder_Integral)*Speed_Ki;  
}
//执行电机控制输出
void Out_Motro(){
 M1.Handle->my_run(M1.Motor_out,M1.Direction);
 M2.Handle->my_run(M2.Motor_out,M2.Direction);
 M3.Handle->my_run(M3.Motor_out,M3.Direction);
 M4.Handle->my_run(M4.Motor_out,M4.Direction);
}
//
void Speed_Control(){
M1.Motor_out_float=Speed_PI(&M1.Encoder_Integral,M1.Speed_Now,M1.Speed_Set);
M2.Motor_out_float=Speed_PI(&M2.Encoder_Integral,M2.Speed_Now,M2.Speed_Set);
M3.Motor_out_float=Speed_PI(&M3.Encoder_Integral,M3.Speed_Now,M3.Speed_Set);
M4.Motor_out_float=Speed_PI(&M4.Encoder_Integral,M4.Speed_Now,M4.Speed_Set);
if (M1.Motor_out_float>255){M1.Motor_out=255;}
else if(M1.Motor_out_float<0){M1.Motor_out=0;}
else{M1.Motor_out=(uint8_t)M1.Motor_out_float;}
if (M2.Motor_out_float>255){M2.Motor_out=255;}
else if(M2.Motor_out_float<0){M2.Motor_out=0;}
else{M2.Motor_out=(uint8_t)M2.Motor_out_float;}
if (M3.Motor_out_float>255){M3.Motor_out=255;}
else if(M3.Motor_out_float<0){M3.Motor_out=0;}
else{M3.Motor_out=(uint8_t)M3.Motor_out_float;}
if (M4.Motor_out_float>255){M4.Motor_out=255;}
else if(M4.Motor_out_float<0){M4.Motor_out=0;}
else{M4.Motor_out=(uint8_t)M4.Motor_out_float;}
}
void Do_Motor(long Time_Period){
Get_Speed( Time_Period);
Speed_Control();
Out_Motro();
//printf ("%ld,%f,%d,%f\n",Time_Period,M1.Speed_Now,M1.Motor_out,M1.Encoder_Integral);
printf ("%f,%f,%f,%f\n",M1.Speed_Now,M2.Speed_Now,M3.Speed_Now,M4.Speed_Now);
}

Speed_Control_4.cpp,主要是设置一个50ms的定时器,然后在定时器中运行速度控制函数,四个电机的速度都设为0.5,然后不断打印出四个轮子速度。

#include <wiringPi.h>
#include "Control.h"
long Cha;
long now,pre_time;
//定时器线程函数
PI_THREAD (timer)
{
  for (;;)
  {
  now=micros();
  Cha=(now-pre_time);
  Do_Motor(Cha);
  pre_time= now;
  delay(50);
  }
}
int main () {
  Motor_Init();
  Encoder_Init();
  piThreadCreate (timer) ;
  while(1) {
  delay(1000);
}
}

运行效果

运行起来,轮子的速度变化比控制一个轮子更加剧烈,同一轮子在不同时间速度变化差异大,不同轮子在同一时间速度变化也大,最大能有20%的速度差。那么使用树莓派来控制4个电机的速度,显然是失败的。

       既然采用速度反馈和PID控制的是失败的,那就只有采用开环控制,也就是直接给控制量,不管实际的速度情况,但是需要针对每个电机进行控制量与速度的标定。

问题

如何提高Linux实时性真是必须要解决的问题了,到底该如何做呢?

源码

Github

Gitee

 

此帖出自创意市集论坛

最新回复

"万里"树莓派小车汇总贴: lb8820265的“万里”树莓派小车开源分享 - DIY/开源硬件专区 - 电子工程世界-论坛 (eeworld.com.cn)   目录:   “万里”树莓派小车开张贴  1“万里”树莓派小车——建立项目仓库 2“万里”树莓派小车——python学习(Thonny的使用) 3“万里”树莓派小车——python学习(定时任务) 4“万里”树莓派小车——C++学习(编译与运行,geany使用) 5“万里”树莓派小车——wiringPi学习(延时与线程模拟定时器) 6“万里”树莓派小车——wiringPi学习(PWM与外部中断模拟定时器) 7“万里”树莓派小车——RPi.GPIO学习(PWM与外部中断模拟定时器) 8“万里”树莓派小车——socket学习(本机通讯) 9“万里”树莓派小车——socket学习(TCP两机通讯) 10“万里”树莓派小车——socket学习(UDP两机通讯) 11“万里”树莓派小车——socket学习(Android发送) 12“万里”树莓派小车——socket学习(Android收发) 13“万里”树莓派小车——配件准备 14“万里”树莓派小车——电机驱动学习 15“万里”树莓派小车——光电编码器学习(正反转的判断) 16“万里”树莓派小车——光电编码器学习(转速的获取) 17“万里”树莓派小车——VSCode学习(编译和调试) 18“万里”树莓派小车——Makefile学习 19“万里”树莓派小车——VSCode学习(多C文件链接调试) 20“万里”树莓派小车——电机控制学习(控制速度) 21“万里”树莓派小车——电机控制学习(4轮速度控制) 22“万里”树莓派小车——手机遥控电机转动 23“万里”树莓派小车——无屏幕连接树莓派 24“万里”树莓派小车——树莓派64位系统bullseye跑分测试 25“万里”树莓派小车——纳姆轮控制 26“万里”树莓派小车——程序开机启动 27“万里”树莓派小车——固定和获取树莓派IP地址 28“万里”树莓派小车——小车组装 29“万里”树莓派小车——直行偏向问题与新控制模式 30.“万里”树莓派小车——第一阶段完成展示(从零开始介绍)   详情 回复 发表于 2022-3-21 13:38
点赞 关注
个人签名QQ:252669569
 
 

回复
举报

1万

帖子

203

TA的资源

管理员

来自 3楼
 

"万里"树莓派小车汇总贴:

lb8820265的“万里”树莓派小车开源分享 - DIY/开源硬件专区 - 电子工程世界-论坛 (eeworld.com.cn)

 

目录:

 

“万里”树莓派小车开张贴 

1“万里”树莓派小车——建立项目仓库

2“万里”树莓派小车——python学习(Thonny的使用)

3“万里”树莓派小车——python学习(定时任务)

4“万里”树莓派小车——C++学习(编译与运行,geany使用)

5“万里”树莓派小车——wiringPi学习(延时与线程模拟定时器)

6“万里”树莓派小车——wiringPi学习(PWM与外部中断模拟定时器)

7“万里”树莓派小车——RPi.GPIO学习(PWM与外部中断模拟定时器)

8“万里”树莓派小车——socket学习(本机通讯)

9“万里”树莓派小车——socket学习(TCP两机通讯)

10“万里”树莓派小车——socket学习(UDP两机通讯)

11“万里”树莓派小车——socket学习(Android发送)

12“万里”树莓派小车——socket学习(Android收发)

13“万里”树莓派小车——配件准备

14“万里”树莓派小车——电机驱动学习

15“万里”树莓派小车——光电编码器学习(正反转的判断)

16“万里”树莓派小车——光电编码器学习(转速的获取)

17“万里”树莓派小车——VSCode学习(编译和调试)

18“万里”树莓派小车——Makefile学习

19“万里”树莓派小车——VSCode学习(多C文件链接调试)

20“万里”树莓派小车——电机控制学习(控制速度)

21“万里”树莓派小车——电机控制学习(4轮速度控制)
22“万里”树莓派小车——手机遥控电机转动

23“万里”树莓派小车——无屏幕连接树莓派

24“万里”树莓派小车——树莓派64位系统bullseye跑分测试

25“万里”树莓派小车——纳姆轮控制

26“万里”树莓派小车——程序开机启动

27“万里”树莓派小车——固定和获取树莓派IP地址

28“万里”树莓派小车——小车组装

29“万里”树莓派小车——直行偏向问题与新控制模式

30.“万里”树莓派小车——第一阶段完成展示(从零开始介绍)

此帖出自创意市集论坛
加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身
个人签名玩板看这里:
https://bbs.eeworld.com.cn/elecplay.html
EEWorld测评频道众多好板等你来玩,还可以来频道许愿树许愿说说你想要玩的板子,我们都在努力为大家实现!
 
 
 

回复

2万

帖子

71

TA的资源

管理员

沙发
 

关注,期待:)

此帖出自创意市集论坛
加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身
个人签名

加油!在电子行业默默贡献自己的力量!:)

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/7 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表