編集履歴一覧に戻る
toppan_hagiwaraのアイコン画像

toppan_hagiwara が 2025年01月30日09時07分00秒 に編集

初版

タイトルの変更

+

落ち葉拾いロボット

タグの変更

+

落ち葉拾い

記事種類の変更

+

製作品

ライセンスの変更

+

(MIT) The MIT License

本文の変更

+

# 概要 本作品は、庭の落ち葉拾いを自動化するロボットです。 Spresenseを中核に、カメラと超音波距離センサーを用いて庭の状況を把握し、回転するブラシで効率的に落ち葉を回収します。 QRコードを読み取ることでホームポジションに自動で戻る機能も搭載し、使いやすさを向上させています。 まるで魔法のように庭を綺麗にする、頼れる相棒を目指しています。 ![キャプションを入力できます](https://camo.elchika.com/259ca81a5e40cd69b0a037b54f55784a90119990/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38376663356665632d613465392d343136322d626634652d3234653263383261353932342f31326461346566612d393131632d346536352d396461342d646166633939623365616335/) # 背景・動機 秋の庭掃除、特に落ち葉拾いは時間と労力を要する作業です。 広い庭をお持ちの方や、高齢者の方にとっては特に負担が大きい作業となります。 そこで、この作業を自動化することで、庭の手入れにかかる負担を軽減し、より多くの時間を庭でのリラックスや他の活動に使えるようにしたいと考えました。 # システム構成 このロボットは、 - 回転するブラシで落ち葉を掻き込みながら走り回る - 障害物を避けられる - ホームポジションへ帰還できる という基本機能を持たせることを目標にシステムに落とし込みました。 SPRESENSEメインボード+拡張ボードを中心に、超音波距離センサーとカメラ制御側SPRESENSE、モーター駆動系が接続されます。 走り回る機能は、キャタピラーを用います。 超音波距離センサーは、周囲の障害物を回避するために使用します。 カメラは、QRコードを読み取るためのものです。ホームポジションを表すQRコードとナビゲーション用QRコードを庭の各所に貼ることで、簡易な仕組みながら自動で帰還する機能を実現できると考えました。 キャタピラー用に2つ、枯れ葉を掻き込む駆動用に1つの合計3つのモーターを持ちます。 電源は、3.7V×3直列をモーター系の電源とし、SPRESENSEボードにはDC/DCコンバータで降圧した5Vを供給します。 ![キャプションを入力できます](https://camo.elchika.com/38802b5c3af2c012683c9d09dc7bd741a3508148/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38376663356665632d613465392d343136322d626634652d3234653263383261353932342f32613237663133662d613334352d343732322d626531662d636263626661386130636664/) # 部品表 |部品名| 個数| 役割| |--|--|--| |SPRESENSEメインボード[CXD5602PWBMAIN1] |2 |制御用メインボード、QRコードリーダー| |SPRESENSE拡張ボード[CXD5602PWBEXT1] |1 |モーター制御用| |SPRESENSEカメラボード [CXD5602PWBCAM1] |1 |QRコード読み取り用| |KEEPPOWER 26650、保護回路付き 6000mAh |3| 3.7V×3直列電源| |DC-DC降圧コンバータ、入力3.0~40.0V、出力1.25V~35V| 1 |5V供給用| |TB67H450モータードライバーモジュール | 3 |キャタピラーモータ、ロータリーブラシモータ用| |ユニバーサル基板 | 4 |DC-DC降圧コンバータ用、モータドライバー用| |緑LED | 3 |モータードライバー回路基準電圧発生用| |ギヤードモータ12V 90rpm|1|ロータリーブラシ用| |超音波センサーモジュール URM37| 1 |衝突回避用| |キャタピラーキット 教育玩具用|1|キャタピラーおよび駆動モータ| |MDF合板 5.5mm厚 200*230mm程度|1|フレーム底部| |MDF合板 2.5mm厚 70*300mm程度|2|フレーム側面等| |カーボン調カッティングシート||フレーム外装| |15mm角材 170mm|1|ロータリーブラシの軸| |2mm厚ゴムシート 35mm×160mm|4|ロータリーブラシのブラシ部| |M4ネジ、スペーサなど||ロータリーブラシの軸| |M3ネジ、スペーサなど||フレーム、基板等の締結| |線材(AWG28, 24, 20等)、コネクタ(XH, PH等)||各種配線| # 回路図 - メイン制御部 ![キャプションを入力できます](https://camo.elchika.com/827840ed4a06682f35bbce6daf164f611dc16f1e/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38376663356665632d613465392d343136322d626634652d3234653263383261353932342f32346166306430662d376666372d346538352d383333362d653139666331313530393831/) - モータードライバー部 ![キャプションを入力できます](https://camo.elchika.com/cd6434e7d048d74b785e7c70538dc3243b3bbf34/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38376663356665632d613465392d343136322d626634652d3234653263383261353932342f63373634616463392d653638302d346432342d623336642d616466376632313233353537/) # ソースコード ## SPRESENSEメインボード+拡張ボード(全体制御部) ロボットの全体動作は、以下となります。 ``` ①電源ON ↓ ②初期化 ↓ ③スイッチ操作待ち ↓ ④収集モード実行 ↓ ⑤帰還モード実行 ↓ 再度③へ ``` ### 収集モード ロータリーブラシを回転させながら、ランダムに走り回って落ち葉を収集します。一定時間(ソースコードでは3分)経過するか、操作スイッチが押されると終了します。 ### 帰還モード カメラでQRコードを読み取りながら、ホームポジションを目指します。**ホームポジションQRコードを検出すると、1.5秒間ロータリーブラシを回転させて帰還した合図をして、モードが終了します。** - LeafCollectionRobot.ino ```cpp /* 落ち葉収集ロボット */ #include "Caterpillar.h" // キャタピラー駆動部 #include "URM37.h" // 超音波距離センサー部 #include "RotaryBrush.h" // ロータリーブラシ駆動部 #include "QRReciever.h" // QRコードリーダー応答受信部 // 定義値 ------------------------------------- // 全体動作 #define DIST_MIN 40 // 障害物との接近限界[cm] #define DIST_MIN2 100 // 障害物との接近限界2[cm] #define DIST_IGNORE 18 // 計測ミス対策 #define TURN_RETRY_LIMIT 18 // 方向転換リトライ回数上限 #define DIRECTION_LEFT 1 // 1=左 #define COLLECT_LEAF_LENGTH 1000 * 60 * 3 // 収集モード実行時間[msec] #define RUNNING_TIME_MAX 4000 // 最大前進継続時間[msec] // QRコードリーダーレシーバ #define QR_BAUD_RATE 9600 // キャタピラーモータ #define TURN_SPEED 160 #define TURN_SPEED2 192 // ロータリーブラシモータ #define PIN_BRUSH_1 10 // D10 #define PIN_BRUSH_2 11 // D11 // 操作スイッチ #define PIN_SW 12 // D12 // 超音波センサー const int URTRIG = 7; const int SENSOR_PIN = A0; // グローバルオブジェクト ------------------------------------- // キャタピラー Caterpillar cat = Caterpillar(); // 超音波センサー URM37 distSensorMain = URM37(); // ロータリーブラシモータ RotaryBrushMotor rbm = RotaryBrushMotor(); // QRReader受信 QRReciever qrr = QRReciever(); // 旋回回数 int direction = 1; // 関数 ------------------------------------------------------- // 操作スイッチ読み取り // 操作有り -> true bool read_switch() { bool pushed = false; // 指を離すまで while (digitalRead(PIN_SW) == LOW) { delay(10); pushed = true; } return pushed; } // 動作モード ------------------------------------------------------- // スイッチ操作チェック void check_switch() { Serial.println("check_switch START!"); while (!read_switch()) { delay(10); } Serial.println("check_switch END!"); } // 落ち葉収集モード // 一定距離未満になったら、停止し左右に方向転換 // 進める方向を向いたら再度前進 void collect_leaf() { unsigned long startMillis = millis(); int dist = 0; int angle = 15; unsigned long runningTime0 = startMillis; unsigned long runningTime; Serial.println("collect_leaf: START!"); delay(5000); // 動作開始 rbm.on(); cat.goFORward(); while (true) { runningTime = millis() - runningTime0; // スイッチをチェック if (read_switch()) { Serial.println("collect_leaf: cancel."); break; } // 規定時間を経過したら終了 if (millis() > COLLECT_LEAF_LENGTH + startMillis) { Serial.printf("collect_leaf: timeout.\n"); break; } // 障害物チェック dist = distSensorMain.measure(); Serial.printf("collect_leaf: dist=%d cm\n", dist); if ((dist >= DIST_MIN || dist < DIST_IGNORE) && runningTime < RUNNING_TIME_MAX) { // 障害物との距離が遠い or 前進継続時間が規定時間未満 continue; } // ↓ 障害物との距離が近い or 前進継続時間が規定時間以上 if (dist < DIST_MIN) { // 障害物との距離が近い場合 // 念のため少し後退する Serial.printf("collect_leaf: Approaching an obstacle\n"); cat.stop(); rbm.off(); cat.goBACKward(); delay(200); cat.stop(); angle = 15; // 障害物のない方向を向くまで左右に方向転換 int turnRetry = 0; cat.turnSpeed = TURN_SPEED2; while (true) { if (direction == DIRECTION_LEFT) { cat.turnLeft(angle, true); Serial.printf("collect_leaf: Turn Left %d deg.\n", angle); } else { cat.turnRight(angle, true); Serial.printf("collect_leaf: Turn Right %d deg.\n", angle); } dist = distSensorMain.measure(); Serial.printf("collect_leaf: dist=%d cm\n", dist); if (dist >= DIST_MIN2) { // 方向転換完了 break; } turnRetry++; if (turnRetry >= TURN_RETRY_LIMIT) { // 上限到達 // 念のため少し後退する cat.stop(); cat.goBACKward(); delay(200); cat.stop(); break; } } } else { // 前進継続時間が規定時間を超過した場合 // 大きく方向を変える angle = 90; if (direction == DIRECTION_LEFT) { cat.turnLeft(angle, true); Serial.printf("collect_leaf: Turn Left %d deg.\n", angle); } else { cat.turnRight(angle, true); Serial.printf("collect_leaf: Turn Right %d deg.\n", angle); } } direction = rand() % 2; // 次回の方向 cat.stop(); // 再度前進 rbm.on(); cat.turnSpeed = TURN_SPEED; cat.goFORward(); Serial.printf("collect_leaf: Go Forward\n"); runningTime0 = millis(); } // 停止処理 cat.stop(); rbm.off(); Serial.println("collect_leaf: END!"); } // 帰還モード void back_home() { Serial.println("back_home: START!"); unsigned long t0 = 0; delay(5000); while (true) { // スイッチをチェック if (read_switch()) { Serial.println("back_home: cancel."); break; } // 障害物との距離が一定以下になるまで前進 cat.goFORward(); t0 = millis(); while (true) { int dist = distSensorMain.measure(); Serial.printf("back_home: distance=%d cm\n", dist); if (dist < DIST_MIN2 && dist >= DIST_IGNORE) { // 障害物発見 Serial.printf("collect_leaf: Approaching an obstacle\n"); break; } delay(50); if (millis() - t0 > RUNNING_TIME_MAX) { // 前進したまま立往生しないように一定時間経ったら前進をやめる break; } } // 一時停止 cat.stop(); cat.goBACKward(); delay(100); cat.stop(); // 周囲を見渡し、方向指示QRを探す int angle = -90; cat.turnLeft(90); String resp; while (true) { // 映像が安定して読み取りやすくするため少し待つ delay(200); // QRリーダー応答待ち resp = qrr.serial2Check(); if (resp != NULL) { // 検出 Serial.println("back_home: 'QR' detected."); break; } cat.turnRight(15); angle += 15; if (angle == 90) { Serial.println("back_home: 'QR' not detected."); break; } } // 進行方向決定 if (resp == NULL) { // 見つからなかった場合、方向を変える cat.turnRight(180); } else { // 見つかった場合 if (resp.startsWith("HOME")) { // ホーム検出 -> 終了 // ホームポジションQRコード検出の合図としてロータリーブラシを1.5秒間回転させる Serial.println("back_home: 'HOME' detected."); rbm.on(); delay(1500); rbm.off(); break; } else if (resp.startsWith("LEFT")) { // 左方向に回転 cat.turnLeft(90); Serial.println("back_home: 'LEFT' detected."); rbm.on(); delay(300); rbm.off(); } else if (resp.startsWith("RIGHT")) { // 右方向に回転 cat.turnRight(90); Serial.println("back_home: 'RIGHT' detected."); rbm.on(); delay(300); rbm.off(); } else { // それ以外 // 見つからなかった場合、方向を変える Serial.println("back_home: 'UNKNOWN' detected."); } } t0 = millis(); while (true) { if (distSensorMain.measure() > DIST_MIN2 || millis() - t0 > 2000) { break; } cat.goBACKward(); delay(200); cat.stop(); } } Serial.println("back_home END!"); } // メイン ------------------------------------------------------------ void setup() { Serial.begin(115200); Serial.println("setup: START!"); // 制御オブジェクト初期化 pinMode(PIN_SW, INPUT); distSensorMain.init(URTRIG, SENSOR_PIN); cat.init(); cat.turnSpeed = TURN_SPEED; rbm.init(PIN_BRUSH_1, PIN_BRUSH_2); qrr.init(QR_BAUD_RATE); Serial.println("setup: END!"); } void loop() { // スイッチ操作チェック check_switch(); // 収集モード実行 collect_leaf(); // 帰還モード実行 back_home(); } ``` - Caterpillar.h ```cpp #define PIN_CAT_L1 6 // PWM0 #define PIN_CAT_L2 5 // PWM1 #define PIN_CAT_R1 9 // PWM2 #define PIN_CAT_R2 3 // PWM3 #define TURN_SPEED_DEFAULT 192 #define TURN_DURATION 10 #define STOP_DURATION 100 /* キャタピラーユニットクラス */ class CaterpillarUnit { public: // 初期化 // pwm1: Hブリッジピン番号1 // pwm2: Hブリッジピン番号2 // direction: 方向(pwm1をHIGH, pwm2をアナログ出力にするのが1, 逆が-1) void init(int pwm1, int pwm2, int direction) { _pwm1 = pwm1; _pwm2 = pwm2; _direction = direction; } // 速度設定 // speed: -255 ~ +255 void setSpeed(int speed) { _speed = speed; int analogPin; int zeroPin; int s; if (_direction * _speed >= 0) { analogPin = _pwm1; zeroPin = _pwm2; } else { analogPin = _pwm2; zeroPin = _pwm1; } if (_speed >= 0) { s = _speed; } else { s = _speed * -1; } // Serial.printf("zeroPin=%d, analogPin=%d, s=%d\n", zeroPin, analogPin, s); analogWrite(zeroPin, 0); // LOW固定 analogWrite(analogPin, s); } /* 停止 */ void stop() { setSpeed(0); } private: int _pwm1; int _pwm2; int _direction = 1; int _speed = 0; // 現在の速度(-255 ~ +255) }; /* キャタピラークラス */ class Caterpillar { public: // 定数 static const int STATUS_FORWARD = 1; static const int STATUS_BACKWORD = -1; static const int STATUS_STOP = 0; static const int STATUS_TURN_RIGHT = 3; static const int STATUS_TURN_LEFT = 2; int stopDuration = STOP_DURATION; int turnSpeed = TURN_SPEED_DEFAULT; int turnDuration = TURN_DURATION; // 初期化 void init() { _catL.init(PIN_CAT_L1, PIN_CAT_L2, 1); _catR.init(PIN_CAT_R1, PIN_CAT_R2, -1); } // 前進 void goFORward() { stop(); _status = STATUS_FORWARD; _catL.setSpeed(turnSpeed); _catR.setSpeed(turnSpeed); } // 後退 void goBACKward() { stop(); _status = STATUS_BACKWORD; _catL.setSpeed(-turnSpeed); _catR.setSpeed(-turnSpeed); } // 左に旋回 void turnLeft(int angle, bool nonStop = false) { if (!nonStop) stop(); _status = STATUS_TURN_LEFT; _catL.setSpeed(-turnSpeed); _catR.setSpeed(turnSpeed); delay(turnDuration * angle); if (!nonStop) stop(); } // 止まる void stop() { _status = STATUS_STOP; _catL.stop(); _catR.stop(); delay(stopDuration); } // 右に旋回 void turnRight(int angle, bool nonStop = false) { if (!nonStop) stop(); _status = STATUS_TURN_RIGHT; _catL.setSpeed(turnSpeed); _catR.setSpeed(-turnSpeed); delay(turnDuration * angle); if (!nonStop) stop(); } int getStatus() { return _status; } private: CaterpillarUnit _catL; CaterpillarUnit _catR; int _status = STATUS_STOP; }; ``` - RotaryBrush.h ```cpp /* ロータリーブラシモータ */ class RotaryBrushMotor { public: // 初期化 void init(int pin1, int pin2) { _pin1 = pin1; _pin2 = pin2; pinMode(_pin1, OUTPUT); pinMode(_pin2, OUTPUT); off(); } // 回転 void on() { digitalWrite(_pin1, HIGH); digitalWrite(_pin2, LOW); } // 停止 void off() { digitalWrite(_pin1, LOW); digitalWrite(_pin2, LOW); } private: int _pin1 = 1; int _pin2 = 2; }; ``` - URM37.h ```cpp /* 超音波距離センサ URM37 */ class URM37 { public: int triggerWait = 24; // トリガー出力後待ち時間[msec](URM37AutoMeasurementModeの周期程度) // 初期化 // urtrig: トリガー出力ピン番号 // sensorPin: アナログ入力ピン番号 void init(int urtrig, int sensorPin) { _urtrig = urtrig; _sensorPin = sensorPin; pinMode(_urtrig, OUTPUT); digitalWrite(_urtrig, HIGH); } // 測定 // distance [cm] = ADC読み値[LSB] / 1024[LSB] * 5000[mV] / 4.125[mV/cm] = ADC読み値 * 0.55 int measure() { // トリガー出力 digitalWrite(_urtrig, LOW); delay(1); digitalWrite(_urtrig, HIGH); delay(triggerWait); _sensorValue = (int) ((analogRead(_sensorPin) * 0.55 * 1.2)); return _sensorValue; } private: int _urtrig = 0; int _sensorPin = 0; int _sensorValue = 0; }; ``` - QRReciever.h ```cpp /* QRコードリーダーのレスポンスを受け取る */ class QRReciever { public: // 初期化 void init(int baudrate) { // シリアルオープン _baudrate = baudrate; Serial2.setTimeout(100); Serial2.begin(_baudrate, SERIAL_8N1); } // Serial2着信 String serial2Check() { String data; if (Serial2.available() > 0) { data = Serial2.readStringUntil("\n"); } return data; } private: int _baudrate = 115200; }; ``` ## SPRESENSEメインボード(カメラ部) 本プログラムは、Yoshino Taroさん作成の https://github.com/YoshinoTaro/QR_decode_for_Arduino を使用し、Spresense_QR_decode.inoをSerial2に結果を出力できるように一部を変更したものとなっています。それ以外のソースコードは、変更ありません。 - QR.ino ```cpp /** * QR code reader using Yoshino Taro's QR_decode_for_Arduino * Serial2への出力を追加 * LGPL version 2.1 Copyright 2025 Takuya Hagiwara */ #include "quirc.h" #include <Camera.h> // #define USE_LCD #ifdef USE_LCD #include "Adafruit_ILI9341.h" #define TFT_DC 9 #define TFT_CS -1 #define TFT_RST 8 Adafruit_ILI9341 tft = Adafruit_ILI9341(&SPI, TFT_DC, TFT_CS, TFT_RST); #endif struct quirc* qr; struct quirc_code code; struct quirc_data data; uint8_t *image; int w, h; int CAM_H = CAM_IMGSIZE_QVGA_H; int CAM_V =CAM_IMGSIZE_QVGA_V; void CamCB(CamImage img) { if (!img.isAvailable()) return; #ifdef USE_LCD tft.drawRGBBitmap(0, 0, (uint16_t *)img.getImgBuff(), CAM_H, CAM_V); #endif image = quirc_begin(qr, &w, &h); if (w != CAM_H || h != CAM_V) { Serial.println("configration error"); while(1); // fatal error to enter the infinite loop (stop process) } // copy gray scale image uint16_t* rgb_image = (uint16_t*)img.getImgBuff(); for (int n = 0; n < w * h; ++n) { uint16_t pix = rgb_image[n]; image[n] = (pix & 0x7E0) >> 5; // extract g image } quirc_end(qr); int num_codes; int i; num_codes = quirc_count(qr); // Serial.println("num codes: " + String(num_codes)); for (int i = 0; i < num_codes; i++) { quirc_decode_error_t err; quirc_extract(qr, i, &code); err = quirc_decode(&code, &data); if (err) { // Serial.printf("DECODE FAILED: %s\n", quirc_strerror(err)); } else { Serial.printf("Data: %s\n", data.payload); Serial2.printf("%s\n", data.payload); #ifdef USE_LCD // QR Codeを検知した場合のみ枠線を描画する draw_polygon_4(&code); #endif } } } void setup() { Serial.begin(115200); Serial2.begin(9600); String ver = quirc_version(); Serial.println("quirc version: " + ver); #ifdef USE_LCD tft.begin(); tft.setRotation(3); #endif CamErr camErr = theCamera.begin(1, CAM_VIDEO_FPS_5, CAM_H, CAM_V, CAM_IMAGE_PIX_FMT_RGB565); if (camErr != NULL) { Serial.println("Camera Error!!!"); } theCamera.startStreaming(true, CamCB); qr = quirc_new(); if (qr == NULL) { Serial.println("can't create quirc object"); return; } if (quirc_resize(qr, CAM_H, CAM_V) < 0) { Serial.println("Failed to allocate video memory"); return; } Serial.println("setup: OK."); } void loop() {} //四辺形を描画する void draw_polygon_4(const struct quirc_code* code) { for (int i = 0; i < 4; i++) { // tft.drawLine(code->corners[i].x, code->corners[i].y, code->corners[(i + 1) % 4].x, code->corners[(i + 1) % 4].y, 0xF800); } } ``` # 組み立て - 主な電気的部品 モータードライバーICは同一のものを3個。カメラとカメラ制御用SPRESENSEメインボードを格納する部分、超音波センサのホルダーは3Dプリンターで作成しました。 ![キャプションを入力できます](https://camo.elchika.com/f9242b860c5e6e3b84052ad6e2575baf00120be5/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38376663356665632d613465392d343136322d626634652d3234653263383261353932342f34336133356661302d626136392d343462322d383262612d336261343231386263663363/) - キャタピラー部品 購入したキットから、キャタピラーと車輪、モーターのみを使用。 ![キャプションを入力できます](https://camo.elchika.com/fa1bd6a10fde90e41fbc5614c8022f2876aae01a/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38376663356665632d613465392d343136322d626634652d3234653263383261353932342f30633737313331662d323130662d346665642d396538382d636365323236346564383465/) - フレーム部材と組み立てた様子 木材(MDF)を切り出し穴あけ。部材同士はネジで締結します。 ![キャプションを入力できます](https://camo.elchika.com/7eea47a704fd68c59e1da0ce8e4ebfb8737a7fe3/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38376663356665632d613465392d343136322d626634652d3234653263383261353932342f66653035303761632d333133612d343361642d613562302d646636656137646136636636/) ![キャプションを入力できます](https://camo.elchika.com/e792e9f9003fe27b64f458082402cefcf2287a24/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38376663356665632d613465392d343136322d626634652d3234653263383261353932342f36336665316438342d623535372d343135662d396637302d656263353935626136613063/) - ロータリーブラシ部 15mm角材の4面に2mm厚のゴムシートを固定。角材の左右に軸を取り付け。ロータリーブラシモータの軸にはM3ネジが取り付けられるので、軸はM3の雄ネジ付スペーサを使用。 ブラシ部分の素材は、いくつか試した中で2mm厚ゴムシートが適度な硬さと柔軟性がありうまく落ち葉を掻き込んでくれたため採用しました。 ![キャプションを入力できます](https://camo.elchika.com/6442f0005da9de44b5c41f79d12dfaf57937c485/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38376663356665632d613465392d343136322d626634652d3234653263383261353932342f32343234626231332d376633342d343065662d623434352d313730663166363232363832/) - 完成 電気部品をフレームに固定し、配線を施し、SPRESENSEにソフトウェアを書き込んだら完成です。 ![キャプションを入力できます](https://camo.elchika.com/e24916c7183ca36ae24e8a26ce4258cea0746f67/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38376663356665632d613465392d343136322d626634652d3234653263383261353932342f33636230663336652d336336382d346238352d623539302d326431303864646264313834/) ![キャプションを入力できます](https://camo.elchika.com/ee7db09c9c60c015bdbee45db104fe229f4dd014/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38376663356665632d613465392d343136322d626634652d3234653263383261353932342f61376634613131322d616535632d346531302d383135362d343437613235633338636439/) # 動作の様子 動作の様子を動画にしました。 ## コース 屋外で適切な場所の確保が難しかったため、室内に約2.5m四方コースを用意しました。 写真右手前の角をホームポジションとして、HOMEのQRコードが貼られています。 また、写真左奥の角からそれぞれ右、左にそれぞれRIGHT、LEFTを表すQRコードが3枚ずつ貼られています。(LEFTの1枚は手前の壁で見えませんが。) ![キャプションを入力できます](https://camo.elchika.com/5e27a27c9803a67d268513beee70522de9e090e0/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38376663356665632d613465392d343136322d626634652d3234653263383261353932342f30646562376564392d396264372d346534302d616364612d303635393733346365353932/) HOME ![キャプションを入力できます](https://camo.elchika.com/eaa6e02c21a8560209546083bf972ff3787e5c8a/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38376663356665632d613465392d343136322d626634652d3234653263383261353932342f32356538363430642d323534642d343664642d623438322d626365316134326130623837/) 収集対象の落ち葉たちをコースにばらまきました。 ![キャプションを入力できます](https://camo.elchika.com/8ed138e2137836ce45a7c7c43833ceee5c42fbd2/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38376663356665632d613465392d343136322d626634652d3234653263383261353932342f32346466616635612d383565302d343439622d613233332d303463313763633164316334/) ## 収集モード 走路上の落ち葉が収集されていることがわかります。ただし、使用した落ち葉が非常に乾燥していて砕けやすいため、砕けてしまったものをすべて拾うことは残念ながらできませんでしたが、大きめの落ち葉はうまく収集できました。 ## 帰還モード 少し走ってカメラでQRコードを探すことを繰り返しながら、動画の最後数秒間で、ホームポジションのQRコードを検出したことを表すロータリーブラシの1.5秒間回転が発生し、無事ホームポジションに帰還できました。 ※落ち葉のない状態で行っています。 # 工夫・苦労した点 本製作を通じた工夫や苦労について、いくつか列挙します。 - ロータリーブラシ - 落ち葉をうまく拾える機構、材料を探すのに苦労した。ある程度柔軟性がありながら硬さも持っていることや、落ち葉の大きさを考慮したブラシサイズが重要であり、いくつか試すことで適切な材料、ブラシ形状にたどり着けた。 - 障害物検知 - 超音波距離センサーを1つだけ搭載する簡素なシステム構成であるため、斜めに壁に接近する場合に検出が難しい。壁に近づきすぎてしまっている場合を考慮し、障害物を検知したら少しだけ後退してから回転するなどして回転するスペースを自ら確保した - 立往生対策 - モーターに大きな電流が流れ続けて損傷や無駄な消費電力を防ぐため、回転しても障害物のない方向を見つけられない場合は後退するようにした - 全体重量 - モータが3つあり重くなりがちであるため、フレーム底部を肉抜きしたり、樹脂スペーサを用いたりすることで重量削減に努めた。 - ホームポジション帰還機能 - SPRESENSEを活用しながら帰還機能を実現するうえで、ARマーカーやBLEビーコン等も検討しながら実現性を考慮してQRコードとした。家庭用掃除ロボットのような高度な周囲環境認識能力を持たない中で、QRコードの配置高さや大きさを試すことで、機能を実現できた。 # 今後の展望 今後の改善点として、以下のような点が挙げられます。 - 走行性能の改善 - 木の根元など、庭で想定される凹凸を乗り越えられるように、モーターのギヤ比、車高等を見なおす - 収集機構の改善 - 地面によりうまくフィットするロータリーブラシ形状や、ブラシが不得意なサイズ、形状の落ち葉に対応できるように吸引機能等を検討する - 落ち葉の自動検出と走行ルートの決定 - より短時間で効率的に収集するため、カメラを活用して落ち葉が検出し最適走行ルートを決定できるようにしたい - 使い勝手の向上 - 収集した落ち葉がごみ袋に直接入るように、ごみ袋装着機能を検討する