技術者の休日

技術者として働いています。休日の息抜きについて書いて行きます♪

【ツェルト2ロング】テント泊登山装備の軽量化検討(テント編)

1. 検討結果

山岳用テントからツェルトに変更したことにより、1.18kgの軽量化に成功。
【変更前】Naturehike、Cloud Up 1 Ultralight Tent 20D:1.52kg
【変更後】finetrack、ツェルト2ロング:0.34kg

2. 検討背景

以前、上高地バスターミナルから涸沢まで歩きました。その際、ザックが重く感じられたあたりから、途中の景色を楽しむ余裕が持てなくなりました。1泊分の衣食住を担ぐ以上、仕方のないことだと考えていましたが、登山関連書籍を調べる過程でウルトラライトと定義される登山スタイルがあることを知りました。これを機に、「あると便利なもの」ではなく「自分に最低限必要なもの」は何かを考え、持ち物を削る作業が新たな楽しさとして私の登山に加わりました。この作業の中で、自身の装備をリストアップし、各重量を計測/整理したところ、テントが最も重いアイテムであることが分かり、見直してみることにしました。

3. 現状把握

整理した結果、涸沢登山時のザック総重量が12kg。内テント重量が1.52kgであり、全体の約12%であることが分かりました。

【Naturehikeを購入した理由】
テント泊デビューする際、メジャーなア○イやモン○ルの山岳用テントには、価格面から手が出ませんでした。代用品を探していた際、たまたまNaturehikeの紹介ブログを見つけ、手の届く山岳用テントであり、軽量ということで購入を決めました。185cmの私が使っても十分なスペースがあり、また設営/撤収/メンテナンスが容易なため、テント泊登山だけでなく、ソロキャンプでも使用しています。

4. 新しいテントに求めたこと

下記3点を重要視しました。

  1. 【重量】軽量であること
  2. 【居住性】身長185cmの私が使用可能であること
  3. 【メンテナンス性】帰宅後の清掃が容易であること

5. 候補テント一覧

色々なメーカのテントを探した結果、自分に合いそうなテントを3つ見つけました。購入判断の決め手となった個人的な見解と理由は下記の通りです。

1)【Six Moon Designs】Lunar Solo

  • 重量:△(771g)
  • 居住性:○
  • メンテナンス性:○-

インナーテントがあるため、浸水/虫対策ができている点は魅力的。一方、インナーテントとフライシートの隙間風が寒がりな自分には心配だったのと、後述のツェルトと比較した際に少し重く感じたため、購入を見送りました。

2)【LOCUS GEAR】Khufu Tyvek

locusgear.com

  • 重量:○-(440g)
  • 居住性:×(185cmのため)
  • メンテナンス性:△

ワンポールテントへの憧れもあったため、一番惹かれたテントになります。しかし、メーカの方に問い合わせさせて頂いたところ、身長185cmの方にはおすすめしないとアドバイス頂き、購入を断念しました。また、Tyvek素材は一般的な素材に比べると扱いには注意が必要とのことでした。LOCUS GEARさんのサイトには他素材含めてラインナップされていますので、私と同じように気になった方は覗いてみてください。

3)【finetrack】ツェルト2ロング

  • 重量:○(340g)
  • 居住性:○-(高さ方向は懸念あり)
  • メンテナンス性:○

圧倒的な軽さとベランダでも干せるメンテナンス性が魅力的で購入を決断しました。結露/虫/浸水に対する懸念はありましたが、4ヶ月程迷った結果、諸先輩方の知恵を活用させてもらうことで許容レベルにできると考えました。

最終的に、ツェルト2ロングに決めました!!

6. 実践投入の準備

6-1. 防水処理

ツェルトをテントとして使用する場合、当たり前ですが防水性は非常に重要となります。ツェルト2ロングの場合、縫い目箇所へのシムシーリング材の塗布が推奨されています。雨漏りしないように丁寧に塗布しましょう。私が使用したシール材は下記の製品です。

6-2. インナーシートの準備

ツェルトの床は2枚の生地を重ねたものを紐で固定している状態です。そのため、雨が降った場合は隙間から浸水する恐れがあります。対策について色々と調べたところ、下記シートをツェルト内部に敷く対策が有効とのことでした。70gと非常に軽量です。

