增加了文档,修改通信协议,更新了平衡底盘的VMC映射和五连杆解算,修复了双板通信数据结构步对齐的错误

This commit is contained in:
NeoZng 2023-04-14 18:17:46 +08:00
parent 681dce3c90
commit 9e364cbaaa
14 changed files with 225 additions and 97 deletions

View File

@ -24,6 +24,7 @@
> ***强烈推荐使用VSCode进行开发Ozone进行调试。***
VSCode可通过Cortex-Debug利用OpenOCD进行调试jlink/stlink/dap-link都支持具体的使用方法和环境配置教程在[VSCode+Ozone使用方法](./VSCode+Ozone使用方法.md)中。**请使用UTF-8编码查看\&编辑此项目**。
**此外,本项目中使用到的物理变量值均采用标准单位制**若有特殊需求可以通过module层的`general_def.h`添加物理量转换关系的宏。
- **分层**
@ -165,11 +166,11 @@ Module层主要存放的是类型定义和实例指针数组在该层没有
务必为模块添加说明文档和使用范例,以及其他需要注意的事项(如果有)。
> **待优化**
> **面向对象小指南**
>
> 由于C语言没有对象的概念对于需要使用通信的module在其.c文件下都需要保存每个实例的指针在收到消息时遍历所有实例指针找到收到消息的实例。这种处理方式可能会导致实时性下降CAN接收时要遍历所有注册了CAN的实例进入module层还需要一次遍历。用C++则可以将对象的this指针和模块的回调函数进行绑定生成一个可调用对象然后再进行CAN的注册使得其不需要module层的遍历。
> 由于C语言没有对象的概念对于需要使用通信的module在其.c文件下都需要保存每个实例的指针在收到消息时(发生回调)遍历所有实例指针,找到收到消息的实例。这种处理方式可能会导致实时性下降,例如CAN接收时要遍历所有注册了CAN的实例进入module层还需要一次遍历。用C++则可以将对象的this指针和模块的回调函数进行绑定生成一个可调用对象然后再进行CAN的注册使得其不需要module层的遍历。
>
> 后续考虑在CAN instance中加入一个额外的`void*`域成员成员变量其内容为module层实例的地址。这样CAN收到消息时只需要遍历所有CAN instance对于相同的模块可以在其回调函数内部获取CAN instance的`void*`指针并通过强制类型转换cast成模块的实例结构体指针类型从而访问特定的模块。
> 考虑在实例中加入一个额外的`void*`域成员成员变量其内容为module层实例的地址。这样CAN收到消息时只需要遍历所有CAN instance对于相同的模块可以在其回调函数内部获取CAN instance的`void*`指针并通过强制类型转换cast成模块的实例结构体指针类型从而访问特定的模块。
> 这实际上是保存“对象”的parent pointer使得实例可以访问拥有自己的实例访问自己的父亲。和回调函数配合就可以防止交叉包含并为底层访问上层内容提供支持。
## APP层(application)
@ -208,6 +209,7 @@ ROOT:.
│ VSCode+Ozone使用方法.md # 开发环境配置和前置知识介绍
│ 修改HAL配置时文件目录的更改.md # 重新配置CubeMX时的步骤和注意事项
│ 必须做&禁止做.md # 开发必看,规范和要求
| 如何定位bug.md # 开发必看,快速定位bug并进行修复.还提供了一些debug典例
|
├─.vscode
│ launch.json # 调试的配置文件
@ -217,7 +219,12 @@ ROOT:.
├─assets # 说明文档的图片
├─application # 应用层
├─bsp # 板级支持包
└─modules # 模块层
├─modules # 模块层
|
├─Src #hal生成的外设初始化源文件
├─Inc #hal生成的外设初始化头文件
├─Drivers #hal driver和cmsis drivers
└─Middlewares # STusb ext , rtos , segger rtt等
```
## BSP/Module/Application介绍
@ -232,7 +239,7 @@ ROOT:.
### 运行任务
![image-20221201144336613](assets/image-20221201144336613.png)
![image-20230413195155471](assets/image-20230413195155471.png)
### 初始化流程

View File

@ -1,6 +1,5 @@
// app
#include "balance.h"
#include "vmc_project.h"
#include "gain_table.h"
#include "robot_def.h"
#include "general_def.h"
@ -12,77 +11,156 @@
#include "super_cap.h"
#include "controller.h"
#include "can_comm.h"
#include "user_lib.h"
// standard
#include "stdint.h"
#include "arm_math.h" // 需要用到较多三角函数
#include "bsp_dwt.h"
static uint32_t balance_dwt_cnt;
static float balance_dt;
/* 底盘拥有的模块实例 */
static BMI088Instance *imu;
static attitude_t *imu_data;
// static BMI088Instance *imu;
static SuperCapInstance *super_cap;
static referee_info_t *referee_data; // 裁判系统的数据会被不断更新
// 电机
static HTMotorInstance *lf;
static HTMotorInstance *rf;
static HTMotorInstance *lb;
static HTMotorInstance *rb;
static LKMotorInstance *l_driven;
static LKMotorInstance *r_driven;
static CANCommInstance *chassis_comm; // 底盘板和云台板通信
// 底盘板和云台板通信
static CANCommInstance *chassis_comm; // 由于采用多板架构,即使使用C板也有空余串口,可以使用串口通信以获得更高的通信速率并降低阻塞
static Chassis_Ctrl_Cmd_s chassis_cmd_recv;
static Chassis_Upload_Data_s chassis_feed_send;
// 由于采用多板架构,即使使用C板也有空余串口,可以使用串口通信以获得更高的通信速率
/* 方便函数间无开销传递参数的中间变量 */
// 若将下面的封装函数取缔,则可以将这些变量放入BalanceTask函数体内.
// static ...
static float leg_len_l, leg_len_r; // 左右腿长(虚拟)
static float leg_angle_l, leg_angle_r; // 左右腿角度(虚拟)
// 倒立摆的虚拟力和虚拟力矩
static float F_virtual_left, T_virtual_left, F_virtual_right, T_virtual_right;
// 左前,左后,右前,右后关节力矩
static float T_joint_lf, T_joint_lr, T_joint_rf, T_joint_rb;
static float T_leg_left, T_leg_right; // 左右驱动电机力矩
// 两个腿的参数,0为左腿,1为右腿
static LinkNPodParam left_side, right_side;
static PIDInstance swerving_pid; // 转向PID,有转向指令时使用IMU的加速度反馈积分以获取速度和位置状态量
static PIDInstance anti_crash_pid; // 抗劈叉,将输出以相反的方向叠加到左右腿的上
static PIDInstance leg_length_pid; // 用PD模拟弹簧的传递函数,不需要积分项(弹簧是一个无积分的二阶系统),增益不可过大否则抗外界冲击响应时太"硬"
static PIDInstance roll_compensate_pid; // roll轴补偿,用于保持机体水平
/* ↓↓↓分割出这些函数是为了提高可读性,使得阅读者的逻辑更加顺畅;但实际上这些函数都不长,可以以注释的形式直接加到BalanceTask里↓↓↓*/
/**
* @brief imu的数据组装为LinkNPodParam结构体
*
*/
static void ParamAssemble()
{
}
/**
* @brief ,
*
* @param p 5
*/
static void Link2Pod(LinkNPodParam *p)
{ // 拟将功能封装到vmc_project.h中
float xD, yD, xB, yB, BD, A0, B0, xC, yC, phi2;
xD = JOINT_DISTANCE + THIGH_LEN * arm_cos_f32(p->phi4);
yD = THIGH_LEN * arm_sin_f32(p->phi4);
xB = 0 + THIGH_LEN * arm_cos_f32(p->phi1);
yB = THIGH_LEN * arm_sin_f32(p->phi1);
BD = powf(xD - xB, 2) + powf(yD - yB, 2);
A0 = 2 * CALF_LEN * (xD - xB);
B0 = 2 * CALF_LEN * (yD - yB);
p->phi2 = 2 * atan2f(B0 + Sqrt(powf(A0, 2) + powf(B0, 2) - powf(BD, 2)), A0 + BD);
xC = xB + CALF_LEN * arm_cos_f32(p->phi2);
yC = yB + CALF_LEN * arm_sin_f32(p->phi2);
#ifdef ANGLE_DIFF_VMC
float p5t = atan2f(yC, xC - JOINT_DISTANCE / 2); // 避免重复计算
float plt = Sqrt(powf(xC - JOINT_DISTANCE / 2, 2) + powf(yC, 2));
p->phi5_w = (p5t - p->phi5) / balance_dt;
p->pod_len_w = (plt - p->pod_len) / balance_dt;
p->phi5 = p5t;
p->pod_len = plt;
#endif
p->phi5 = atan2f(yC, xC - JOINT_DISTANCE / 2);
p->pod_len = Sqrt(powf(xC - JOINT_DISTANCE / 2, 2) + powf(yC, 2));
p->phi3 = atan2(yC - yD, xC - xD); // 稍后用于计算VMC
#ifdef VELOCITY_DIFF_VMC
float phi1_pred = p->phi1 + p->phi1_w * balance_dt; // 预测下一时刻的关节角度(利用关节角速度)
float phi4_pred = p->phi4 + p->phi4_w * balance_dt;
// 重新计算腿长和腿角度
xD = JOINT_DISTANCE + THIGH_LEN * arm_cos_f32(phi4_pred);
yD = THIGH_LEN * arm_sin_f32(phi4_pred);
xB = 0 + THIGH_LEN * arm_cos_f32(phi1_pred);
yB = THIGH_LEN * arm_sin_f32(phi1_pred);
BD = powf(xD - xB, 2) + powf(yD - yB, 2);
A0 = 2 * CALF_LEN * (xD - xB);
B0 = 2 * CALF_LEN * (yD - yB);
phi2 = 2 * atan2f(B0 + Sqrt(powf(A0, 2) + powf(B0, 2) - powf(BD, 2)), A0 + BD); // 不要用link->phi2,因为这里是预测的
xC = xB + CALF_LEN * arm_cos_f32(phi2);
yC = yB + CALF_LEN * arm_sin_f32(phi2);
// 差分计算腿长变化率和腿角速度
p->pod_w = (atan2f(yC, xC - JOINT_DISTANCE / 2) - p->phi5) / balance_dt;
p->pod_v = (Sqrt(powf(xC - JOINT_DISTANCE / 2, 2) + powf(yC, 2)) - p->pod_len) / balance_dt;
#endif // VELOCITY_DIFF_VMC
}
/**
* @brief ,LQR的反馈增益,LQR的输出
* ,使,
* @note
* PID的反馈增益计算
*
*/
static void CalcLQR()
static void CalcLQR(LinkNPodParam *p)
{
}
/**
* @brief LQR的输出映射到关节和驱动电机的输出
*
* @brief :; :
* @todo
*/
static void VMCProject()
{ // 拟将功能封装到vmc_project.h中
}
/**
* @brief :
*
*/
static PIDInstance swerving_pid; // 转向PID,有转向指令时使用IMU的加速度反馈积分以获取速度和位置状态量
static PIDInstance anti_crash_pid; // 抗劈叉,将输出以相反的方向叠加到左右腿的上
static void SynthesizeMotion()
{
PIDCalculate(&anti_crash_pid, left_side.phi5 - right_side.phi5, 0);
left_side.T_pod += anti_crash_pid.Output;
right_side.T_pod -= anti_crash_pid.Output;
// PIDCalculate(&swerving_pid, imu_data->Yaw, 0); // 对速度闭环还是使用角度增量闭环?
left_side.T_wheel -= swerving_pid.Output;
right_side.T_wheel += swerving_pid.Output;
}
/**
* @brief : + roll轴补偿(),PD模拟弹簧的传递函数
* @brief :.PD模拟弹簧的传递函数
*
*/
static PIDInstance leg_length_pid; // 用PD模拟弹簧的传递函数,不需要积分项(弹簧是一个无积分的二阶系统),增益不可过大否则抗外界冲击响应时太"硬"
static PIDInstance roll_compensate_pid; // roll轴补偿,用于保持机体水平
static void LegControl()
static void LegControl(LinkNPodParam *p, float target_length)
{
p->F_pod += PIDCalculate(&leg_length_pid, p->pod_len, target_length);
}
/**
* @brief roll轴补偿()
*
*/
static void RollCompensate()
{
PIDCalculate(&roll_compensate_pid, imu_data->Roll, 0);
left_side.F_pod += roll_compensate_pid.Output;
right_side.F_pod -= roll_compensate_pid.Output;
}
/**
* @brief T和F映射为关节电机输出
*
*/
static void VMCProject(LinkNPodParam *p)
{
float s23 = arm_sin_f32(p->phi2 - p->phi3);
float phi25 = p->phi2 - p->phi5;
float phi35 = p->phi3 - p->phi5;
float F_m_L = p->F_pod * p->pod_len;
p->T_back = -(THIGH_LEN * arm_sin_f32(p->phi1 - p->phi2) * (p->T_pod * arm_cos_f32(phi35) - F_m_L * arm_sin_f32(phi35))) / (p->pod_len * s23);
p->T_front = -(THIGH_LEN * arm_sin_f32(p->phi3 - p->phi4) * (p->T_pod * arm_cos_f32(phi25) - F_m_L * arm_sin_f32(phi25))) / (p->pod_len * s23);
}
/**
@ -90,6 +168,7 @@ static void LegControl()
*
*
*/
static uint8_t air_flag;
static void FlyDetect()
{
}
@ -98,14 +177,13 @@ static void FlyDetect()
* @brief ,
*
*/
static void WattLimit()
static void WattLimit(LinkNPodParam *p)
{
}
void BalanceInit()
{
{ // IMU初始化
BMI088_Init_Config_s imu_config = {
// IMU初始化
.spi_acc_config = {
.GPIOx = GPIOC,
.cs_pin = GPIO_PIN_4,
@ -116,16 +194,6 @@ void BalanceInit()
.cs_pin = GPIO_PIN_4,
.spi_handle = &hspi1,
},
.acc_int_config = {
.exti_mode = EXTI_TRIGGER_FALLING,
.GPIO_Pin = GPIO_PIN_10,
.GPIOx = GPIOA,
},
.gyro_int_config = {
.exti_mode = EXTI_TRIGGER_FALLING,
.GPIO_Pin = GPIO_PIN_11,
.GPIOx = GPIOA,
},
.heat_pid_config = {
.Kp = 0.0f,
.Kd = 0.0f,
@ -141,6 +209,7 @@ void BalanceInit()
.work_mode = BMI088_BLOCK_PERIODIC_MODE,
};
// imu = BMI088Register(&imu_config);
imu_data = INS_Init();
SuperCap_Init_Config_s cap_conf = {
// 超级电容初始化
@ -165,7 +234,6 @@ void BalanceInit()
rb = HTMotorInit(&joint_conf);
// ↓↓↓---------------驱动电机初始化----------------↓↓↓
Motor_Init_Config_s driven_conf = {
// 写一个,剩下的修改方向和id即可
.can_init_config.can_handle = &hcan1,
@ -184,7 +252,6 @@ void BalanceInit()
.close_loop_type = CURRENT_LOOP,
},
.motor_type = LK9025,
};
driven_conf.can_init_config.tx_id = 1;
l_driven = LKMotorInit(&driven_conf);
@ -204,7 +271,7 @@ void BalanceInit()
};
chassis_comm = CANCommInit(&chassis_comm_conf);
referee_data = RefereeInit(&huart6); // 裁判系统串口
// referee_data = RefereeInit(&huart6); // 裁判系统串口
// ↓↓↓---------------综合运动控制----------------↓↓↓
PID_Init_Config_s swerving_pid_conf = {
@ -251,4 +318,32 @@ void BalanceInit()
/* balanceTask可能需要以更高频率运行,以提高线性化的精确程度 */
void BalanceTask()
{
chassis_cmd_recv = *(Chassis_Ctrl_Cmd_s *)CANCommGet(chassis_comm);
ParamAssemble(); // 参数组装,将电机和IMU的参数组装到一起
// 将五连杆映射成单杆
Link2Pod(&left_side);
Link2Pod(&right_side);
// 根据单杆计算处的角度和杆长,计算反馈增益
CalcLQR(&left_side);
CalcLQR(&right_side);
// 腿长控制
LegControl(&left_side, 0);
LegControl(&right_side, 0);
SynthesizeMotion(); // 综合运动控制,转向+抗劈叉
RollCompensate(); // 俯仰角补偿,保持机体水平
// VMC映射成关节输出
VMCProject(&left_side);
VMCProject(&right_side);
FlyDetect(); // 滞空检测
// 电机输出限幅
WattLimit(&left_side);
WattLimit(&right_side);
// code to go here... 裁判系统,UI,多机通信
CANCommSend(chassis_comm, (uint8_t*)&chassis_feed_send);
}

View File

@ -1,5 +1,36 @@
#pragma once
// 底盘参数
#define CALF_LEN 0.25f // 小腿
#define THIGH_LEN 0.15f // 大腿
#define JOINT_DISTANCE 0.108f // 关节间距
#define VELOCITY_DIFF_VMC //通过速度计算增量,然后通过差分计算腿长变化率和腿角速度
// #define ANGLE_DIFF_VMC //直接保存上一次的值,通过差分计算腿长变化率和腿角速度
typedef struct
{
// joint
float phi1_w, phi4_w;
float T_back, T_front;
// link angle,phi1-ph5, phi5 is pod angle
float phi1, phi2, phi3, phi4, phi5;
// wheel
float wheel_angle;
float wheel_w;
float T_wheel;
// pod
float pod_len;
float pod_w;
float pod_v;
float F_pod;
float T_pod;
} LinkNPodParam;
/**
* @brief
*
@ -11,7 +42,3 @@ void BalanceInit();
*
*/
void BalanceTask();

View File

@ -1,14 +0,0 @@
#ifndef VMC_PROJECT_H
#define VMC_PROJECT_H
#include "stm32f407xx.h"
#include "arm_math.h"
#include "math.h"
#include "general_def.h"
// 将五连杆和直杆的vmc映射放在此处,方便修改和调试,balance.c不会太长
#endif // !VMC_PROJECT_H

View File

@ -136,7 +136,7 @@ void GimbalInit()
gimbal_pub = PubRegister("gimbal_feed", sizeof(Gimbal_Upload_Data_s));
gimbal_sub = SubRegister("gimbal_cmd", sizeof(Gimbal_Ctrl_Cmd_s));
}
int aaaaaaa;
/* 机器人云台控制核心任务,后续考虑只保留IMU控制,不再需要电机的反馈 */
void GimbalTask()
{
@ -178,10 +178,7 @@ void GimbalTask()
default:
break;
}
// if(yaw_motor->motor_measure.total_angle>120)
// {
// aaaaaaa++;
// }
// 在合适的地方添加pitch重力补偿前馈力矩
// 根据IMU姿态/pitch电机角度反馈计算出当前配重下的重力矩
// ...

View File

@ -10,7 +10,6 @@
#if defined(ONE_BOARD) || defined(CHASSIS_BOARD)
#include "chassis.h"
#include "referee.h"
#endif
#if defined(ONE_BOARD) || defined(GIMBAL_BOARD)
@ -19,6 +18,11 @@
#include "robot_cmd.h"
#endif
#ifdef BALANCE_BAORD
#include "balance.h"
#endif // BALANCE_BOARD
void RobotInit()
{
// 关闭中断,防止在初始化过程中发生中断
@ -35,9 +39,13 @@ void RobotInit()
#endif
#if defined(ONE_BOARD) || defined(CHASSIS_BOARD)
// Referee_Interactive_init();
ChassisInit();
#endif
#ifdef BALANCE_BAORD
BalanceInit();
#endif // BALANCE_BA
// 初始化完成,开启中断
__enable_irq();
}
@ -52,6 +60,9 @@ void RobotTask()
#if defined(ONE_BOARD) || defined(CHASSIS_BOARD)
ChassisTask();
// Referee_Interactive_task();
#endif
#ifdef BALANCE_BAORD
BalanceTask();
#endif // BALANCE_BA
}

View File

@ -131,7 +131,7 @@ typedef enum
LOAD_BURSTFIRE, // 连发
} loader_mode_e;
// 功率限制,从裁判系统获取
// 功率限制,从裁判系统获取,是否有必要保留?
typedef struct
{ // 功率控制
float chassis_power_mx;
@ -188,7 +188,9 @@ typedef struct
/* @todo : 对于平衡底盘,需要新增控制模式和控制数据 */
typedef struct
{
attitude_t chassis_imu_data;
#if defined(CHASSIS_BOARD) || defined(GIMBAL_BOARD) // 非单板的时候底盘还将imu数据回传(若有必要)
// attitude_t chassis_imu_data;
#endif
// 后续增加底盘的真实速度
// float real_vx;
// float real_vy;
@ -198,8 +200,6 @@ typedef struct
Bullet_Speed_e bullet_speed; // 弹速限制
Enemy_Color_e enemy_color; // 0 for blue, 1 for red
// 是否需要剩余电量?(电容)
} Chassis_Upload_Data_s;
/* @todo : 对于平衡底盘,需要不同的反馈数据 */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,9 @@
> 1. 对`CANCommGet()`进行修改,使得其可以返回数据是否更新的相关信息。
## 重要提醒
如果传输过程中出现多次丢包或长度校验不通过尤其是传输长度较大的时候请开启CAN的Auto Retransmission并尝试修改CANComm实例的发送和接受ID以提高在总线仲裁中的优先级
## 总览和封装说明
@ -135,3 +138,5 @@ CAN comm的通信协议如下
接收的流程见代码注释。
流程图如下:![未命名文件](../../assets/CANcomm.png)

View File

@ -13,7 +13,7 @@
## 一、串口配置
通信方式是串口,配置为波特率 1152008 位数据位1 位停止位,无硬件流控,无校验位。
通信方式是串口,配置为波特率 9216008 位数据位1 位停止位,无硬件流控,无校验位。
## 二、数据帧说明

View File

@ -32,7 +32,7 @@ static void RectifyRCjoystick()
* @param[out] rc_ctrl: remote control data struct point
* @retval none
*/
uint16_t aaaaa;
static void sbus_to_rc(const uint8_t *sbus_buf)
{