※なにか気になる点がありましたらコメント欄にご記入ください。また、工作や回路を製作する場合には、細かい作業などに対して、細心の注意を払われるようお願いいたします。
1.はじめに
サーボモータ(指令値に対し回転数を制御するタイプ)を用いて倒立振子を組み立ててみました。Youtubeのチャンネルを参考にしました。
参考にしたYoutubeのチャンネル
www.youtube.com
結論から言って、まだ十分にバランスが取れてはいないです。今後、さらに取り組んでいきたいです。
この記事では、ソフトの部分を中心に書いていきます。ハードに関する記事は以下を参照してください。
ハード編のリンク先:
サーボモータで倒立振子を作ってみた~ハード編 - Tanuki_Bayashin’s diary
マイコンには”Raspberry Pi Pico”を使用しました。(以下Picoと記述します)
※アマチュア電子工作erの記事です。至らない点は大目に見てくださいませ。(*- -)(*_ _)ペコリ
訂正
2023/10/27 リストg-2 timer_irq_end()内にs[100]を宣言する行を追加しました。
2.制作したコード群
制作したコードを列記していきます。MPU6050という加速度センサモジュールを使用したかったのですが、microPythonにライブラリがなく、そのため言語はPico SDK C/C++を利用しました。Pico SDK C/C++を使用するための簡単なあらましを、以下の記事に昔書きました。参考にして下さい。(大元のラズベリーパイ財団のドキュメントを直接読まれる方が、早く理解できるかもしれません。リンク先を張っておきます)
筆者が過去に記述した記事3本です。
① Raspberry Pi Pico SDK C/C++の構築 - Tanuki_Bayashin’s diary
② pico-sdk C/C++ での開発の進め方 - Tanuki_Bayashin’s diary
③ ラズパイPicoで使うPico‐SDK C/C++ に関する覚え書き - Tanuki_Bayashin’s diary
ラズベリーパイ財団 ”Getting started with Raspberry Pi Pico”(英語版)の日本語バージョンです。
④ https://datasheets.raspberrypi.com/pico/getting-started-with-pico-JP.pdf
全体の構成のあらましを図1に示します。
a. プログラム冒頭部
プログラムの冒頭部です。
ファイルのインクルード、マクロの定義、グローバル変数の定義を行っています。
(グローバル変数を多用したりしています。各部の関数の働きなどを見るときに必要かと思うので、一応乗せておきます。)
リストa-1
//========================================================= // File Name: Servo_Pendulum.c // MPU board: Raspberry Pi Pico // Accelerometer + Gyro sensor: MPU6050 // UART Device : FT231X(Akizuki) // // 2023/10/10 written by @Tanuki_Bayashin // // This Program is to Control the Inverted Pendulum System. // version 1.0 (as a Servo_Pendulum.c) // // Apply Mr.DHA Electronic Hobby Study Class // for Raspberry Pi Pico. //========================================================= #include <stdio.h> #include <string.h> #include <math.h> #include <stdlib.h> #include "pico/stdlib.h" #include "hardware/uart.h" #include "pico/binary_info.h" #include "hardware/gpio.h" #include "hardware/pwm.h" #include "hardware/adc.h" #include "hardware/i2c.h" #include "hardware/timer.h" //========================================================= // Port Setting // DigitalOut; // I2C i2c(PB_9, PB_8); //Gyro + Accelerometer (SDA, SCLK) // Serial uart_usb(USBTX, USBRX); //UART (over USB) //========================================================= //UARTに関する部分です #define UART_ID uart0 #define BAUD_RATE 115200 #define UART_TX_PIN 12 //(GPIO12 #16) #define UART_RX_PIN 13 //(GPIO13 #17) // MPU6050を使用するのに必要な部分です。 #define PICO_I2C_SDA_PIN 8 //(GPIO8 #11) #define PICO_I2C_SCL_PIN 9 //(GPIO9 #12) #define PI 3.1415926 #define ONE_G 16383 // 2^14 - 1 = 16383 // PWMに関する部分です。サーボモータの制御に必要になってきます。 #define SERVO_1 0 //(GPIO0 #1Pin PWM_A[0]) #define SERVO_2 3 //(GPIO3 #5Pin PWM_B[1]) //パイロットランプ(Pico上のLED)用です。 #define LED_25 25 // LED_25 //========================================================= //Accelerometer and gyro offset //========================================================= //加速度センサ(ジャイロセンサを含む)の読み取り値のオフセットに関する部分です。 // variables for calculate offset // for offset of accelaration float acc_Y_offset = 0.0; float acc_Z_offset = 0.0; // for offset of angular velocity float gyro_X_offset = 0.0; //ラディアンとディグリーの間の換算係数です。 float deg2rad = 0.0174533; // 3.141593 / 180. float rad2deg = 57.296; // 180.0 / 3.141593 // 姿勢のオフセットです。水平の位置からどれだけずれているかを示しています。 float deg_offset = 0.0; // [deg] float rad_offset = 0.055; // [rad] // 相補フィルタに関する外部変数です。 float angle_int = 0.0, angle_adj = 0.0; float angle_adj_t, rad_e, rads_e; //========================================================= //Accelerometer and gyro data(加速度センサ用です) //========================================================= // By default these devices are on bus address 0x68 static int addr = 0x68; int16_t acceleration[3], gyro[3]; int16_t* temperature; int sample_num = 1000; //角度と角速度の計算用です。 float P_angle, P_angle_dot, P_angle_ave, P_angle_dot_ave; float P_angle2; float P_angle_data[10] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; float P_angle_dot_data[10] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; //========================================================= // PWM to Mortor Voltage サーボモータを動かすときに使います。 //========================================================= uint pwm1_slice_num, pwm2_slice_num; float volts; int pwm_value; //==================================== //Motor control variables 制御に関する部分などです //==================================== float feedback_rate = 0.02; //sec struct repeating_timer timer_control, timer_standing, timer_Filter; bool cancelled_control, cancelled_standing, cancelled_Filter; static uint32_t t_start = 0, t_end = 0; static uint32_t t_start_control, t_end_control; static uint32_t t_start_Filter, t_end_Filter; float speed_L, speed_R, adj_L, adj_R; int duty_L, duty_R; // コントロール・ゲインです //float k1 = 8.0, k2 = 0.07; float k1 = 4.5, k2 = 0.013; //============================
b. UART部
Pico内の変数の値をPC側に送信する部分です。
PC側の受信用のコードは(2-2. PC側のUART受信用ソースコードへのリンク)にあります。(Pythonにて記述)
リストb-1
void uart_initial(){ // process something important ? stdio_init_all(); // Set up our UART with the required speed. uart_init(UART_ID, BAUD_RATE); // Set the TX and RX pins by using the function select on the GPIO gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART); gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART); }
uart_initial():初期設定を行っています。(Pico SDK のサンプル・コードそのままです)
リストb-2 uart_send()
void uart_send(){ static char s[100]; snprintf(s, sizeof(s), "%4.3f, %4.3f, %4.3f, %4.3f\n", rad_e, rads_e, speed_L, speed_R); uart_puts(UART_ID, s); }
実際に変数の値を送信しています。
snprintf()の変数の部分を変えれば、他の変数の値も出力できます。(フォーマット(""ではさまれた部分)も、必要に応じて変更する必要があります)
c. PWM初期設定
PWMによる処理の初期設定を行っています。main()内から呼びだしてます。
サーボモータをPicoで制御する場合、PWM制御にて行います。(詳しい説明は「1.はじめに」でご紹介した動画内にて解説されています。ご参照ください)
20ms周期でPWM信号を発生させています。理論値とだいぶ違いますが、実際にこのリストの値で20msになりました。周期が大きい場合、このような事があるのかもしれないです。
(Pico SDKでのPWMに関する内容は、この章の冒頭部でご紹介した③のリンク先の2.2節をご覧ください)
リストc-1 pwm_setting();
/* GPIOにPWMを割り当てる */ gpio_set_function(SERVO_1, GPIO_FUNC_PWM); gpio_set_function(SERVO_2, GPIO_FUNC_PWM); pwm1_slice_num = pwm_gpio_to_slice_num(SERVO_1); pwm2_slice_num = pwm_gpio_to_slice_num(SERVO_2); /* clkdiv と wrap を指定 */ //pwm frequency: 50[Hz] //以下の値は理論値とだいぶ違っています。 pwm_set_clkdiv(pwm1_slice_num, 610.375); pwm_set_wrap(pwm1_slice_num, 9764); pwm_set_clkdiv(pwm2_slice_num, 610.375); pwm_set_wrap(pwm2_slice_num, 9764); /* レベル値(デューティカウント値)を設定(ここでは0) */ pwm_set_chan_level(pwm1_slice_num, PWM_CHAN_A, 732 - 5); pwm_set_chan_level(pwm2_slice_num, PWM_CHAN_B, 732 - 5); /* pwm0 start */ pwm_set_enabled(pwm1_slice_num, true); pwm_set_enabled(pwm2_slice_num, true); /* END of PWM Module Setting */
PWMの初期設定に関する記述です。Pico SDKのサンプル・プログラムにあったものを参考にしました。
d. MPU6050部
加速度センサ(3自由度)MPU6050を使用したので、これを使用可能にしたり、データを読み取ったりしています。
(ジャイロセンサ(3自由度)も含まれています)
リストd-1 mpu6050_reset()
//========================================================= // Accelerometer (MPU6050) // Gyroscope //========================================================= // #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] //ジャイロセンサの読みとり値を±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 //加速度センサの読みとり値を±2G に設定しています。 buf[0] = 0x1C; buf[1] = 0x00; i2c_write_blocking(i2c_default, addr, buf, 2, false); // set Digital LPF // BW 44Hz(4.9ms delay) for Accel, // BW 42Hz(4.8ms delay) for Gyro //加速度センサ内部のディジタルフィルタの帯域の設定です。 buf[0] = 0x1A; buf[1] = 0x00; i2c_write_blocking(i2c_default, addr, buf, 2, false); } #endif
初期設定を行っています。(#ifdef i2c_default などの記述はよく分かっていないです)
リストd-2 get_P_angle()
float get_P_angle() { uint8_t buffer[6]; float sum; float x, y; // Start reading acceleration registers from register 0x3B for 6 bytes uint8_t val = 0x3B; // true to keep master control of bus i2c_write_blocking(i2c_default, addr, &val, 1, true); i2c_read_blocking(i2c_default, addr, buffer, 6, false); acceleration[0] = (buffer[0] << 8) | buffer[1]; acceleration[1] = (buffer[2] << 8) | buffer[3]; acceleration[2] = (buffer[4] << 8) | buffer[5]; // 86.03 is the value of compensation for initial posture x = (float)acceleration[1] - acc_Y_offset; y = (float)acceleration[2] - acc_Z_offset; P_angle = - (float)atan2((double)x, (double)y); // [rad] //P_angle *= rad2deg; // [deg] for (int i = 0; i < 9; i++) { P_angle_data[9 - i] = P_angle_data[8 - i]; } P_angle_data[0] = P_angle; sum = 0.0; for (int i = 0; i < 5; i++) { // 10 -> 5 ** 2023/07/23 sum += P_angle_data[i]; } P_angle_ave = sum / 5.0; // 10.0 -> 5.0 ** return P_angle; // [rad] }
実際に加速度を読み取り、その値から倒立振子の傾斜角を求めています。
まず、加速度を求め、Y軸成分とZ軸成分から傾斜角を求めています。
傾斜角を求める原理は以下の筆者の記事を参考にして下さい。
傾斜計の試作機の製作 - Tanuki_Bayashin’s diary
計算結果の単位はラディアンで、ノイズを減らすために5ステップ分の平均を求めています。
リストd-3 get_P_angle_dot()
float get_P_angle_dot() { uint8_t buffer[2]; float sum; // Now gyro data from reg 0x43 for 6 bytes uint8_t val = 0x43; i2c_write_blocking(i2c_default, addr, &val, 1, true); // False - finished with bus i2c_read_blocking(i2c_default, addr, buffer, 2, false); 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; // [deg/s] P_angle_dot *= deg2rad; // [rad/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; // [rad/s] }
倒立振子の傾斜角の時間微分(角加速度)P_angle_dotを求めています。単位は[rad/s]です。
(ジャイロセンサの出力を単位が合うように換算しています。こちらも平均値を求めています)
リストd-4 Calibration()
void Calibration() { //// Calculate these variables // acc_Y_offset; // acc_Z_offset; // gyro_X_offset; float acc_Y_sum = 0.0; float acc_Z_sum = 0.0; float gyro_X_sum = 0.0; float ONE_G_sin, ONE_G_cos, P_angle_0; P_angle_0 = 90.0; // 2023/07/29 measured ONE_G_sin = ONE_G * sin(P_angle_0 * PI / 180.0); ONE_G_cos = ONE_G * cos(P_angle_0 * PI / 180.0); /////////////////////////////////////////////// // Calculate offset of acc and gyro raw data // // sample_num = 1000 // /////////////////////////////////////////////// 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_cos; acc_Z_sum += acceleration[2] - ONE_G_sin; gyro_X_sum += gyro[0]; // delay 500[μ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; return; }
main()関数内から呼びだしています。加速度センサやジャイロセンサのオフセット値(測定時の一定の誤差)を算出しています。倒立振子を水平にした状謡で電源を入れると、自動で計測します。傾斜角や角速度を計算するときに、利用しています。
リストd-5 mpu6050_initial()
void mpu6050_initial(){ #if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN) #warning i2c / mpu6050_i2c example requires a board with I2C pins uart_puts(UART_ID, "Default I2C pins were not defined"); #else uart_puts(UART_ID, "Hello, MPU6050! Reading raw data from registers..."); // This example will use I2C0 on the default SDA and SCL pins (4, 5 on a Pico) i2c_init(i2c_default, 400 * 1000); gpio_set_function(PICO_I2C_SDA_PIN, GPIO_FUNC_I2C); gpio_set_function(PICO_I2C_SCL_PIN, GPIO_FUNC_I2C); gpio_pull_up(PICO_I2C_SDA_PIN); gpio_pull_up(PICO_I2C_SCL_PIN); // Make the I2C pins available to picotool bi_decl(bi_2pins_with_func(PICO_I2C_SDA_PIN, PICO_I2C_SCL_PIN, GPIO_FUNC_I2C)); mpu6050_reset(); #endif uart_puts(UART_ID, " mpu6050 reset!\n\r"); }
main()関数内から呼びだしています。MPU6050が使えるようにするための初期設定をする関数です。
e. 相補フィルター部
リストe-1 Comple_Filter()
//========================================================= // Complement Filter Proccess // (2023/09/15) //========================================================= bool Comple_Filter(struct repeating_timer* t) { t_start_Filter = time_us_32(); // 車体の角度と回転速度を計算する(相補フィルタ) P_angle = get_P_angle(); // ロボットの角度[rad] P_angle_dot = get_P_angle_dot(); // ロボットの角速度[rad/s] // 相補フィルタ angle_adj_t = angle_adj; angle_int += P_angle_dot_ave * 0.002; angle_adj = P_angle_ave - angle_int; angle_adj = angle_adj_t + 0.002 * (angle_adj - angle_adj_t) * 2.0; rad_e = angle_int + angle_adj; rads_e = P_angle_dot_ave; t_end_Filter = time_us_32() - t_start_Filter; return true; }
「1.はじめに」の動画で紹介されていたフィルタです。傾斜角の値のノイズを除去してくれる処理です。
図2に相補フィルタの動作の様子を示します。赤は傾斜角のデータ、青が相補フィルタの出力です。左の図ではノイズが除去されている様子が見てとれます。また右の図では、ノイズが除去されていると言った感じではないですが、値の変化が緩やかになっているように見えます。
ネットなどで調べてみましたが、その動作原理などは理解できていません。時間のあるときに、詳しく調べたいです。
f. コントローラ部
リストf-1 Control_Pendulum()
////////////////////////////////////////// // Calculate the velosity of servo motor // (2023/10/10) ////////////////////////////////////////// bool Control_Pendulum(struct repeating_timer* t) { //about 400usec passed for this process t_start_control = time_us_32(); // モーターが出す速度の計算 speed_L = ((rad_e - rad_offset) + rads_e * k2) * k1; // speedの最大値・最小値処理 if (speed_L < -1.0) { speed_L = -1.0; } if (speed_L > 1.0) { speed_L = 1.0; } //サーボモータが中性点で停止させるための調整値です。 adj_L = -5.0; adj_R = -5.0; //speed_Lの値からspeed_Rを求めています。 //左右のサーボモータの回転数を合わせるための処理です。 if (speed_L <= -1.0) { speed_R = 1.0; } else if (speed_L < -0.4) { speed_R = -speed_L * 0.826 + 0.00353; } else if (speed_L < 0.0) { speed_R = -speed_L * 0.758 - 0.0299; } else if (speed_L == 0) { speed_R = 0.0; } else if (speed_L < 0.4) { speed_R = -speed_L * 0.8025 - 0.0267; } else if (speed_L < 1.0) { speed_R = -speed_L * 1.182 + 0.158; } else { speed_R = -1.0; } // モーターのスピードをDuty比に変換しています。 duty_L = (int)(speed_L * 244.1 + 732.3 + adj_L); duty_R = (int)(speed_R * 244.1 + 732.3 + adj_R); // MAX:9764 /* レベル値(デューティカウント値)を設定 */ pwm_set_chan_level(pwm1_slice_num, PWM_CHAN_A, duty_L); pwm_set_chan_level(pwm2_slice_num, PWM_CHAN_B, duty_R); //この処理にかかる時間を計測しています。(およそ400μsecでした) t_end_control = time_us_32() - t_start_control; return true; }
倒立振子の傾斜角と角速度のデータから、左右のサーボモータへの指令値を求め、PWMのデューティー比を設定することで、倒立振子のバランスを取るように制御している部分です。
まだバランスがうまく取れていないのですが、いろいろやっている最中に、片方のサーボモータが壊れてしまい、現在手を付けていないです。
g. タイマ割り込み部
タイマ割り込みとは、処理を一定周期で繰り返したいときに使うと便利な機能です。
リストg-1 timer_irq()
void timer_irq(){ // start main control process (2.0 ms) add_repeating_timer_ms(-2, Comple_Filter, NULL, &timer_Filter); sleep_ms(1); // start main control process (20.0 ms) add_repeating_timer_ms(-20, Control_Pendulum, NULL, &timer_control); sleep_ms(1); }
相補フィルター部と、コントローラ部を、それぞれ2.0msと20ms周期で繰り返して実行するための処理です。
リストg-2 timer_irq_end()
void timer_irq_end(){ static char s[100]; cancelled_control = cancel_repeating_timer(&timer_control); uart_puts(UART_ID, "cancelled... _control"); snprintf(s, sizeof(s), "%d", cancelled_control); uart_puts(UART_ID, s); uart_puts(UART_ID, "\0"); sleep_ms(1); cancelled_Filter = cancel_repeating_timer(&timer_Filter); uart_puts(UART_ID, "cancelled... _Filter"); snprintf(s, sizeof(s), "%d", cancelled_Filter); uart_puts(UART_ID, s); uart_puts(UART_ID, "\0"); sleep_ms(1); }
タイマ割り込みを終了させるための処理です。(しかしこのプログラムでは、実際に実行されることはまずないです。この上の無限ループから抜け出ることがないためです)
(Pico SDKでのタイマ割り込みに関する内容は、この章の冒頭部でご紹介した③のリンク先の2.4節をご覧ください。正確にはタイマ割り込みとは呼ばないようです)
h. main部
main()関数です。処理はここからスタートし、この処理の中から、上に挙げた関数が呼び出されています。
リストh-1 main()
//========================================================= // Main //========================================================= void main() { static char s[100]; int LED_cnt; //rad_offset = deg_offset * deg2rad; deg_offset = rad_offset * rad2deg; ///////////////////////// // UART 初期設定 // ///////////////////////// uart_initial(); sleep_ms(5000); uart_puts(UART_ID, " Hello, UART!\n\r"); ///////////////////////////// // 加速度センサー 初期設定 // ///////////////////////////// mpu6050_initial(); ///////////////////////////////////////// // GPIO(for LED_25) initialize ///////////////////////////////////////// gpio_init(LED_25); gpio_set_dir(LED_25, GPIO_OUT); /////////////////////////////////// // Calibration Start /////////////////////////////////// //キャリブレーションの処理です Calibration(); snprintf(s, sizeof(s), "%5.3f,%5.3f,%5.3f\n", acc_Y_offset, acc_Z_offset, gyro_X_offset); uart_puts(UART_ID, s); sleep_ms(100); /////////////////////////// // PWM の前処理です // // (Motor_IN1, Motor_IN2)// /////////////////////////// pwm_setting(); /////////////////////////////////////////////// // 関数 get_P_angle(), get_P_angle_dot() 内で// // 平均を求めるときに使用するdata の初期設定 // /////////////////////////////////////////////// // calculate P_angle [deg] as initial values P_angle_data[0] = get_P_angle(); for (int i = 1; i < 10; i++) { P_angle_data[i] = P_angle_data[0]; } // calculate P_angle_dot [deg/s] as initial values P_angle_dot_data[0] = get_P_angle_dot(); for (int i = 1; i < 10; i++) { P_angle_dot_data[i] = P_angle_dot_data[0]; } ///////////////////////////////////////////// // Timer ///////////////////////////////////////////// //タイマ割り込みを設定しています timer_irq(); uart_puts(UART_ID, "Start!!\n"); sleep_ms(1); //=========================================== //Main loop //it takes 10 msec (calculation) //=========================================== LED_cnt = 0; while(1) { t_start = time_us_32(); //ここでUARTにてデータをPC側に送信しています uart_send(); //Picoの基板上のLEDを点滅させています。 //Picoの動作が正常であることを示すためでもあります。 LED_cnt++; if (LED_cnt > 50) { LED_cnt = 0; } if (LED_cnt > 25) { gpio_put(LED_25, 1); // LED_25 is ON } else { gpio_put(LED_25, 0); // LED_25 is OFF } sleep_ms(20); t_end = time_us_32() - t_start; } //=========================================== //Main loop (end) //=========================================== //タイマ割り込みを停止しています timer_irq_end(); return; }
コメント文にておおよその動作を説明しています。今まで上に挙げた関数を呼び出している感じです。
2-2. PC側のUART受信用ソースコード
PC側での受信用コードを示します。(Teratermなどでも見ることはできます。データを保存すれば、グラフに表示することもできます)
リストi-1 PC側のソースコード
#ライブラリを呼びだしています import csv import serial import time #================================================= # プログラムの起点 #================================================= if __name__ == '__main__': data = [] #適切なCOMポートを指定してください COM = "COM13" bitRate = 115200 #受信用ポートをOpen ser = serial.Serial(COM, bitRate, timeout=0.1) #受信用ポート番号を確認 print ('受信用ポート番号:',ser.portstr) print('受信テスト') data = [] count = 0 try: while True: #データを読み込んでいます。 #utf-8でコーディング。エラーを非表示にしています a=ser.readline() data = a.decode('utf-8', errors='ignore').strip() count += 1 #データを受信したとき、PCに表示しています if len(data) != 0: print(count, data) time.sleep(0.005) # ctl-C が押されたときの処理です except KeyboardInterrupt: exit(0) #強制終了します
4.動画
まず回路に電気を流さない状態で、バランスの具合を見てみました。
なんと、カーペットの上だとバランスが取れてしまいました。(笑)
次は通電した状態での様子です。撮影時のアングルがいまいちですが、バランスは取れています。
しかし、机の上で同じことをすると、徐々に揺れが大きくなって、バランスを崩してしまいました。(汗)
5.まとめ
以上、今回の取り組みを紹介しました。
まだ十分にバランスが取れているとは言えないですが、一応補助輪付きで合格と自分では判断しています。
今後に期待したいです。
お付き合いくださり、ありがとうございました。
ハード編へのリンク先:
サーボモータで倒立振子を作ってみた~ハード編 - Tanuki_Bayashin’s diary