ROS机器人DIY教程,编码器数据读取

简介:
        编码器是作为电机反馈的一个重要传感器,可以用于判断电机的转速和运动方向,在ROS机器人中可以为机器人提供一个位置参考。编码器又有增量式编码器、绝对式编码器、混合绝对式编码器等,具体的大家可以百度了解。

本文主要讲解常用的AB相编码器,一般是霍尔或者光电增量式编码器,实现的工具:

starrobot底层开发板、12V 5200ma锂电池、GB37-520减速电机、USB数据线、Keil5

starrobot底层开发板板载了A4950电机驱动器,预留和电机相同线序的XH2.54-6P接口,即插即用。电机转动主要使用到电机线+、电机线-两根线,编码器GND、编码器B相、编码器A相、编码器5V主要用于测速。
        STM32的定时器1、2、3、4、5、8是具有编码器模式的,我们只需要按照手册进行配置即可,具体的大家可查阅STM32F4XX中文手册进行了解:


具体代码配置如下:
encoder.cpp

void Encoder::init(void)
{ 
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  
    TIM_ICInitTypeDef TIM_ICInitStructure;  
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB1PeriphClockCmd(ENCODER_TIM_CLK[this->encoder_], ENABLE);  
    RCC_AHB1PeriphClockCmd(ENCODER_A_PORT_CLK[this->encoder_]|ENCODER_B_PORT_CLK[this->encoder_], ENABLE);

    GPIO_InitStructure.GPIO_Pin = ENCODER_A_PIN[this->encoder_];
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(ENCODER_A_PORT[this->encoder_], &GPIO_InitStructure);  

    GPIO_InitStructure.GPIO_Pin = ENCODER_B_PIN[this->encoder_]; 
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(ENCODER_B_PORT[this->encoder_], &GPIO_InitStructure);  

    GPIO_PinAFConfig(ENCODER_A_PORT[this->encoder_],ENCODER_A_GPIO_PinSource[this->encoder_],ENCODER_GPIO_AF_TIM[this->encoder_]);  
    GPIO_PinAFConfig(ENCODER_B_PORT[this->encoder_],ENCODER_B_GPIO_PinSource[this->encoder_],ENCODER_GPIO_AF_TIM[this->encoder_]);   

    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);

    TIM_TimeBaseStructure.TIM_Prescaler = 0x0;                 // No prescaling //不分频
    TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD;  //设定计数器自动重装值
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //选择时钟分频:不分频
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数    
    TIM_TimeBaseInit(ENCODER_TIM[this->encoder_], &TIM_TimeBaseStructure);  //初始化定时器
    TIM_EncoderInterfaceConfig(ENCODER_TIM[this->encoder_], TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3
//    TIM_EncoderInterfaceConfig(ENCODER_TIM[this->encoder_], TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Rising);//使用编码器模式3
    TIM_ICStructInit(&TIM_ICInitStructure);
    TIM_ICInitStructure.TIM_ICFilter = 0;
    TIM_ICInit(ENCODER_TIM[this->encoder_], &TIM_ICInitStructure);

    TIM_ClearFlag(ENCODER_TIM[this->encoder_], TIM_FLAG_Update);//清除TIM的更新标志位
    TIM_ITConfig(ENCODER_TIM[this->encoder_], TIM_IT_Update, ENABLE);
    TIM_Cmd(ENCODER_TIM[this->encoder_], ENABLE);
}


    配置好编码器驱动后,我们再编写一个定时器的驱动,100ms读取一次编码器的值并把编码器的值转换为rpm值,定时器代码如下:

