HOME | Raspberry Pi | ビジネス書籍紹介 | 2026-01-04 (Sun) Today's Access : 229 Total : 1260927. Since 10 Sep. 2019

簡易二足歩行ロボット
2025.03.10

YouTube でも紹介しています。画像をクリックすると再生できます。


ピッコロボ(piccorobo)
Freaduino というArduino互換ボードを探していたところ偶然ピッコロボを見つけてぽちっとしてしまいました。 中古品ですが送料込み1400円で変えちゃいました。2014年発売当時の税込み価格は9,955円です。 この旧型ピッコロボには Freaduino が使用されています。



Freaduino Uno Rev1.8
Freaduinoは、モータードライバモジュールなしにそれ自身で最大2A出力することができます。


Freaduino Uno Rev1.8
features:
・Inherits all of Arduino Uno's features.
・Compatible to Arduino Uno/Duemilanove’s pin layout, screw hole and dimensions.
・3.3V or 5V Operating Voltage selecable
・More visible location of Indicator LEDs
・Wide range external input from 7~23V DC
・UART/IIC and SPI interface breakout
・Evolved with SMD components
・Digital I/O pins: 14 (of which 6 provide PWM output)
・Flash memory: 32 KB of which 0.5 KB used by bootloader
・SRAM: 2KB
・EEPROM: 1KB
・Clock speed: 16MHz

【注意】スケッチの書き込み時はサーボやLEDを接続していると、電圧降下でブートローダーが壊れてしまうことがあります。

Arduinoボードの電源の選択肢

UNO Rev3では3.3V出力ピンの最大負荷150mA、5V出力ピンで最大1A、 Freaduinoでは、3.3Vで800mA、5Vで2Aまでの出力が可能です。

【補足:Arduino UNO R4 WiFi】
VINピンから電源を供給すると、電圧を5Vに下げるためにボード上のレギュレーターを利用します。 このとき5Vピンは1.2Aまで供給することができます。 この電圧レギュレータ―は、MCUやLEDを含む他の全ての部品に電源を供給しています。 大電流を引き出す外部デバイス(例: サーボモーター)は5Vピンから電源を供給しないでください。 5Vピンはセンサーモジュールなどの低電流のデバイスで利用することを意図しています。
※USB-Cコネクタ経由で電源を供給すると、ボード上の電圧レギュレータ―は完全に迂回します。 この場合、5Vピンはボードを壊すことなく2Aまで供給することができます
Arduino UNO R4 WiFiユーザーマニュアル

後継のピッコロボIoTでは、サーボモータ、センシング、ネットワーク通信機能を1枚のボードに搭載しています。 Freaduino に実装されていた3.3V/5V動作電圧切替スイッチはなくなっています。 後継のこのボードは環境設定も含め使い勝手が悪いです。 ちなみにFreaduino V1.2ボードは製造が終了していますが、当時の単品価格は2280円程度でした。

FreaduinoにはWiFi機能がないので、ESP8266をシリアル接続して、ネットワーク経由でTELNET接続して制御するようにしました。

WeMos D1 mini V3.0




ピッコロボには4つのサーボモータが取り付けられています。 頭部のサーボモータは首振りなので今回は無視します。
胸のサーボモータは足の上げ下げに使います。 両足のサーボモータは足首を捻るのに使います。


・腰サーボモータ(1本線):4番ピン
・左足サーボモータ(2本線):5番ピン
・右足サーボモータ(3本線):6番ピン
・頭部サーボモータ(4本線):7番ピン


WiFi通信用のESP8266(WeMos D1 mini)とFreaduinoをシリアル接続しています。 ESP8266はFreaduinoからの5V供給で動作させています。
ESP8266Freaduino
TX - RX
5V - 5V
GND - GND


単三電池6本がはいる電池ボックスが同梱されているのですが、試験的にちょっと使ってみるだけなので、 Freaduino UNOのDCプラグにモバイルバッテリーから電力を供給します。

●二足歩行の仕組み

前進を例にとって説明します。


胸のサーボモータを時計回りに回転させて左脚をあげます。


宙に浮いた左脚サーボモータを反時計方向に回転させ、足ビレを反時計方向に回します。


右脚サーボモータを反時計方向に回転させると、地面に着いている足ビレは動くことができず、 筐体側が逆に時計方向に回転し、ロボットの左側が前に迫り出します。


胸のサーボを反時計方向に回転させて、左脚を着地させます。


さらに胸のサーボを反時計方向に回転させて右脚を上げます。


宙に浮いた右脚サーボを時計方向に回転させ、足ビレを時計方向に回します。


左脚サーボを時計方向に回転させると、地面に着いている足ビレは動くことができず、 筐体側が逆に反時計方向に回転し、ロボットの右側が前に迫り出します。


