#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 "servo.h" #include "math.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 WHEEL_LENGTH 30 #define WHEEL_WIDTH 15 #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; typedef struct { /* 从Mudbus服务器端得到设置参数 */ uint16_t speed; uint16_t angle; /* 发送到Modbus服务器显示的数据 */ uint16_t encoder_accumul; /* 编码器的累计值 修正:可能装不下,考虑用多少位的来扩展 */ uint16_t encoder_accumur; uint16_t expect_speedl; uint16_t real_speedl; /* 真实速度 */ uint16_t real_motor_outputl; /* 真实电机输出 */ uint16_t expect_speedr; uint16_t real_speedr; /* 真实速度 */ uint16_t real_motor_outputr; /* 真实电机输出 */ } motor_control_t; /* gWordVar寄存器组对应到motor_control_t的各个结构 */ motor_control_t *motor_control = (motor_control_t *)&gWordVar[0]; 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; /* 从Modbus服务器端获得期望速度和期望角度 */ int speed = (short)motor_control->speed; float angle = (short)motor_control->angle; /* 注意:uint32_t强转float在有符号情况下会出错,需要先转成short */ // speed = speed * 1.0 / BDC_SPEED_INPUT_RANGE * BDC_MCPWM_DUTY_TICK_MAX; angle = -1.0 * angle / BDC_SERVO_INPUT_RANGE * 45.0; /* 将输入范围映射到-45->45度上 */ /* 前轮转向控制 */ servo_set_angle(angle); /* 后轮速度控制 */ /* 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) { motor_control->encoder_accumul = (short)cur_pulse_count; motor_control->expect_speedl = (short)speed; motor_control->real_speedl = (short)real_pulses; motor_control->real_motor_outputl = (short)cur_pwm_compare[ctx->id]; } else if (ctx->id == 1) { motor_control->encoder_accumur = (short)cur_pulse_count; motor_control->expect_speedr = (short)speed; motor_control->real_speedr = real_pulses; motor_control->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<