6-3. ツェルト用ガイドラインの準備

ツェルトを設営するためには、通常トレッキングポールとガイドラインも必要となります。今回ガイドラインは口コミが高かったツェルトメーカの純正品を購入しました。雨の中使用しましたが、伸びることなくツェルトのテンションを高く保ってくれました。

6-4. ツェルト用ペグの準備

従来使用していたNaturehikeテントの付属品(10g/本)を流用しました。

7. 実践投入

登山開始。テント以外の装備も見直した結果、ザック総重量の軽量化に成功(12kg→8kg)。-4kgの恩恵は大きくテント場までの景色を十分に楽しむことができ、充実した登山となりました。テント場到着後、初めてのツェルト設営のためロープワークを中心に苦戦するもなんとか設営できました。ツェルト入り口周辺に腰掛けると、座高が高い私でも頭をツェルトに当てることなく快適に過ごせました。
f:id:nocchi2017:20210516200818j:plain
この後、天気予報が大きく外れまさかの大雨。仕方なく、ツェルト内部に引きこもり読書していましたが、内部の高さがないためほとんど寝転がっていました。後、防水処理が甘い箇所が1箇所あり、水滴が垂れてきました。
f:id:nocchi2017:20210516200856j:plain
翌朝、内部は結露していましたが、シュラフには垂れておらず快適な睡眠が得られました。しかし、ツェルト内部で座ると頭が常にツェルトと接してしまいます。その結果、朝食後には頭がビショビショになりました。結露している時に起き上がる際は、ツェルト内部でもパーカーをかぶった方が良さそうです。雨が降ってくれたおかげで、様々な経験できた良いツェルト泊となりました。

8. 振り返り

良かった点
  • 軽量化により登山中の景色を楽しめる余裕が生まれた
  • 晴れていれば十分な居住性を確保できた
  • 慣れると設営/撤収/メンテナンスが簡単だった
  • インナーシート上部は浸水しなかった
悪かった点
  • 雨が降り内部に引きこもると狭さを感じた
  • 結露が発生した
  • 頭が天井に当たり髪が濡れた

今回は春先の稜線下でツェルトを使用してみました。今後、虫が多い時期や風が強い稜線上でも使用してみたいと思います。様々な工夫をすることでザック総重量を抑えながら、程よく快適なテント泊ができそうなアイテムのため、今後も使用していきます。

最後まで読んて頂き、ありがとうございます。


少しだけお手伝いしているWebアプリです

地図上のアイコンをクリックすると、該当するキャンプ場の情報が見れます。
良かったら覗いてみてください。愛知県以外も順次追加予定です。
camplabo2021.com

【備忘録】【Navio2✖️ROS】PC上でセンサデータを共有

開発環境

 ・OS : Linux (Ubuntu14.04 LTS)
 ・Raspberry Pi + Navio2

事前準備

1. Raspberry Piの有線接続時のIPアドレス固定
 1) デフォルトゲートウェイを確認

$ route

  結果は、192.168.20.20と仮定

 2) Raspberry Piの設定ファイルを変更

$nano /etc/dhcpcd.conf

下記内容を追記し保存
interface eth0
static ip_address=192.168.20.30(←任意)/24
static routers=192.168.20.20
static domain_name_servers=192.168.20.20

$sudo nano /boot/cmdline.txt
末尾に「net.ifnames=0」を追記し保存$

$sudo reboot

再起動後、次のコマンドでログイン可能
$ sudo ssh pi@192.168.20.30(←/etc/dhcpcd.conf設定内容を参照)

2. Raspberry PiにNavio2操作用ライブラリをインストール

$ git clone https://github.com/emlid/Navio2.git
$ cd Navio2/C++/Navio
$ make all
ライブラリ(libnavio.a)が生成される 

3. Raspberry Piで取得したセンサデータをROS上に書き出す(パブリッシュ)
 1) ROSプログラムのビルド

ワークスペースの作成
$ echo "source /opt/ros/kinetic/setup.bash" >> ~/.bashrc
$ mkdir ~/catkin_ws
$ mkdir ~/catkin_ws/src