胸のサーボを時計方向に回転させて右脚を着地させます。

●プログラミング

ピッコロボのデモプログラムは下記を参考にしてください
ピッコロボのWiki

今回は歩行の仕組みについての解説なので、プログラムの詳細には触れません。 Freaduino側のソースコードを添付しますので、興味ある方はご覧ください。
・理解しやすいように大幅にプログラムを書き換えました。
・プログラム実行開始時は各サーボモータの角度をゼロに初期化していますが、ロボットに傾きがある場合はオフセット補正を行います。
・各サーボモータに角度を指定した場合に、実際の回転角度にばらつきがある場合、回転角補正が必要です(未実装)。

#include  "Servo.h"

#define FRAME      20   // interval time from current step to next step: 20msecc
int numberOfStep = 10;

int current_angle[4];
int servo_trim[4];      // trim to adjust each servo motors's angle to center
Servo servo[4];

#define PIN_CENTER   4
#define PIN_RIGHT    6
#define PIN_LEFT     5
#define PIN_NECK     7
#define PIN_DELAY   50

#define MOTOR_CENTER 0
#define MOTOR_RIGHT  1
#define MOTOR_LEFT   2
#define MOTOR_NECK   3

char buf[16];

void initServo()
{
	// attach pins to each servo
	servo[0].attach(PIN_CENTER);
	servo[1].attach(PIN_RIGHT);
	servo[2].attach(PIN_LEFT);
	servo[3].attach(PIN_NECK);

	// set trim to each servo
	servo_trim[0] = 0;
	servo_trim[1] = 0;
	servo_trim[2] = 0;
	servo_trim[3] = 0;

	current_angle[0] = 0;
	current_angle[1] = 0;
	current_angle[2] = 0;
	current_angle[3] = 0;

	// rotate all servos to center position
	servo[0].write(90 + servo_trim[0]);
	servo[1].write(90 + servo_trim[1]);
	servo[2].write(90 + servo_trim[2]);
	servo[3].write(90 + servo_trim[3]);
}

void playMotion(int motor, int target_angle)
{
	int loop;

	// check limit
	if(target_angle > 90) {
		target_angle = 90;
	} else if (target_angle < -90) {
		target_angle = -90;
	}

	float rotating_angle = ((float)target_angle - (float)current_angle[motor]) / (float)numberOfStep;
	float tmp_angle      = (float)current_angle[motor];

	loop = numberOfStep;
	while(loop)
	{
		tmp_angle += rotating_angle;

		if (rotating_angle < 0){
			if(current_angle[motor] > target_angle) current_angle[motor]  = (int)tmp_angle; else current_angle[motor] = target_angle;

		} else if(rotating_angle > 0) {
			if (current_angle[motor] < target_angle) current_angle[motor] = (int)tmp_angle; else current_angle[motor] = target_angle;
		}
		servo[motor].write(current_angle[motor] + servo_trim[motor] + 90);

		delay(FRAME);
		loop--;
	}

	// adjust current_angle
	current_angle[motor] = target_angle;
	servo[motor].write(current_angle[motor]+servo_trim[motor]+90);
}

// 遷移時間をフレーム間隔(msec)で割る
void setInterval(int msecInterval)
{
	numberOfStep = msecInterval / FRAME;    //total number of steps to move to next position
}

void setTrim(int motor, int trim)
{
	servo_trim[motor] = trim;

	servo[motor].write(servo_trim[motor] + 90);
}

void moveForward()
{
	setInterval(200);
	playMotion(MOTOR_CENTER,  35); // 左脚を上げる
	playMotion(MOTOR_LEFT,   -15); // 宙に浮いている左脚を時計回りに捩じる
	playMotion(MOTOR_RIGHT,  -15); // 右脚を反時計回りに捩じり、宙に浮いている左脚を前に出す
	setInterval(600);
	playMotion(MOTOR_CENTER,   0); // 左脚を下す
	setInterval(200);
	playMotion(MOTOR_CENTER, -35); // 右脚を上げる
	playMotion(MOTOR_RIGHT,   15); // 宙に浮いている右脚を時計回りに捩じる
	playMotion(MOTOR_LEFT,    15); // 左脚を反時計回りに捩じり、宙に浮いている右脚を前に出す
	setInterval(600);
	playMotion(MOTOR_CENTER,   0); // 右脚を下す
}

