久等了,上次说到任务三,自动搜索目标,并进行电磁炮的发射,我们首先要做的是进行一些参数的传递以及变量的初始化。
代码:
void SHOOT_Init(shoot_move_t *shoot_move_init)
{
shoot_move_init->shoot_OM_data = get_OM_data_point();//传入openmv数据
shoot_move_init->shoot_Usart_data = get_Usart1_data_point();//传入openmv数据
shoot_move_init->offset_x = 0;
shoot_move_init->offset_y = 0;
shoot_move_init->ad_value = AD_Value();
shoot_move_init->shoot_state = SHOOT_PATROL;
shoot_move_init->distance = GetData(CMD_1);//不用时注释
shoot_move_init->add_pa = 800;//舵机的初值
shoot_move_init->add_p_y = 800;
shoot_move_init->av_distance = 0;
flag = 0;
sign = 0;
sign_shoot = 0;
RELAY_2(SW_OFF);
RELAY_1(SW_OFF);
}
接着就要写循环里面的逻辑了,首先我们要做的第一步是要将用到的数据进行再次更新,
代码:
void shoot_feedback_update(shoot_move_t *shoot_move_update)
{
// shoot_move_update->shoot_OM_data = get_OM_data_point();//传入openmv数据
// float add_x = 0;
// float add_y = 0;
// add_x = shoot_move_update->shoot_OM_data->OpenMV_X - CENTER_X;
// add_y = shoot_move_update->shoot_OM_data->OpenMV_Y - CENTER_Y;
shoot_move_update->offset_x = shoot_move_update->shoot_OM_data->OpenMV_X - CENTER_X;
shoot_move_update->offset_y = shoot_move_update->shoot_OM_data->OpenMV_Y - CENTER_Y;
shoot_move_update->sation_x = shoot_move_update->shoot_OM_data->OpenMV_X;
shoot_move_update->sation_y = shoot_move_update->shoot_OM_data->OpenMV_Y;
shoot_move_update->ad_value = AD_Value();
shoot_move_update->dev_x = shoot_move_update->last_sation_x -
shoot_move_update->sation_x;
shoot_move_update->dev_y = shoot_move_update->last_sation_y -
shoot_move_update->sation_y;
shoot_move_update->distance = GetData(CMD_1);
shoot_move_update->av_distance = av_distance(shoot_move_update);
shoot_move_update->key = Key_Scan(GPIOB,GPIO_Pin_12);
}
变量有点多,每一种变量表示什么意思,就不用我再次说明了吧,如果又不懂的,可以私信我,最重要的数据当然是openmv的数据了,这里就衔接这上次讲的内容了,openmv的数据是通过指针的方式传递的。进行状态更新过后,接着就是状态的转换,因为这里设置了两个模式,一个是巡逻模式,一个是射击模式,其中准备发射模式是写在瞄准模式里面的。
typedef enum
{
SHOOT_PATROL = 0, //巡逻模式
SHOOT_AIM, //瞄准模式
SHOOT_READY //准备发射
} shoot_state_t;
我们要想的就是要定一个条件,进行两种模式的相互切换,我这里是用上位机的数据进行判断的,如果上位机没有识别到目标,上位机通过串口会发送一个0到下位机,也就是这个0,我用来作为状态转换的标志。
代码:
void GIMBAL_sta_management()
{
if( shoot_move.shoot_OM_data->OpenMV_X == 0)
{
shoot_move.shoot_state = SHOOT_PATROL;//
if(sign!=3)
sign = 1;
sign_shoot = 0;
}
else
{
shoot_move.shoot_state = SHOOT_AIM;
if(sign !=4)
sign = 2;
}
// if(shoot_move.offset_x > -OFFSETMIN_X && shoot_move.offset_x < OFFSETMIN_X)
// {
// shoot_move.shoot_state = SHOOT_READY;
// }
}
接着就是写每种状态里面的代码了,最简单的就是巡逻模式
if(shoot_move_control->shoot_state == SHOOT_PATROL)
{
if(sign == 2)
{
shoot_move_control->add_pa = shoot_move_control->en_st_x;
shoot_move_control->add_p_y = shoot_move_control->en_st_y;
}
sign = 3;
/* X轴限位 */
if(shoot_move_control->add_pa == X_MAX || shoot_move_control->add_pa > X_MAX)
flag = 1;
if(shoot_move_control->add_pa == X_MIN || shoot_move_control->add_pa < X_MIN)
flag = 0;
if(flag == 1)
shoot_move_control->add_pa -= ADD_CE;
if(flag == 0)
shoot_move_control->add_pa += ADD_CE;
/* Y 轴限位 */
if(shoot_move_control->add_p_y == Y_MAX || shoot_move_control->add_p_y > Y_MAX)
flag_y = 1;
if(shoot_move_control->add_p_y == Y_MIN || shoot_move_control->add_p_y < Y_MIN)
flag_y = 0;
if(flag_y == 1)
shoot_move_control->add_p_y -= ADD_CE_Y;
if(flag_y == 0)
shoot_move_control->add_p_y += ADD_CE_Y;
}
其中sign是一个标志位,因为为了使两种模式下舵机值的一个承接,而这个承接又只能运行一次,所以设置了sign这个变量,也是比较巧妙的一个方法,或许还有更好的方法,属于说是在下愚昧了。舵机云台是两自由度的,分为x轴和y轴。接下来就是瞄准模式了,先上代码:
else if(shoot_move_control->shoot_state == SHOOT_AIM)
{
shoot_move_control->add_x = 0;
if(shoot_move_control->offset_x < 0)
{
shoot_move_control->add_x += ADD_COE_X;
}
if(shoot_move_control->offset_x > 0)
{
shoot_move_control->add_x -= ADD_COE_X;
}
if(shoot_move_control->offset_y > 0 )
{
shoot_move_control->add_y += ADD_COE_Y;
}
else
{
shoot_move_control->add_y -= ADD_COE_Y;
}
if(shoot_move_control->offset_x > -OFFSETMIN_X && shoot_move_control->offset_x < OFFSETMIN_X)
{
shoot_move_control->add_x = 0;
if(sign_shoot == 0)
{
TIM_SetCompare3(TIM4,1000);
shoot_move_control->av_distance = av_distance(shoot_move_control);
relay_launch(shoot_move_control);
}
delay_ms(10);
RELAY_2(SW_OFF);
RELAY_1(SW_OFF);
sign_shoot = 1;
}
if(shoot_move_control->offset_y > -OFFSETMIN_Y && shoot_move_control->offset_y < OFFSETMIN_Y)
{
shoot_move_control->add_y = 0;
}
if(sign == 2)
{
shoot_move_control->en_st_x = shoot_move_control->add_pa;
shoot_move_control->en_st_y = shoot_move_control->add_p_y;
}
sign = 4;
shoot_move_control->en_st_x += shoot_move_control->add_x;
shoot_move_control->en_st_y += shoot_move_control->add_y;
if(shoot_move_control->en_st_x == X_MAX || shoot_move_control->en_st_x > X_MAX)
{
shoot_move_control->en_st_x = X_MAX;
}
if(shoot_move_control->en_st_x == X_MIN || shoot_move_control->en_st_x < X_MIN)
{
shoot_move_control->en_st_x = X_MIN;
}
if(shoot_move_control->en_st_y == Y_MAX || shoot_move_control->en_st_y > Y_MAX)
{
shoot_move_control->en_st_y = Y_MAX;
}
if(shoot_move_control->en_st_y == Y_MIN || shoot_move_control->en_st_y < Y_MIN)
{
shoot_move_control->en_st_y = Y_MIN;
}
delay_ms(10);
}
通过上位机发送过来的数据,判断目标物处于中心点的那个位置,可以简单的分为4个象限,为了是目标物到达中心点,舵机的运动轨迹可以分为了x和y轴,用到中心点的正负作为转动方向的依据,也是一个比较简单的逻辑判断,再设置了一个变量OFFSETMIN_X,作为一个误差值,因为不可能完完全全到达中心点,由于识别的误差或者种种原因,提高效率,设置了这个变量作为已经到达中心点的依据,只要是+-OFFSETMIN_X范围内,就是到达了中心点,然后进入发射状态。
其实最重要的忘记说了,就是把值传入到舵机,先上代码:
void st_en(shoot_move_t *shoot_move_en)
{
if(shoot_move_en->shoot_state == SHOOT_PATROL)
{
TIM_SetCompare2(TIM4,shoot_move_en->add_pa);
if(shoot_move_en->key == 0)
shoot_move_en->add_p_y = 1000;
TIM_SetCompare3(TIM4,shoot_move_en->add_p_y);
}//TIM_SetCompare3(TIM4,y);
else
{
TIM_SetCompare2(TIM4,shoot_move_en->en_st_x);
if(shoot_move_en->key == 0)
shoot_move_en->en_st_y = 1000;
TIM_SetCompare3(TIM4,shoot_move_en->en_st_y);
}
}
还有一个就是延时函数,普通的延时函数是没办法做到延时很久的,我写了一个延时plus函数,
void delay_puls(int time)
{
int i = time/1000;
int j = time - i*1000;
int times = 0;
for(;times<i;times++)
{
delay_ms(1000);
}
if(j == 0)
{
j = 10;
}
delay_ms(j);
}
判断余数j是否为0是为了解决延时0ms的bug,这个会导致程序卡死。
好的,关于电磁炮的代码就到这里了,后续有什么问题可以通过私信或者QQ都行。
QQ:488628560