【山东大学软件学院 21 级项目实训】机器人底盘运动实现

首先是导入 相应的 .h文件

#include "car_ros2.h"

下面是宏定义和全局变量

// 限制值在范围内
#define LIMIT_VALUE(value, min, max)    (value>max?max:(value<min?min:value))
//参数宏定义
// #define ENCODER_RESOLUTION      2160   //编码器分辨率, 轮子转一圈,编码器产生的脉冲数
#define ENCODER_RESOLUTION      2016   //编码器分辨率,实测这个值
#define WHEEL_DIAMETER          0.058    //轮子直径,单位:米
#define D_X                     0.30     //底盘X轴前后两轮中心的间距
#define D_Y                     0.28     //底盘Y轴左右两轮中心的间距
#define PID_RATE                100       //PID调节PWM值的频率速度变化1,编码器变化100
#define PI_ROS                      3.1415926
​
double pulse_time = 0.01;
double pulse_per_meter = 0;
float rx_plus_ry_cali = 0.3;
double angular_correction_factor = 1.0;
double linear_correction_factor = 1.0;
float ros_speed[3];
static int port_imu = 0;
static int imu_enable = 0;
  • kinematics_init(void) : 运动学解析参数初始化

float r_x = D_X/2;
float r_y = D_Y/2;
void kinematics_init(void)
{
    // 轮子转动一圈,移动的距离为轮子的周长WHEEL_DIAMETER*3.1415926,编码器产生的脉冲信号为ENCODER_RESOLUTION。
    // 则电机编码器转一圈产生的脉冲信号除以轮子周长可得轮子前进1m的距离所对应编码器计数的变化
    pulse_per_meter = (float)(ENCODER_RESOLUTION/(WHEEL_DIAMETER * PI_ROS)) / linear_correction_factor;
    
    r_x = D_X/2;
    r_y = D_Y/2;
    rx_plus_ry_cali = (r_x + r_y)/angular_correction_factor;
}
  • encoder2dis()数作用:将编码器值转换为实际变化的距离(单位:米)。

float encoder2dis(int encoder_in)
{   
    return (float)encoder_in/pulse_per_meter;
}
  • convert_sptrue()将速度转换为实际速度。

​
void convert_sptrue(int* speed_in, float* speed_out)
{   
    for(int i=0;i<4;i++){
        speed_out[i] = speed_in[i] * PID_RATE / pulse_per_meter;
    }
}
  • convert_spvirtual() 将实际速度转换为控制器速度。

​
void convert_spvirtual(float* speed_in, int* speed_out){
    for(int i=0;i<4;i++){
        speed_out[i] = (int16_t)(speed_in[i] * pulse_per_meter / PID_RATE);
    }
}
  • kinematics_forward()正向运动学解析,四个轮子位置变化->车子状态 车子位置

void kinematics_forward(float* dis_dt, float time_dt, float* car_state, float* car_pos){
    static float car_dx[10], car_dy[10], car_dyaw[10], car_dtime[10];
    
    static float car_dx_sum, car_dy_sum, car_dyaw_sum, car_time_sum;
    static int car_dt_cnt = 0;
    
    car_dt_cnt = (car_dt_cnt+1)%10;
​
    car_time_sum = car_time_sum - car_dtime[car_dt_cnt];
    car_dx_sum = car_dx_sum - car_dx[car_dt_cnt];
    car_dy_sum = car_dy_sum - car_dy[car_dt_cnt];
    car_dyaw_sum = car_dyaw_sum - car_dyaw[car_dt_cnt];
​
    car_dtime[car_dt_cnt] = time_dt;
    car_time_sum += car_dtime[car_dt_cnt];
​
    car_dx[car_dt_cnt] = (dis_dt[0] + dis_dt[1] + dis_dt[2] + dis_dt[3])/4.0;  // 车x方向位移
    car_dx_sum += car_dx[car_dt_cnt];
    car_state[0] = car_dx_sum / car_time_sum;      // x轴瞬时速度, 10次平均值
​
    car_dy[car_dt_cnt] = (0 - dis_dt[0] + dis_dt[1] + dis_dt[2] - dis_dt[3])/4.0;  // 车y方向位移
    car_dy_sum += car_dy[car_dt_cnt];
    car_state[1] = car_dy_sum / car_time_sum;      // y轴瞬时速度,10次平均值
​
    //积分计算里程计坐标系(odom_frame)下的机器人X,Y,Yaw轴坐标
    //dx = ( vx*cos(theta) - vy*sin(theta) )*dt
    //dy = ( vx*sin(theta) + vy*cos(theta) )*dt
    car_pos[0] += (cos((double)car_pos[2])*car_dx[car_dt_cnt] - sin((double)car_pos[2])*car_dy[car_dt_cnt]);
    car_pos[1] += (sin((double)car_pos[2])*car_dx[car_dt_cnt] + cos((double)car_pos[2])*car_dy[car_dt_cnt]);
​
    if(imu_enable){
        car_state[2] = (imu_get_angle(port_imu) - car_pos[2]) / time_dt;
        car_pos[2] = imu_get_angle(port_imu);
    }
    else{
        car_dyaw[car_dt_cnt] = ( 0 - dis_dt[0] + dis_dt[1] - dis_dt[2] + dis_dt[3] )/4.0 /(r_y + r_x);  // 车yaw转向角
        car_dyaw_sum = car_dyaw_sum + car_dyaw[car_dt_cnt];
        car_state[2] = car_dyaw_sum / car_time_sum;      // omega瞬时速度
        car_pos[2] += (car_dyaw[car_dt_cnt]);
    }
​
}
  • kinematics_inverse ()逆向运动学解析,将底盘三轴速度转换为四个轮子的目标速度。

