测试环境
CubeMX
MDK5 IDE
STM32F429IGT6 芯片
正点原子开发板 阿波罗
STM32F103C8T6 芯片
Stm32最小系统板
RT-Thread操作系统
面向对象模块接口设计
LPD3806-400BM-G5-24C 脉冲AB相增量式光电旋转编码器
大疆M3508直流无刷电机3
大疆C620无刷电机调速器3
两个3508电机用来驱动摩擦轮,仅速度控制
一个3508电机用来驱动取球爪子,角度控制
但由于3508电机位置控制的问题配合编码器读取爪子转动角度
原因:https://www.yuque.com/wangxi_chn/qaxke0/ulrbrk#ZzYzP
硬件条件
- 因为使用的是正点原子开发板,芯片引脚被多处复用,定时器的两个引脚无法正常读取脉冲数(如果使用的是实验室自己绘制的核心板就不会有这个问题)
- 所以这里借用了一个Stm32最小系统板只用来读取编码器,然后通过串口把数据发送给开发板
两块板通过串口线连接,同时正点原子开发板为最小系统板供电,共地
代码工程
stm32f103_MIN.rar
stm32f429_ALIENTEX.rar
工程基础:
LED频闪指示灯模块: https://www.yuque.com/wangxi_chn/qaxke0/mltilp
DJIC610620电机群模块:https://www.yuque.com/wangxi_chn/qaxke0/ulrbrk
蓝牙驱动遥控模块:https://www.yuque.com/wangxi_chn/qaxke0/dmwgwv
串口板级通信模块:https://www.yuque.com/wangxi_chn/qaxke0/xpkyvm
ROTARY编码器驱动模块:https://www.yuque.com/wangxi_chn/qaxke0/ketr3p工程组成
-
STM32F429主控 main
初始化配置
MODULE_LED dev_led_state ={.pin = GET_PIN(B, 0),.LED_TIME_CYCLE = 4000,.LED_TIME_OUTPUT = 200};
频闪系统运行状态显示灯
MODULE_BLUETOOTHHC06 dev_communicate ={.Property_UartDevName = "uart2",};
蓝牙模块
MODULE_BSPCOMMUNICATE dev_BspCommunicate ={.Property_UartDevName = "uart3",};
串口板级通信模块
和最小系统板通信,读取编码器信息
MODULE_DjiC610620GROUP dev_DjiC610620group ={.Property_CanDevName = "can1",.Value_module_DjiC610620[DjiC610620ID_1] ={.Enable = 1,.Mode = DjiC610620MODE_SPEED,.ENCODER = DjiC610620SELF,.PID_Speed ={.Property_Kp = 6, .Property_Ki = 2, .Property_Kd = 0, .Property_dt = 0.005,.Property_integralMax = 500, .Property_AimMax = 9000, .Property_OutputMax =16300,.Property_integralErrMax = 500,}},.Value_module_DjiC610620[DjiC610620ID_2] ={.Enable = 1,.Mode = DjiC610620MODE_SPEED,.ENCODER = DjiC610620SELF,.PID_Speed ={.Property_Kp = 6, .Property_Ki = 2, .Property_Kd = 0, .Property_dt = 0.005,.Property_integralMax = 500, .Property_AimMax = 9000, .Property_OutputMax =16300,.Property_integralErrMax = 500,}},.Value_module_DjiC610620[DjiC610620ID_3] ={.Enable = 1,.Mode = DjiC610620MODE_ANGLE,.ENCODER = ROTARYENCODER,.PID_Speed ={.Property_Kp = 6, .Property_Ki = 1, .Property_Kd = 0, .Property_dt = 0.005,.Property_integralMax = 2000, .Property_AimMax = 9000, .Property_OutputMax =16300,.Property_integralErrMax = 1000,},.PID_Angle ={.Property_Kp = 2, .Property_Ki = 0.5, .Property_Kd = 0, .Property_dt = 0.005,.Property_integralMax = 2000, .Property_AimMax = 60000, .Property_OutputMax =1000,.Property_integralErrMax = 10000,},},};
电机群模块
- 分别使能三个ID为1、2、3的3508电机
- 1、2为速度模式,3为角度模式
- 配置1,2电机的速度控制PID,3电机的角度控制PID
- 注意角度控制PID PID_Angle 的 Property_OutputMax 即为达到目标角度的速度大小
- 注意设置电机的编码来源:电机1和2使用自身编码器,电机3使用ROTARY编码器
这里ROTARY编码器没有集成在里面,如果 .ENCODER = ROTARYENCODER, 需要自己手动实现对电机角度的赋值,可看后面内容
Module_Led_Config(&dev_led_state);dev_led_state.Set(&dev_led_state,9);Module_DjiC610620Group_Config(&dev_DjiC610620group);Module_BlueToothHC06_Config(&dev_communicate);Module_BspCommunicate_Config(&dev_BspCommunicate);
使能配置
/* system running shine led thread */rt_thread_t led_thread = rt_thread_create("ledshine", led_shine_entry, RT_NULL,512, RT_THREAD_PRIORITY_MAX - 3, 20);if (led_thread != RT_NULL){rt_thread_startup(led_thread);}/* motor control thread */rt_thread_t motor_thread = rt_thread_create("motor", motor_entry, RT_NULL,1024, RT_THREAD_PRIORITY_MAX - 6, 20);if (motor_thread != RT_NULL){rt_thread_startup(motor_thread);}/* blue tooth updata thread */rt_thread_t bluetupdata_thread = rt_thread_create("bluetupdata", bluetupdata_entry, RT_NULL,512, RT_THREAD_PRIORITY_MAX - 5, 20);if (bluetupdata_thread != RT_NULL){rt_thread_startup(bluetupdata_thread);}/* blue tooth control thread */rt_thread_t bluetcontrol_thread = rt_thread_create("bluetcontrol", bluetcontrol_entry, RT_NULL,512, RT_THREAD_PRIORITY_MAX - 5, 20);if (bluetcontrol_thread != RT_NULL){rt_thread_startup(bluetcontrol_thread);}/* bsp communicate thread */rt_thread_t bspCommnicate_thread = rt_thread_create("bspCommnicate", bspCommnicate_entry, RT_NULL,512, RT_THREAD_PRIORITY_MAX - 6, 20);if (bspCommnicate_thread != RT_NULL){rt_thread_startup(bspCommnicate_thread);}
配置线程
static void motor_entry(void *parameter){while (1){//get data from outsidedev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_3].Value_motor_TotalAngle =-(dev_BspCommunicate.Value_RotaryEncoder * 8);/* Communicate with motor group */dev_DjiC610620group.Method_Feed(&dev_DjiC610620group);rt_thread_mdelay(1);dev_DjiC610620group.Method_Send(&dev_DjiC610620group);}}
在电机线程中,实现读取报文,计算PID,发送报文
- 注意这里电机3的角度从编码器中获得,即从板级通信中获得,角度值已在板级通信的线程中更新
- 加上负号是为了和电机自带的编码器角度读数方向一致,避免电机PID逻辑错误
乘上了8是为了让编码器获得的脉冲数和电机自带编码器的数量级一致,方便统一PID参数的大小
static void bluetcontrol_entry(void *parameter){static rt_uint32_t time_1000m = 0;while(1){rt_thread_mdelay(1);time_1000m++;switch(dev_communicate.Value_keyMask){case (0x0001<<0): //speed up the friction wheeldev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_1].Value_motor_AimRPM -= 200;dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_2].Value_motor_AimRPM += 200;break;case (0x0001<<1): //start up the friction wheeldev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_1].Value_motor_AimRPM = -2000;dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_2].Value_motor_AimRPM = 2000;break;case (0x0001<<2): //slow down the friction wheelif(dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_2].Value_motor_AimRPM >= 200){dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_1].Value_motor_AimRPM += 200;dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_2].Value_motor_AimRPM -= 200;}break;case (0x0001<<3): //raise up the pawdev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_3].PID_Angle.Property_Kp = 3;dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_3].PID_Angle.Property_Ki = 0.5;dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_3].PID_Speed.Value_integral = 0;dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_3].PID_Angle.Value_integral = 0;dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_3].PID_Angle.Property_integralErrMax = 1000;dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_3].PID_Angle.Property_OutputMax = 1500;dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_3].Value_motor_AimAngle = -2200;break;case (0x0001<<4): //stop the friction wheeldev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_1].Value_motor_AimRPM = 0;dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_2].Value_motor_AimRPM = 0;break;case (0x0001<<5): //put down the pawdev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_3].PID_Angle.Property_Kp = 3;dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_3].PID_Speed.Property_Kp = 4;dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_3].PID_Angle.Property_Ki = 0.1;dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_3].PID_Speed.Value_integral = 0;dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_3].PID_Angle.Value_integral = 0;dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_3].PID_Angle.Property_integralErrMax = 30;dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_3].PID_Angle.Property_OutputMax = 300;dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_3].Value_motor_AimAngle = 0;break;case (0x0001<<6):break;case (0x0001<<7):break;case (0x0001<<8):break;case (0x0001<<9):break;case (0x0001<<10):break;case (0x0001<<11):break;}dev_communicate.Value_keyMask = 0x0000;if(time_1000m >= 1000){time_1000m = 0;/* 可以在这里数据回显,打印等,可调整发送频率 */// dev_communicate.Method_Send(&dev_communicate,PARAM_SHOW_1,// dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_2].Value_motor_AimRPM);// dev_communicate.Method_Send(&dev_communicate,WAVE_SHOW,// dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_2].Value_motor_AimRPM);// rt_device_read( dev_DjiC610620group.Value_module_DjiC610620[DjiC610620ID_3].Encoder_dev, 0,// &count, 1);// rt_kprintf("get count %d\n",count);}}}
通过蓝牙按键实现动作控制
初始化配置
MODULE_BSPCOMMUNICATE dev_BspCommunicate ={.Property_UartDevName = "uart2",};
串口板级通信模块
int main(void){rt_err_t ret = RT_EOK;rt_device_t pulse_encoder_dev = RT_NULL; /* 脉冲编码器设备句柄 *//* 查找脉冲编码器设备 */pulse_encoder_dev = rt_device_find(PULSE_ENCODER_DEV_NAME);if (pulse_encoder_dev == RT_NULL){rt_kprintf("pulse encoder sample run failed! can't find %s device!\n", PULSE_ENCODER_DEV_NAME);return RT_ERROR;}/* 以只读方式打开设备 */ret = rt_device_open(pulse_encoder_dev, RT_DEVICE_OFLAG_RDONLY);if (ret != RT_EOK){rt_kprintf("open %s device failed!\n", PULSE_ENCODER_DEV_NAME);return ret;}rt_pin_mode(GET_PIN(B, 6), PIN_MODE_INPUT_PULLUP);rt_pin_mode(GET_PIN(B, 7), PIN_MODE_INPUT_PULLUP);
这里没有将编码器功能封装成模块,直接使用
rt_pin_mode(GET_PIN(C, 13), PIN_MODE_OUTPUT);
发送状态指示灯
Module_BspCommunicate_Config(&dev_BspCommunicate);
使能板级通信模块
while(1){rt_thread_mdelay(1);/* 读取脉冲编码器计数值 */rt_pin_write(GET_PIN(C, 13), PIN_LOW);rt_device_read(pulse_encoder_dev, 0, &(dev_BspCommunicate.Value_RotaryEncoder), 1);if(abs(dev_BspCommunicate.Value_RotaryEncoder)>20000){/* 清空脉冲编码器计数值 */rt_device_control(pulse_encoder_dev, PULSE_ENCODER_CMD_CLEAR_COUNT, RT_NULL);}dev_BspCommunicate.Method_Send(&dev_BspCommunicate,M0_D1,dev_BspCommunicate.Value_RotaryEncoder);rt_pin_write(GET_PIN(C, 13), PIN_HIGH);}
没有使用多余线程,这里只使用了主线程工作
- LPD3806-400BM-G5-24C 脉冲AB相增量式光电旋转编码器,不能抗冲击,当落爪子力度较大,会导致读到异常数据
- 这里判断读数大于20000强制编码器读数清零,
-
存在问题
由于3508电机位置控制缺陷,原因:https://www.yuque.com/wangxi_chn/qaxke0/ulrbrk#ZzYzP
- 需要额外配置编码器,因此建议角度控制电机选用其他类型(舵机、步进电机、3863这类内部集成脉冲编码器的电机)
取球爪的PID是个较为复杂的过程
换用3863电机,角度控制,但是力量会很大,可以使用速度角度控制(目前机械方案修改方向)
- 还需仔细研究串级PID结构控制原理
