直流无刷电机的调试与代码开源(配套资源)

2023-05-16

本周对手头的一款大疆M3508直流无刷电机调试的相关内容进行整理及个人的代码进行分享。

一、M3508直流无刷电机

直流无刷电机的工作原理此处不做阐述,相关资料也易查询。

1.1电机结构与连接样式图

1.2电机参数

具体不多加阐述,感兴趣的可到官网下载相关资料和软件驱动,连接如下:

M3508减速电机套装 (robomaster.com)

1.3分电板

电源采用12V,通过一块分电板对单片机主板、电机和电源模块进行连接,这个分电板通过PCB简单设计,实现多少电压输入,多少电压输出。分电板的PCB图如下所示:

2.STM32程序:

控制一个电机,需要1个PWM,2个高低电位

2.1 控制原理图

在这里插入图片描述
反馈值(测量值)- 设定值(目标值)= 控制偏差;
控制偏差给到控制器;
从而将偏差给到电机,在经过编码器获得反馈;
实现闭环

 

相关pid.c代码如下:

#include"pid.h"

#define speed_max 482




struct _pid 
{
	float ExpectedValue;//定义设定值
	float ActualValue;//定义实际值    
	float err;//定义偏差值
	float err_prev;//定义前一个的偏差值
	float integral;//定义积分项
	float Kp, Ki, Kd;//定义比例、积分、微分系数
	float curmax, curmin;//定义电流最值
}pid;

void PID_Init(){//初始化pid各值
	
	pid.ExpectedValue=0.0;
	pid.ActualValue = 0.0;
	pid.err = 0.0;
	pid.err_prev = 0.0;
	pid.integral = 0.0;
	pid.Kp = 1.0;
	pid.Ki = 0.1;
	pid.Kd = 0.2;
	pid.curmax =  3000;
	pid.curmin = -3000;
	
}
//限幅
void abs_limit(float *object, float abs_max)
{
    if(*object > abs_max)  *object =  abs_max;
    if(*object < -abs_max) *object = -abs_max;
}

/*
输入:期望转速
输出:位置型单环PID结果
	
*/
float PID_Realize(float set_speed,float get_speed){//ste_speed期望转速 £»get_speed实际转速
	
	pid.ExpectedValue = set_speed;
	pid.ActualValue = get_speed;
	pid.err = pid.ExpectedValue-pid.ActualValue;//计算误差err
	//计算积分项
	pid.integral += pid.err;
	//位置式PID公式
	float Value = pid.Kp*pid.err + pid.Ki*pid.integral+pid.Kp*(pid.err-pid.err_prev);
	//误差换代
	pid.err_prev=pid.err;
	
	if(Value<pid.curmin)
		return pid.curmin;
	else if(Value>pid.curmax)
		return pid.curmax;
	else
		return Value;

}

/********************************************************************************************************/
void pid_struct_init(pid_t* pid, float kp, float ki, float kd)
{
	
		pid->err[NOW] = 0;
		pid->err[LAST] = 0;
		pid->err[LLAST] = 0;
		pid->pos_out = 0;
	  pid->pout = 0;
		pid->iout = 0;
	  pid->dout = 0;
    pid->p = kp;
    pid->i = ki;
    pid->d = kd;
		pid->pos_abs = 0;
		pid->pos_goal = 0;
		
}

//µ¥»·pid

float single_pid_calculate(pid_t * pid_s)
{
	pid_s->err[NOW] = pid_s->target - pid_s->measure;//当前误差
	//p项 比例项
	pid_s->pout = pid_s->p * pid_s->err[NOW];
	//i项 积分项
	pid_s->iout += pid_s->i * pid_s->err[NOW];
	//d项 微分项
	pid_s->dout = pid_s->d * (pid_s->err[NOW] - pid_s->err[LAST]);
	
	pid_s->pos_out = pid_s->pout + pid_s->iout + pid_s->dout;
	//误差更新
	pid_s->err[LAST] = pid_s->err[NOW];
	
	abs_limit(&(pid_s->pos_out),3000);
	return pid_s->pos_out;
}