void moveBack()
{
	setInterval(200);
	playMotion(MOTOR_CENTER, -35); // 右脚を上げる
	playMotion(MOTOR_RIGHT,  -15); // 宙に浮いている右脚を反時計回りに捩じる
	playMotion(MOTOR_LEFT,   -15); // 左脚を時計回りに捩じる
	setInterval(600);
	playMotion(MOTOR_CENTER,   0); // 右脚を下す
	setInterval(200);
	playMotion(MOTOR_CENTER,  35); // 左脚を上げる
	playMotion(MOTOR_LEFT,    15); // 宙に浮いている左脚を時計回りに捩じる
	playMotion(MOTOR_RIGHT,   15); // 右脚を時計回りに捩じる
	setInterval(600);
	playMotion(MOTOR_CENTER,   0);
}

void turnLeft()
{
	setInterval(200);
	playMotion(MOTOR_CENTER, -35);
	playMotion(MOTOR_RIGHT,    0);
	playMotion(MOTOR_LEFT,    15);
	setInterval(600);
	playMotion(MOTOR_CENTER,   0);
	setInterval(200);
	playMotion(MOTOR_CENTER,  35);
	playMotion(MOTOR_LEFT,     0);
	playMotion(MOTOR_RIGHT,   15);
	setInterval(600);
	playMotion(MOTOR_CENTER,   0);
}

void turnRight()
{
	setInterval(200);
	playMotion(MOTOR_CENTER,  35);
	playMotion(MOTOR_LEFT,     0);
	playMotion(MOTOR_RIGHT,  -15);
	setInterval(600);
	playMotion(MOTOR_CENTER,   0);
	setInterval(200);
	playMotion(MOTOR_CENTER, -35);
	playMotion(MOTOR_RIGHT,    0);
	playMotion(MOTOR_LEFT,   -15);
	setInterval(600);
	playMotion(MOTOR_CENTER,   0);
}

void lookAround()
{
	setInterval(200);
	playMotion(MOTOR_CENTER, 0);
	playMotion(MOTOR_RIGHT,  0);
	playMotion(MOTOR_LEFT,   0);
	playMotion(MOTOR_NECK,   0);
	setInterval(300);
	playMotion(MOTOR_NECK,  60);
	playMotion(MOTOR_NECK,   0);
	playMotion(MOTOR_NECK, -60);
	playMotion(MOTOR_NECK,   0);
}

void homePosition()
{
	if (current_angle[MOTOR_CENTER] > 0) {
		playMotion(MOTOR_LEFT,   0);
	} else if (current_angle[MOTOR_CENTER] < 0) {
		playMotion(MOTOR_RIGHT,  0);
	}

	if (current_angle[MOTOR_LEFT] != 0) {
		playMotion(MOTOR_CENTER,  35);
		playMotion(MOTOR_LEFT,     0);
	}
	if (current_angle[MOTOR_RIGHT] != 0) {
		playMotion(MOTOR_CENTER, -35);
		playMotion(MOTOR_RIGHT,    0);
	}

	playMotion(MOTOR_CENTER, 0);
	playMotion(MOTOR_NECK,   0);

	setInterval(300);
}

void setup()
{
	Serial.begin(115200);

  initServo();
}

void loop()
{
	static int pos = 0;

	if(Serial.available()){
		char cmd = Serial.read();
		switch (cmd){
			case 'c': buf[0] = cmd; pos = 1; break;
			case 'r': buf[0] = cmd; pos = 1; break;
			case 'l': buf[0] = cmd; pos = 1; break;
			case 'n': buf[0] = cmd; pos = 1; break;
			case 'i': buf[0] = cmd; pos = 1; break;  // Interval(msec)
			case 0x0a:
				buf[pos] = 0x0;
				pos = 0;
				if (buf[1] == 't') {
					switch(buf[0]) {
						case 'c': setTrim(MOTOR_CENTER, atoi(&buf[2])); break;
						case 'r': setTrim(MOTOR_RIGHT,  atoi(&buf[2])); break;
						case 'l': setTrim(MOTOR_LEFT,   atoi(&buf[2])); break;
						case 'n': setTrim(MOTOR_NECK,   atoi(&buf[2])); break;
					}
				} else {
					switch (buf[0]) {
						case 'c': playMotion(MOTOR_CENTER, atoi(&buf[1])); break;
						case 'r': playMotion(MOTOR_RIGHT,  atoi(&buf[1])); break;
						case 'l': playMotion(MOTOR_LEFT,   atoi(&buf[1])); break;
						case 'n': playMotion(MOTOR_NECK,   atoi(&buf[1])); break;
						case 'i': setInterval(atoi(&buf[1]));              break;
						case 'F': moveForward();                           break;
						case 'B': moveBack();                              break;
						case 'L': turnLeft();                              break;
						case 'R': turnRight();                             break;
						case 'N': lookAround();                            break;
						case 'H': homePosition();                          break;
					}
				}
				break;
			default :
				if (pos < 15) {
					buf[pos] = cmd;
					pos++;
				} else {
					pos = 0;
				}
				 break;
		}
	}
	delay(10);
}