パッケージの作成
$ cd ~/catkin_ws/src
$ catkin_create_pkg accel_gyro_mag std_msgs roscpp rospy
$ cd ~/catkin_ws
$ catkin_make

メッセージファイルの作成
※ メッセージが定義されているヘッダーファイルは/opt/ros/kinetic/includeにある。
$ cd ~/catkin_ws/src/accel_gyro_mag
$ mkdir msg
$ cd msg
$ touch sensor_9dof.msg
$ nano sensor_9dof.msg
 下記内容を追記
 float32 temperature
 geometry_msgs/Vector3 acc
 geometry_msgs/Vector3 gyro
 geometry_msgs/Vector3 mag

プログラムの作成
$ cd ~/catkin_ws/src/accel_gyro_mag/src/
$ nano accel_gyro_mag.cpp

CMakeLists.txtの編集
$ cd ~/catkin_ws/src/accel_gyro_mag
$ nano CMakeLists.txt
 ファイル内の似た記述に下記を追加し、保存
 cmake_minimum_required(VERSION 2.8.3)
 project(accel_gyro_mag)
 find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  geometry_msgs
  message_generation
 )
 add_message_files(
  FILES
  sensor_9dof.msg
 )
 generate_messages(
  DEPENDENCIES
  std_msgs
  geometry_msgs
 )
 include_directories(~/(環境依存)/Navio2/C++/Navio)
 link_directories(~/Drivers/(環境依存)/C++/Navio)
 add_executable(accel_gyro_mag src/accel_gyro_mag.cpp)
 target_link_libraries(accel_gyro_mag ${catkin_LIBRARIES})
 target_link_libraries(accel_gyro_mag navio)

ビルド
$ cd ~/catkin_ws
$ catkin_make
※ make後エラーが発生したが、~/catkin_ws/src/accel_gyro_mag/msg/._sensor_9dof.msgを削除後に解決

環境のセットアップ
source ~/catkin_ws/devel/setup.bash
(~/.bashrcの最終行に追加しておくと、手動セットアップを省略可)

 2) ROSプログラム
  accel_gyro_mag.cppは下記の通り

#include <ros/ros.h> 		            // ROSヘッダファイル
#include "Common/MPU9250.h"	   // For Navio
#include "Navio2/LSM9DS1.h"	   // For Navio
#include "Common/Util.h"	           // For Navio
#include "accel_gyro_mag/sensor_9dof.h" // For ROS Message
#include <unistd.h>
#include <string>
#include <memory>

std::unique_ptr <InertialSensor> get_inertial_sensor( std::string sensor_name)
{
    if (sensor_name == "mpu") {
        printf("Selected: MPU9250\n");
        auto ptr = std::unique_ptr <InertialSensor>{ new MPU9250() };
        return ptr;
    } else {
        return NULL;
    }
}

