测试环境
大疆开发板 C型
STM32F407IGH6TR 芯片
大疆M3508直流无刷电机2
大疆C620无刷电机调速器2
大疆M2006直流无刷电机2
大疆C610无刷电机调速器2
蓝牙模块
蓝牙调试助手APP
RT-Thread操作系统
面向对象模块接口设计
双舵轮底盘,四个小全向轮为支撑轮
代码工程
stm32f407_DJIc.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
工程组成
将电机群和蓝牙进一步封装为底盘控制的模块。命名为
- App_SteerWheelBase.c
-
App_SteerWheelBase.h
#define SPEEDWHEELID_LEFT DjiC610620ID_1#define SPEEDWHEELID_RIGHT DjiC610620ID_2#define ANGLEWHEELID_LEFT DjiC610620ID_3#define ANGLEWHEELID_RIGHT DjiC610620ID_4
指定使用的电机对应的电机号
- SPEEDWHEELID_LEFT:左侧速度轮
- SPEEDWHEELID_RIGHT:右侧速度轮
- ANGLEWHEELID_LEFT:左侧转向轮
ANGLEWHEELID_RIGHT:右侧转向轮
enum STEERWHEELBASE_STATION{STATIC = 0,GO_AHEAD,GO_AHEAD_RIGHT,GO_RIGHT,GO_RIGHT_BACK,GO_BACK,GO_BACK_LEFT,GO_LEFT,GO_LEFT_AHEAD,ROTATE_CLOCKWISE,ROTATE_UNCLOCKWISE,};
枚举指示底盘运动的各种状态,包括八个方位和顺时针逆时针转动
struct _APP_STEERWHEELBASE{/* Property */rt_uint16_t SPEEDWHEELRATE;rt_uint16_t ANGLEWHEELRATE;MODULE_DjiC610620GROUP dev_motorGroup;MODULE_BLUETOOTHHC06 dev_btControler;
模块结构体中的必要初始化属性
- SPEEDWHEELRATE:3508电机转速比率(电机群的控制针对的永远是转子得到的原始数据,通过设置这个参数,实现减速比的转换,齿轮的转换等,映射到轮子的转速)
- ANGLEWHEELRATE:2006电机角度转换比率,(电机群的控制针对的永远是转子得到的原始数据,通过设置这个参数,实现减速比的转换,齿轮的转换等,映射到转向轮转过的角度)
- dev_motorGroup:使用的电机群模块实体(结构体嵌套)
- dev_btControler:使用的蓝牙模块实体(结构体嵌套)
/* Value */enum STEERWHEELBASE_STATION Value_station;rt_uint16_t Value_DataShowPeriod;rt_int16_t Value_Speed; //mm/srt_int16_t Value_Angle; //°
模块结构体中的中间数值
- Value_station:当前底盘状态
- Value_DataShowPeriod:蓝牙回传显示数据的周期
- Value_Speed:底盘运动速度
- Value_Angle:转向轮转过的角度
/* Method */void (*Method_Init)(struct _APP_STEERWHEELBASE *app);void (*Method_OrderUpdate)(struct _APP_STEERWHEELBASE *app);void (*Method_DataUpdate)(struct _APP_STEERWHEELBASE *app);void (*Method_DataShow)(struct _APP_STEERWHEELBASE *app);void (*Method_Control)(struct _APP_STEERWHEELBASE *app);};typedef struct _APP_STEERWHEELBASE APP_STEERWHEELBASE;
模块结构体提供的方法函数(函数指针成员)
- Method_Init:模块初始化函数,不为用户调用,在调用Config全局函数时自动进行
- Method_OrderUpdate:模块命令更新函数,在某一线程中被用户调用,用来更新蓝牙遥控下达的命令
- Method_DataUpdate:模块数据更新函数,在某一线程中被用户调用,用来更新电机回传数据
- Method_DataShow:模块数据显示函数,在某一线程中被用户调用,用来回传到手机中参数,比如当前车速等
- Method_Control:模块控制函数,在某一线程中被用户调用,用来发送电机的控制报文
/* Glodal Method */rt_err_t App_SteerWheelBase_Config(APP_STEERWHEELBASE *App_SteerWheelBase);
使能模块的全局函数
这里重点讲述底盘控制的方法函数
static void App_SteerWheelBaseControl(APP_STEERWHEELBASE *App_SteerWheelBase){rt_int8_t ClockWiseFlag_L = 1;rt_int8_t ClockWiseFlag_R = 1;
指示底盘顺时针逆时针旋转的电机控制系数
switch(App_SteerWheelBase->dev_btControler.Value_keyMask){case (0x0001<<0):App_SteerWheelBase->Value_station = GO_LEFT_AHEAD;App_SteerWheelBase->Value_Speed += 5;App_SteerWheelBase->Value_Angle = 45;App_SteerWheelBase->dev_btControler.Value_keyMask = 0;break;.........case (0x0001<<11):App_SteerWheelBase->Value_station = ROTATE_CLOCKWISE;App_SteerWheelBase->Value_Speed += 5;App_SteerWheelBase->Value_Angle = 0;App_SteerWheelBase->dev_btControler.Value_keyMask = 0;break;}
读取蓝牙模块下的按键标识掩码(在模块的命令更新方法中被标记哪个按键被按下)
- 根据标识,修改底盘模块状态、控制参数(速度,角度)
清空标识,放置反复作用(这里为什么不放在整个switch后清空,避免按键还未发生作用就没清空,影响控制效率,确保按键生效,修改控制参数后再清空)
if(App_SteerWheelBase->Value_station == ROTATE_CLOCKWISE){ClockWiseFlag_L = 1;ClockWiseFlag_R = -1;}else if(App_SteerWheelBase->Value_station == ROTATE_UNCLOCKWISE){ClockWiseFlag_L = -1;ClockWiseFlag_R = 1;}else{ClockWiseFlag_L = 1;ClockWiseFlag_R = 1;}
根据底盘状态(顺时针/逆时针),修改参数值
/* If open the blue tooth connect check, and now is disconnected, stop all movement */if( App_SteerWheelBase->dev_btControler.ConnectCheckSwitch == 1 &&App_SteerWheelBase->dev_btControler.ConnectStatus == DISCONNECTED){App_SteerWheelBase->Value_Angle = 0;App_SteerWheelBase->Value_Speed = 0;}
蓝牙掉线保护,蓝牙是否发生掉线在蓝牙数据发送中已判断,这里直接读取状态
当蓝牙保护已开启,而且发生掉线时,强制制动,角度归零 ```c App_SteerWheelBase->dev_motorGroup.Value_module_DjiC610620[ANGLEWHEELID_LEFT].Value_motor_AimAngle = App_SteerWheelBase->Value_Angle App_SteerWheelBase->ANGLEWHEELRATE; App_SteerWheelBase->dev_motorGroup.Value_module_DjiC610620[ANGLEWHEELID_RIGHT].Value_motor_AimAngle = App_SteerWheelBase->Value_Angle App_SteerWheelBase->ANGLEWHEELRATE;
App_SteerWheelBase->dev_motorGroup.Value_module_DjiC610620[SPEEDWHEELID_LEFT].Value_motor_AimRPM = ClockWiseFlag_L App_SteerWheelBase->Value_Speed App_SteerWheelBase->SPEEDWHEELRATE; App_SteerWheelBase->dev_motorGroup.Value_module_DjiC610620[SPEEDWHEELID_RIGHT].Value_motor_AimRPM = ClockWiseFlag_R App_SteerWheelBase->Value_Speed App_SteerWheelBase->SPEEDWHEELRATE;
App_SteerWheelBase->dev_motorGroup.Method_Send(&(App_SteerWheelBase->dev_motorGroup));
- 将底盘参数作用于电机群上,发送电机控制报文<a name="fhP7i"></a>## main- 初始化配置```cMODULE_LED dev_led_state ={.pin = GET_PIN(H, 10),.Switch = 1,.LED_TIME_CYCLE = 4000,.LED_TIME_OUTPUT = 200};MODULE_LED dev_led_alarm ={.pin = GET_PIN(H, 12),.Switch = 1,.LED_TIME_CYCLE = 4000,.LED_TIME_OUTPUT = 200};
频闪指示灯(运行灯和警示灯)
APP_STEERWHEELBASE app_SteerWheelBase = {.SPEEDWHEELRATE = 189 ,//45*23/11*3591/187*2*3.1416/60.ANGLEWHEELRATE = 148 ,//66/16*36.Value_DataShowPeriod = 500,/* motor device */.dev_motorGroup = {.Property_CanDevName = "can1",/* Left Speed Wheel */.Value_module_DjiC610620[SPEEDWHEELID_LEFT] = {.Enable = 1,.Mode = DjiC610620MODE_SPEED,.PID_Speed = {.Property_Kp = 5, .Property_Ki = 0, .Property_Kd = 0, .Property_dt = 0.005,.Property_integralMax = 500, .Property_AimMax = 9000, .Property_OutputMax =16300},},/* Right Speed Wheel */.Value_module_DjiC610620[SPEEDWHEELID_RIGHT] = {.Enable = 1,.Mode = DjiC610620MODE_SPEED,.PID_Speed = {.Property_Kp = 5, .Property_Ki = 0, .Property_Kd = 0, .Property_dt = 0.005,.Property_integralMax = 500, .Property_AimMax = 9000, .Property_OutputMax =16300},},/* Left Direction Wheel */.Value_module_DjiC610620[ANGLEWHEELID_LEFT] = {.Enable = 1,.Mode = DjiC610620MODE_ANGLE,.PID_Speed = {.Property_Kp = 3, .Property_Ki = 1, .Property_Kd = 0, .Property_dt = 0.005,.Property_integralMax = 500, .Property_AimMax = 14000, .Property_OutputMax =10000},.PID_Angle = {.Property_Kp = 1, .Property_Ki = 0.2, .Property_Kd = 0, .Property_dt = 0.005,.Property_integralMax = 500, .Property_AimMax = 60000, .Property_OutputMax =14000},},/* Right Direction Wheel */.Value_module_DjiC610620[ANGLEWHEELID_RIGHT] = {.Enable = 1,.Mode = DjiC610620MODE_ANGLE,.PID_Speed = {.Property_Kp = 3, .Property_Ki = 1, .Property_Kd = 0, .Property_dt = 0.005,.Property_integralMax = 500, .Property_AimMax = 14000, .Property_OutputMax =10000},.PID_Angle = {.Property_Kp = 1, .Property_Ki = 0.2, .Property_Kd = 0, .Property_dt = 0.005,.Property_integralMax = 500, .Property_AimMax = 60000, .Property_OutputMax =14000},},},/* bluetooth controler device */.dev_btControler = {.Property_UartDevName = "uart6",.ConnectStatusPin = GET_PIN(I, 7),.ConnectCheckSwitch = 1,},};
对模块的各个成员赋值,这里仅对Property属性赋值即可,模块作用的必要参数
- 换算率
- 显示周期
- 电机群模块
- 电机群can设备
- 电机1
- 电机使能
- 电机模式(速度控制/角度控制)
- 电机速度PID
- 电机角度PID
- 电机2
- …
- …
- 电..
- 蓝牙模块
使能各个模块
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);}rt_thread_t OrderUpdate_thread = rt_thread_create("OrderUpdate_thread", OrderUpdate_entry, RT_NULL,512, RT_THREAD_PRIORITY_MAX - 9, 20);if (OrderUpdate_thread != RT_NULL){rt_thread_startup(OrderUpdate_thread);}rt_thread_t DataUpdate_thread = rt_thread_create("DataUpdate_thread", DataUpdate_entry, RT_NULL,512, RT_THREAD_PRIORITY_MAX - 8, 20);if (DataUpdate_thread != RT_NULL){rt_thread_startup(DataUpdate_thread);}rt_thread_t Control_thread = rt_thread_create("Control_thread", Control_entry, RT_NULL,512, RT_THREAD_PRIORITY_MAX - 10, 20);if (Control_thread != RT_NULL){rt_thread_startup(Control_thread);}rt_thread_t DataShow_thread = rt_thread_create("DataShow_thread", DataShow_entry, RT_NULL,512, RT_THREAD_PRIORITY_MAX - 4, 20);if (DataShow_thread != RT_NULL){rt_thread_startup(DataShow_thread);}
-
控制效果
角度响应很快,但是否准确还需再测试,原因可见链接https://www.yuque.com/wangxi_chn/qaxke0/ulrbrk#ZzYzP
还未配合舵轮上的磁编码器使用,手动定转向轮初始位置
现有双舵轮+四支撑轮结构对地面平整要求较高,如果不平整会导致两个舵轮被架空
两轮转速可能会受地面影响而不一致导致自然偏斜,需要配合陀螺仪角度闭环
