基于SimpleFOCShield和Arduino UNO的线控转向
点击这里下载STL 文件 - 图片和Youtube视频中用于STEP的 IPower GM4108H-120T 电机 3d 打印支架以及CUI amt103 编码器支架的solidworks 项目文件。
连接所有硬件
下面图片是本项目使用的初始设置。接下来,请查看 Arduino 代码看下对应的引脚数字。
Arduino 代码
这个例程的代码十分简单易懂。由于要驱动两个无刷直流电机,我们需要使用到 Arduino UNO 的全部6个pwm引脚,这意味着我们没有多余的硬件中断引脚。因此,我们需要使用软件中断。在本例程中,我们会使用到 PciManager
库。它操作简易,因此尤为推荐,当然也可以用其他软件中断库来取代它。
两个电机可以同时在力矩/电压控制模式下进行控制,一个电机搭配作为传感器的编码器,另一个将使用具有I2C通信的磁性传感器(AS5600)。 在所有电机和传感器初始化后,无需进行配置,我们就可以初始化这两个电机的FOC算法initFOC()
并准备好进行实时执行。
不要忘记将smart stuff 置于 loop()
函数中。 😄
为了维持两个电机位置间的虚拟链接,我们编写下列代码:
motor1.move( 5*(motor2.shaft_angle - motor1.shaft_angle));
motor2.move( 5*(motor1.shaft_angle - motor2.shaft_angle));
本控制算法设置电机的电压与电机同另一电机位置的距离成比例。 常数 5
是一个增益,它需要根据实验情况而定,每次设置都会有所不同。但一般来说,数量越高,连接越坚固,就越难在电机位置之间引入偏移。
下面就是例程的完整代码。
#include <SimpleFOC.h>
// 软件中断库
#include <PciManager.h>
#include <PciListenerImp.h>
BLDCMotor motor1 = BLDCMotor(11);
BLDCDriver3PWM driver1 = BLDCDriver3PWM(3, 10, 6, 7);
Encoder encoder1 = Encoder(A2, 2, 500, A0);
void doA1(){encoder1.handleA();}
void doB1(){encoder1.handleB();}
void doI1(){encoder1.handleIndex();}
// 编码器中断初始化
PciListenerImp listenerA(encoder1.pinA, doA1);
PciListenerImp listenerB(encoder1.pinB, doB1);
PciListenerImp listenerI(encoder1.index_pin, doI1);
BLDCMotor motor2 = BLDCMotor( 11);
BLDCDriver3PWM driver2 = BLDCDriver3PWM(9, 11, 5, 8);
MagneticSensorI2C sensor2 = MagneticSensorI2C(0x36, 12, 0x0E, 4);
void setup() {
// 初始化硬件编码器
encoder1.init();
// 中断初始化
PciManager.registerListener(&listenerA);
PciManager.registerListener(&listenerB);
PciManager.registerListener(&listenerI);
// 将电机连接到传感器上
motor1.linkSensor(&encoder1);
// 初始化驱动程序
driver1.init();
// 连接驱动器
motor1.linkDriver(&driver1);
// 设置要使用的控制回路类型
motor1.controller = MotionControlType::torque;
// 初始化电机
motor1.init();
// 校准编码器并启动FOC
motor1.initFOC();
// 初始化磁传感器硬件
sensor2.init();
// 将电机连接到传感器上
motor2.linkSensor(&sensor2);
// 初始化驱动器
driver2.init();
// 连接驱动器
motor2.linkDriver(&driver2);
// 设置要使用的控制回路类型
motor2.controller = MotionControlType::torque;
// 初始化电机
motor2.init();
// 校准编码器并启动FOC
motor2.initFOC();
Serial.println("Steer by wire ready!");
_delay(1000);
}
void loop() {
// 迭代设定FOC相位电压
motor1.loopFOC();
motor2.loopFOC();
// 虚拟链接代码
motor1.move( 5*(motor2.shaft_angle - motor1.shaft_angle));
motor2.move( 5*(motor1.shaft_angle - motor2.shaft_angle));
}
基于SimpleFOCShield和Stm Nucleo-64的触碰速度控制
连接所有硬件
下面图片是本项目使用的初始设置。接下来,请查看 Arduino 代码看下对应的引脚数字。
Arduino 代码
这个例程使用 Nucleo-64 板和两个带编码器的无刷直流电机。Nucleo 没有硬件中断缺乏的问题 (每个引脚都能外部中断),不会发生因使用6个pwn引脚而出现的复杂情况。Nucleo 和Arduino UNO 唯一的区别是 Nucleo 的引脚 11
不能用于PWM,因此我们需要使用引脚13
替代。代码的其余部分非常简单。
我们定义两个电机和编码器并将其连接到一起。
#include <SimpleFOC.h>
BLDCMotor motor1 = BLDCMotor(11);
BLDCDriver3PWM driver1 = BLDCDriver3PWM(9, 6, 5, 7);
Encoder encoder1 = Encoder(A1, A2, 8192);
void doA1(){encoder1.handleA();}
void doB1(){encoder1.handleB();}
BLDCMotor motor2 = BLDCMotor(11);
BLDCDriver3PWM driver2 = BLDCDriver3PWM(3, 13, 10, 8);
Encoder encoder2 = Encoder(A3, 2, 500);
void doA2(){encoder2.handleA();}
void doB2(){encoder2.handleB();}
void setup() {
// 初始化硬件编码器
encoder1.init();
encoder1.enableInterrupts(doA1,doB1);
encoder2.init();
encoder2.enableInterrupts(doA2,doB2);
// 将电机连接到传感器上
motor1.linkSensor(&encoder1);
motor2.linkSensor(&encoder2);
// 配置驱动程序
driver1.init();
driver2.init();
// 把电机连接到驱动器上
motor1.linkDriver(&driver1);
motor2.linkDriver(&driver2);
}
void loop(){}
然后,我们通过定义运动控制类型,其中一个电机为电压控制,另一个电机为速度控制:
// 设置要使用的控制回路类型
motor1.controller = MotionControlType::torque;
motor2.controller = MotionControlType::velocity;
此外,我们通过增加 Tf
值引入更高程度的滤波,并提高一点积分增益 I
,以便更好地追踪。
// 增加过滤
motor2.LPF_velocity.Tf = 0.02;
// 上升I环收益
motor2.PID_velocity.I = 40;
最后,完成 setup()
,我们就能初始化电机和FOC算法。
再次提醒不要忘记将smart stuff 置于 loop()
函数中。虚拟连接 代码如下所示:
// 虚拟链接代码
motor1.move(5*(motor2.shaft_velocity/10 - motor1.shaft_angle));
motor2.move(10*motor1.shaft_angle);
此控制方案基本上能说明 motor2
的目标速度与 motor1
的位置成比例。此外,它还能将 motor1
的电压设置为与motor2
的速度差和 motor1
的位置差成比例。这将在这两个变量之间创建一个 虚拟连接 。
常数 5
是与上一例程中类似的增益。它仅仅会使得 motor1
在跟随 motor2
速度时或快或慢地进行响应,并希望两者响应差值保持在0。
常数 10
有点不同。这是一个比例因子,能够有助于更好地将速度映射到位置。例如,在本例中,我们使用的 motor2
的最大速度为60rad/秒
,但我们不希望仪表旋转10圈以显示此速度。我们希望它1圈最多旋转~6弧度
,因此常数为10
。但你也许将运行一个无人机电机,它以数千转/分的速度旋转,这种情况下你可能想要设置更大的缩放比例达100甚至1000。 此外,也许你会想要一个非常精确的低速电机,它的转速低于1弧度/秒. 你会想要使用~0.1
或更小的值。因此,这将取决于你的应用程序和你需要的精度。
此外,还有一件有趣的事情需要注意,这个比例因子可以是可变的,所以你也可以实时更改它。
以下是完整的例程代码:
#include <SimpleFOC.h>
BLDCMotor motor1 = BLDCMotor(11);
BLDCDriver3PWM driver1 = BLDCDriver3PWM(9, 6, 5, 7);
Encoder encoder1 = Encoder(A1, A2, 8192);
void doA1(){encoder1.handleA();}
void doB1(){encoder1.handleB();}
BLDCMotor motor2 = BLDCMotor(11);
BLDCDriver3PWM driver2 = BLDCDriver3PWM(3, 13, 10, 8);
Encoder encoder2 = Encoder(A3, 2, 500);
void doA2(){encoder2.handleA();}
void doB2(){encoder2.handleB();}
void setup() {
// 初始化硬件编码器
encoder1.init();
encoder1.enableInterrupts(doA1,doB1);
encoder2.init();
encoder2.enableInterrupts(doA2,doB2);
// 将电机连接到传感器上
motor1.linkSensor(&encoder1);
motor2.linkSensor(&encoder2);
// 配置驱动程序
driver1.init();
driver2.init();
// 把电机连接到驱动器上
motor1.linkDriver(&driver1);
motor2.linkDriver(&driver2);
// 设置要使用的控制回路类型
motor1.controller = MotionControlType::torque;
motor2.controller = MotionControlType::velocity;
motor2.LPF_velocity.Tf = 0.02;
motor2.PID_velocity.I = 40;
// 使用串行监控电机初始化
// 监控端口
Serial.begin(115200);
// 启用监控
motor1.useMonitoring(Serial);
motor2.useMonitoring(Serial);
// 初始化运动
motor1.init();
motor2.init();
// 校准编码器并启动FOC
motor1.initFOC();
motor2.initFOC();
Serial.println("Interactive gauge ready!");
_delay(1000);
}
void loop() {
// 迭代设定FOC相位电压
motor1.loopFOC();
motor2.loopFOC();
// 设定外循环目标的迭代函数
motor1.move(5*(motor2.shaft_velocity/10 - motor1.shaft_angle));
motor2.move(10*dead_zone(motor1.shaft_angle));
}
float dead_zone(float x){
return abs(x) < 0.2 ? 0 : x;
}