int main(int argc, char **argv) {
  // Init for ROS------------------------------------------------------------------------------------------------------
  ros::init(argc, argv, "accel_gyro_mag"); // ROS初期化(ノード名を"accel_gryo_mag"に設定)
  ros::NodeHandle nh; // ノードハンドルの生成
  ros::Publisher pub = nh.advertise<accel_gyro_mag::sensor_9dof>("Sensor9Dof_data", 1000);   //Publisherとしての定義
  ros::Rate rate(1); // 周期設定(1Hzに設定)
  
  // Init for Navio-----------------------------------------------------------------------------------------------------
    if (check_apm()) {
        return 1;
    }
    //MPU Sensor----------------------------------------------------------
    auto mpu_sensor   = get_inertial_sensor("mpu");
    if (!mpu_sensor->probe()) {
        printf("MPU Sensor not enabled\n");
        return EXIT_FAILURE;
    }
    mpu_sensor->initialize();
    accel_gyro_mag::sensor_9dof mpu_data;     // accel_gyro_mag::sensor_9dof型のオブジェクトを定義 
    
    float ax, ay, az;
    float gx, gy, gz;
    float mx, my, mz;
  
   
  while(ros::ok()) { // Ctrl+Cが押されるまで繰り返し
    // Get Sensor data-------------------------------------------------------------------------------------
    mpu_sensor->update();
    mpu_sensor->read_accelerometer(&ax, &ay, &az);
    mpu_sensor->read_gyroscope(&gx, &gy, &gz);
    mpu_sensor->read_magnetometer(&mx, &my, &mz);
    
    // Publisherへのデータ格納-------------------------------------------------------------------------
    mpu_data.acc.x = ax; mpu_data.acc.y = ay; mpu_data.acc.z = az; 
    mpu_data.gyro.x = gx; mpu_data.gyro.y = gy; mpu_data.gyro.z = gz; 
    mpu_data.mag.x = mx; mpu_data.mag.y = my; mpu_data.mag.z = mz; 
    mpu_data.temperature = 1.1; //センサ値の代入が必要
    pub.publish(mpu_data);
    ros::spinOnce();

    printf("Acc: %+7.3f %+7.3f %+7.3f  ", ax, ay, az);
    printf("Gyr: %+8.3f %+8.3f %+8.3f  ", gx, gy, gz);
    printf("Mag: %+7.3f %+7.3f %+7.3f\n", mx, my, mz);
    //ROS_INFO_STREAM("Acc: %+7.3f, %+7.3f, %+7.3f", ax, ay, az);
    //ROS_INFO_STREAM("Acc:", ax, ",", ay, ",", az); 
    //ROS_INFO_STREAM("Gyr:", gx, ",", gy, ",",  gz); 
    //ROS_INFO_STREAM("Mag:", mx, ",", my, ",", mz); 
  
    rate.sleep(); // 指定された周期待ち(ループ内の他処理時間を引いた時間分を待つ)
  }
}

実行

ROSマスターの起動
$ roscore

ROSプログラムの実行
$ rosrun accel_gyro_mag accel_gyro_mag

ROSトピックの確認
$ rostopic echo /Sensor9Dof_data

ROSプログラムの停止
Ctl+C or $ rosnode kill accel_gyro_mag

【備忘録】ROS上でORB-SLAM2サンプルプログラムの実行

2. 事前準備(インストール、必要ファイル等)

詳細は、https://github.com/ayushgaud/ORB_SLAM2を参照のこと
1) Pangolinのインストール
 ・https://github.com/stevenlovegrove/Pangolin
2) OpenCVのインストール
 ・https://opencv.org/
3) Eigen3のインストール
 ・http://eigen.tuxfamily.org/index.php?title=Main_Page
4) BLASLAPACKのインストール

sudo apt-get install libblas-dev
sudo apt-get install liblapack-dev

5) DBoW2とg2oのインストール
 ・https://github.com/dorian3d/DBoW2
 ・https://github.com/RainerKuemmerle/g2o
6) ROSのインストール

git clone https://github.com/raulmur/ORB_SLAM2.git ORB_SLAM2
cd ORB_SLAM2
chmod +x build.sh
./build.sh

7) ORB-SLAM2のROS用コードのビルド

gedit ~/.bashrcを開き、下記コードを最終行に追加
export ROS_PACKAGE_PATH=${ROS_PACKAGE_PATH}:(ORB_SLAM2ディレクトリへのパス)/ORB_SLAM2/Examples/ROS
cd  ~
source .bashrc
cd (ORB_SLAM2ディレクトリへのパス)/ORB_SLAM2/Examples/ROS/ORB_SLAM2
mkdir build
cd build
cmake .. -DROS_BUILD_TYPE=Release
make -j

3. ORB-SLAM2実行の手順

$roscose
$rosparam load ros_set_camera.yaml
($rosparam set /usb_cam/pixel_format yuyv)
($rosparam set /usb_cam/video_device /dev/video1)
$rosrun usb_cam usb_cam_node
$rosrun ORB_SLAM2 Mono PATH_TO_VOCABULARY(Vocabulary/ORBvoc.txt) PATH_TO_SETTINGS_FILE(logicool_B910.yaml) /camera/image_raw:=/usb_cam/image_raw

4. ORB-SLAM用画像データの収集方法

$ rosbag record /usb_cam/image_raw
$ rosbag play (生成されたファイル名).bag

【備忘録】Navio2取得データをROSへ出力

1. 実行結果

 ROS上に出力されたIMUデータ
 f:id:nocchi2017:20180507052015p:plain

2. 手順

