FOC:在MCU上检验Clark和Park坐标变换是否正确
文章目录
前言
仿真简单,可以参考仿真的结果,但是实际中将代码移植到MCU
,会出现一些新的问题,所以需要对坐标变换部分算法进行测试,最终可以将结果同仿真进行对比,从而验证坐标变换算法的正确性。本文通过程序中模拟ABC三相信号,最终采集Clark/Park变换之后的数据,通过串口示波软件显示,最终与仿真进行对比。
程序
头文件
正弦余弦查表函数;
/**
* @brief Trigonometrical functions type definition
*/
typedef struct
{
int16_t hCos;
int16_t hSin;
} Trig_Components;
/**
* @brief Two components stator current type definition
*/
typedef struct
{
int16_t qI_Component1;
int16_t qI_Component2;
} Curr_Components;
#define divSQRT_3 (int32_t)0x49E6 /* 1/sqrt(3) in q1.15 format=0.5773315*/
#define SIN_COS_TABLE {\
0x0000,0x00C9,0x0192,0x025B,0x0324,0x03ED,0x04B6,0x057F,\
0x0648,0x0711,0x07D9,0x08A2,0x096A,0x0A33,0x0AFB,0x0BC4,\
0x0C8C,0x0D54,0x0E1C,0x0EE3,0x0FAB,0x1072,0x113A,0x1201,\
0x12C8,0x138F,0x1455,0x151C,0x15E2,0x16A8,0x176E,0x1833,\
0x18F9,0x19BE,0x1A82,0x1B47,0x1C0B,0x1CCF,0x1D93,0x1E57,\
0x1F1A,0x1FDD,0x209F,0x2161,0x2223,0x22E5,0x23A6,0x2467,\
0x2528,0x25E8,0x26A8,0x2767,0x2826,0x28E5,0x29A3,0x2A61,\
0x2B1F,0x2BDC,0x2C99,0x2D55,0x2E11,0x2ECC,0x2F87,0x3041,\
0x30FB,0x31B5,0x326E,0x3326,0x33DF,0x3496,0x354D,0x3604,\
0x36BA,0x376F,0x3824,0x38D9,0x398C,0x3A40,0x3AF2,0x3BA5,\
0x3C56,0x3D07,0x3DB8,0x3E68,0x3F17,0x3FC5,0x4073,0x4121,\
0x41CE,0x427A,0x4325,0x43D0,0x447A,0x4524,0x45CD,0x4675,\
0x471C,0x47C3,0x4869,0x490F,0x49B4,0x4A58,0x4AFB,0x4B9D,\
0x4C3F,0x4CE0,0x4D81,0x4E20,0x4EBF,0x4F5D,0x4FFB,0x5097,\
0x5133,0x51CE,0x5268,0x5302,0x539B,0x5432,0x54C9,0x5560,\
0x55F5,0x568A,0x571D,0x57B0,0x5842,0x58D3,0x5964,0x59F3,\
0x5A82,0x5B0F,0x5B9C,0x5C28,0x5CB3,0x5D3E,0x5DC7,0x5E4F,\
0x5ED7,0x5F5D,0x5FE3,0x6068,0x60EB,0x616E,0x61F0,0x6271,\
0x62F1,0x6370,0x63EE,0x646C,0x64E8,0x6563,0x65DD,0x6656,\
0x66CF,0x6746,0x67BC,0x6832,0x68A6,0x6919,0x698B,0x69FD,\
0x6A6D,0x6ADC,0x6B4A,0x6BB7,0x6C23,0x6C8E,0x6CF8,0x6D61,\
0x6DC9,0x6E30,0x6E96,0x6EFB,0x6F5E,0x6FC1,0x7022,0x7083,\
0x70E2,0x7140,0x719D,0x71F9,0x7254,0x72AE,0x7307,0x735E,\
0x73B5,0x740A,0x745F,0x74B2,0x7504,0x7555,0x75A5,0x75F3,\
0x7641,0x768D,0x76D8,0x7722,0x776B,0x77B3,0x77FA,0x783F,\
0x7884,0x78C7,0x7909,0x794A,0x7989,0x79C8,0x7A05,0x7A41,\
0x7A7C,0x7AB6,0x7AEE,0x7B26,0x7B5C,0x7B91,0x7BC5,0x7BF8,\
0x7C29,0x7C59,0x7C88,0x7CB6,0x7CE3,0x7D0E,0x7D39,0x7D62,\
0x7D89,0x7DB0,0x7DD5,0x7DFA,0x7E1D,0x7E3E,0x7E5F,0x7E7E,\
0x7E9C,0x7EB9,0x7ED5,0x7EEF,0x7F09,0x7F21,0x7F37,0x7F4D,\
0x7F61,0x7F74,0x7F86,0x7F97,0x7FA6,0x7FB4,0x7FC1,0x7FCD,\
0x7FD8,0x7FE1,0x7FE9,0x7FF0,0x7FF5,0x7FF9,0x7FFD,0x7FFE}
#define SIN_MASK 0x0300u
//uhindex /= ( uint16_t )64;
//ANGLE >> 6
/**
| hAngle | angle | std |
| (0,16384] | U0_90 | (0,0.5] |
| (16384,32767] | U90_180 | (0.5,0.99]|
| (-16384,-1] | U270_360 | (0,-0.5] |
| (-16384,-32768] | U180_270 | (-0.5,-1) |
*/
#define U_TI_0_90 0x0000u //0x0200u
#define U_TI_90_180 0x0100u
#define U_TI_180_270 0x0200u
#define U_TI_270_360 0x0300u
Trig_Components trig_functions_ti( int16_t hAngle )
{
int32_t shindex;
uint16_t uhindex;
Trig_Components Local_Components;
/* 10 bit index computation */
uhindex = ( uint16_t )hAngle;
uhindex /= ( uint16_t )64;
/**
| hAngle | angle | std |
| (0,16384] | U0_90 | (0,0.5] |
| (16384,32767] | U90_180 | (0.5,0.99]|
| (-16384,-1] | U270_360 | (0,-0.5] |
| (-16384,-32768] | U180_270 | (-0.5,-1) |
*/
//SIN_MASK 0x0300u
switch ( ( uint16_t )( uhindex ) & SIN_MASK )
{
case U_TI_0_90:
Local_Components.hSin = hSin_Cos_Table[( uint8_t )( uhindex )];
Local_Components.hCos = hSin_Cos_Table[( uint8_t )( 0xFFu - ( uint8_t )( uhindex ) )];
break;
case U_TI_90_180:
Local_Components.hSin = hSin_Cos_Table[( uint8_t )( 0xFFu - ( uint8_t )( uhindex ) )];
Local_Components.hCos = -hSin_Cos_Table[( uint8_t )( uhindex )];
break;
case U_TI_180_270:
Local_Components.hSin = -hSin_Cos_Table[( uint8_t )( uhindex )];
Local_Components.hCos = -hSin_Cos_Table[( uint8_t )( 0xFFu - ( uint8_t )( uhindex ) )];
break;
case U_TI_270_360:
Local_Components.hSin = -hSin_Cos_Table[( uint8_t )( 0xFFu - ( uint8_t )( uhindex ) )];
Local_Components.hCos = hSin_Cos_Table[( uint8_t )( uhindex )];
break;
default:
break;
}
return ( Local_Components );
}
clark 变换 C实现
Curr_Components clarke_ti( Curr_Components Curr_Input )
{
Curr_Components Curr_Output;
int32_t qIa_divSQRT3_tmp, qIb_divSQRT3_tmp ;
int32_t wIbeta_tmp;
int16_t hIbeta_tmp;
/* qIalpha = qIas*/
Curr_Output.qI_Component1 = Curr_Input.qI_Component1;
qIa_divSQRT3_tmp = divSQRT_3 * ( int32_t )Curr_Input.qI_Component1;
qIb_divSQRT3_tmp = divSQRT_3 * ( int32_t )Curr_Input.qI_Component2;
/*qIbeta = (2*qIbs+qIas)/sqrt(3)*/
#ifdef FULL_MISRA_C_COMPLIANCY
wIbeta_tmp = ( ( qIa_divSQRT3_tmp ) + ( qIb_divSQRT3_tmp ) +
( qIb_divSQRT3_tmp ) ) / 32768;
#else
/* WARNING: the below instruction is not MISRA compliant, user should verify
that Cortex-M3 assembly instruction ASR (arithmetic shift right) is used by
the compiler to perform the shift (instead of LSR logical shift right) */
wIbeta_tmp = ( ( qIa_divSQRT3_tmp ) + ( qIb_divSQRT3_tmp ) +
( qIb_divSQRT3_tmp ) ) >> 15;
#endif
/* Check saturation of Ibeta */
if ( wIbeta_tmp > INT16_MAX )
{
hIbeta_tmp = INT16_MAX;
}
else if ( wIbeta_tmp < ( -32768 ) )
{
hIbeta_tmp = ( -32768 );
}
else
{
hIbeta_tmp = ( int16_t )( wIbeta_tmp );
}
Curr_Output.qI_Component2 = hIbeta_tmp;
if ( Curr_Output.qI_Component2 == ( int16_t )( -32768 ) )
{
Curr_Output.qI_Component2 = -32767;
}
return ( Curr_Output );
}
park c 变换实现
Curr_Components park_ti( Curr_Components Curr_Input, uint16_t Theta )
{
//v->Qs = _IQmpy(v->Beta,Cosine) - _IQmpy(v->Alpha,Sine);
Curr_Components Curr_Output;
int32_t qId_tmp_1, qId_tmp_2, qIq_tmp_1, qIq_tmp_2;
Trig_Components Local_Vector_Components,tmp;
int32_t wIqd_tmp;
int16_t hIqd_tmp;
//tmp = trig_functions_ti( Theta );
//Local_Vector_Components.hSin = tmp.hCos;
//Local_Vector_Components.hCos = tmp.hSin;
Local_Vector_Components = trig_functions_ti( Theta );
/*No overflow guaranteed*/
/*qIq_tmp_1 = qIalpha *sin(Theta) */
qIq_tmp_1 = Curr_Input.qI_Component1 * ( int32_t )Local_Vector_Components.hSin;
/*No overflow guaranteed*/
/*qIq_tmp_2 = qIbeta *cos(Theta) */
qIq_tmp_2 = Curr_Input.qI_Component2 * ( int32_t )Local_Vector_Components.hCos;
/*Iq component in Q1.15 Format */
/*Iq=-qIalpha *sin(Theta)+qIbeta *cos(Theta) */
#ifdef FULL_MISRA_C_COMPLIANCY
wIqd_tmp = ( qIq_tmp_2 - qIq_tmp_1 ) / 32768;
#else
/* WARNING: the below instruction is not MISRA compliant, user should verify
that Cortex-M3 assembly instruction ASR (arithmetic shift right) is used by
the compiler to perform the shift (instead of LSR logical shift right) */
wIqd_tmp = ( qIq_tmp_2 - qIq_tmp_1 ) >> 15;
#endif
/* Check saturation of Iq */
if ( wIqd_tmp > INT16_MAX )
{
hIqd_tmp = INT16_MAX;
}
else if ( wIqd_tmp < ( -32768 ) )
{
hIqd_tmp = ( -32768 );
}
else
{
hIqd_tmp = ( int16_t )( wIqd_tmp );
}
Curr_Output.qI_Component2 = hIqd_tmp;
if ( Curr_Output.qI_Component2 == ( int16_t )( -32768 ) )
{
Curr_Output.qI_Component2 = -32767;
}
//v->Ds = _IQmpy(v->Alpha,Cosine) + _IQmpy(v->Beta,Sine);
/*No overflow guaranteed */
/*qId_tmp_1 = qIalpha *cos(theta) */
qId_tmp_1 = Curr_Input.qI_Component1 * ( int32_t )Local_Vector_Components.hCos;
/*No overflow guaranteed*/
/*qId_tmp_2 = qIbeta *sin(Theta) */
qId_tmp_2 = Curr_Input.qI_Component2 * ( int32_t )Local_Vector_Components.hSin;
/*Id component in Q1.15 Format */
/*Id=qIalpha *cos(theta)+qIbeta *sin(Theta) */
#ifdef FULL_MISRA_C_COMPLIANCY
wIqd_tmp = ( qId_tmp_1 + qId_tmp_2 ) / 32768;
#else
/* WARNING: the below instruction is not MISRA compliant, user should verify
that Cortex-M3 assembly instruction ASR (arithmetic shift right) is used by
the compiler to perform the shift (instead of LSR logical shift right) */
wIqd_tmp = ( qId_tmp_1 + qId_tmp_2 ) >> 15;
#endif
/* Check saturation of Id */
if ( wIqd_tmp > INT16_MAX )
{
hIqd_tmp = INT16_MAX;
}
else if ( wIqd_tmp < ( -32768 ) )
{
hIqd_tmp = ( -32768 );
}
else
{
hIqd_tmp = ( int16_t )( wIqd_tmp );
}
Curr_Output.qI_Component1 = hIqd_tmp;
if ( Curr_Output.qI_Component1 == ( int16_t )( -32768 ) )
{
Curr_Output.qI_Component1 = -32767;
}
return ( Curr_Output );
}
void test_park_ti(void){
static int16_t cnt = 0;
int32_t uart_data[4];
int16_t detal = 0x5555; //120°
Trig_Components cur_a,cur_b;
Curr_Components Curr_Input;
Curr_Components Curr_Output_clark;
Curr_Components Curr_Output_dq;
//cur_a = trig_functions_ti( cnt-=64);
//cur_b = trig_functions_ti( (uint16_t)((int32_t)cnt-detal)); //模拟B相电流 +2/3*PI
#if 0
cur_a = trig_functions_ti( cnt+=64);
cur_b = trig_functions_ti( (int16_t)((int32_t)cnt-t_angle_120)); //模拟B相电流 +2/3*PI
Curr_Input.qI_Component1 = cur_a.hSin/64;
Curr_Input.qI_Component2 = cur_b.hSin/64;
Curr_Output_clark = clarke_ti(Curr_Input);
Curr_Output_dq = park_ti(Curr_Output_clark,cnt);
#else
cur_a = trig_functions_ti( cnt+=64);
cur_b = trig_functions_ti( (int16_t)((int32_t)cnt+t_angle_120)); //模拟B相电流 +2/3*PI
Curr_Input.qI_Component1 = cur_a.hSin/64;
Curr_Input.qI_Component2 = cur_b.hSin/64;
Curr_Output_clark = clarke_ti(Curr_Input);
Curr_Output_dq = park_ti(Curr_Output_clark,-cnt);
#endif
uart_data[0] = Curr_Output_clark.qI_Component1;
uart_data[1] = Curr_Output_clark.qI_Component2;
uart_data[2] = Curr_Output_dq.qI_Component1;
uart_data[3] = Curr_Output_dq.qI_Component2;
SDS_OutPut_Data_INT(uart_data);
}
、
仿真
FOC:在MCU上检验Clark和Park坐标变换是否正确的更多相关文章
- 在树莓派2或3的kali上 RCA(a/v connector)接口的正确使用方法(多图)(原创)
AV接口又称(RCA),AV接口算是出现比较早的一种接口,它由黄.白.红三种颜色的线组成,其中黄线为视频,红色为左声道,白色为右声道. ...
- 不会在CentOS 8上安装htop?今天教你正确安装
它显示有关CPU和RAM利用率,正在执行的任务,平均负载和正常运行时间的信息.另外,htop显示所有正在运行的进程的列表,并且还可以树状格式显示这些进程. htop优于top的优势包括 以颜色标记输出 ...
- win7(32 bit) + IE8 环境,IE8无法弹窗(错误提示:“此网页上的错误可能会使它无法正确运行”),有关的系统注册信息损坏——解决方法
错误截图如下: IE有关的系统注册信息损坏,导致IE无法正常弹窗. 解决办法:重新注册与IE有关的DLL文件,具体如下: 1.以管理员身份运行附件脚本(新建txt文件,将下面代码复制到txt文 ...
- 在floodlight源码的基础上添加一个新的module并正确运行
1.在src/main/java目录下新建一个package,目录结构如下: 2.在新建的package下,新建一个class,名字就是自定义的module,接下来implements想实现的serv ...
- 【STM32系列汇总】小白博主的STM32实战快速进阶之路(持续更新)
我把之前在学习和工作中使用STM32进行嵌入式开发的经验和教程等相关整理到这里,方便查阅学习,如果能帮助到您,请帮忙点个赞: 本文的宗旨 STM32 只是一个硬件平台,同样地他可以换成MSP430,N ...
- 有感FOC算法学习与实现总结
文章目录 基于STM32的有感FOC算法学习与实现总结 1 前言 2 FOC算法架构 3 坐标变换 3.1 Clark变换 3.2 Park变换 3.3 Park反变换 4 SVPWM 5 反馈部分 ...
- 在Cortex-M系列上如何准确地做us级延时?
前几天刚好同事问起在Cortex-M上延时不准的问题,在网上也没找到比较满意的答案,干脆自己对这个问题做一个总结. 根据我们的经验,最容易想到的大概通过计算指令周期来解决.该思路在Cortex上并不是 ...
- PPAS上运行pg_dump经过
目前我有两台机器, 分别已经安装了PPAS9.1,安装后建立了OS系统用户enterprisedb和数据库用户enterprisedb. 机器1:master 192.168.10.88 机器2: ...
- php laravel 帧 该文件上传
好,我承认我的忠告. 今天laravel框架编写一个文件上传部分.总能找到不正确的路径.但是,终于攻克. 以下我分享一下自己的学习体会吧. client <form method="P ...
随机推荐
- 接口测试中实际发生的几个问题——python中token传递
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:AFKplayer PS:如有需要Python学习资料的小伙伴可以加点 ...
- 一篇文章快速搞懂Redis的慢查询分析
什么是慢查询? 慢查询,顾名思义就是比较慢的查询,但是究竟是哪里慢呢?首先,我们了解一下Redis命令执行的整个过程: 发送命令 命令排队 命令执行 返回结果 在慢查询的定义中,统计比较慢的时间段指的 ...
- Linux学习笔记(七)关机、重启及常用的网络命令
关机.重启命令 sync shutdown reboot init sync 英文原意:flush file system buffers 功能:刷新文件系统缓冲区,将内存中的数据保存到硬盘中 语法: ...
- jquary 动画j
1) 点击 id为d1的正方体,将其后所有class为div1的正方体背景色设置为绿色. 代码如下: <div class="div1" > </di ...
- 转载-linux内核长什么样
来源:Linux中国 今天,我来为大家解读一幅来自 TurnOff.us 的漫画 "InSide The Linux Kernel" . TurnOff.us是一个极客漫画网站,作 ...
- Python中的可视化神器!你知道是啥吗?没错就是pyecharts!
pyecharts是一款将python与echarts结合的强大的数据可视化工具,本文将为你阐述pyecharts的使用细则 前言 我们都知道python上的一款可视化工具matplotlib,而前些 ...
- angularjs: draggable js
var startX = 0, startY = 0, x = 0, y = 0, minDragRang = 50; var targetContainer = angular.element(do ...
- TensorFlow命令行参数FLAGS使用
import os os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' import tensorflow as tf #tensorboard --logdir=&qu ...
- 0day笔记(1)PE文件格式与虚拟文件内存的映射
PE文件格式 PE 文件格式把可执行文件分成若干个数据节(section),不同的资源被存放在不同的节中. 一个典型的 PE 文件中包含的节如下: .text 存放着二进制的机器代码 .data 初始 ...
- input type file onchange上传文件的过程中,同一个文件二次上传无效的问题。
不要采用删除当前input[type=file]这个节点,然后再重新创建dom这种方案,这样是不合理的.解释如下:input[type=file]使用的是onchange去做,onchange监听的为 ...