这个仅仅是零碎的知识点,还没有总结,总结将会在二月二十五号开始,到时候所有的自学笔记我会完全整理成步骤,到时候会系统的学习,另外这个笔记是学习利用cube使用stm32,而且学习的主要目的是robomasrter,所以出现的大部分历程都是RM的
1月13日
1 机器人状态.h文件
本代码用于定义机器人状态量
#ifndef _ROBOT_H_
#define _ROBOT_H_
//以上一般定义时很有用
#define CHASSIS //底盘
#define PREPARE_TIME 10 //准备时间
// include "pid.h" //pid function
//这个以后补上
//工作及控制状态
enum WorkState_e
{
STOP, //停止状态
PREPARE, //准备状态
RemoteControl, //遥控器控制状态
MouseKeyControl, //鼠标键盘控制状态
AutoControl //自动控制状态
};
//底盘结构体
struct Chassis_t
{
int FBSpeed;
int LRSoeed;
int RoboteAngle;
};
//角度结构体
struct Gimbal_t
{
int PitchAngle; //俯仰角
int PitchBiasAngle; //
int YawAngle; // 后面的注有说到
int YawBiasAngle; //
};
//射击状态
enum ShootState_e
{
StopShoot, //停止设计
SingleShoot, //点射
ContinuousShoot //连发
};
//射击具体参数结构体
struct Shoot_t
{
int ShootSpeed; //射击速度
int FireRate; //开火频率
enum ShootState_e shootstate; //射击状态
};
//总的机器人结构体,把之前的用结构体涵盖
struct Robot_t
{
enum WorkState_e workstate; //定义工作状态
enum WorkState_e last_workstate; //定义最后时刻工作状态
struct Chassis_t chassis ; //定义底盘状态
struct Gimbal_t gimbal; //定义角度状态
struct Shoot_t shoot; //定义设计状态
};
void RobotParamInit(void);
//此处加这个函数的目的是在引用该.h文件时可以直接用这个函数,类似于给它一个接口
#endif
注:
2.PID各变量定义(pid.h)
#ifndef _PID_H_
#define _PID_H_
struct PID_t
{
float KP;
float KI;
float KD;
int error[2];
int error_sum;
int error_max;
int fdb; // feedback
int ref;
int output;
int outputMax;
};
#define DEFUALT_PID \
{0,0,0,{0,0},0,0,0,0,0,0}
//此处的“\”用于行,因为有些句子太长需要换行,而且在\前面需要有个空格
void PID_Cale (struct PID_t *pid);
#endif
3.具体pid代码(pid.c)
#include "pid.h"
//将所需要的结构体定义包含在该.c文件中
//定义PID计算函数
void PID_Calc(struct PID_t *pid)
{
pid->error[0] = pid->error[1];
pid->error[1] = pid->ref-pid->fdb;
pid->error_sum+=pid->error[1];
//将偏差求和,用于求积分I
//积分上限,防止积分累计过大导致失去控制
if(pid->error_sum > pid->error_max ) pid->error_sum = pid->error_max;
if(pid->error_sum < -pid->error_max) pid->error_sum = -pid->error_max;
//具体积分式子
pid->output = pid->KP * pid->error[1] + pid->KI * pid->error_sum + pid->KD * (pid->error[1] - pid->error[0]);
//输出上下限,防止过量输出
if(pid->output > pid->outputMax) pid->output = pid->outputMax ;
if(pid->output <-pid->outputMax) pid->output = -pid->outputMax ;
}
4.can通讯具体操作步骤
1.查看单片机引脚信息,找到can通讯相关引脚,例如RM开发板(以STM32F427II6)
2.然后在cub中找到相应的引脚,并使能
3.接下来就要开始配置时钟了
这里一般保证不过载问题就不大(由于我学识有限,还不知道如何配置最合理,就暂时随便配,等以后熟系了会更新)
对这个图,我们设置can通讯应该注意APB1,因为这个时钟包含了can通讯,为42MHZ
4.然后选择中断
根据需求选择中断,由于我们只需要在接收到数据的时候触发一个中断,所以这里只需要一个RX中断即可
5.然后需要根据所需的波特率配置波特率,其中波特率信息一般在你用can通信的原件说明书上有,这里的例子是RM3510的电机的说明书
这里的要求是需要can总线的比特率为1Mbps,
如图所示,prescarler是预分频
42/((1+4+9)*3)=1
接下里就到keil中
6.先是电机相关数据的结构体定义(can_motor.h)
#ifndef _CAN_MOTOR_H_
#define _CAN_MOTOR_H_
#include "can.h"
#include "pid.h"
#include "robot.h"
#define CAN_MOTOR1_ID 0x205
#define CAN_BUF_SIZE 6
struct CAN_Motor
{
int fdbPosition; //电机的编码器的反馈值
int last_fdbPosition; //电机上次的编码器的反馈值
int bias_position; //机器人初始状态电机位置环设定值
int fdbSpeed; //电机反馈的转速
int round; //电机转过的圈数
int real_position; //过零处理后的电机转子
int diff; //本次与上次电机过零处理后的编码器反馈值之差
int velocity; //根据电机编码器计算得到的速度
int can_buf[CAN_BUF_SIZE]; //电机编码器值缓存数组
int buf_count; //缓存数组计数,用于后面计算电机平均速度
struct PID_t position_pid; //电机位置环PID
struct PID_t speed_pid; //电机速度环PID
};
#define DEFAULT_CAN_NOTOR {0,0,0,0,0,0,0,0,{0},0,DEFUALT_PID,DEFAULT_PID}
#ifdef CHASSIS
void CanDataEncoderProcess(struct CAN_Motor *motor);
void CanDataReveive (int motor_index);
void CanTransmit_1234(CAN_HandleTypeDef *hcanx,int16_t cml_iq, int16_t cm2_iq, int16_t cm3_iq, int16_t cm4_iq);
extern struct CAN_Motor m3510_1;//定义m3501电机的数据结构体
#endif
HAL_StatusTypeDef CanFilterInit(CAN_HandleTypeDef* hcan);//给.c文件中写的过滤器一个接口,这样在main函数中好引入
#endif
7.然后是电机数据的处理以及can通讯的收发和初始化(can_motor.c)
#include "can_motor.h" //将.h中的结构体引入到该.c中
uint8_t CanReceiveData[8]; //can接收的电机反馈信息
#ifdef CHASS //如果定义了底盘,则定义底盘电机的参数结构体
struct CAN_Motor m3510_1 = DEFAULT_CAN_MOTOR;
#endif
/**
* @brief 根据电机信息的ID号进行对应的数据解析
* @param 电机ID号
* @retval None
*/
//且该函数是中断触发的
void CanDataReveive (int motor_index)
{
switch(motor_index)
{
#ifdef CHASSIS
case CAN_MOTOR1_ID:
CanDataEncoderProcess(&m3510_1); //电机具体解析函数
break;
#endif
}
}
/**
* @brief CAN通信电机的反馈数据具体解析函数
* @param 电机数据结构体
* @retval None
*/
void CanDataEncoderprocess(struct CAN_Motor *motor)
{
int i=0;
int32_t temp_sum = 0;
motor->last_fdbPosition = motor->fdbPosition ;
motor->fdbPosition = CanReceiveData [0]<<8|CanReceiveData [1];
motor->fdbSpeed = CanReceiveData[2]<<8|CanReceiveData [3];
//此处的移位操作在电机说明书里明确标出,具体会写在后面
/*电机位置数据过零处理,避免出现位置突变,电机频率为1000hz,也就是周期是1ms,但它每个周期最多转800左右(相对数据)
所以发生了跃变,一定是转过一圈*/
if(motor->fdbPosition - motor->last_fdbPosition >4096)
{
motor->round --;
motor->diff = motor->fdbPosition - motor->last_fdbPosition - 8192;
}else if (motor -> fdbPosition - motor->last_fdbPosition < -4096)
{
motor->round ++;
motor->diff = motor->fdbPosition - motor->last_fdbPosition + 8192;
}
else
{
motor->diff = motor->fdbPosition - motor->last_fdbPosition;
}
motor->real_position = motor->fdbPosition + motor->round * 8192;
motor->can_buf[motor->buf_count++] = motor->diff;//先算括号里的
//用于如果数据存满了就使之清零
if(motor->buf_count == CAN_BUF_SIZE)
{
motor->buf_count = 0;
}
//计算编码器值好在后面求平均
for(i =0; i<CAN_BUF_SIZE; i++)
{
temp_sum +=motor->can_buf[i];
}
motor->velocity = temp_sum/CAN_BUF_SIZE ;
//将电机速度反馈值有无符号整型转变为有符号整形
if(motor->fdbPosition >32768)
{
motor->fdbSpeed -=65536;
}
}
/**
* @brief ID为1~4的电机信号发送函数
* @param ID为1~4的各个电机的电流数值
* @retval None
*/
void CanTransmit_1234(CAN_HandleTypeDef *hcanx,int16_t cml_iq, int16_t cm2_iq, int16_t cm3_iq, int16_t cm4_iq)
{
CAN_TxHeaderTypeDef TxMessage;
TxMessage.DLC=0x08;
TxMessage.StdId=0x200;
TxMessage.IDE=CAN_ID_STD;
TxMessage.RTR=CAN_RTR_DATA;
uint8_t TxData[8];
TxData[0] = (uint8_t)(cml_iq >> 8);//电压给定高八位
TxData[1] = (uint8_t)cml_iq;//电压给定低八位
if(HAL_CAN_AddTxMessage(hcanx,&TxMessage,TxData,(uint32_t*)CAN_TX_MAILBOX0)!=HAL_OK)
{
Error_Handler(); //如果CAN信息发送失败则进入死循环
}
}
/**
* @brief CAN外设过滤器初始化
* @param can结构体
* @retval None
*/
HAL_StatusTypeDef CanFilterInit(CAN_HandleTypeDef* hcan)
{
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = 0x0000;
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000;
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.SlaveStartFilterBank = 14;
if(hcan == &hcan1)
{
sFilterConfig.FilterBank = 0;
}
/* if(hcan == &hcan2)
{
sFilterConfig.FilterBank = 14;
}
*/
if(HAL_CAN_ConfigFilter(hcan, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_CAN_Start(hcan) != HAL_OK)
{
Error_Handler();
}
if (HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
{
Error_Handler();
}
return HAL_OK;
}