//双环pid
float double_pid_calculate(pid_t * pid_I , pid_t* pid_O)//I是速度,o是角度
{
	/****************************角度环**************************************/
	pid_O->err[NOW] = pid_O->target - pid_O->measure;//当前误差
/*
	//p项 比例项
	pid_O->pout = pid_O->p * (pid_O->err[NOW] - pid_O->err[LAST]);
	//i项 积分项
	pid_O->iout += pid_O->i * pid_O->err[NOW];
	//d项 微分项
	pid_O->dout = pid_O->d * (pid_O->err[NOW] - 2*pid_O->err[LAST] + pid_O->err[LLAST]);
*/
	
	if((pid_O->err[NOW]>-50) || (pid_O->err[NOW]<50))
	{
		pid_O->iout = 0;																//设置死区
	}
		
	pid_O->pout = pid_O->p * pid_O->err[NOW];
	
	pid_O->iout += pid_O->i * pid_O->err[NOW];
	
	pid_O->dout = pid_O->d * (pid_O->err[NOW] - pid_O->err[LAST]);
	
	pid_O->pos_out = pid_O->pout + pid_O->iout + pid_O->dout;//算出速度应设置的值
	abs_limit(&(pid_O->pos_out),3000);
	//误差更新
	pid_O->err[LAST] = pid_O->err[NOW];
	pid_O->err[LLAST] = pid_O->err[LAST];
	
	/*******************************速度环************************************/
	pid_I->err[NOW] = pid_O->pos_out - pid_I->measure;//µ±Ç°Îó²î
	//p项 比例项
	pid_I->pout = pid_I->p * pid_I->err[NOW];
	//i项 积分项
	pid_I->iout += pid_I->i * pid_I->err[NOW];
//d项 微分项
	pid_I->dout = pid_I->d * (pid_I->err[NOW] - pid_I->err[LAST]);
	
	pid_I->result =pid_I->pout + pid_I->iout + pid_I->dout;//应设置的电流值
	
	//误差更新
	pid_I->err[LAST] = pid_I->err[NOW];
	abs_limit(&(pid_I->result),8000);
	return pid_I->result;
}

pid.h


enum
{
    LLAST	= 0,
    LAST 	= 1,
    NOW 	= 2,
};
void PID_Init(void);
float PID_Realize(float current,float ActualValue);

typedef struct __pid_t
{
    float p;
    float i;
    float d;
    

		float target;				  //目标值
	  float measure;				  //测量值
    float err[3];				      //误差
		float result;
	
    float pout;							
    float iout;							
    float dout;		
	
		float pos_goal;          //目标角度		
		float pos_abs;			 //绝对角度
    float pos_out;			     //本次位置式输出
}pid_t;

typedef struct
{
	float pos_goal;//目标位置
	float pos_abs;//绝对位置
	float pos_offset;//位置补偿
	float eer;//误差
	float eer_eer;//上次误差
}ANGLE_TypeDef;

void pid_struct_init(pid_t* pid, float kp, float ki, float kd);

float single_pid_calculate(pid_t * pid_s);
float double_pid_calculate(pid_t * pid_I , pid_t* pid_O);

主函数

#include "main.h"
#include "key.h"
#include "led.h"
#include "can.h"
#include "tim.h"
#include "pid.h"

int i;

unsigned char Temp_Value_3508 = { 0 };//CAN接受电机温度
short	Torque_Value_3508 = { 0 };      //CAN实际扭矩电流
short	Velocity_Value_3508 = { 0 };  	//CAN接受实际转速
short	Position_Value_3508 = { 0 };    //CAN接受机械角度
short Velocity_3508_ID1=0;            //CAN实际转速
short Position_3508_ID1=0;						//CAN接受机械角度
short Torque_3508_ID1=0;							//CAN实际扭转电流
unsigned char Temp_Value_3508_ID1=0;	//CAN接受电机温度
pid_t PID,PID_I,PID_O;								//o为角度,i为速度
short set_current1;						        //PID计算后当前需要传输电流值

short Velocity_3508_ID1_Set_Speed=0;	//设定转速

ANGLE_TypeDef motor_angle;


