修复了djimotor发送时的致命错误,编写了shoot app的框架

This commit is contained in:
NeoZng 2022-12-04 20:26:15 +08:00
parent 8e32fc0e6f
commit e94bb504b8
12 changed files with 254 additions and 92 deletions

View File

@ -30,7 +30,12 @@
"robot_cmd.h": "c", "robot_cmd.h": "c",
"chassis_cmd.h": "c", "chassis_cmd.h": "c",
"referee.h": "c", "referee.h": "c",
"arm_math.h": "c" "arm_math.h": "c",
"bsp_dwt.h": "c",
"compare": "c",
"*.tcc": "c",
"type_traits": "c",
"typeinfo": "c"
}, },
"C_Cpp.default.configurationProvider": "ms-vscode.makefile-tools", "C_Cpp.default.configurationProvider": "ms-vscode.makefile-tools",
} }

View File

@ -26,7 +26,7 @@
#define CENTER_GIMBAL_OFFSET_X 0 // 云台旋转中心距底盘几何中心的距离,前后方向,云台位于正中心时默认设为0 #define CENTER_GIMBAL_OFFSET_X 0 // 云台旋转中心距底盘几何中心的距离,前后方向,云台位于正中心时默认设为0
#define CENTER_GIMBAL_OFFSET_Y 0 // 云台旋转中心距底盘几何中心的距离,左右方向,云台位于正中心时默认设为0 #define CENTER_GIMBAL_OFFSET_Y 0 // 云台旋转中心距底盘几何中心的距离,左右方向,云台位于正中心时默认设为0
#define RADIUS_WHEEL 60 // 轮子半径 #define RADIUS_WHEEL 60 // 轮子半径
#define REDUCTION_RATIO 19 // 电机减速比,因为编码器量测的是转子的速度而不是输出轴的速度故需进行转换 #define REDUCTION_RATIO 19.0f // 电机减速比,因为编码器量测的是转子的速度而不是输出轴的速度故需进行转换
/* 自动计算的参数 */ /* 自动计算的参数 */
#define HALF_WHEEL_BASE (WHEEL_BASE / 2.0f) #define HALF_WHEEL_BASE (WHEEL_BASE / 2.0f)
@ -39,11 +39,11 @@
#include "ins_task.h" #include "ins_task.h"
static CANCommInstance *chasiss_can_comm; // 双板通信CAN comm static CANCommInstance *chasiss_can_comm; // 双板通信CAN comm
IMU_Data_t *Chassis_IMU_data; IMU_Data_t *Chassis_IMU_data;
#endif // CHASSIS_BOARD #endif // CHASSIS_BOARD
static referee_info_t *referee_data; // 裁判系统的数据 static referee_info_t *referee_data; // 裁判系统的数据
// static SuperCAP* cap; 尚未增加超级电容 // static SuperCAP* cap; 尚未增加超级电容
static dji_motor_instance *motor_lf; // left right forward back static dji_motor_instance *motor_lf; // left right forward back
static dji_motor_instance *motor_rf; static dji_motor_instance *motor_rf;
static dji_motor_instance *motor_lb; static dji_motor_instance *motor_lb;
static dji_motor_instance *motor_rb; static dji_motor_instance *motor_rb;
@ -76,6 +76,7 @@ void ChassisInit()
.controller_setting_init_config = { .controller_setting_init_config = {
.angle_feedback_source = MOTOR_FEED, .angle_feedback_source = MOTOR_FEED,
.speed_feedback_source = MOTOR_FEED, .speed_feedback_source = MOTOR_FEED,
.outer_loop_type = SPEED_LOOP,
.close_loop_type = SPEED_LOOP | CURRENT_LOOP, .close_loop_type = SPEED_LOOP | CURRENT_LOOP,
.reverse_flag = MOTOR_DIRECTION_REVERSE, .reverse_flag = MOTOR_DIRECTION_REVERSE,
}, },
@ -97,6 +98,7 @@ void ChassisInit()
.controller_setting_init_config = { .controller_setting_init_config = {
.angle_feedback_source = MOTOR_FEED, .angle_feedback_source = MOTOR_FEED,
.speed_feedback_source = MOTOR_FEED, .speed_feedback_source = MOTOR_FEED,
.outer_loop_type = SPEED_LOOP,
.close_loop_type = SPEED_LOOP | CURRENT_LOOP, .close_loop_type = SPEED_LOOP | CURRENT_LOOP,
.reverse_flag = MOTOR_DIRECTION_REVERSE, .reverse_flag = MOTOR_DIRECTION_REVERSE,
}, },
@ -118,6 +120,7 @@ void ChassisInit()
.controller_setting_init_config = { .controller_setting_init_config = {
.angle_feedback_source = MOTOR_FEED, .angle_feedback_source = MOTOR_FEED,
.speed_feedback_source = MOTOR_FEED, .speed_feedback_source = MOTOR_FEED,
.outer_loop_type = SPEED_LOOP,
.close_loop_type = SPEED_LOOP | CURRENT_LOOP, .close_loop_type = SPEED_LOOP | CURRENT_LOOP,
.reverse_flag = MOTOR_DIRECTION_REVERSE, .reverse_flag = MOTOR_DIRECTION_REVERSE,
}, },
@ -139,6 +142,7 @@ void ChassisInit()
.controller_setting_init_config = { .controller_setting_init_config = {
.angle_feedback_source = MOTOR_FEED, .angle_feedback_source = MOTOR_FEED,
.speed_feedback_source = MOTOR_FEED, .speed_feedback_source = MOTOR_FEED,
.outer_loop_type = SPEED_LOOP,
.close_loop_type = SPEED_LOOP | CURRENT_LOOP, .close_loop_type = SPEED_LOOP | CURRENT_LOOP,
.reverse_flag = MOTOR_DIRECTION_REVERSE, .reverse_flag = MOTOR_DIRECTION_REVERSE,
}, },
@ -198,7 +202,6 @@ static void EstimateSpeed()
// 根据电机速度和imu的速度解算 // 根据电机速度和imu的速度解算
// chassis_feedback_data.vx vy wz // chassis_feedback_data.vx vy wz
// ... // ...
} }
void ChassisTask() void ChassisTask()
@ -208,6 +211,7 @@ void ChassisTask()
SubGetMessage(chassis_sub, &chassis_cmd_recv); SubGetMessage(chassis_sub, &chassis_cmd_recv);
// 根据控制模式设定旋转速度 // 根据控制模式设定旋转速度
// 后续增加不同状态的过渡模式?
switch (chassis_cmd_recv.chassis_mode) switch (chassis_cmd_recv.chassis_mode)
{ {
case CHASSIS_NO_FOLLOW: case CHASSIS_NO_FOLLOW:
@ -220,7 +224,10 @@ void ChassisTask()
// chassis_cmd_recv.wz // 当前维持定值,后续增加不规则的变速策略 // chassis_cmd_recv.wz // 当前维持定值,后续增加不规则的变速策略
break; break;
case CHASSIS_ZERO_FORCE: case CHASSIS_ZERO_FORCE:
DJIMotorStop(); // 如果出现重要模块离线或遥控器设置为急停,让电机停止 DJIMotorStop(motor_lf); // 如果出现重要模块离线或遥控器设置为急停,让电机停止
DJIMotorStop(motor_rf);
DJIMotorStop(motor_lb);
DJIMotorStop(motor_rb);
break; break;
default: default:
break; break;
@ -242,11 +249,11 @@ void ChassisTask()
// 根据电机的反馈速度计算 // 根据电机的反馈速度计算
EstimateSpeed(); EstimateSpeed();
//获取裁判系统数据 // 获取裁判系统数据
// 我方颜色id小于7是红色,大于7是蓝色,注意这里发送的是对方的颜色, 0:blue , 1:red // 我方颜色id小于7是红色,大于7是蓝色,注意这里发送的是对方的颜色, 0:blue , 1:red
chassis_feedback_data.enemy_color = referee_data->GameRobotStat.robot_id > 7 ? 1 : 0; // chassis_feedback_data.enemy_color = referee_data->GameRobotStat.robot_id > 7 ? 1 : 0; //
chassis_feedback_data.bullet_speed = referee_data->GameRobotStat.shooter_id1_17mm_speed_limit; chassis_feedback_data.bullet_speed = referee_data->GameRobotStat.shooter_id1_17mm_speed_limit;
chassis_feedback_data.rest_heat=referee_data->PowerHeatData.shooter_heat0; chassis_feedback_data.rest_heat = referee_data->PowerHeatData.shooter_heat0;
// 推送反馈消息 // 推送反馈消息
PubPushMessage(chassis_pub, &chassis_feedback_data); PubPushMessage(chassis_pub, &chassis_feedback_data);

View File

@ -43,6 +43,7 @@ void GimbalInit()
.controller_setting_init_config = { .controller_setting_init_config = {
.angle_feedback_source = MOTOR_FEED, .angle_feedback_source = MOTOR_FEED,
.speed_feedback_source = MOTOR_FEED, .speed_feedback_source = MOTOR_FEED,
.outer_loop_type=ANGLE_LOOP,
.close_loop_type = ANGLE_LOOP | SPEED_LOOP, .close_loop_type = ANGLE_LOOP | SPEED_LOOP,
.reverse_flag = MOTOR_DIRECTION_REVERSE, .reverse_flag = MOTOR_DIRECTION_REVERSE,
}, },
@ -69,6 +70,7 @@ void GimbalInit()
.controller_setting_init_config = { .controller_setting_init_config = {
.angle_feedback_source = MOTOR_FEED, .angle_feedback_source = MOTOR_FEED,
.speed_feedback_source = MOTOR_FEED, .speed_feedback_source = MOTOR_FEED,
.outer_loop_type=ANGLE_LOOP,
.close_loop_type = ANGLE_LOOP | SPEED_LOOP, .close_loop_type = ANGLE_LOOP | SPEED_LOOP,
.reverse_flag = MOTOR_DIRECTION_REVERSE, .reverse_flag = MOTOR_DIRECTION_REVERSE,
}, },
@ -101,7 +103,8 @@ void GimbalTask()
switch (gimbal_cmd_recv.gimbal_mode) switch (gimbal_cmd_recv.gimbal_mode)
{ {
case GIMBAL_ZERO_FORCE: case GIMBAL_ZERO_FORCE:
DJIMotorStop(); DJIMotorStop(yaw_motor);
DJIMotorStop(pitch_motor);
break; break;
case GIMBAL_GYRO_MODE: case GIMBAL_GYRO_MODE:
DJIMotorChangeFeed(yaw_motor, ANGLE_LOOP, OTHER_FEED); DJIMotorChangeFeed(yaw_motor, ANGLE_LOOP, OTHER_FEED);

View File

@ -4,17 +4,17 @@
* @author Even * @author Even
* @version 0.1 * @version 0.1
* @date 2022-12-02 * @date 2022-12-02
* *
* @copyright Copyright (c) HNU YueLu EC 2022 all rights reserved * @copyright Copyright (c) HNU YueLu EC 2022 all rights reserved
* *
*/ */
#ifndef ROBOT_DEF_H #ifndef ROBOT_DEF_H
#define ROBOT_DEF_H #define ROBOT_DEF_H
#define PI 3.14159f #define PI 3.14159f
#define RAD_2_ANGLE (180.0f/PI) #define RAD_2_ANGLE (180.0f / PI)
#define ANGLE_2_RAD (PI/180.0f) #define ANGLE_2_RAD (PI / 180.0f)
#include "ins_task.h" #include "ins_task.h"
#include "master_process.h" #include "master_process.h"
@ -29,7 +29,6 @@
/* 重要参数定义,注意根据不同机器人进行修改 */ /* 重要参数定义,注意根据不同机器人进行修改 */
#define YAW_MID_ECD #define YAW_MID_ECD
#if (defined(ONE_BOARD) && defined(CHASSIS_BOARD)) || \ #if (defined(ONE_BOARD) && defined(CHASSIS_BOARD)) || \
(defined(ONE_BOARD) && defined(GIMBAL_BOARD)) || \ (defined(ONE_BOARD) && defined(GIMBAL_BOARD)) || \
(defined(CHASSIS_BOARD) && defined(GIMBAL_BOARD)) (defined(CHASSIS_BOARD) && defined(GIMBAL_BOARD))
@ -55,7 +54,7 @@ typedef enum
// 底盘模式设置 // 底盘模式设置
/** /**
* @brief ,,. * @brief ,,.
* *
*/ */
typedef enum typedef enum
{ {
@ -68,9 +67,9 @@ typedef enum
// 云台模式设置 // 云台模式设置
typedef enum typedef enum
{ {
GIMBAL_ZERO_FORCE, // 电流零输入 GIMBAL_ZERO_FORCE, // 电流零输入
GIMBAL_FREE_MODE, // 云台自由运动模式,即与底盘分离(底盘此时应为NO_FOLLOW)反馈值为电机total_angle GIMBAL_FREE_MODE, // 云台自由运动模式,即与底盘分离(底盘此时应为NO_FOLLOW)反馈值为电机total_angle
GIMBAL_GYRO_MODE, // 云台陀螺仪反馈模式,反馈值为陀螺仪pitch,total_yaw_angle,底盘可以为小陀螺和跟随模式 GIMBAL_GYRO_MODE, // 云台陀螺仪反馈模式,反馈值为陀螺仪pitch,total_yaw_angle,底盘可以为小陀螺和跟随模式
} gimbal_mode_e; } gimbal_mode_e;
// 发射模式设置 // 发射模式设置
@ -83,11 +82,12 @@ typedef enum
typedef enum typedef enum
{ {
LID_CLOSE, // 弹舱盖打开 LID_CLOSE, // 弹舱盖打开
LID_ON, // 弹舱盖关闭 LID_OPEN, // 弹舱盖关闭
} lid_mode_e; } lid_mode_e;
typedef enum typedef enum
{ {
SHOOT_STOP, // 停止整个发射模块,后续可能隔离出来
LOAD_STOP, // 停止发射 LOAD_STOP, // 停止发射
LOAD_REVERSE, // 反转 LOAD_REVERSE, // 反转
LOAD_1_BULLET, // 单发 LOAD_1_BULLET, // 单发
@ -98,13 +98,9 @@ typedef enum
// 功率限制,从裁判系统获取 // 功率限制,从裁判系统获取
typedef struct typedef struct
{ // 功率控制 { // 功率控制
} Chassis_Power_Data_s; } Chassis_Power_Data_s;
/* ----------------CMD应用发布的控制数据,应当由gimbal/chassis/shoot订阅---------------- */ /* ----------------CMD应用发布的控制数据,应当由gimbal/chassis/shoot订阅---------------- */
/** /**
* @brief ,pc在云台, * @brief ,pc在云台,
@ -120,8 +116,8 @@ typedef struct
float offset_angle; // 底盘和归中位置的夹角 float offset_angle; // 底盘和归中位置的夹角
chassis_mode_e chassis_mode; chassis_mode_e chassis_mode;
//UI部分 // UI部分
// ... // ...
} Chassis_Ctrl_Cmd_s; } Chassis_Ctrl_Cmd_s;
@ -137,17 +133,15 @@ typedef struct
// cmd发布的发射控制数据,由shoot订阅 // cmd发布的发射控制数据,由shoot订阅
typedef struct typedef struct
{ // 发射弹速控制 {
loader_mode_e load_mode; loader_mode_e load_mode;
lid_mode_e lid_mode; lid_mode_e lid_mode;
shoot_mode_e shoot_mode; shoot_mode_e shoot_mode;
Bullet_Speed_e bullet_speed; Bullet_Speed_e bullet_speed; // 弹速枚举
uint8_t rest_heat; uint8_t rest_heat;
float shoot_rate; //连续发射的射频,unit per s,发/秒
} Shoot_Ctrl_Cmd_s; } Shoot_Ctrl_Cmd_s;
/* ----------------gimbal/shoot/chassis发布的反馈数据----------------*/ /* ----------------gimbal/shoot/chassis发布的反馈数据----------------*/
/** /**
* @brief cmd订阅,. * @brief cmd订阅,.
@ -165,11 +159,11 @@ typedef struct
// float real_vy; // float real_vy;
// float real_wz; // float real_wz;
uint8_t rest_heat; //剩余枪口热量 uint8_t rest_heat; // 剩余枪口热量
Bullet_Speed_e bullet_speed; //弹速限制 Bullet_Speed_e bullet_speed; // 弹速限制
Enemy_Color_e enemy_color; // 0 for blue, 1 for red Enemy_Color_e enemy_color; // 0 for blue, 1 for red
//是否需要剩余电量?(电容) // 是否需要剩余电量?(电容)
} Chassis_Upload_Data_s; } Chassis_Upload_Data_s;

View File

@ -2,18 +2,27 @@
#include "robot_def.h" #include "robot_def.h"
#include "dji_motor.h" #include "dji_motor.h"
#include "message_center.h" #include "message_center.h"
#include "bsp_dwt.h"
/* 对于双发射机构的机器人,将下面的数据封装成结构体即可,生成两份shoot应用实例 #define ONE_BULLET_DELTA_ANGLE 0 // 发射一发弹丸拨盘转动的距离,由机械设计图纸给出
*/ #define REDUCTION_RATIO 49.0f // 拨盘电机的减速比,英雄需要修改为3508的19.0f
#define NUM_PER_CIRCLE 1 // 拨盘一圈的装载量
/* 对于双发射机构的机器人,将下面的数据封装成结构体即可,生成两份shoot应用实例 */
static dji_motor_instance *friction_l; // 左摩擦轮 static dji_motor_instance *friction_l; // 左摩擦轮
static dji_motor_instance *friction_r; // 右摩擦轮 static dji_motor_instance *friction_r; // 右摩擦轮
static dji_motor_instance *loader; // 拨盘电机 static dji_motor_instance *loader; // 拨盘电机
// static servo_instance *lid; 需要增加弹舱盖
static Publisher_t *shoot_pub; static Publisher_t *shoot_pub;
static Shoot_Ctrl_Cmd_s shoot_cmd_recv; // 来自gimbal_cmd的发射控制信息 static Shoot_Ctrl_Cmd_s shoot_cmd_recv; // 来自gimbal_cmd的发射控制信息
static Subscriber_t *shoot_sub; static Subscriber_t *shoot_sub;
static Shoot_Upload_Data_s shoot_feedback_data; // 来自gimbal_cmd的发射控制信息 static Shoot_Upload_Data_s shoot_feedback_data; // 来自gimbal_cmd的发射控制信息
// 定时,计算冷却用
static uint32_t INS_DWT_Count = 0;
static float dt = 0, t = 0;
void ShootInit() void ShootInit()
{ {
// 左摩擦轮 // 左摩擦轮
@ -38,6 +47,8 @@ void ShootInit()
.controller_setting_init_config = { .controller_setting_init_config = {
.angle_feedback_source = MOTOR_FEED, .angle_feedback_source = MOTOR_FEED,
.speed_feedback_source = MOTOR_FEED, .speed_feedback_source = MOTOR_FEED,
.outer_loop_type = SPEED_LOOP,
.close_loop_type = SPEED_LOOP | CURRENT_LOOP, .close_loop_type = SPEED_LOOP | CURRENT_LOOP,
.reverse_flag = MOTOR_DIRECTION_REVERSE, .reverse_flag = MOTOR_DIRECTION_REVERSE,
}, },
@ -64,11 +75,12 @@ void ShootInit()
.controller_setting_init_config = { .controller_setting_init_config = {
.angle_feedback_source = MOTOR_FEED, .angle_feedback_source = MOTOR_FEED,
.speed_feedback_source = MOTOR_FEED, .speed_feedback_source = MOTOR_FEED,
.outer_loop_type = SPEED_LOOP,
.close_loop_type = SPEED_LOOP | CURRENT_LOOP, .close_loop_type = SPEED_LOOP | CURRENT_LOOP,
.reverse_flag = MOTOR_DIRECTION_REVERSE, .reverse_flag = MOTOR_DIRECTION_REVERSE,
}, },
.motor_type = M3508}; .motor_type = M3508};
// 拨盘电机 // 拨盘电机
Motor_Init_Config_s loader_config = { Motor_Init_Config_s loader_config = {
.can_init_config = { .can_init_config = {
.can_handle = &hcan1, .can_handle = &hcan1,
@ -76,9 +88,13 @@ void ShootInit()
}, },
.controller_param_init_config = { .controller_param_init_config = {
.angle_PID = { .angle_PID = {
// 如果启用位置环来控制发弹,需要较大的I值保证输出力矩的线性度否则出现接近拨出的力矩大幅下降
.Kd = 10, .Kd = 10,
.Ki = 1, .Ki = 1,
.Kd = 2, .Kd = 2,
},
.angle_PID = {
}, },
.speed_PID = { .speed_PID = {
@ -88,12 +104,13 @@ void ShootInit()
}, },
}, },
.controller_setting_init_config = { .controller_setting_init_config = {
.angle_feedback_source = MOTOR_FEED, .angle_feedback_source = MOTOR_FEED, .speed_feedback_source = MOTOR_FEED,
.speed_feedback_source = MOTOR_FEED, .outer_loop_type = SPEED_LOOP, // 初始化成SPEED_LOOP,让拨盘停在原地,防止拨盘上电时乱转
.close_loop_type = SPEED_LOOP | CURRENT_LOOP, .close_loop_type = ANGLE_LOOP | SPEED_LOOP | CURRENT_LOOP,
.reverse_flag = MOTOR_DIRECTION_REVERSE, .reverse_flag = MOTOR_DIRECTION_REVERSE, // 注意方向设置为拨盘的拨出的击发方向
}, },
.motor_type = M2006}; .motor_type = M2006 // 英雄使用m3508
};
friction_l = DJIMotorInit(&left_friction_config); friction_l = DJIMotorInit(&left_friction_config);
friction_r = DJIMotorInit(&right_friction_config); friction_r = DJIMotorInit(&right_friction_config);
@ -105,4 +122,76 @@ void ShootInit()
void ShootTask() void ShootTask()
{ {
// 从cmd获取控制数据
SubGetMessage(shoot_sub, &shoot_cmd_recv);
// 根据控制模式进行电机参考值设定和模式切换
switch (shoot_cmd_recv.load_mode)
{
// 停止三个电机
case SHOOT_STOP:
DJIMotorStop(friction_l);
DJIMotorStop(friction_r);
DJIMotorStop(loader);
break;
// 停止拨盘
case LOAD_STOP:
DJIMotorOuterLoop(loader, SPEED_LOOP);
DJIMotorSetRef(loader, 0);
break;
// 单发模式,根据鼠标按下的时间,触发一次之后需要进入不响应输入的状态(否则按下的时间内可能多次进入)
// 激活能量机关/干扰对方用,英雄用.
case LOAD_1_BULLET:
DJIMotorOuterLoop(loader, ANGLE_LOOP);
DJIMotorSetRef(loader, loader->motor_measure.total_angle + ONE_BULLET_DELTA_ANGLE); // 增加一发弹丸
break;
// 三连发,如果不需要后续可能删除
case LOAD_3_BULLET:
DJIMotorOuterLoop(loader, ANGLE_LOOP);
DJIMotorSetRef(loader, loader->motor_measure.total_angle + 3 * ONE_BULLET_DELTA_ANGLE); // 增加3发
break;
// 连发模式,对速度闭环,射频后续修改为可变
case LOAD_BURSTFIRE:
DJIMotorOuterLoop(loader, SPEED_LOOP);
DJIMotorSetRef(loader, shoot_cmd_recv.shoot_rate * 360 * REDUCTION_RATIO / NUM_PER_CIRCLE);
// x颗/秒换算成速度: 已知一圈的载弹量,由此计算出1s需要转的角度,注意换算角速度
break;
// 拨盘反转,对速度闭环,后续增加卡弹检测(通过裁判系统剩余热量反馈)
// 可能需要从switch-case中独立出来
case LOAD_REVERSE:
DJIMotorOuterLoop(loader, SPEED_LOOP);
// ...
break;
default:
break;
}
// 根据收到的弹速设置设定摩擦轮参考值,需实测后填入
switch (shoot_cmd_recv.bullet_speed)
{
case SMALL_AMU_15:
DJIMotorSetRef(friction_l, 0);
DJIMotorSetRef(friction_l, 0);
break;
case SMALL_AMU_18:
DJIMotorSetRef(friction_l, 0);
DJIMotorSetRef(friction_l, 0);
break;
case SMALL_AMU_30:
DJIMotorSetRef(friction_l, 0);
DJIMotorSetRef(friction_l, 0);
break;
default:
break;
}
// 开关弹舱盖
if (shoot_cmd_recv.lid_mode == LID_CLOSE)
{
//...
}
else if (shoot_cmd_recv.lid_mode == LID_OPEN)
{
//...
}
} }

View File

@ -19,17 +19,17 @@
#include "user_lib.h" #include "user_lib.h"
static INS_t INS; static INS_t INS;
IMU_Param_t IMU_Param; static IMU_Param_t IMU_Param;
PID_t TempCtrl = {0}; static PID_t TempCtrl = {0};
const float xb[3] = {1, 0, 0}; const float xb[3] = {1, 0, 0};
const float yb[3] = {0, 1, 0}; const float yb[3] = {0, 1, 0};
const float zb[3] = {0, 0, 1}; const float zb[3] = {0, 0, 1};
// 用于获取两次采样之间的时间间隔 // 用于获取两次采样之间的时间间隔
uint32_t INS_DWT_Count = 0; static uint32_t INS_DWT_Count = 0;
static float dt = 0, t = 0; static float dt = 0, t = 0;
float RefTemp = 40; // 恒温设定温度 static float RefTemp = 40; // 恒温设定温度
static void IMU_Param_Correction(IMU_Param_t *param, float gyro[3], float accel[3]); static void IMU_Param_Correction(IMU_Param_t *param, float gyro[3], float accel[3]);

View File

@ -8,3 +8,7 @@
## 算法解析 ## 算法解析
介绍EKF四元数姿态解算的教程在:[四元数EKF姿态更新算法](https://zhuanlan.zhihu.com/p/454155643) 介绍EKF四元数姿态解算的教程在:[四元数EKF姿态更新算法](https://zhuanlan.zhihu.com/p/454155643)
## 模块移植
由于历史遗留问题,IMU模块耦合程度高.后续实现BSP_SPI,将bmi088 middleware删除.仅保留BMI088读取的协议和寄存器定义等,单独实现IMU模块.

View File

@ -1 +1,3 @@
#include "monitor.h" #include "monitor.h"
#include "bsp_dwt.h"

View File

@ -1,10 +1,9 @@
#include "dji_motor.h" #include "dji_motor.h"
#define PI2 3.141592f #define PI2 (3.141592f * 2) // 2pi
#define ECD_ANGLE_COEF 0.043945f // 360/8192 #define ECD_ANGLE_COEF 0.043945f // 360/8192,将编码器值转化为角度制
static uint8_t idx = 0; // register idx,是该文件的全局电机索引,在注册时使用 static uint8_t idx = 0; // register idx,是该文件的全局电机索引,在注册时使用
static uint8_t stop_flag = 0;
/* DJI电机的实例,此处仅保存指针,内存的分配将通过电机实例初始化时通过malloc()进行 */ /* DJI电机的实例,此处仅保存指针,内存的分配将通过电机实例初始化时通过malloc()进行 */
static dji_motor_instance *dji_motor_info[DJI_MOTOR_CNT] = {NULL}; static dji_motor_instance *dji_motor_info[DJI_MOTOR_CNT] = {NULL};
@ -196,6 +195,7 @@ void DJIMotorSetRef(dji_motor_instance *motor, float ref)
void DJIMotorControl() void DJIMotorControl()
{ {
// 预先通过静态变量定义避免反复释放分配栈空间,直接保存一次指针引用从而减小访存的开销 // 预先通过静态变量定义避免反复释放分配栈空间,直接保存一次指针引用从而减小访存的开销
// 同样可以提高可读性
static uint8_t group, num; static uint8_t group, num;
static int16_t set; static int16_t set;
static dji_motor_instance *motor; static dji_motor_instance *motor;
@ -213,40 +213,51 @@ void DJIMotorControl()
motor_controller = &motor->motor_controller; motor_controller = &motor->motor_controller;
motor_measure = &motor->motor_measure; motor_measure = &motor->motor_measure;
// pid_ref会顺次通过被启用的环充当数据的载体 // pid_ref会顺次通过被启用的闭环充当数据的载体
if (motor_setting->close_loop_type & ANGLE_LOOP) // 计算位置环 // 计算位置环,只有启用位置环且外层闭环为位置时会计算速度环输出
if ((motor_setting->close_loop_type & ANGLE_LOOP) && motor_setting->outer_loop_type == ANGLE_LOOP)
{ {
if (motor_setting->angle_feedback_source == OTHER_FEED) if (motor_setting->angle_feedback_source == OTHER_FEED)
pid_measure = *motor_controller->other_angle_feedback_ptr; pid_measure = *motor_controller->other_angle_feedback_ptr;
else // MOTOR_FEED else // MOTOR_FEED
pid_measure = motor_measure->total_angle; pid_measure = motor_measure->total_angle; // 对total angle闭环,防止在边界处出现突跃
// 更新pid_ref进入下一个环 // 更新pid_ref进入下一个环
motor_controller->pid_ref = PID_Calculate(&motor_controller->angle_PID, pid_measure, motor_controller->pid_ref); motor_controller->pid_ref = PID_Calculate(&motor_controller->angle_PID, pid_measure, motor_controller->pid_ref);
} }
if (motor_setting->close_loop_type & SPEED_LOOP) // 计算速度环 // 计算速度环,(外层闭环为速度或位置)且(启用速度环)时会计算速度环
if ((motor_setting->close_loop_type & SPEED_LOOP) && (motor_setting->outer_loop_type | (ANGLE_LOOP | SPEED_LOOP)))
{ {
if (motor_setting->speed_feedback_source == OTHER_FEED) if (motor_setting->speed_feedback_source == OTHER_FEED)
pid_measure = *motor_controller->other_speed_feedback_ptr; pid_measure = *motor_controller->other_speed_feedback_ptr;
else else
pid_measure = motor_measure->speed_rpm; pid_measure = motor_measure->speed_rpm;
// 更新pid_ref进入下一个环
motor_controller->pid_ref = PID_Calculate(&motor_controller->speed_PID, pid_measure, motor_controller->pid_ref); motor_controller->pid_ref = PID_Calculate(&motor_controller->speed_PID, pid_measure, motor_controller->pid_ref);
} }
if (motor_setting->close_loop_type & CURRENT_LOOP) // 计算电流环 // 计算电流环,只要启用了电流环就计算,不管外层闭环是什么,并且电流只有电机自身的反馈
if (motor_setting->close_loop_type & CURRENT_LOOP)
{ {
motor_controller->pid_ref = PID_Calculate(&motor_controller->current_PID, motor_measure->given_current, motor_controller->pid_ref); motor_controller->pid_ref = PID_Calculate(&motor_controller->current_PID, motor_measure->given_current, motor_controller->pid_ref);
} }
set = (int16_t)motor_controller->pid_ref; // 获取最终输出 // 获取最终输出
if (motor_setting->reverse_flag == MOTOR_DIRECTION_REVERSE) // 设置反转 set = (int16_t)motor_controller->pid_ref;
set *= -1; if (motor_setting->reverse_flag == MOTOR_DIRECTION_REVERSE)
set *= -1; // 设置反转
// 分组填入发送数据 // 分组填入发送数据
group = motor->sender_group; group = motor->sender_group;
num = motor->message_num; num = motor->message_num;
sender_assignment[group].tx_buff[num] = 0xff & set >> 8; sender_assignment[group].tx_buff[2 * num] = 0xff & set >> 8;
sender_assignment[group].tx_buff[2 * num + 1] = 0xff & set; sender_assignment[group].tx_buff[2 * num + 1] = 0xff & set;
// 电机是否停止运行
if (motor->stop_flag == MOTOR_STOP)
{ // 若该电机处于停止状态,直接将buff置零
memset(sender_assignment[group].tx_buff + 2 * num, 0, 16u);
}
} }
else // 遇到空指针说明所有遍历结束,退出循环 else // 遇到空指针说明所有遍历结束,退出循环
break; break;
@ -257,21 +268,22 @@ void DJIMotorControl()
{ {
if (sender_enable_flag[i]) if (sender_enable_flag[i])
{ {
if (stop_flag) // 如果紧急停止,将所有发送信息置零
{
memset(sender_assignment[i].tx_buff, 0, 8);
}
CANTransmit(&sender_assignment[i]); CANTransmit(&sender_assignment[i]);
} }
} }
} }
void DJIMotorStop() void DJIMotorStop(dji_motor_instance *motor)
{ {
stop_flag = 1; motor->stop_flag = MOTOR_STOP;
} }
void DJIMotorEnable() void DJIMotorEnable(dji_motor_instance *motor)
{ {
stop_flag = 0; motor->stop_flag = MOTOR_ENALBED;
}
void DJIMotorOuterLoop(dji_motor_instance *motor, Closeloop_Type_e outer_loop)
{
motor->motor_settings.outer_loop_type = outer_loop;
} }

View File

@ -61,6 +61,8 @@ typedef struct
Motor_Type_e motor_type; Motor_Type_e motor_type;
Motor_Working_Type_e stop_flag;
} dji_motor_instance; } dji_motor_instance;
/** /**
@ -106,16 +108,25 @@ void DJIMotorChangeFeed(dji_motor_instance *motor, Closeloop_Type_e loop, Feedba
void DJIMotorControl(); void DJIMotorControl();
/** /**
* @brief ,, * @brief ,,
* *
*/ */
void DJIMotorStop(); void DJIMotorStop(dji_motor_instance* motor);
/** /**
* @brief , * @brief ,
* ,stop_flag的默认值为0 * ,stop_flag的默认值为0
* *
*/ */
void DJIMotorEnable(); void DJIMotorEnable(dji_motor_instance* motor);
/**
* @brief ()
*
* @param motor
* @param outer_loop
*/
void DJIMotorOuterLoop(dji_motor_instance *motor, Closeloop_Type_e outer_loop);
#endif // !DJI_MOTOR_H #endif // !DJI_MOTOR_H

View File

@ -11,7 +11,15 @@
> 如果你不需要理解该模块的工作原理,你只需要查看这一小节。 > 如果你不需要理解该模块的工作原理,你只需要查看这一小节。
dji_motor模块对DJI智能电机包括M2006M3508以及GM6020进行了详尽的封装。你不再需要关心PID的计算以及CAN报文的发送和接收解析你只需要专注于根据应用层的需求设定合理的期望值并通过`DJIMotorSetRef()`设置对应电机的输入参考即可。如果你希望更改电机的反馈来源比如进入小陀螺模式这时候你想要云台保持静止使用IMU的yaw角度值作为反馈来源只需要调用`DJIMotorChangeFeed()`电机便可立刻切换反馈数据来源至IMU。 dji_motor模块对DJI智能电机包括M2006M3508以及GM6020进行了详尽的封装。你不再需要关心PID的计算以及CAN报文的发送和接收解析你只需要专注于根据应用层的需求设定合理的期望值并通过`DJIMotorSetRef()`设置对应电机的输入参考即可。
**==设定值的单位==**
1. 位置环为角度,角度制。
2. 速度环为角速度,单位为度/每秒
3.
如果你希望更改电机的反馈来源,比如进入小陀螺模式/视觉模式这时候你想要云台保持静止使用IMU的yaw角度值作为反馈来源只需要调用`DJIMotorChangeFeed()`电机便可立刻切换反馈数据来源至IMU。
要获得一个电机,请通过`DJIMotorInit()`并传入一些参数,他就会返回一个电机的指针。你也不再需要查看这些电机和电调的说明书,**只需要设置其电机id**6020为拨码开关值2006和3508为电调的闪动次数该模块会自动为你计算CAN发送和接收ID并搞定所有硬件层的琐事。 要获得一个电机,请通过`DJIMotorInit()`并传入一些参数,他就会返回一个电机的指针。你也不再需要查看这些电机和电调的说明书,**只需要设置其电机id**6020为拨码开关值2006和3508为电调的闪动次数该模块会自动为你计算CAN发送和接收ID并搞定所有硬件层的琐事。
@ -41,9 +49,9 @@ dji_motor模块对DJI智能电机包括M2006M3508以及GM6020进行了详
CURRENT_LOOP CURRENT_LOOP
SPEED_LOOP SPEED_LOOP
ANGLE_LOOP ANGLE_LOOP
CURRENT_LOOP| SPEED_LOOP // 同时对电流和速度闭环 CURRENT_LOOP | SPEED_LOOP // 同时对电流和速度闭环
SPEED_LOOP | ANGLE_LOOP // 同时对速度和位置闭环 SPEED_LOOP | ANGLE_LOOP // 同时对速度和位置闭环
CURRENT_LOOP| SPEED_LOOP |ANGLE_LOOP // 三环全开 CURRENT_LOOP | SPEED_LOOP |ANGLE_LOOP // 三环全开
``` ```
- 是否反转 - 是否反转
@ -120,9 +128,11 @@ Motor_Init_Config_s config = {
.motor_type = M3508, // 要注册的电机为3508电机 .motor_type = M3508, // 要注册的电机为3508电机
.can_init_config = {.can_handle = &hcan1, // 挂载在CAN1 .can_init_config = {.can_handle = &hcan1, // 挂载在CAN1
.tx_id = 1}, // C620每隔一段时间闪动1次,设置为1 .tx_id = 1}, // C620每隔一段时间闪动1次,设置为1
// 采用电机编码器角度与速度反馈,启用速度环和电流环,不反转 // 采用电机编码器角度与速度反馈,启用速度环和电流环,不反转,最外层闭环为速度环
.controller_setting_init_config = {.angle_feedback_source = MOTOR_FEED, .controller_setting_init_config = {.angle_feedback_source = MOTOR_FEED,
.close_loop_type = SPEED_LOOP | CURRENT_LOOP,
.outer_loop_type = SPEED_LOOP,
.close_loop_type = SPEED_LOOP | CURRENT_LOOP,
.speed_feedback_source = MOTOR_FEED, .speed_feedback_source = MOTOR_FEED,
.reverse_flag = MOTOR_DIRECTION_NORMAL}, .reverse_flag = MOTOR_DIRECTION_NORMAL},
// 电流环和速度环PID参数的设置,不采用计算优化则不需要传入Improve参数 // 电流环和速度环PID参数的设置,不采用计算优化则不需要传入Improve参数
@ -218,7 +228,9 @@ typedef struct
/* sender assigment*/ /* sender assigment*/
uint8_t sender_group; uint8_t sender_group;
uint8_t message_num; uint8_t message_num;
uint8_t stop_flag;
Motor_Type_e motor_type; Motor_Type_e motor_type;
} dji_motor_instance; } dji_motor_instance;
``` ```
@ -233,6 +245,7 @@ typedef struct
```c ```c
typedef struct /* 电机控制配置 */ typedef struct /* 电机控制配置 */
{ {
Closeloop_Type_e outer_loop_type;
Closeloop_Type_e close_loop_type; Closeloop_Type_e close_loop_type;
Reverse_Flag_e reverse_flag; Reverse_Flag_e reverse_flag;
Feedback_Source_e angle_feedback_source; Feedback_Source_e angle_feedback_source;
@ -257,6 +270,9 @@ typedef struct
``` ```
以M3508为例假设需要进行**速度闭环**和**电流闭环**,那么在初始化时就将这个变量的值设为`CURRENT_LOOP | SPEED_LOOP`。在`DJIMotorControl()`中,函数将会根据此标志位判断设定的参考值需要经过那些控制器的计算。 以M3508为例假设需要进行**速度闭环**和**电流闭环**,那么在初始化时就将这个变量的值设为`CURRENT_LOOP | SPEED_LOOP`。在`DJIMotorControl()`中,函数将会根据此标志位判断设定的参考值需要经过那些控制器的计算。
另外,你还需要设置当前电机的最外层闭环,即电机的闭环目标为什么类型的值。初始化时需要设置`outer_loop_type`。以M2006作为拨盘电机时为例你希望它在单发/双发等固定发射数量的模式下对位置进行闭环(拨盘转过一定角度对应拨出一颗弹丸),但你也有可能希望在连发的时候让拨盘连续的转动,以一定的频率发射弹丸。我们提供了`DJIMotorOuterLoop()`用于修改电机的外层闭环,改变电机的闭环对象。
> 注意务必分清串级控制多环和外层闭环的区别。前者是为了提高内环的性能使得其能更好地跟随外环参考值而后者描述的是系统真实的控制目标闭环目标。如3508没有电流环仍然可以对速度完成闭环对于高层的应用来说它们本质上不关心电机内部是否还有电流环它们只把外层闭环为速度的电机当作一个**速度伺服执行器****外层闭环**描述的就是真正的闭环目标。
- 为了避开恼人的正负号,提高代码的可维护性,在初始化电机时设定`reverse_flag`使得所有电机都按照你想要的方向旋转,其定义如下: - 为了避开恼人的正负号,提高代码的可维护性,在初始化电机时设定`reverse_flag`使得所有电机都按照你想要的方向旋转,其定义如下:
@ -321,6 +337,12 @@ void DJIMotorChangeFeed(dji_motor_instance *motor,
Feedback_Source_e type); Feedback_Source_e type);
void DJIMotorControl(); void DJIMotorControl();
void DJIMotorStop(dji_motor_instance *motor);
void DJIMotorEnable(dji_motor_instance *motor);
void DJIMotorOuterLoop(dji_motor_instance *motor);
``` ```
- `DJIMotorInit()`是用于初始化电机对象的接口传入包括电机can配置、电机控制配置、电机控制器配置以及电机类型在内的初始化参数。**它将会返回一个电机实例指针**,你应当在应用层保存这个指针,这样才能操控这个电机。 - `DJIMotorInit()`是用于初始化电机对象的接口传入包括电机can配置、电机控制配置、电机控制器配置以及电机类型在内的初始化参数。**它将会返回一个电机实例指针**,你应当在应用层保存这个指针,这样才能操控这个电机。
@ -337,6 +359,12 @@ void DJIMotorControl();
2. 根据反转标志位,确定是否将输出反转 2. 根据反转标志位,确定是否将输出反转
3. 根据每个电机的发送分组将最终输出值填入对应的分组buff 3. 根据每个电机的发送分组将最终输出值填入对应的分组buff
4. 检查每一个分组,若该分组有电机,发送报文 4. 检查每一个分组,若该分组有电机,发送报文
- `DJIMotorStop()`和`DJIMotorEnable()`用于控制电机的启动和停止。当电机被设为stop的时候不会响应任何的参考输入。
- `DJIMotorOuterLoop()`用于修改电机的外部闭环类型,即电机的真实闭环目标。
## 私有函数和变量 ## 私有函数和变量
@ -350,7 +378,7 @@ static dji_motor_instance *dji_motor_info[DJI_MOTOR_CNT] = {NULL};
这是管理所有电机实例的入口。idx用于电机初始化。 这是管理所有电机实例的入口。idx用于电机初始化。
```c ```c
#define PI2 3.141592f #define PI2 (3.141592f * 2)
#define ECD_ANGLE_COEF 3.835e-4 // ecd/8192*pi #define ECD_ANGLE_COEF 3.835e-4 // ecd/8192*pi
``` ```
@ -404,6 +432,7 @@ Motor_Init_Config_s config = {
}, },
.controller_setting_init_config = { .controller_setting_init_config = {
.angle_feedback_source = MOTOR_FEED, .angle_feedback_source = MOTOR_FEED,
.outer_loop_type = SPEED_LOOP,
.close_loop_type = SPEED_LOOP | ANGLE_LOOP, .close_loop_type = SPEED_LOOP | ANGLE_LOOP,
.speed_feedback_source = MOTOR_FEED, .speed_feedback_source = MOTOR_FEED,
.reverse_flag = MOTOR_DIRECTION_NORMAL .reverse_flag = MOTOR_DIRECTION_NORMAL
@ -436,4 +465,4 @@ dji_motor_instance *djimotor = DJIMotorInit(&config);
DJIMotorSetRef(djimotor, 10); DJIMotorSetRef(djimotor, 10);
``` ```
前提是已经将`MotorTask()`放入实时系统任务当中。你也可以单独执行`MotorControl()`。 前提是已经将`DJIMotorControl()`放入实时系统任务当中或以一定d。你也可以单独执行`DJIMotorControl()`。

View File

@ -17,7 +17,6 @@
#define LIMIT_MIN_MAX(x, min, max) (x) = (((x) <= (min)) ? (min) : (((x) >= (max)) ? (max) : (x))) #define LIMIT_MIN_MAX(x, min, max) (x) = (((x) <= (min)) ? (min) : (((x) >= (max)) ? (max) : (x)))
/** /**
* @brief ,,使 * @brief ,,使
* : CURRENT_LOOP|SPEED_LOOP * : CURRENT_LOOP|SPEED_LOOP
@ -49,13 +48,20 @@ typedef enum
MOTOR_DIRECTION_REVERSE = 1 MOTOR_DIRECTION_REVERSE = 1
} Reverse_Flag_e; } Reverse_Flag_e;
typedef enum
{
MOTOR_STOP = 0,
MOTOR_ENALBED = 1,
} Motor_Working_Type_e;
/* 电机控制设置,包括闭环类型,反转标志和反馈来源 */ /* 电机控制设置,包括闭环类型,反转标志和反馈来源 */
typedef struct typedef struct
{ {
Closeloop_Type_e close_loop_type; Closeloop_Type_e outer_loop_type; // 最外层的闭环,未设置时默认为最高级的闭环
Reverse_Flag_e reverse_flag; Closeloop_Type_e close_loop_type; // 使用几个闭环(串级)
Feedback_Source_e angle_feedback_source; Reverse_Flag_e reverse_flag; // 是否反转
Feedback_Source_e speed_feedback_source; Feedback_Source_e angle_feedback_source; // 角度反馈类型
Feedback_Source_e speed_feedback_source; // 速度反馈类型
} Motor_Control_Setting_s; } Motor_Control_Setting_s;
@ -63,9 +69,9 @@ typedef struct
// 后续增加前馈数据指针 // 后续增加前馈数据指针
typedef struct typedef struct
{ {
float *other_angle_feedback_ptr; float *other_angle_feedback_ptr; // 其他反馈来源的反馈数据指针
float *other_speed_feedback_ptr; float *other_speed_feedback_ptr;
// float *angle_foward_ptr; // float *angle_foward_ptr; //前馈数据指针
// float *speed_foward_ptr; // float *speed_foward_ptr;
// float *current_foward_ptr; // float *current_foward_ptr;
@ -83,7 +89,7 @@ typedef enum
M3508 = 1, M3508 = 1,
M2006 = 2, M2006 = 2,
LK9025 = 3, LK9025 = 3,
HT04 = 4 HT04 = 4,
} Motor_Type_e; } Motor_Type_e;
/** /**