直流无刷电机的调试

这篇博客分享了使用STM32对大疆M3508直流无刷电机的调试过程,包括电机结构、参数、分电板设计以及STM32控制原理和PID代码实现。提供了电机连接样式图和相关资源链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本周对手头的一款大疆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 );
}

### RM3508电机速度和角度双环控制原理 在RM3508电机的速度和角度双环控制系统中,通常采用嵌套反馈结构来实现精确的位置和速度控制。外层的角度控制器负责设定目标位置,并计算所需的目标角速度;内层的速度控制器则根据这个目标角速度调整实际转速。 #### 外环——角度控制器 角度控制器接收来自上位机或其他传感器提供的指令信号,通过PID算法处理当前的实际角度与期望之间的误差,从而得出一个代表理想状态下的角加速度或角速度变化量的数值[^1]。此值并非直接对应任何具体的机械运动参数,而是作为内部逻辑运算的结果传递给下一层级。 #### 内环——速度控制器 得到由角度调节器产生的输出后,速度控制器将其视为新的参考输入,即希望达到的理想转动速率。同样利用PID机制对比实时监测到的真实速度同该预期值间的差异,进而决定施加于驱动装置上的PWM占空比或者其他形式的动力调控措施,确保执行元件能够按照预定轨迹平稳运行[^2]。 ```python def angle_controller(desired_angle, current_angle): error = desired_angle - current_angle output = pid_compute(error) # PID 计算函数 return output def speed_controller(target_speed, actual_speed): delta_v = target_speed - actual_speed pwm_signal = pid_adjust(delta_v) # 另一 PID 函数用于速度控制 apply_pwm(pwm_signal) ``` 这种架构的优势在于可以有效解耦复杂的多变量动态过程,简化了各自独立部分的设计难度并提高了整体响应性能。当仅对外部姿态给予阶跃激励时,不会引起底层动力源无节制运转的风险,因为每一环节都受到严格监控和适时修正。 ### 实现方法 为了构建上述提到的功能模块,在硬件选型方面需考虑支持快速采样率以及具备足够分辨率编码器接口的微处理器单元(MCU),以便精准获取即时的状态信息。软件层面应精心编写各子程序间的数据交换协议,保障数据流畅通无阻的同时维持良好的时间同步特性。 对于具体编程而言,除了定义好两个主要闭环流程之外,还应当加入必要的保护机制防止过载损坏设备组件。比如设置合理的限幅范围限制最大允许偏差幅度,引入软启动策略减少开机瞬间冲击电流等等。 ### 调试技巧 调试过程中建议遵循渐进式的验证路径: - **单步确认**:先单独检验每一个构成要素能否正常运作; - **逐步集成**:再依次叠加其他关联部件形成完整的回路体系; - **全面联调**:最后进行全面性的联合测试评估综合表现效果。 特别需要注意的是,在向系统注入外部刺激之前务必谨慎选择合适的类型强度,以免造成不必要的损害。例如只针对角度通道实施脉冲加载而非贸然作用于速度端口引发连续自旋现象。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值