1) Raspberry PIにログイン

$ ssh pi@navio.local
初期ユーザー名:pi
初期パスワード:raspberry

2) ROSの起動

pi@navio: ~ $ roscore

3) Navio2(Ardupilot)の設定

pi@navio: ~ $ wget http://firmware.eu.ardupilot.org/Copter/stable/navio2/arducopter
pi@navio: ~ $ chmod +x arducopter
pi@navio: ~ $ sudo nano /etc/default/arducopter

ファイルを開いた後、IPアドレス及びポートを次の通りに変更
TELEM1="-A udp:127.0.0.1:14650"

pi@navio: ~ $ sudo systemctl start arducopter
pi@navio: ~ $ sudo systemctl enable arducopter

3) ROSノードの設定

pi@navio: ~ $ rosrun mavros mavros_node \
       _fcu_url:=udp://:14650@ \
       _gcs_url:=udp://:14551@192.168.1.189:14550
pi@navio: ~ $ rosservice call /mavros/set_stream_rate 0 10 1
pi@navio: ~ $ rostopic echo /mavros/imu/data

⇒実行結果が表示される

3. 参考

1) Navio2
 https://docs.emlid.com/navio2/common/dev/ros/
2) tmux
・コマンド入力後"$ tmux new -s session-name"にエラー発生"tmux: incalid LC_ALL, LC_CTYPE or LANG"
  https://github.com/GameServerManagers/LinuxGSM/issues/817
  https://webkaru.net/linux/debian-locales/
 ・tmuxの操作方法(Ctl+bとは?、設定ファイル)
  https://teratail.com/questions/15200
  https://github.com/tmux/tmux/issues/959
  http://kanjuku-tomato.blogspot.be/2014/02/tmux.html
  

4. 今後やりたいこと

 ・ROS上に書き出したデータにPC上からアクセスし、データを読み込む 


最後まで読んで頂き、ありがとうございました。

【備忘録】openMVGとopenMVSを用いた3次元復元

1. 3次元復元結果

f:id:nocchi2017:20180429221421p:plain

2. 事前準備(インストール、必要ファイル等)

1) openMVGのインストール

GitHub - openMVG/openMVG: open Multiple View Geometry library. Basis for 3D computer vision and Structure from Motion.

2) openMVSのインストール

Building · cdcseacave/openMVS Wiki · GitHub
Usage · cdcseacave/openMVS Wiki · GitHub


3) MeshLabのインストール

 $ sudo apt-get update

 $ sudo apt-get upgrade

 $ sudo apt-get install meshlab

4) 撮影カメラのセンササイズを準備

 ・データベースに含まれている場合:追加作業不要

 ・データベースに含まれていない場合:センササイズを調査しデータベースへの追加が必要

 [データベース場所]

 ・・・/openMVG/src/openMVG/exif/sensor_width_database/sensor_width_camera_database.txt

openMVG/sensor_width_camera_database.txt at master · openMVG/openMVG · GitHub


    ※iPhone SEはデータベースに含まれていなかったため、下記サイトを参考に4.8を採用

Apple iPhone SE - Specifications


 

3. 3次元復元の手順

1) 3次元復元用の画像データを用意

    ・/MyImg・・・画像ファイル保管先

2) 下記Phythonコードを実行

 ・$ python 180429_3D_Reconstruction_by_openMVG_and_openMVS.py ・・・/MyImg・・・/MyImg/Output3D

 [Pythonコード要修正箇所]

 a) openMVGをインストールした場所の絶対パス

 b) openMVGをインストールした場所の絶対パス

180429_3D_Reconstruction_by_openMVG_and_openMVS.py(下記コードを参考に追記)
openMVG/SfM_SequentialPipeline.py.in at master · openMVG/openMVG · GitHub

OPENMVG_SFM_BIN = "/home/(a:要変更)/openMVG/openMVG_Build/Linux-x86_64-RELEASE"

# Indicate the openMVG camera sensor width directory
CAMERA_SENSOR_WIDTH_DIRECTORY = "/home/(b:要変更)/openMVG/src/software/SfM" + "/../../openMVG/exif/sensor_width_database"

import commands
import os
import subprocess
import sys