上記プログラミングでは、Servo Libraryを使用しています。

arduino-libraries/Servo

操作コマンド
F前進
B後退
L左回転
R右回転
N首振り
H正立
c整数値足を上げる
r整数値右足を回す
l整数値左足を回す
n整数値首を回す
i整数値動作間隔を指定する
ct整数値胸サーボのオフセット設定
rt整数値右足サーボのオフセット設定
lt整数値左足サーボのオフセット設定
nt整数値首サーボのオフセット設定

ESP8266によるTELNETプログラムは下記を参考にしてください。

TELNETサーマルプリンタ
Raspberry Pi(ラズベリー パイ)は、ARMプロセッサを搭載したシングルボードコンピュータ。イギリスのラズベリーパイ財団によって開発されている。
2022.05.27 まずはロボットカーでお勉強
2022.06.10 第1回テスト環境構築
2022.07.02 第2回テレメトリ通信
2023.05.01 番外編 micro:bit磁気加速度センサ
2024.06.23 第3回 機体制御 雑談
2024.07.23 第4回 慣性センサ
2025.02.22 第5回 自作シリアル・プロポ
2025.03.10 簡易二足歩行ロボット
2025.04.24 第6回 自作TELNET・プロポ
2025.10.23 M5STACK BugC

たいていのことは100日あれば、うまくいく。長田英知著
「時間がなくて、なかなか自分のやりたいことができない」 「一念発起して何かを始めても、いつも三日坊主で終わってしまう」 「色んなことを先延ばしにしたまま、時間だけが過ぎていく」 そこで本書では、そんな著者が独自に開発した、 まったく新しい目標達成メソッド「100日デザイン」について、 その知識と技術を、余すところなくご紹介します。

まんがで納得ナポレオン・ヒル 思考は現実化する
OLとして雑務をこなす日々に飽き足らず、科学者だった父が残した薬品を商品化すべく、起業を決意した内山麻由(27)。彼女はセミナーで知り合った謎の女性からサポートを得ながら、彼女と二人三脚でナポレオン・ヒルの成功哲学を実践し、さまざまな問題を乗り越えていく。 ヒル博士の<ゴールデンルール>に従い、仕事に、恋に全力疾走する彼女の、成功への物語。

今日は人生最悪で最高の日 1秒で世界を変えるたったひとつの方法 ひすいこたろう著
偉人の伝記を読むと、最悪な日は、不幸な日ではなく、新しい自分が始まる日であることがわかります。最悪な出来事は、自分の人生が、想像を超えて面白くなる兆しなのです。偉人伝を読むことで、このときの不幸があったおかげで、未来にこういう幸せがくるのかと、人生を俯瞰する視線が立ち上がるのです。

ご飯は私を裏切らない heisoku著
辛い現実から目を背けて食べるご飯は、いつも美味しく幸せを届けてくれる。 29歳、中卒、恋人いない歴イコール年齢。バイト以外の職歴もなく、短期バイトを転々とする日々。ぐるぐると思索に耽るけど、ご飯を食べると幸せになれる。奇才の新鋭・heisokuが贈るリアル労働グルメ物語!

【最新版Gemini 3に対応!】できるGemini (できるシリーズ)
Geminiを「最強の知的生産パートナー」として使いこなすための、実践的なノウハウを凝縮した一冊です。 基本的な操作方法から、具体的なビジネスシーンでの活用、日々の業務を自動化するGoogle Workspaceとの連携、さらには自分だけのオリジナルAIを作成する方法まで余すところなく解説します。

Rustプログラミング完全ガイド 他言語との比較で違いが分かる!
Rustの各手法や考え方を幅広く解説! 500以上のサンプルを掲載。実行結果も確認。 全24章の包括的なチュートリアル。

ポチらせる文章術
販売サイト・ネット広告・メルマガ・ブログ・ホームページ・SNS… 全WEB媒体で効果バツグン! カリスマコピーライターが教える「見てもらう」「買ってもらう」「共感してもらう」すべてに効くネット文章術

小型で便利な Type-C アダプター USB C オス - USB3.1 オスアダプター
Type-C端子のマイコンボードをこのアダプタを介して直接Raspberry Piに挿すことができます。ケーブルなしで便利なツールです。

Divoom Ditoo Pro ワイヤレススピーカー
15W高音質重低音/青軸キーボード/Bluetooth5.3/ピクセルアート 専用アプリ/USB接続/microSDカード

電源供給USBケーブル スリム 【5本セット】
USB電源ケーブル 5V DC電源供給ケーブル スリム 【5本セット】 電源供給 バッテリー 修理 自作 DIY 電子工作 (100cm)

Copyright © 2011-2027 Sarako Tsukiyono All rights reserved®.