void CAN1_TX_IRQHandler( void )    //CAN发送中断   清楚发送中断标志
{
	if ( CAN_GetITStatus( CAN1, CAN_IT_TME ) != RESET )
	{
		CAN_ClearITPendingBit( CAN1, CAN_IT_TME );
	}
}
void CAN1_RX0_IRQHandler( void )    //CA接受中断 更新当前实际转速 且清楚接受中断标志位
{
	CanRxMsg rx_message;
	if ( CAN_GetITStatus( CAN1, CAN_IT_FMP0 ) != RESET )//判断是否是fifo0消息挂起中断(即收到消息)
	{
		CAN_ClearITPendingBit( CAN1, CAN_IT_FMP0 );
		CAN_Receive( CAN1, CAN_FIFO0, &rx_message);
		if ( (rx_message.IDE == CAN_Id_Standard) && (rx_message.RTR == CAN_RTR_Data) && (rx_message.DLC == 8) ) /* ±ê×¼Ö¡¡¢Êý¾ÝÖ¡¡¢Êý¾Ý³¤¶ÈΪ8 */
		{
			
				Temp_Value_3508 = rx_message.Data[6];    
				Torque_Value_3508	= (rx_message.Data[4] << 8) | (rx_message.Data[5]);
				Velocity_Value_3508	= (rx_message.Data[2] << 8) | (rx_message.Data[3]);
				Position_Value_3508	= (rx_message.Data[0] << 8) | (rx_message.Data[1]);
				
				
				Velocity_3508_ID1=Velocity_Value_3508;	
				Position_3508_ID1=Position_Value_3508;
				Torque_3508_ID1=Torque_Value_3508;
				Temp_Value_3508_ID1=Temp_Value_3508;
			
				PID.measure = Velocity_3508_ID1;//²âÁ¿Öµ
				PID_I.measure = Velocity_Value_3508;
				PID_O.measure = Position_Value_3508;
		}
	}
}




int main(void)
{
	

	delay_init( 180 );     
	delay_ms( 100 );
	KEY_Init();
	Led_Init();

	CAN1_Mode_Init(CAN_SJW_1tq,CAN_BS2_7tq,CAN_BS1_7tq,3,CAN_Mode_Normal);  £¬Ò»°ã²»Óö¯£¬¶¼ÊÇ500kbps
	delay_ms(100);
	PID_Init(); 
	pid_struct_init(&PID,1,0.1,0.2);//³õʼ»¯pidµÄÈý¸ö²ÎÊýÖµp£¬i£¬d
	pid_struct_init(&PID_I,5,0.1,3);
	pid_struct_init(&PID_O,10,0.1,5);
	

	Tick_TIM7_Init(1000);
	
	while (1){	
			
			if(key==0)
				{
						motor_angle.pos_goal = 0;
						LED3=1;
						LED4=0;
					}
			else if(key==1)
					{
						motor_angle.pos_goal = -2160;
						LED3=0;
						LED4=1;
					}
	
	
		delay_ms(10);
	}

}

void Motor_Angle_Cal(float T) 
{
	float res1, res2;
	static float pos,pos_old;
	pos =(PID_O.measure/8192.0f*360.0f);
	motor_angle.eer=pos - pos_old;//½Ç¶ÈÎó²îµÄ¼ÆËã
		if(motor_angle.eer>0) 
	{													
		res1=motor_angle.eer-T;
		res2=motor_angle.eer;
	}
	else
	{
		res1=motor_angle.eer+T;
		res2=motor_angle.eer;
	}
	
	if(fabs(res1)<fabs(res2)) 
	{
		motor_angle.eer_eer = res1;
	}
	else
	{
		motor_angle.eer_eer = res2;
	}

	motor_angle.pos_abs += motor_angle.eer_eer; 
	motor_angle.pos_offset=motor_angle.pos_goal-motor_angle.pos_abs;
	pos_old  = pos;
}

void TIM7_IRQHandler( void ) 
{
	if ( TIM_GetITStatus( TIM7, TIM_IT_Update ) == SET )
	{
		/******************µ¥»·******************/
		
//		set_current1 = single_pid_calculate(&PID);
		
		/******************Ë«»·******************/
		
		Motor_Angle_Cal(360);
		PID_O.target = motor_angle.pos_offset;
		PID_O.measure = 0;
		set_current1 = 0;
		
//		set_current1=PID_Realize(Velocity_3508_ID1_Set_Speed,Velocity_3508_ID1); 
		
		CAN1_Send_Msg(set_current1,set_current1,set_current1,set_current1);//ÉèÖõçÁ÷Öµ
	}
	TIM_ClearITPendingBit( TIM7, TIM_IT_Update );
}

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

