不久前,我们发布了与此主题相关的博客文章。不同之处在于,先前的博客文章使用旋转编码器来控制步进电机。这次我们将用普通的电位计换掉旋转编码器,并用它来控制步进电机的位置或速度。我们将使用前面提到的博客文章中的代码作为基础,并进行一些更改以实现所需的行为。就像上次一样,我们的目标是不使用任何库。
步进电机,电位器和Arduino –此博客文章的硬件成分。
这篇博文将主要包含两个代码示例并进行一些解释。这两个示例实际上都未经测试,因此请随时报告在下面的评论部分中发现的所有错误。
在阅读本文之前,您应该了解电位器的工作原理。
目录
位置
让我们看看如何使用电位器控制步进电机的位置。
这里我们有两个重要参数:
DRIVER_PULSE_PERIOD_US
设定移动速度。STEPS_PER_ANALOG_VALUE
本质上决定了运动范围。
该代码不执行任何校准,而只是假设电动机和电位器都在系统启动时居中。该代码还假定使用10位ADC(1024个模拟值)。
#define DRIVER_DIR_PIN 9
#define DRIVER_EN_PIN 8
#define POT_PIN A0
#define DRIVER_PULSE_PERIOD_US 1600
#define STEPS_PER_ANALOG_VALUE 10
enum Driver_pulse_state_enum {PULSE_IDLE, PULSE_HIGH, PULSE_LOW};
unsigned long time_now = 0;
uint16_t driver_pulse_hold_time_us = DRIVER_PULSE_PERIOD_US/2;
uint8_t driver_pulse_state = PULSE_IDLE;
uint16_t target_pos = 512*STEPS_PER_ANALOG_VALUE;
uint16_t actual_pos = 512*STEPS_PER_ANALOG_VALUE;
int pos_error = 0;
void setup() {
pinMode(DRIVER_STEP_PIN, OUTPUT);
pinMode(DRIVER_DIR_PIN, OUTPUT);
pinMode(DRIVER_EN_PIN, OUTPUT);
digitalWrite(DRIVER_EN_PIN, HIGH);
}
void loop() {
target_pos = analogRead(POT_PIN)*STEPS_PER_ANALOG_VALUE;
pos_error = target_pos - actual_pos;
if((pos_error) && (driver_pulse_state == PULSE_IDLE)){
write_pulse_high();
}
if((micros() - time_now > driver_pulse_hold_time_us) && (driver_pulse_state == PULSE_LOW)){
write_pulse_high();
}
if((micros() - time_now > driver_pulse_hold_time_us) && (driver_pulse_state == PULSE_HIGH)){
write_pulse_low();
}
}
void write_pulse_high(void){
driver_pulse_state = PULSE_HIGH;
if(pos_error > 0){
digitalWrite(DRIVER_DIR_PIN, HIGH);
actual_pos += 1;
}
else if(pos_error < 0){
digitalWrite(DRIVER_DIR_PIN, LOW);
actual_pos -= 1;
}
digitalWrite(DRIVER_STEP_PIN, HIGH);
time_now = micros();
}
void write_pulse_low(void){
digitalWrite(DRIVER_STEP_PIN, LOW);
time_now = micros();
if(pos_error){
driver_pulse_state = PULSE_LOW;
}
else{
driver_pulse_state = PULSE_IDLE;
}
}
这实际上是一个P控制器,我们在其中找到pos_error
实际位置(actual_pos
)与目标位置(target_pos
)之间的差()并使用它来控制电动机。
速度
这种方法有些不同。在这种情况下,可以以任意一种方式无限量地运行电动机。电位器控制电动机的运行速度和方向。当锅居中时,电机不动。
该代码中的三个重要参数是:
DEADZONE
设定电动机空转的中心电位器范围。MIN_DRIVER_PULSE_PERIOD_US
设置最大速度。MAX_DRIVER_PULSE_PERIOD_US
设置最低速度。
#define DRIVER_DIR_PIN 9
#define DRIVER_EN_PIN 8
#define POT_PIN A0
#define DEADZONE 50
#define MIN_DRIVER_PULSE_PERIOD_US 1000 //max speed
#define MAX_DRIVER_PULSE_PERIOD_US 3000 //min speed
enum Driver_pulse_state_enum {PULSE_IDLE, PULSE_HIGH, PULSE_LOW};
unsigned long time_now = 0;
uint16_t driver_pulse_hold_time_us = MIN_DRIVER_PULSE_PERIOD_US/2;
uint8_t driver_pulse_state = PULSE_IDLE;
int normalized_analog_value = 0;
uint8_t idle_flag = 1;
void setup() {
pinMode(DRIVER_STEP_PIN, OUTPUT);
pinMode(DRIVER_DIR_PIN, OUTPUT);
pinMode(DRIVER_EN_PIN, OUTPUT);
digitalWrite(DRIVER_EN_PIN, HIGH);
}
void loop() {
normalized_analog_value = analogRead(POT_PIN) - 512;
if(abs(normalized_analog_value)-DEADZONE < 0){
idle_flag = 1;
}
else{
idle_flag = 0;
}
driver_pulse_hold_time_us = map(abs(normalized_analog_value), DEADZONE, 512, MAX_DRIVER_PULSE_PERIOD_US, MIN_DRIVER_PULSE_PERIOD_US)/2;
if(!idle_flag && driver_pulse_state == PULSE_IDLE){
write_pulse_high();
}
if((micros() - time_now > driver_pulse_hold_time_us) && (driver_pulse_state == PULSE_LOW)){
write_pulse_high();
}
if((micros() - time_now > driver_pulse_hold_time_us) && (driver_pulse_state == PULSE_HIGH)){
write_pulse_low();
}
}
void write_pulse_high(void){
driver_pulse_state = PULSE_HIGH;
if(normalized_analog_value > 0){
digitalWrite(DRIVER_DIR_PIN, HIGH);
}
else if(normalized_analog_value < 0){
digitalWrite(DRIVER_DIR_PIN, LOW);
}
digitalWrite(DRIVER_STEP_PIN, HIGH);
time_now = micros();
}
void write_pulse_low(void){
digitalWrite(DRIVER_STEP_PIN, LOW);
time_now = micros();
if(!idle_flag){
driver_pulse_state = PULSE_LOW;
}
else{
driver_pulse_state = PULSE_IDLE;
}
}
改进和结论
在这种类型的设置中,对模拟信号进行低通滤波几乎至关重要,特别是对于位置控制代码而言。这可以在硬件或固件中完成(例如,使用指数移动平均滤波器)。我们的运动控制中仍然没有任何形式的加速,因此太高的速度变化将导致步数丢失。
这些代码示例的目的是为正在制作涉及控制步进电机的项目的其他人员提供启发。有很多方法可以做到这一点,使用步进控制库和/或屏蔽罩可能会更好,以实现更安全,更健壮和更平滑的运动和控制。