if len(sys.argv) < 3:
    print ("Usage %s image_dir output_dir" % sys.argv[0])
    sys.exit(1)

input_dir = sys.argv[1]
output_dir = sys.argv[2]
matches_dir = os.path.join(output_dir, "matches")
reconstruction_dir = os.path.join(output_dir, "reconstruction_sequential")
camera_file_params = os.path.join(CAMERA_SENSOR_WIDTH_DIRECTORY, "sensor_width_camera_database.txt")

print ("Using input dir  : ", input_dir)
print ("      output_dir : ", output_dir)

# Create the ouput/matches folder if not present
if not os.path.exists(output_dir):
  os.mkdir(output_dir)
if not os.path.exists(matches_dir):
  os.mkdir(matches_dir)

print ("1. Intrinsics analysis")
pIntrisics = subprocess.Popen( [os.path.join(OPENMVG_SFM_BIN, "openMVG_main_SfMInit_ImageListing"),  "-i", input_dir, "-o", matches_dir, "-d", camera_file_params] )
pIntrisics.wait()

print ("2. Compute features")
pFeatures = subprocess.Popen( [os.path.join(OPENMVG_SFM_BIN, "openMVG_main_ComputeFeatures"),  "-i", matches_dir+"/sfm_data.json", "-o", matches_dir, "-m", "SIFT"] )
pFeatures.wait()

print ("3. Compute matches")
pMatches = subprocess.Popen( [os.path.join(OPENMVG_SFM_BIN, "openMVG_main_ComputeMatches"),  "-i", matches_dir+"/sfm_data.json", "-o", matches_dir] )
pMatches.wait()

# Create the reconstruction if not present
if not os.path.exists(reconstruction_dir):
    os.mkdir(reconstruction_dir)

print ("4. Do Sequential/Incremental reconstruction")
pRecons = subprocess.Popen( [os.path.join(OPENMVG_SFM_BIN, "openMVG_main_IncrementalSfM"),  "-i", matches_dir+"/sfm_data.json", "-m", matches_dir, "-o", reconstruction_dir] )
pRecons.wait()

print ("5. Colorize Structure")
pRecons = subprocess.Popen( [os.path.join(OPENMVG_SFM_BIN, "openMVG_main_ComputeSfM_DataColor"),  "-i", reconstruction_dir+"/sfm_data.bin", "-o", os.path.join(reconstruction_dir,"colorized.ply")] )
pRecons.wait()

# optional, compute final valid structure from the known camera poses
print ("6. Structure from Known Poses (robust triangulation)")
pRecons = subprocess.Popen( [os.path.join(OPENMVG_SFM_BIN, "openMVG_main_ComputeStructureFromKnownPoses"),  "-i", reconstruction_dir+"/sfm_data.bin", "-m", matches_dir, "-f", os.path.join(matches_dir, "matches.f.bin"), "-o", os.path.join(reconstruction_dir,"robust.bin")] )
pRecons.wait()

pRecons = subprocess.Popen( [os.path.join(OPENMVG_SFM_BIN, "openMVG_main_ComputeSfM_DataColor"),  "-i", reconstruction_dir+"/robust.bin", "-o", os.path.join(reconstruction_dir,"robust_colorized.ply")] )
pRecons.wait()

# Use function of OpenMVS
pConvert = subprocess.Popen( ["openMVG_main_openMVG2openMVS",  "-i", reconstruction_dir+"/sfm_data.bin", "-o", os.path.join(reconstruction_dir,"scene.mvs")] )
pConvert.wait()

pDensify = subprocess.Popen( ["DensifyPointCloud", os.path.join(reconstruction_dir,"scene.mvs")] )
pDensify.wait()

pMesh = subprocess.Popen( ["ReconstructMesh", os.path.join(reconstruction_dir,"scene_dense.mvs")] )
pMesh.wait()

pRefine = subprocess.Popen( ["RefineMesh", os.path.join(reconstruction_dir,"scene_mesh.mvs")] )
pRefine.wait()

pTexture = subprocess.Popen( ["TextureMesh", os.path.join(reconstruction_dir,"scene_dense_mesh.mvs")] )
pTexture.wait()

最後まで読んで頂き、ありがとうございました。