Tanuki_Bayashin’s diary

電子工作を趣味としています。最近はラズベリーパイPicoというマイコンを使って楽しんでいます

倒立振子のコード その3 状態変数の測定

※なにか気になる点がありましたらコメント欄にご記入ください。


※この記事は以下のリンクから貼られており、倒立振子という創作物に関するコードの一部を載せています。詳しくはリンク元をご覧ください。
倒立振子に挑む - Tanuki_Bayashin’s diary

使用しているマイコンRaspberry Pi Pico と呼ばれるものです。
記述はC言語を用いています。(pico-SDK C/C++ という環境を使用しています)



ここでは4つの変数、本体の傾斜角[rad]、その角速度[rad/s]、車輪の回転角[rad]、その角速度[rad/s] を測定します。

本体の傾斜角の測定には加速度センサー(MPU6050)を用います。傾斜角の測定に関しては、以下のリンクを参照してください。(大元はトラ技2019年7月号の特集に依っています)
またMPU6050に関する処理は、運よくPico-SDKのサンプルプログラムのなかに載っていました。
傾斜計の試作機の製作 - Tanuki_Bayashin’s diary

本体の傾斜角の角速度は、同じく加速度センサー(MPU6050)を用いています。

車輪の回転角の測定は、車輪に取り付けてあるロータリーエンコーダーからの値から測定しました。A相とB相という2種類の信号をうまく使うことにより、0.9°までの精度で測定することができます。
(こちらも大元はトラ技2019年7月号の特集に依っています)

車輪の回転角の角速度は、制御周期ごとにサンプリングしたデータから、引き算を行い求めました。

リスト3 状態変数の測定と平均、分散の測定

//=========================================================
// Accelerometer (MPU6050)
// Gyroscope
//========================================================= 
// int16_t acceleration[3], gyro[3];
// int16_t* temperature;
//
//get data
//float theta_mean;
//float theta_variance;
//float theta_dot_mean;
//float theta_dot_variance;

// 初期化しています
#ifdef i2c_default
static void mpu6050_reset() {
    // Two byte reset. First byte register, second byte data
    // There are a load more options to set up the device in different ways that could be added here
    uint8_t buf[] = { 0x6B, 0x00 };
    i2c_write_blocking(i2c_default, addr, buf, 2, false);

    // set P_angle_dot ±500[degree/sec]
    buf[0] = 0x1B;
    buf[1] = 0x08;  // 0x01 => 0x08 changed(2022/08/11)
    i2c_write_blocking(i2c_default, addr, buf, 2, false);

    // set accelaration ±2G 
    buf[0] = 0x1C;
    buf[1] = 0x00;
    i2c_write_blocking(i2c_default, addr, buf, 2, false);

    // set LPF BW 260Hz(0.0ms delay) for Accel,
    // 256Hz(0.98ms delay) for Gyro
    buf[0] = 0x1A;
    buf[1] = 0x00;
    i2c_write_blocking(i2c_default, addr, buf, 2, false);

}

// 本体の傾斜角を測定しています
float get_P_angle() {

    uint8_t buffer[4];

    // Start reading acceleration registers from register 0x3B for 6 bytes
    uint8_t val = 0x3D;
    // true to keep master control of bus
    i2c_write_blocking(i2c_default, addr, &val, 1, true);
    i2c_read_blocking(i2c_default, addr, buffer, 4, false);

    acceleration[1] = (buffer[0] << 8) | buffer[1];
    acceleration[2] = (buffer[2] << 8) | buffer[3];

    // 86.03 is the value of compensation for initial posture
    P_angle = 90.0 - atan2((double)acceleration[1] - acc_Y_offset,
        (double)acceleration[2] - acc_Z_offset) * 180.0 / PI;

    return P_angle; // [degree]

}

// 傾斜角の角速度を測定しています
float get_P_angle_dot() {

    uint8_t buffer[2];
    float sum;
    //float P_angle_dot;

    // Now gyro data from reg 0x43 for 6 bytes
    uint8_t val = 0x43;
    i2c_write_blocking(i2c_default, addr, &val, 1, true);
    i2c_read_blocking(i2c_default, addr, buffer, 2, false);  // False - finished with bus

    gyro[0] = (buffer[0] << 8) | buffer[1];

    // 2^15 - 1 = 32767, ±500 [degree/s] for Full range
    // -500を掛けているのは座標系を合わせるため
    P_angle_dot = (gyro[0] - gyro_X_offset) * (-500.0) / 32767.0 - theta_data[1][0]; // [degree/s]

    for (int i = 0; i < 9; i++) {
        P_angle_dot_data[9 - i] = P_angle_dot_data[8 - i];
    }
    P_angle_dot_data[0] = P_angle_dot;

    sum = 0.0;
    for (int i = 0; i < 10; i++) {
        sum += P_angle_dot_data[i];
    }

    // 角速度の平均を求めています
    P_angle_dot_ave = sum / 10.0;

    return P_angle_dot;
}

#endif


