#include #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/queue.h" #include "esp_log.h" #include "esp_timer.h" #include "driver/pulse_cnt.h" #include "driver/gpio.h" #include "bdc_motor.h" #include "pid_ctrl.h" #include "modbus.h" #include "math.h" #include "servo.h" static const char *TAG = "bdc_control"; /* PWM输出的定时器配置 */ #define BDC_MCPWM_TIMER_RESOLUTION_HZ 10000000 /* 精度,即计数器的频率 10MHz, 1 tick = 0.1us */ #define BDC_MCPWM_FREQ_HZ 25000 /* PWM周期频率 25KHz PWM */ #define BDC_MCPWM_DUTY_TICK_MAX (BDC_MCPWM_TIMER_RESOLUTION_HZ / BDC_MCPWM_FREQ_HZ) /* PWM占空比最大值,由定时器计数频率和PWM周期频率计算得来 */ /* 编码器对应IO口 */ #define BDC_ENCODER_GPIO_A 35 #define BDC_ENCODER_GPIO_B 34 #define BDC_ENCODER_GPIO_A1 26 #define BDC_ENCODER_GPIO_B1 33 /* 有刷直流电机H桥控制,IR203s驱动芯片的LOW是低有效导通 */ #define BDC_CH1_LOW0_GPIO 1 #define BDC_CH1_HI0_GPIO 2 #define BDC_CH1_LOW1_GPIO 3 #define BDC_CH1_HI1_GPIO 4 #define BDC_CH1_AOUT_GPIO 5 #define BDC_CH0_AOUT_GPIO 6 #define BDC_CH0_LOW0_GPIO 7 #define BDC_CH0_HI0_GPIO 8 #define BDC_CH0_LOW1_GPIO 9 #define BDC_CH0_HI1_GPIO 10 /* 编码器计数范围 */ #define BDC_ENCODER_PCNT_HIGH_LIMIT 1000 #define BDC_ENCODER_PCNT_LOW_LIMIT -1000 /* pid控制时间周期ms */ #define BDC_PID_LOOP_PERIOD_MS 50 /* 范围 */ #define BDC_SERVO_INPUT_RANGE 1000 #define BDC_SPEED_INPUT_RANGE 1000 #define BDC_SERVO_REAL_RANGE (45) #define BDC_SPEED_REAL_RANGE (50) /* 实际还要更高,为了测试先降低一点 */ /* 车体测量值 */ #define WHEEL_LENGTH (31.0) #define WHEEL_WIDTH (32.4) #define WHEEL_R0 (WHEEL_WIDTH / 2) /* 数学值定义 */ #define PI 3.1415926 typedef struct { int id; /* 标识一个电机 */ uint32_t bdc_low0_gpio; uint32_t bdc_low1_gpio; bdc_motor_handle_t motor; pcnt_unit_handle_t pcnt_encoder; pid_ctrl_block_handle_t pid_ctrl; esp_timer_handle_t pid_loop_timer; int accumu_count; int report_pulses; } motor_control_context_t; static bool example_pcnt_on_reach(pcnt_unit_handle_t unit, const pcnt_watch_event_data_t *edata, void *user_ctx) { int *accumu_count = (int *)user_ctx; *accumu_count += edata->watch_point_value; return false; } /* 根据期望速度和期望角度算左轮或者右轮的速度 */ /* id=0左 id=1右 */ int speed_lr_calculate(int speed, int angle, int id) { double r = WHEEL_LENGTH / (tan(angle * PI / 180)); /* 得到旋转半径,以中轴线计算 */ if (id == 0) return (int)(speed - WHEEL_R0 / r * speed); else return (int)(speed + WHEEL_R0 / r * speed); } /* 定时器定时触发,使用PID算法控速 * 注意:速度以每次采样期间编码器计数个数为衡量标准 * 待修正:对于转弯的逻辑,可能左右轮的速度是期望速度的不同函数关系 */ static void pid_loop_cb(void *args) { static int last_pulse_count[2] = {0, 0}; static int cur_pwm_compare[2] = {0, 0}; motor_control_context_t *ctx = (motor_control_context_t *)args; pcnt_unit_handle_t pcnt_unit = ctx->pcnt_encoder; pid_ctrl_block_handle_t pid_ctrl = ctx->pid_ctrl; bdc_motor_handle_t motor = ctx->motor; /* pid参数设置 */ if (modbus_data->pid_flag) { pid_ctrl->Kp = (short)modbus_data->p / 100.0; pid_ctrl->Ki = (short)modbus_data->i / 100.0; pid_ctrl->Kd = (short)modbus_data->d / 100.0; pid_ctrl->integral_err = 0; pid_ctrl->last_output = 0; pid_ctrl->previous_err1 = 0; pid_ctrl->previous_err2 = 0; ESP_LOGI(TAG, "id%d pid set successfully:%f %f %f", ctx->id, pid_ctrl->Kp, pid_ctrl->Ki, pid_ctrl->Kd); modbus_data->speed = 0; modbus_data->angle = 0; bdc_motor_set_speed(ctx->motor, 0, ctx->id); servo_set_angle(0); modbus_data->pid_flag &= ~(0x01 << (ctx->id)); } /* 从Modbus服务器端获得期望速度和期望角度 */ int speed = (short)modbus_data->speed; float angle = (short)modbus_data->angle; speed = speed * 1.0 / BDC_SPEED_INPUT_RANGE * BDC_SPEED_REAL_RANGE; angle = -1.0 * angle / BDC_SERVO_INPUT_RANGE * BDC_SERVO_REAL_RANGE; /* 后轮速度控制 */ /* 1. 根据预期速度和角度计算左轮或者右轮的期望速度 */ speed = speed_lr_calculate(speed, angle, ctx->id); /* 2. 从编码器获得计数,得到实际速度 */ int cur_pulse_count = 0; pcnt_unit_get_count(pcnt_unit, &cur_pulse_count); cur_pulse_count += ctx->accumu_count; int real_pulses = cur_pulse_count - last_pulse_count[ctx->id]; //新的脉冲数减去上一次的脉冲数为真实脉冲数 last_pulse_count[ctx->id] = cur_pulse_count; ctx->report_pulses = real_pulses; /* 3. 计算速度误差->增量式PID->控制电机输出 */ float error = speed - real_pulses; /* 注意:这里使用的隐式类型转换 */ float output = 0; pid_compute(pid_ctrl, error, &output); output += cur_pwm_compare[ctx->id]; cur_pwm_compare[ctx->id] = output; if (output > 0) ESP_ERROR_CHECK(bdc_motor_forward(ctx->motor, ctx->id)); else if (output < 0) { ESP_ERROR_CHECK(bdc_motor_reverse(ctx->motor, ctx->id)); output = -output; } else ESP_ERROR_CHECK(bdc_motor_brake(ctx->motor, ctx->id)); bdc_motor_set_speed(ctx->motor, (uint32_t)output, ctx->id); /* 更新Modbus端的数据 */ if (ctx->id == 0) { modbus_data->encoder_accumul = (int)cur_pulse_count; modbus_data->expect_speedl = (short)speed; modbus_data->real_speedl = (short)real_pulses; modbus_data->real_motor_outputl = (short)cur_pwm_compare[ctx->id]; } else if (ctx->id == 1) { modbus_data->encoder_accumur = (int)cur_pulse_count; modbus_data->expect_speedr = (short)speed; modbus_data->real_speedr = real_pulses; modbus_data->real_motor_outputr = (short)cur_pwm_compare[ctx->id]; } } extern uint32_t low_gpio[2][2]; void bdc_motor_dir_gpio_init(motor_control_context_t motor_ctrl_ctx) { ESP_LOGI(TAG, "DC motor dir gpio init"); /* low mos管状态初始化,使用GPIO驱动,初始化高电平断开 */ gpio_config_t io_conf = {}; io_conf.intr_type = GPIO_INTR_DISABLE; io_conf.mode = GPIO_MODE_OUTPUT; io_conf.pin_bit_mask = ((1ULL<