void BaseBoard_TIM6_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    //84MHz
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE);  
    
    TIM_TimeBaseInitStructure.TIM_Period = arr;     
    TIM_TimeBaseInitStructure.TIM_Prescaler=psc;  
    TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; 
    TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; 
    
    TIM_TimeBaseInit(TIM6,&TIM_TimeBaseInitStructure);
    
    TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE); 
    TIM_Cmd(TIM6,ENABLE); 
    
    NVIC_InitStructure.NVIC_IRQChannel=TIM6_DAC_IRQn; 
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03; 
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}
void TIM6_DAC_IRQHandler(void)
{   
    int delta_ticks_1;
    int delta_ticks_2;
    int delta_ticks_3;
    int delta_ticks_4;
    if(TIM_GetITStatus(TIM6,TIM_IT_Update)==SET) 
    {
        编码器采样周期 100ms    
        delta_ticks_1= (short)((TIM5 -> CNT) - 0x7fff);
        delta_ticks_2= (short)((TIM2 -> CNT) - 0x7fff);
        delta_ticks_3= (short)((TIM3 -> CNT) - 0x7fff);
        delta_ticks_4= (short)((TIM4 -> CNT) - 0x7fff);        
        Motor_Rpm.Current_Rpm1 = ((float)(delta_ticks_1 / Time_counts_per_rev)*600.f);
        Motor_Rpm.Current_Rpm2 = ((float)(delta_ticks_2 / Time_counts_per_rev)*600.f);
        Motor_Rpm.Current_Rpm3 = ((float)(delta_ticks_3 / Time_counts_per_rev)*600.f);
        Motor_Rpm.Current_Rpm4 = ((float)(delta_ticks_4 / Time_counts_per_rev)*600.f);
        Motor_Rpm.MotorEncoder1 += ((float)(delta_ticks_1 / Time_counts_per_rev));
        Motor_Rpm.MotorEncoder2 += ((float)(delta_ticks_2 / Time_counts_per_rev));
        Motor_Rpm.MotorEncoder3 += ((float)(delta_ticks_3 / Time_counts_per_rev));
        Motor_Rpm.MotorEncoder4 += ((float)(delta_ticks_4 / Time_counts_per_rev));
        TIM2->CNT = 0x7fff;
        TIM3->CNT = 0x7fff;
        TIM4->CNT = 0x7fff;
        TIM5->CNT = 0x7fff;    
    }
    TIM_ClearITPendingBit(TIM6,TIM_IT_Update);  
}


Motor_Rpm 是一个全局变量的结构体

typedef struct _Moto_
{
    float Current_Rpm1;
    float Current_Rpm2;
    float Current_Rpm3;
    float Current_Rpm4;
    float MotorEncoder1;
    float MotorEncoder2;
    float MotorEncoder3;
    float MotorEncoder4;
}_Moto_Str;


我们读取这个结构体里面的数据即可得到编码器的数据,为了把编码器的数据通过串口发送给ROS我们还需要定义一个消息类型和节点。

starrobot_msgs::Velocities raw_vel_msg;
ros::Publisher raw_vel_pub("raw_vel", &raw_vel_msg);
raw_vel_msg.encoder_motor1 = Motor_Rpm.MotorEncoder1*wheel_circumference_s*Direction_encoder1_value;
raw_vel_msg.encoder_motor2 = Motor_Rpm.MotorEncoder2*wheel_circumference_s*Direction_encoder2_value;
raw_vel_msg.encoder_motor3 = Motor_Rpm.MotorEncoder3*wheel_circumference_s*Direction_encoder3_value;
raw_vel_msg.encoder_motor4 = Motor_Rpm.MotorEncoder4*wheel_circumference_s*Direction_encoder4_value;
current_vel = kinematics.getVelocities(Motor1_Feedback_,Motor2_Feedback_,Motor3_Feedback_,Motor4_Feedback_);
raw_vel_msg.linear_y = current_vel.linear_y;
raw_vel_msg.linear_x = current_vel.linear_x;
raw_vel_msg.angular_z = current_vel.angular_z;
raw_vel_pub.publish(&raw_vel_msg);


然后就可以在上层收到编码器的数据。

3、总结

编码器数据获取主要使用到STM32 定时器的编码器模式,STM32的定时器在实际的应用中有很大作用,大家可以自己多扩展扩展定时器的其他功能,如果你也在自己动手制作ROS机器人小车的话就扫描一下二维码进群把:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值