// 各値にはオフセットが含まれています。その値を測定します。
// また、各角度や角速度の平均や分散を求めています。
// これらはカルマンフィルタを用いる上で必要となってきます。

void Calibration() {

    //// Calculate these variables
    // acc_Y_offset;
    // acc_Z_offset;
    // gyro_X_offset;
    // 
    // theta_mean;
    // theta_variance;
    // theta_dot_mean;
    // theta_dot_variance;

    //float P_angle, P_angle_dot;
    float acc_Y_sum = 0.0;
    float acc_Z_sum = 0.0;
    float gyro_X_sum = 0.0;
    float P_angle_sum = 0.0;
    float P_angle_dot_sum = 0.0;
    float temp_std;
    float ONE_G_sin, ONE_G_cos, P_angle_0;

    //P_angle_0 = 86.03;
    P_angle_0 = 78.0;
    //P_angle_0 = 90.0;
    ONE_G_sin = ONE_G * sin((90.0 - P_angle_0) * PI / 180.0);
    ONE_G_cos = ONE_G * cos((90.0 - P_angle_0) * PI / 180.0);
    ///////////////////////////////////////////////
    // Calculate offset of acc and gyro raw data //
    ///////////////////////////////////////////////
    for (int i = 0; i < sample_num; i++) {
        P_angle = get_P_angle();
        P_angle_dot = get_P_angle_dot();

        // #define ONE_G 16383 = 2^14 - 1
        acc_Y_sum += acceleration[1] - ONE_G_sin;
        acc_Z_sum += acceleration[2] - ONE_G_cos;
        gyro_X_sum += gyro[0];

        // delay 100[μs]
        sleep_us(500);
    }

    // results of calculation of mean values
    acc_Y_offset = acc_Y_sum / sample_num;
    acc_Z_offset = acc_Z_sum / sample_num;
    gyro_X_offset = gyro_X_sum / sample_num;

    ///////////////////////////////////////
    // Calculate mean value of variables //
    ///////////////////////////////////////
    P_angle_sum = 0.0;
    P_angle_dot_sum = 0.0;

    for (int i = 0; i < sample_num; i++) {

        P_angle_sum += get_P_angle() - P_angle_0;
        P_angle_dot_sum += get_P_angle_dot();

        // delay 100[μs]
        sleep_us(500);
    }

    theta_mean = P_angle_sum / sample_num; // [degree]
    theta_dot_mean = P_angle_dot_sum / sample_num; // [degree/s]

    ///////////////////////////////////////////
    // Calculate variance value of variables //
    ///////////////////////////////////////////
    P_angle_sum = 0.0;
    P_angle_dot_sum = 0.0;

    for (int i = 0; i < sample_num; i++) {

        temp_std = get_P_angle() - P_angle_0 - theta_mean;
        P_angle_sum += temp_std * temp_std;

        temp_std = get_P_angle_dot() - theta_dot_mean;
        P_angle_dot_sum += temp_std * temp_std;

        // delay 100[μs]
        sleep_us(500);
    }

    theta_variance = P_angle_sum / sample_num; // [degree]^2
    theta_dot_variance = P_angle_dot_sum / sample_num; // [degree/s]^2

    return;
}

// ロータリーエンコーダーに関する処理です
//=========================================================
//Rotary encoder polling function
//It takes 4usec. (NUCLEO-F401RE 84MHz)
// under 1usec. (Raspberry Pi Pico 125MHz)
//=========================================================
int read_encoder() {

    phaseA = gpio_get(rotary_encorder_phaseA);
    phaseB = gpio_get(rotary_encorder_phaseB);

    return (phaseB << 1) + phaseA;

}

bool rotary_encoder_check(struct repeating_timer* t)
{
    //t_start = time_us_32();

    int temp;

    temp = read_encoder();
    //check the movement
    code = ((code << 2) + temp) & 0xf;
    //update the encoder value
    int value = 1 * table[code];

    encoder_value += value;

    //encoder_value = 0;
    //count_encoder++;
    //t_end = time_us_32() - t_start;

    return true;
}

// 車輪の角度の値から、角速度を求めています
void calculate_W_angle_dot()  // angular velocity of wheels
{
    float temp;

    // calculate W_angle [degree]
    temp = (float)encoder_value / 1074.0 * 360.0;

    // calculate W_angle_dot [degree / s]
    W_angle_dot = (temp - W_angle[9]) * 10.0; // 掛け算の方が早い( / 0.1[sec])

    W_angle[9] = W_angle[8];  // next 10 step old
    W_angle[8] = W_angle[7];  // next  9 step old
    W_angle[7] = W_angle[6];  // next  8 step old
    W_angle[6] = W_angle[5];  // next  7 step old
    W_angle[5] = W_angle[4];  // next  6 step old
    W_angle[4] = W_angle[3];  // next  5 step old
    W_angle[3] = W_angle[2];  // next  4 step old
    W_angle[2] = W_angle[1];  // next  3 step old
    W_angle[1] = W_angle[0];  // next  2 step old

    W_angle[0] = temp; // next 1 step old

    return;
}


このページのトップに戻る

リンクを貼られているページに戻る