直流无刷电机的调试与代码开源(配套资源) 的相关文章

  • 多线程编程实验

    xff08 一 xff09 查看下列程序并运行 xff0c 掌握如何通过扩展Thread类创建线程 span class token keyword package span span class token namespace case1
  • 实验一:基于Ubuntu系统实现无人机自主飞行

    ps xff1a 为避免出现错误 xff0c 在进行新的一步时 xff0c 需要关闭由于上一步操作打开的终端 xff0c 并开启一个新的终端 例如 xff1a 在开始第5步 安装MAVROS 之前 xff0c 关闭由于第3步 安装ROS 打
  • 5000字学习C语言错误处理的四种方式。

    C错误处理 在C语言中 xff0c 错误处理是一个非常重要的主题 通常情况下 xff0c 程序员需要在代码中处理错误 xff0c 以保证程序能够在出现错误时正确地处理这些情况 C语言中常见的错误类型包括 xff1a 语法错误 逻辑错误 运行
  • yum 源制作

    YUM介绍 YUM主要用于自动升级 安装 移除 rpm 软件包 xff0c 它能自动查找并解决 rpm 包之间的依赖关系 xff0c 要成功的使用 YUM 工具更新系统和软件 xff0c 需要有一个包含各种 rpm 软件包的 reposit
  • MATLAB021b与VS2022混编

    MATLAB2021b与VS2022混编 前言 目前在做一个大创项目 xff0c 其中用到关于Matlab与C的混合编程 xff0c 特此记录 Matlab2021b 如图所示 xff0c 红线划出的是即将使用的 c函数 xff0c 在左侧
  • 香橙派5使用NPU加速yolov5的实时视频推理(一)

    前言 xff1a 寒假里 xff0c 博主完成了树莓派4B搭载yolofastest V2的ncnn加速 xff0c 效果挺不错的 xff0c 但总感觉还是稍微差点意思 xff0c 于是就购买了一块香橙派5 xff0c 想要用RK3588芯
  • 香橙派5使用NPU加速yolov5的实时视频推理(二)

    三 将best onnx转为RKNN格式 这一步就需要我们进入到Ubuntu20 04系统中了 xff0c 我的Ubuntu系统中已经下载好了anaconda xff0c 使用anaconda的好处就是可以方便的安装一些库 xff0c 而且
  • 【STM32学习】——串口通信协议&STM32-USART外设&数据帧/输入数据策略/波特率发生器&串口发送/接受实操

    文章目录 前言一 串口通信1 通信接口2 串口通信 xff08 1 xff09 串口简介 xff08 2 xff09 串口硬件电路 xff08 3 xff09 串口软件部分 二 STM32的USART外设1 USART简介2 图示详解 三
  • 【STM32学习】——USART串口数据包&HEX/文本数据包&收发流程&串口收发HEX/文本数据包实操

    文章目录 前言一 数据包格式 xff08 江科大规定 xff09 1 HEX数据包2 文本数据包3 两者对比 二 数据包收发流程1 HEX数据包接收 xff08 只演示固定包长 xff09 2 文本数据包接收 xff08 只演示可变包长 x
  • buuctf simplerev 中的小头位序

    33条消息 BUUCTF SimpleRev xff08 涉及大小端序存储的问题 xff09 Ireb9z的博客 CSDN博客 buuctfsimplerev https blog csdn net afanzcf article deta
  • 简要分析网络编程——UDP编程

    计算机网络是指两台或更多的计算机组成的网络 xff0c 在同一个网络中 xff0c 任意两台计算机都可以直接通信 xff0c 因为所有计算机都需要遵循同一种网络协议 网络编程中有很多协议 xff0c 如 xff0c TCP协议 UDP协议
  • 数据结构之二叉树 Python实现

    树 树是一种非线性的数据结构 树 xff0c 它是若干结点的集合 xff0c 是由唯一的根和若个棵互不相交的子树组成的 其中 xff0c 每一棵子树又是一棵树 xff0c 也是由唯一的根结点和若干棵互不相交的子树组成的 由此可知 xff0c
  • 《奔跑吧Linux内核(第二版)》第四章笔记

    内核配置 内核配置工具常见的有 xff1a make config make oldconfig make menuconfig 内核配置工具最终会在Linux内核源码的根目录下生成一个隐藏文件 config文件 xff0c 这个文件包含了
  • GDB+QEMU调试Linux内核

    1 使用qemu创建虚拟机 使用qemu创建ARM64架构虚拟机可以参考我的另一篇博客 xff1a Ubuntu18 04使用qemu搭建ARM64架构虚拟机 xff08 二 xff09 2 安装gdb multiarch工具包 span
  • centos7 HA

    本文以两台机器实现双集热备高可用集群 xff0c 主机名node1的IP为192 168 122 168 xff0c 主机名node2的IP为192 168 122 169 一 安装集群软件 必须软件pcs xff0c pacemaker
  • 《奔跑吧Linux内核(第二版)》第五章笔记

    Linux内核采用宏内核架构 xff0c 即操作系统的大部分功能都在内核中实现 xff0c 比如进程管理 内存管理 进程调度 设备管理等 xff0c 并且都在特权模式下 xff08 内核空间 xff09 运行 而与之相反的另一种流行的架构是
  • 交叉编译内核模块

    本实验在x86环境中交叉编译ARM64架构模块 xff0c 然后qemu启动ARM64架构虚拟机 xff0c 加载该模块运行 1 创建ARM64虚拟机 详见 xff1a Ubuntu18 04使用qemu搭建ARM64架构虚拟机 xff08
  • Linux内核模块相互调用

    编写一个内核模块A xff0c 通过导出模块符号的方式来实现接口函数 编写另一个内核模块B xff0c 调用内核模块A暴露出来的接口函数 1 源文件 内核模块A xff08 test A c xff09 span class token m
  • RTOS论文笔记(二)

    李在林 韩宏克 嵌入式Linux实时性分析及改造 2010 Linux 的实时性测试 中断延迟测试 在Linux中 xff0c 内核或驱动程序显式地关 开中断 xff0c 一般是通过调用 cli sti 来进行操作的 在调用 cli 时 x
  • LXC容器相关论文笔记

    段赫 基于LXC容器资源优化的研究与实现 2016 一 绪论 容器虚拟化技术 传统虚拟化技术 xff0c 实现一个虚拟机就意味着需要消耗了硬件资源来在底层系统上虚拟一个新的操作系统 xff0c 所以除了传统模拟硬件的虚拟化技术 xff0c

