- 2024-12-14
-
回复了主题帖:
【极海APM32M3514电机通用评估板】滑膜观测器电角度与霍尔采集到的电角度数据比对
qiao--- 发表于 2024-12-13 15:50
这个滑膜,电机可以反转吗?
可以的,有拨码可以控制正反转
- 2024-12-12
-
回复了主题帖:
【极海APM32M3514电机通用评估板】滑膜观测器电角度与霍尔采集到的电角度数据比对
秦天qintian0303 发表于 2024-12-12 12:18
这是软件自动测量的吗?
自己弄的代码测试出来的,例程代码不带这些
-
发表了主题帖:
【极海APM32M3514电机通用评估板】滑膜观测器电角度与霍尔采集到的电角度数据比对
本帖最后由 IllusionXX 于 2024-12-12 10:57 编辑
这次官方只提供了无感的例程,我手头上的电机是带有霍尔线的,于是想要将滑膜观测器观测得到的电角度与霍尔采集到的电角度进行比较一下,看一下滑膜观测器是性能参数
首先在官方例程代码中加入HALL GPIO的初始化:因为只是简单的读取IO口,所以并没有加入中断,如果使用霍尔观测器那么是需要加入霍尔口的中断的
void HALL_GPIO_Init(void)
{
GPIO_Config_T GPIO_InitStructure;
GPIO_InitStructure.pin = GPIO_PIN_3;
GPIO_InitStructure.speed = GPIO_SPEED_50MHz;
GPIO_InitStructure.mode = GPIO_MODE_IN;
GPIO_InitStructure.pupd = GPIO_PUPD_NO;
GPIO_Config(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.pin = GPIO_PIN_1;
GPIO_InitStructure.speed = GPIO_SPEED_50MHz;
GPIO_InitStructure.mode = GPIO_MODE_IN;
GPIO_InitStructure.pupd = GPIO_PUPD_NO;
GPIO_Config(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.pin = GPIO_PIN_0;
GPIO_InitStructure.speed = GPIO_SPEED_50MHz;
GPIO_InitStructure.mode = GPIO_MODE_IN;
GPIO_InitStructure.pupd = GPIO_PUPD_NO;
GPIO_Config(GPIOA, &GPIO_InitStructure);
}
定义一个存储用的结构体:
user_uart.h
typedef struct
{
int16_t debug_num;//记录当前计数的个数
// int16_t U[DEBUG_MAX_NUM];
// int16_t V[DEBUG_MAX_NUM];
// int16_t W[DEBUG_MAX_NUM];
int16_t LOC[DEBUG_MAX_NUM];//滑膜观测器的电角度
uint8_t HALL[DEBUG_MAX_NUM];//采集到的霍尔值
} debugdata_S;
extern debugdata_S debug_data;
在相关代码加入数据的采集,同样也是在电机稳定运行后采集数据
static void M1_RunSpinFast(void)
{
Motor_type.Foc.s16CmdTheta += Motor_type.stc_SmoPara.s16q15SpdObs*Motor_type.Foc.s16SpdToTheta >> 15;
Motor_type.Foc.stc_SinCos = sin_cos_cal(Motor_type.Foc.s16CmdTheta);
static uint32_t cnt=0;
cnt++;
if(cnt>=100000)
{
// printf("cnt =10000\n");
if(debug_data.debug_num<=1000)
{
debug_data.debug_num++;
debug_data.LOC[debug_data.debug_num] = Motor_type.Foc.s16CmdTheta;//采集滑膜观测器的电角度
debug_data.HALL[debug_data.debug_num] = (GPIO_ReadInputBit(GPIOA,GPIO_PIN_3)<<2) + (GPIO_ReadInputBit(GPIOA,GPIO_PIN_1)<<1) + (GPIO_ReadInputBit(GPIOA,GPIO_PIN_0));//采集霍尔值
}
}
if(cnt==1)
{
// printf("cnt =0\n");
}
/*Current loop*/
current_ctrl(&Motor_type);
Motor_type.Foc.u16q15Vdq_sqrt = isqrt32((uint32_t)(Motor_type.Foc.stc_Vdq.s16q15_D * Motor_type.Foc.stc_Vdq.s16q15_D + Motor_type.Foc.stc_Vdq.s16q15_Q * Motor_type.Foc.stc_Vdq.s16q15_Q));
/* observation */
Motor_type.stc_SmoPara.s16q15_Ialpha = Motor_type.Foc.stc_Iab.s16q15_A;
Motor_type.stc_SmoPara.s16q15_Ibeta = Motor_type.Foc.stc_Iab.s16q15_B;
Motor_type.stc_SmoPara.s16q15_Ualpha = Motor_type.Foc.stc_Vab.s16q15_A;
Motor_type.stc_SmoPara.s16q15_Ubeta = Motor_type.Foc.stc_Vab.s16q15_B;
smo_cal(&Motor_type.stc_SmoPara);
/* PLL */
Motor_type.stc_PiPll.s16_Error = ((-Motor_type.stc_SmoPara.s16q15_Ealpha*Motor_type.Foc.stc_SinCos.s16q15_Cos)
- (Motor_type.stc_SmoPara.s16q15_Ebeta*Motor_type.Foc.stc_SinCos.s16q15_Sin)) >> 15;
anti_pi(&Motor_type.stc_PiPll);
Motor_type.stc_SmoPara.s16q15SpdObs = Motor_type.stc_PiPll.s32_Out;
/* svpwm */
Motor_type.Foc.stc_SvpwmPara.s16q15_Vbus = Motor_type.Foc.s16Vbus;
Motor_type.Foc.stc_SvpwmPara.s16q15_Valpha = Motor_type.Foc.stc_Vab.s16q15_A;
Motor_type.Foc.stc_SvpwmPara.s16q15_Vbeta = Motor_type.Foc.stc_Vab.s16q15_B;
svpwm7_cal(&Motor_type.Foc.stc_SvpwmPara);
PWM_Update(&Motor_type);
}
main.c while循环中添加数据采集完成后打印数据,并进行霍尔电角度线性化的代码:
debug_data.debug_num++;
for(int32_t i = 0;i<DEBUG_MAX_NUM +1;i++)
{
static int16_t hall_loc;//霍尔值赋对应电角度后的值
static uint8_t last_hall=0;//上一次霍尔值,线性化霍尔电角度使用,用来进行插补
static uint8_t same_hall=0;//相同霍尔值的次数,线性化霍尔电角度使用,用来进行插补
static uint8_t last_same_hall=0;//上次相同霍尔值的次数,线性化霍尔电角度使用,用来进行插补
if(last_hall!=debug_data.HALL[i])//与上次霍尔值不同
{
last_hall = debug_data.HALL[i];
last_same_hall = same_hall;
same_hall = 0;
if(debug_data.HALL[i]==5)
{
hall_loc = 0;
}
else if(debug_data.HALL[i]==1)
{
hall_loc = 10922;
}
else if(debug_data.HALL[i]==3)
{
hall_loc = 21844;
}
else if(debug_data.HALL[i]==2)
{
hall_loc = -32767;
}
else if(debug_data.HALL[i]==6)
{
hall_loc = -21845;
}
else if(debug_data.HALL[i]==4)
{
hall_loc = -10923;
}
else
{
hall_loc = 0;
}
}//与上次霍尔值相同
else
{
same_hall++;
// hall_loc+=32767/3/last_same_hall;//可注释,不注释进行霍尔值电角度线性化
}
printf("%d,%d,%d\n",debug_data.LOC[i],debug_data.HALL[i],hall_loc);
}
}
下面是我对读取到的霍尔值简单赋值对应电角度所得到的数据,对应不同的电机,以及正转还是反转,赋的值会有不同,上面代码赋的值只适配与我的电机
红线为滑膜观测的电角度,蓝线为霍尔值赋对应电角度的值
这样看并不明显,下面将我们的对霍尔电角度插补来进行线性化,下面是我线性化的结果
红线为滑膜观测的电角度,蓝线为霍尔值赋对应电角度进行线性化后的值
可以看到滑膜观测器的电角度值是紧跟采集到的霍尔电角度值,上面图片两条线有一定相位偏移是因为我的代码在赋值霍尔电角度时只是简单的在霍尔变化时候赋32768/3 的整数倍值,实际的情况需要根据你的电机霍尔安装位置来进行准确赋值,如果赋值准确的话,两条线应该是基本重合的。可以看出例程所给出的滑膜观测器对转子的电角度的观测值是基本与霍尔电角度基本吻合的,是可以完美控制电机的
- 2024-12-10
-
发表了主题帖:
【极海APM32M3514电机通用评估板】电机调试和AD采样数据分析
本帖最后由 IllusionXX 于 2024-12-10 13:34 编辑
接上篇,如果使用内部8MHz晶振会导致电机无法正常运行,还是改用外部晶振,打印口变更为烧录口的PA14的SWCLK,但是在串口初始化之前,我们需要增加一个延时函数,防止一上电,SWCLK就被初始化成串口而无法烧录程序。
如图接好电机,我们将调速器逆时针转到底,此时应该是最大的速度,未设置parameter.h的参数前,电机会因为IQ的KP和KI以及速度环的KP和KI过大,由于电机转子起转较慢,导致电机无法起转成功,
我们可以先将IQ的KP调小,KI调为0,发现可以转动一定角度,后来转子就无法跟上观测器设置的电角度,此时将速度环的Ki调小,此时基本完美启动,再将IQ的Ki值加上,所以造成无法电机无法启动的原因基本就是给定的启动值和加速度太大,电机转子无法跟上设定的速度,造成启动失败。以下是我的电机参数和调试的参数
/*电流环PI参数设定*/
#define CUR_PI_Q (15) // 15 or 10, can't select others, 15:Q15 format KP&KI, 10:Q10 format KP&KI
#define M1_IQ_KP (500) // Q15 format KP of Q-axis Current loop
#define M1_IQ_KI (1) // Q15 format KI of Q-axis Current loop
#define M1_ID_KP (500) // Q15 format KP of D-axis Current loop
#define M1_ID_KI (1) // Q15 format KI of D-axis Current loop
/*速度环PI参数设定*/
#define SPD_PI_Q (15) // 15 or 10, can't select others, 15:Q15 format KP&KI, 10:Q10 format KP&KI
#define M1_SPEED_KP (16384) // Q15 format KP of speed loop
#define M1_SPEED_KI (16) // Q15 format KI of speed loop
起转视频:
下面测试稳定转动时UVW的三相电流,程序上添加打印程序如下:
user_uart.h
/**/
#define DEBUG_MAX_NUM 1024
typedef struct
{
int16_t debug_num;
int16_t U[DEBUG_MAX_NUM];
int16_t V[DEBUG_MAX_NUM];
int16_t W[DEBUG_MAX_NUM];
} debugdata_S;
extern debugdata_S debug_data;
FOC_Ctrl.c
void Get_ADC_Result(Motor_TypeDef *Motor)
{
int16_t s16Ib_Temp, s16Ia_Temp;
int16_t s16IbusTemp;
/*get the two phase current */
s16Ib_Temp = (int16_t)ADC_GetValue(CURR_CHANNEL_V) << 3;
s16Ia_Temp = (int16_t)ADC_GetValue(CURR_CHANNEL_U) << 3;
s16IbusTemp = (int16_t)ADC_GetValue(IBUS_CHANNEL) << 3;
if(Motor->User.s8Direction != -1)
{
Motor->Foc.stc_Iuvw.s16q15_V = (-s16Ib_Temp + Motor->Foc.stc_IuvwOffset.s16q15_V) * IGAIN_Q10 >>10 ;
Motor->Foc.stc_Iuvw.s16q15_U = (-s16Ia_Temp + Motor->Foc.stc_IuvwOffset.s16q15_U) * IGAIN_Q10 >>10 ;
}
else
{
Motor->Foc.stc_Iuvw.s16q15_U = (-s16Ib_Temp + Motor->Foc.stc_IuvwOffset.s16q15_V) * IGAIN_Q10 >>10 ;
Motor->Foc.stc_Iuvw.s16q15_V = (-s16Ia_Temp + Motor->Foc.stc_IuvwOffset.s16q15_U) * IGAIN_Q10 >>10 ;
}
static uint32_t cnt=0;
cnt++;
if(cnt>=100000)
{
if(debug_data.debug_num<=1000)
{
debug_data.debug_num++;
debug_data.U[debug_data.debug_num]=Motor->Foc.stc_Iuvw.s16q15_U;
debug_data.V[debug_data.debug_num]=Motor->Foc.stc_Iuvw.s16q15_V;
debug_data.W[debug_data.debug_num]=0-debug_data.U[debug_data.debug_num]-debug_data.V[debug_data.debug_num];
}
}
if(cnt==1)
{
}
Motor->Foc.s16q15Ibus = (s16IbusTemp - Motor->Foc.s16q15IbusOffset) * IGAIN_Q10 >>10 ;
/*LPF fc:200hz*/
Motor->Foc.s16q15Ibusfilt = (Motor->Foc.s16q15Ibusfilt * 30382 + Motor->Foc.s16q15Ibus * 2386)>>15;
Motor->Foc.s16Vbus = (int16_t)ADC_GetValue(VDC_CHANNEL) << 3;
Motor->Foc.u32Power = (abs(Motor->Foc.s16q15Ibusfilt) * Motor->Foc.s16Vbus >>15) * 29075 >> 9;
/*LPF fc:100hz*/
Motor->Foc.u32PowerFilt = (Motor->Foc.u32PowerFilt * 31530 + Motor->Foc.u32Power * 1238)>>15;
if(Motor->Foc.u32PowerFilt <= 32767)
{
Motor->Foc.s16PowerFilt = Motor->Foc.u32PowerFilt;
}
else
{
}
// Motor->Foc.s16q15AdVot = (int16_t)ADC_GetValue(IPM_TEMP) << 3;
Motor->User.s16Vsp = ((int16_t)ADC_GetValue(Handle_CHANNEL) << 3);
if(Motor->User.bDirSwitchEnable == true)
{
Motor->User.s16Vsp = 0 ;
}
}
mian.c while循环中
if(debug_data.debug_num==1000)
{
debug_data.debug_num++;
for(int32_t i = 0;i<DEBUG_MAX_NUM +1;i++)
{
// printf("NUM=%d U=%d V=%d W=%d\n\n",i,debug_data.U[i],debug_data.V[i],debug_data.W[i]);
printf("%d,%d,%d\n",debug_data.U[i],debug_data.V[i],debug_data.W[i]);
}
}
以上程序在电机转动一定时间后开始收据数据,在收集一定量数据后在main函数while中打印采集的数据,从而不影响电机转动
以下是我采集到的三相电流数据并绘制的波形图
其中U相和V相电流数据是采集到的,W相电流数据是根据基尔霍夫电流定律计算得到的,以上数据是标幺化的数据,并没有转成0-3.3的形式
可以看到在提供的电机例程中,其电机驱动的代码很好的让无刷电机驱动起来,并让其产生的电流以比较完美的正弦波展现出来
以上数据是对电流环ID轴的Kp和Ki值并未进行深入调试,理论上可以将三相电流正弦波形展现的更完美
-
回复了主题帖:
极海APM32M3514串口使用PF0和PF1问题
lang518899 发表于 2024-12-9 21:18
用内部晶振,他们CASE使用了串口,应该切换到内部晶振,
切换到内部晶振,也是8MHz,串口调到PF0打印,可以打印了,但是电机跑不起来了,不知道是不是有什么影响,他们这个太逆天了
- 2024-12-09
-
发表了主题帖:
【极海APM32M3514电机通用评估板】软件环境搭建和所遇到的问题与解决
软件环境搭建所遇到的问题:
官网下载的PACK包出现安装报错的问题,如图:
是keil版本不够高的问题,原keil版本为5.14版本,后续更新keil为5.25版本解决此问题
官方例程编译报错的问题,报错类型为error: #3092: anonymous unions are only supported in --gnu mode, or when enabled with #pragma anon_unions
解决方法:在apm32m35xx.h文件下添加以下代码解决
#if defined ( __CC_ARM )
#pragma anon_unions
#endif
后发现跟编译器的版本有关,使用VC6编译器也可以解决
开发板硬件资源简单介绍
1:CH340串口打印电路,很方便可以直接使用typeC接口直连电脑进行打印
2:调速器,采用可变电阻旋钮调节 0~3.3V 的电压输入作为速度命令的输入端,以此调节电机转速
3:霍尔接口:有部分电机会有5V的霍尔,建议可以增加一个12转5V的电源芯片
4:电流采样
5:UVW接口
注意:因为芯片引脚资源的限制,实际上晶振口和CH340串口打印口是用的相同的两个引脚,但是只能选其一使用,芯片默认使用晶振接口(打印接口两个0欧电阻没有焊接),如果想要使用打印需要将两个连接晶振的0欧电阻挪到ch340口(即R37和 R38挪到 R44和R45),同时程序上需要修改打印口并将下面代码注释才可以正常运行(我使用的是GPIO例程)
#define SYSTEM_CLOCK_72MHz (72000000) /** HSE (8MHz) used to clock the PLL, and the PLL is used as system clock source */
这一点文档并没有明确提及,不知道是厂家资料没有给全还是本身就没有说明,希望能够注意一下,这一点对于不知道的人来说会花费很多时间,论坛上也有人在讨论这一点。同时官方给的无感例程中,并没有初始化打印部分的代码,可以添加一下,对于有意使用这款芯片开发而申请样品的人能减少很多工作量!!!!
未完待续,后续继续测评电机部分的调试
-
回复了主题帖:
极海APM32M3514串口使用PF0和PF1问题
IllusionXX 发表于 2024-12-9 10:51
电机开发板PF0和PF1接的是晶振,你这样可不得跑飞吗,看了一下官方就没有留出打印口,那我想知道测试总要打 ...
而且他硬件原理图,连接串口的电阻是没有焊的,接晶振的电阻是焊上的,看不懂这是要干嘛,也没有文档说明,既然打印不了,干嘛要预留一个打印口呢
-
回复了主题帖:
极海APM32M3514串口使用PF0和PF1问题
电机开发板PF0和PF1接的是晶振,你这样可不得跑飞吗,看了一下官方就没有留出打印口,那我想知道测试总要打印数据吧,他们是怎么打印的呢????
- 2024-11-27
-
回复了主题帖:
测评入围名单:极海APM32M3514电机通用评估板
个人信息无误,确认可以完成测评分享计划