void kinematics_inverse(float v_tx, float v_ty, float omega, float* speed_out)
{
    speed_out[0] = v_tx - v_ty - (r_x + r_y)*omega;
    speed_out[1] = v_tx + v_ty + (r_x + r_y)*omega;
    speed_out[2] = v_tx + v_ty - (r_x + r_y)*omega;
    speed_out[3] = v_tx - v_ty + (r_x + r_y)*omega;
}
  • mecanum_wheel()控制麦克纳姆轮机器人的运动。

void mecanum_wheel(float v_tx, float v_ty, float omega)
{
    static int motor_speeds[4];
    static float wheel_speeds[4];
    kinematics_init();
    kinematics_inverse(v_tx, v_ty, omega, wheel_speeds);
    convert_spvirtual(wheel_speeds, motor_speeds);
    set_motor(1, motor_speeds[0]);
    set_motor(2, -motor_speeds[1]);
    set_motor(3, motor_speeds[2]);
    set_motor(4, -motor_speeds[3]);
}
  • 与Ros2 相关的全局变量

static float car_pos[3];    // 位置值
static float car_state[3];  // 状态值
​
static float car_control[3];  // 控制值
static float time_last_run = 0;
  • ros2_process ()ROS2 主要处理函数,包括控制机器人运动,编码器值转换,运动学解析等

void ros2_process()
{   
    static float dis_dt[4];
​
    static float time_last;
    static float time_dt;
    static int encoder_now[4], encoder_last[4], encoder_dt[4];
    static int encoder_p[4] = {1, -1, 1, -1};
    static float time_during = 0;
​
    time_during = seconds() - time_last_run;
    if(time_during < 0.2){
        mecanum_wheel(car_control[0], car_control[1], car_control[2]);
    }
    else if(time_during < 0.6 && time_during > 0.5){
        mecanum_wheel(0, 0, 0);
    }
​
    for(int i=0;i<4;i++){
        encoder_now[i] = motor_encoder(i+1) * encoder_p[i];
        encoder_dt[i] = encoder_now[i] - encoder_last[i];
        encoder_last[i] = encoder_now[i];
        dis_dt[i] = encoder2dis(encoder_dt[i]);
    }
    time_dt = seconds() - time_last;
    kinematics_forward(dis_dt, time_dt, car_state, car_pos);
    time_last = seconds();
}
  • ros2_set_control()设置控制值

void ros2_set_control(float* control_sp){
    car_control[0] = control_sp[0];
    car_control[1] = control_sp[1];
    car_control[2] = control_sp[2];
    time_last_run = seconds();
}
  • show_odom()显示机器人位置信息

void show_odom(){
    while(1){
        printf("car_pos\nx: %.2f y: %.2f \nyaw: %.2f\n", car_pos[0], car_pos[1], car_pos[2]);
        wait(0.1);
    }
}

下面几个关于IMU的是用来控制和设置IMU端口的。

void ros_imu_enable(){
    if(imu_enable){
        return;
    }
    imu_adjust_angle_init(port_imu, 50);
    imu_enable = 1;
}
​
void ros_imu_disable(){
    imu_enable = 0;
}
​
// 设置imu端口号
void ros_set_imu_port(int port_id){
    port_imu = port_id;
    ros_imu_enable();
}
  • reset_odom_ros 重置位置信息

void reset_odom_ros(){
    if(imu_enable){
        imu_reset();
    }
    // 数据清零
    for(int i=0;i<3;i++){
        // 获取状态
        car_state[i] = 0;
        // 获取位置
        car_pos[i] = 0;
    }
}
  • ros2_odom_start()ROS2里程计初始化函数

void ros2_odom_start()
{
    static uint8_t ros_start_flag = -1;
    if(ros_start_flag != 1){
        ros_start_flag = 1;
        kinematics_init();
        setTimerHandle(ros2_process, 10);
    }
    else{
        reset_odom_ros();
    }
}
  • get_odom_ros()获取机器人状态和位置信息

void get_odom_ros(float* state_car, float* pos_car){
    for(int i=0;i<3;i++){
        // 获取状态
        state_car[i] = car_state[i];
        // 获取位置
        pos_car[i] = car_pos[i];
    }
}
​
  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值