随机推荐

  • fork和clone系统调用小实验

    实验一 xff1a 使用fork 函数创建一个子进程 xff0c 然后在父进程和子进程中分别使用printf语句来判断谁是父进程和子进程 fork 函数被调用后会立即创建一个子进程 xff0c 子进程和父进程同时独立运行互不干扰 返回值 x
  • 【RTOS论文笔记】A Comparative Analysis of RTOS and Linux Scalability on an Embedded Many-core Processor

    背景 以往对多核实时操作系统的研究主要集中在多核处理器上任务集的可调度性和响应时间分析 同时 xff0c 许多研究人员声称 xff0c 在不久的将来 xff0c 高端嵌入式系统还将包括高性能并行应用程序 xff0c 以支持复杂的任务 xff
  • 树莓派4B内核打RT-preempt实时补丁的实现

    硬件环境 xff1a 树莓派4B 操作系统 xff1a 树莓派版Ubuntu server 20 04 LTS xff08 64bit xff09 1 依赖环境的安装 运行如下命令 xff1a span class token functi
  • RT-Preempt笔记

    基于Zynq平台的Linux实时性研究及在数据采集中的应用 马啸 嵌入式实时系统研究现状 实时操作系统专门用于在时间约束条件下运行时间关键的应用程序 用于操作处理实时任务所需的最坏情况执行时间 xff08 Worst Case Execut
  • Mac上vmware fusion装的ubuntu不能与主机复制粘贴的问题

    解决方法一 xff1a 安装vmware tools 依次点击 xff1a 虚拟机 gt 安装vmware tools 会在ubuntu桌面上出现vmware tools xff0c 双击打开 解压tar gz包 xff0c 执行解压命令t
  • ceph的一些优化

    最近一直在忙着搞Ceph存储的优化和测试 xff0c 看了各种资料 xff0c 但是好像没有一篇文章把其中的方法论交代清楚 xff0c 所以呢想在这里进行一下总结 xff0c 很多内容并不是我原创 xff0c 只是做一个总结 如果其中有任何
  • Ubuntu 20.04使用qemu搭建ARM64 Linux系统

    1 安装所需依赖 span class token function sudo span span class token function apt get span span class token function install sp
  • GCC 编译选项总结

    c 只激活预处理 编译 和汇编 也就是他只把程序做成obj文件 例子用法 gcc span class token operator span c hello span class token punctuation span c 他将生成
  • 常见排序算法 Python实现

    冒泡排序 span class token keyword def span span class token function bubbleSort span span class token punctuation span lst s
  • GCC 编译优化等级

    参考资料 xff1a Using the GNU Compiler Collection 打开优化标志会使编译器尝试以牺牲编译时间和调试程序的能力为代价来提高性能和 或代码大小 根据目标和 GCC 的配置方式 xff0c 在每个 O 级别启
  • 树莓派4B开机自动发微信报告ip地址

    方法的实现基本基于博主虚宇宸轩的 实现 树莓派开机自动向微信发消息报告ip地址 xff08 无第三方代理 xff09 xff0c 具体原理和操作过程 xff0c 请参考该博主的文章 xff0c 写得很详细 xff0c 原理解释的也很清楚 本
  • 解决arm-none-eabi-gcc交叉编译helloworld程序segmentation fault 错误

    目标 xff1a 在x86 64主机上 windows 10或ubuntu 20 04 上交叉编译arm架构的helloworld程序 xff0c 然后通过scp将编译完的可执行文件传给树莓派4B xff0c 在树莓派上执行该程序 xff0
  • 中断方式及DMA工作详解

    一 轮询方式 对I O设备的程序轮询的方式 xff0c 是早期的计算机系统对I O设备的一种管理方式 它定时对各种设备轮流询问一遍有无处理要求 轮流询问之后 xff0c 有要求的 xff0c 则加以处理 在处理I O设备的要求之后 xff0
  • 构成帧的方法

    组帧 组帧的原因是为了在出错的时候只需要重发出错的帧 xff0c 而不需要重发全部数据 xff0c 从而提高效率 为了能让接收方正确地接收并检查所传输的帧 xff0c 发送方必须依据一定的规则把网络层传下来的分组封装成帧 这个动作称为组帧
  • AUTOWARE下底盘CAN通讯实现

    无人驾驶系列 autoware与底盘CAN通讯实现 本文介绍在使用autoware时 xff0c 如何将autoware发送的消息传输给底盘 xff0c 实现对线控底盘的控制 准备工作 xff1a 1 系统具备ROS和autoware 2
  • Boost库安装与使用

    Boost 库很不错 xff0c 所以我今天就安了它一下下 Boost 库不是 C 43 43 标准库的一部分 xff08 据说在下一版本的 C 43 43 标准会采纳它 xff09 xff0c 但它有一些标准库所没有的很有用的一些功能 x
  • ROS Ubuntu20.04多版本opencv运行及bug解决

    ubuntu系统装有多个版本opencv使用方法及可能出现的问题 xff1a 单opencv版本时使用命令 xff1a find package OpenCV 3 2 REQUIRED 其默认是在user local 目录下安装的openc
  • UDP编程

    一切以包裹为中心 xff0c 字节数组为核心 一 UDP的基本步骤 xff1a 1 xff09 发送端 package com sxt udp import java net DatagramPacket import java net D
  • LWIP之lwip_select函数使用

    本测试基于lwip2 1 2 参考了CSDN博主 64 则强 的文章 原文链接 xff1a https blog csdn net baidu 39191253 article details 127630186 部分地方做了修改 xff0
  • 直流无刷电机的调试与代码开源(配套资源)

    本周对手头的一款大疆M3508直流无刷电机调试的相关内容进行整理及个人的代码进行分享 一 M3508直流无刷电机 直流无刷电机的工作原理此处不做阐述 xff0c 相关资料也易查询 1 1电机结构与连接样式图 1 2电机参数 具体不多加阐述