空飛ぶロボットのつくりかた

ロボットをつくるために必要な技術をまとめます。ロボットの未来についても考えたりします。

ROS x ビデオカメラで遊んでみる

やりたいこと

ROSでUSBカメラを使ってみる

www.logicool.co.jp

教科書

ROSプログラミング | 森北出版株式会社

ライセンス

  <maintainer email="fei@kanto-gakuin.ac.jp">Fei Qian</maintainer>
  <license>BSD</license>
  <author>Fei Qian</author>

環境設定

  1. sudo apto-get install ros-kinetic-usb-cam

  2. sudo apt-get install ros-kinetico-image-pipeline

設定

usb_camについて詳細: usb_cam - ROS Wiki

pixel_format

RGB データと YUV データについて:  YUVは、Yは輝度、Uは輝度と青の差、Vは輝度と赤の差

  1. http://www.eizo.co.jp/support/db/files/technical_information/2010/WP10-009.pdf

👆超わかりやすい

コーデックについて:

  1. 動画コーデックの種類と違い(H.264・VP9・MPEG・Xvid・DivX・WMV等)【比較】

  2. 色空間:RGBとYUVの違い - ぼくんちのTV 別館

フォーマットの設定と受信、確認

$ rosparam set /usb_cam/pixel_format mjpeg

$ rosrun usb_cam usb_cam_node &

$ rosrun image_view image_view image:=/usb_cam/image_raw

この他に

$ rqtのpluginsのvisualizationから見ることもできる

$ rqt_image_viewでも見れる

<launch>
  <node name="usb_cam" pkg="usb_cam" type="usb_cam_node" output="screen" >
    <param name="video_device" value="/dev/video0" />
    <param name="camera_frame_id" value="usb_cam" />
  </node>
  <node name="image_view" pkg="image_view" type="image_view" respawn="false" output="screen" required="true">
    <remap from="image" to="/usb_cam/image_raw"/>
    <param name="autosize" value="true" />
  </node>
</launch>

[ WARN] [1519217872.252076062]: Camera calibration file /home/takuya-usb/.ros/camera_info/head_camera.yaml not found.

カメラキャリブレーション

f:id:robonchu:20180126150406p:plain

参考: カメラ キャリブレーションとは - MATLAB & Simulink - MathWorks 日本

f:id:robonchu:20180221221301p:plain

  1. チェッカーボードをダウンロード

  2. キャリブレーション

    • rosrun usb_cam usb_cam_node

    • rosrun camera_calibration cameracalibrator.py --size 8x6 --square 0.026 image:=/usb_cam/image_raw

  3. キャリブデータの抽出

    1. cd /tmp

    2. tar zxvf calibrationdata.tar.gz

    3. cp ost.txt ost.ini

    4. rosrun camera_calibration_parsers convert ost.ini head_camera.yaml

    5. head_camera.yamlのcamera_nameをhead_cameraに変更

    6. head_camera.yamlを~/.ros/camera.infoにコピー

ビデオストリームの配布と購読

参考:image_transport - ROS Wiki

image_transport

image transportを用いると、イメージを圧縮して送ることにより、ビデオストリームを転送する際の帯域への圧迫を緩和することができる。

  • pythonはimage_transportをサポートしていないため、以下のようにrepublishを用いる。

    • $ republish [in_transport] in:=<in_base_topic> [out_transport] out:=<out_base_topic>
  • cppでは#include <image_transport/image_transport.h>で使用可能

参考: 【ROS】画像圧縮によってフレームレートを向上させるパッケージの紹介 – MPRG Robot Think Tanks

image_transportでトピック配布

以下の2つのメソッドを提供する

  1. ImageTransportのadvertise

  2. ImageTransportのadvertiseCamera

  • image_transportのpublisherは単一トピック配布とは異なり、複数の独立したトピックを同時に配布することができる。

  • compressedで別のトピックで配布するなら、トピック名は<ベーストピック名>/compressedとなる。

  • このcompressedのような補助機能をサブトピックという。

$rosmsg show sensor_msgs/Image

std_msgs/Header header
  uint32 seq
  time stamp
  string frame_id
uint32 height
uint32 width
string encoding
uint8 is_bigendian
uint32 step
uint8[] data
image_transportが提供しているプラグイン
  1. image_transport ("raw") - The default transport, sending sensor_msgs/Image through ROS.

  2. compressed_image_transport ("compressed") - JPEG or PNG image compression.

  3. theora_image_transport ("theora") - Streaming video using the Theora codec.

  • CameraPublisherでは上記トピックに加えて、base_topic/camera_infoも利用することができる

  • Theora(セオラ)とは: オープンな非可逆の動画圧縮ファイルフォーマット、または、そのコーデックである。

  • PNGとは: Portable Network Graphics

  • JPEGとは: Joint Photographic Experts Group

参考:

Theoraコーデックを使ってみた① [インストール] - It’s now or never

どうやって使い分けるべきなのか。JPG・PNG・GIFの違いをまとめてみました。 | ギズモード・ジャパン

image_transportの使い方

image_transport/Tutorials/WritingNewTransport - ROS Wiki

購読する時はベーストピック名を使用する。

サブトピック名は以下のようにパラメータでセットする。

  • rosparam set base_topic/image_transport [raw | compressed | theora]

キーフレームとは: キーフレームとは?Iフレーム・Pフレーム・Bフレームの違い【GOP】

参考:

camera_calibration - ROS Wiki

cv_bridge

ROSではOpenCVを簡単に使えるようなインターフェースcv_bridgeを提供している。

  • ROSのストリームタイプ: sensor_msgs/Image

  • OpenCVのタイプ: Intel image processing library

f:id:robonchu:20180126154843p:plain

  • #include <cv_bridge/cv_bridge.h>とすれば使用可能。

イメージ購読プログラム

#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <cv_bridge/cv_bridge.h>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>

//#include <opencv2/highgui/highgui.hpp>


void imageCallback(const sensor_msgs::ImageConstPtr& msg)
{
    try {
        cv::imshow("Image_Subscriber", cv_bridge::toCvShare(msg, "bgr8")->image);
        cv::waitKey(30);
    }
    catch (cv_bridge::Exception& e) {
        ROS_ERROR("Could not convert from '%s' to 'bgr8'.", msg->encoding.c_str());
    }
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "image_subscriber");
    ros::NodeHandle nh;
    cv::namedWindow("Image_Subscriber");
    cv::startWindowThread();

    image_transport::ImageTransport it(nh);
    image_transport::Subscriber sub = it.subscribe("camera_raw", 1, imageCallback);

    ros::spin();
    cv::destroyWindow("camera_view");

    return EXIT_SUCCESS;
}

CMake

参考: ja/catkin/CMakeLists.txt - ROS Wiki

cmake_minimum_required(VERSION 2.8.3)
project(my_camera)

find_package(catkin REQUIRED COMPONENTS
  roscpp
  std_msgs
  cv_bridge
  image_transport
)

catkin_package(
  CATKIN_DEPENDS roscpp std_msgs cv_bridge image_transport
)

find_package(OpenCV)

include_directories(
  include
  ${catkin_INCLUDE_DIRS}
  ${OpenCV_INCLUDE_DIRS}
)

add_executable(image_sub src/image_subscriber.cpp)
target_link_libraries(image_sub
  ${catkin_LIBRARIES}
  ${OpenCV_LIBRARIES}
)

参考: ja/catkin/CMakeLists.txt - ROS Wiki

pakage.xml

  <buildtool_depend>catkin</buildtool_depend>

  <build_depend>roscpp</build_depend>
  <build_depend>std_msgs</build_depend>
  <build_depend>image_transport</build_depend>
  <build_depend>cv_bridge</build_depend>

  <run_depend>roscpp</run_depend>
  <run_depend>std_msgs</run_depend>
  <run_depend>image_transport</run_depend>
  <run_depend>cv_bridge</run_depend>

イメージ配布プログラム

以下でカメラの各パラメータをチェック

  • $ v4l2-ctl -d /dev/video0 --list-formats-ext
#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <cv_bridge/cv_bridge.h>
#include <sensor_msgs/image_encodings.h>
#include <opencv2/highgui/highgui.hpp>

int main( int argc, char **argv )
{
    ros::init( argc, argv, "image_publisher" );
    ros::NodeHandle nh;

    // Open camera with CAMERA_INDEX (webcam is typically #0).
    const int CAMERA_INDEX = 0;
    cv::VideoCapture capture( CAMERA_INDEX );
    if( not capture.isOpened() ) {
        ROS_INFO_STREAM("Failed to open camera with index " << CAMERA_INDEX << "!");
        ros::shutdown();
    }
    // Set the parameters using the results of executing the following command:
    //   v4l2-ctl -d /dev/video0 --list-formats-ext
    capture.set(CV_CAP_PROP_FRAME_WIDTH, 640);
        capture.set(CV_CAP_PROP_FRAME_HEIGHT, 480);
        capture.set(CV_CAP_PROP_FPS, 30);
    //double width = capture.get(CV_CAP_PROP_FRAME_WIDTH);
    //double height = capture.get(CV_CAP_PROP_FRAME_HEIGHT);
    //ROS_DEBUG_STREAM("width: " << width << ", height: " << height);

    image_transport::ImageTransport it(nh);
    image_transport::Publisher pub_image = it.advertise( "camera_raw", 1 );

    // for debug
    cv::namedWindow("Image_Publisher", CV_WINDOW_AUTOSIZE | CV_WINDOW_FREERATIO);

    cv_bridge::CvImagePtr frame = boost::make_shared< cv_bridge::CvImage >();
    frame->encoding = sensor_msgs::image_encodings::BGR8;
    while( ros::ok() ) {
        // for debug
        cv::Mat image;
        capture >> image;
        cv::imshow("Image_Publisher", image);
        //

        capture >> frame->image;
        if( frame->image.empty() ) {
            ROS_ERROR_STREAM( "Failed to capture frame!" );
            ros::shutdown();
        }
        frame->header.stamp = ros::Time::now();
        pub_image.publish( frame->toImageMsg() );

        if(cv::waitKey(30) >= 0) break;
        ros::spinOnce();
    }

    capture.release();
    return EXIT_SUCCESS;
}

launch

<launch>
  <param name="pixel_format" value="mjpeg" />
  <node name="image_publisher"  pkg="my_camera" type="image_pub"  output="screen"/>
  <node name="image_subscriber" pkg="my_camera" type="image_sub" output="screen"/>
</launch>

f:id:robonchu:20180221230211p:plain

$ rostopic list

/camera_raw
/camera_raw/compressed
/camera_raw/compressed/parameter_descriptions
/camera_raw/compressed/parameter_updates
/camera_raw/compressedDepth
/camera_raw/compressedDepth/parameter_descriptions
/camera_raw/compressedDepth/parameter_updates
/camera_raw/theora
/camera_raw/theora/parameter_descriptions
/camera_raw/theora/parameter_updates
/rosout
/rosout_agg

カメライメージ配布プログラム

#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <cv_bridge/cv_bridge.h>
#include <sensor_msgs/image_encodings.h>
#include <opencv2/highgui/highgui.hpp>

int main( int argc, char **argv )
{
    ros::init( argc, argv, "image_Camerapublisher" );
    ros::NodeHandle n;

    // Open camera with CAMERA_INDEX (webcam is typically #0).
    const int CAMERA_INDEX = 0;
    cv::VideoCapture capture( CAMERA_INDEX );
    if( not capture.isOpened() ) {
        ROS_INFO_STREAM("Failed to open camera with index " << CAMERA_INDEX << "!");
        ros::shutdown();
    }

    // Set the parameters using the results of executing the following command:
    //   v4l2-ctl -d /dev/video0 --list-formats-ext
    capture.set(CV_CAP_PROP_FRAME_WIDTH, 640);
        capture.set(CV_CAP_PROP_FRAME_HEIGHT, 480);
        capture.set(CV_CAP_PROP_FPS, 30);

    image_transport::ImageTransport it( n );
    image_transport::CameraPublisher pub_image = it.advertiseCamera( "camera_raw", 1 );
    sensor_msgs::CameraInfo cam_info;

    // for debug
    //cv::namedWindow("Image_CameraPublisher", CV_WINDOW_AUTOSIZE | CV_WINDOW_FREERATIO);

    cv_bridge::CvImagePtr frame = boost::make_shared< cv_bridge::CvImage >();
    frame->encoding = sensor_msgs::image_encodings::BGR8;
    while( ros::ok() ) {
        // for debug
        //cv::Mat image;
        //capture >> image;
        //cv::imshow("Image_CameraPublisher", image);
        //

        capture >> frame->image;
        if( frame->image.empty() ) {
            ROS_ERROR_STREAM( "Failed to capture frame!" );
            ros::shutdown();
        }
        frame->header.stamp = ros::Time::now();
        cam_info.header.stamp = frame->header.stamp;
        pub_image.publish( frame->toImageMsg() , 
            sensor_msgs::CameraInfoConstPtr(new sensor_msgs::CameraInfo(cam_info)));

        if(cv::waitKey(30) >= 0) break;
        ros::spinOnce();
    }

    capture.release();
    return EXIT_SUCCESS;
}

カメライメージ購読プログラム

#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>
#include <cv_bridge/cv_bridge.h>

void imageCallback(const sensor_msgs::ImageConstPtr& msg, const sensor_msgs::CameraInfoConstPtr& info)
{
    try {
        cv::imshow("image_CameraSubscriber", cv_bridge::toCvShare(msg, "bgr8")->image);
        if(cv::waitKey(30) >= 0) 
            ros::shutdown();
    }
    catch (cv_bridge::Exception& e) {
        ROS_ERROR("Could not convert from '%s' to 'bgr8'.", msg->encoding.c_str());
    }
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "image_CameraSubscriber");
    ros::NodeHandle nh;
    cv::namedWindow("image_CameraSubscriber");
    cv::startWindowThread();

    image_transport::ImageTransport it(nh);
    image_transport::CameraSubscriber sub;

    sub = it.subscribeCamera("camera_raw", 1, imageCallback);

    ros::spin();
    cv::destroyWindow("image_CameraSubscriber");

    return EXIT_SUCCESS;
}

launch

<launch>
  <node name="camera_publisher"  pkg="my_camera" type="camera_pub"  output="screen"/>
  <node name="camera_subscriber" pkg="my_camera" type="camera_sub" 
    required="true" output="screen"/>
</launch>

f:id:robonchu:20180221231418p:plain

$ rostopic list

/camera_info
/camera_raw
/camera_raw/compressed
/camera_raw/compressed/parameter_descriptions
/camera_raw/compressed/parameter_updates
/camera_raw/compressedDepth
/camera_raw/compressedDepth/parameter_descriptions
/camera_raw/compressedDepth/parameter_updates
/camera_raw/theora
/camera_raw/theora/parameter_descriptions
/camera_raw/theora/parameter_updates
/rosout
/rosout_agg

ROS x JoyStick で遊んでみる

やりたいこと

rosでjoy stickを操作してみる

buffalo.jp

教科書

ROSプログラミング | 森北出版株式会社

ライセンス

  <maintainer email="fei@kanto-gakuin.ac.jp">Fei Qian</maintainer>
  <license>BSD</license>
  <author>Fei Qian</author>

設定手順

バイス確認

  • ls /dev/input/js*

命令の確認

  • jstest /dev/input/js0

パッケージのインストール

入力デバイスの変更

  • rosparam set joy_node/dev "dev/input/jsX" <- X:デバイス番号

実行手順

  • rosrun joy joy_node

  • rostopic list

/diagnostics
/joy
/rosout
/rosout_agg
  • rostopic info /joy
Type: sensor_msgs/Joy

Publishers: 
 * /joy_node (http://172.17.0.1:34890/)

Subscribers: None
  • rosmsg show sensor_msgs/Joy
std_msgs/Header header
  uint32 seq
  time stamp
  string frame_id
float32[] axes
int32[] buttons
  • rostopic echo joy
header: 
  seq: 1
  stamp: 
    secs: 1516022983
    nsecs: 706817308
  frame_id: ''
axes: [0.0, 1.0]
buttons: [0, 0, 0, 0, 0, 0, 0, 0]
---
header: 
  seq: 2

プログラム作成

子ガメをjoy stickで操作してみる

  • rosrun turtlesim turtlesim_node &

  • rosnode info /turtlesim

Node [/turtlesim]
Publications: 
 * /rosout [rosgraph_msgs/Log]
 * /turtle1/color_sensor [turtlesim/Color]
 * /turtle1/pose [turtlesim/Pose]

Subscriptions: 
 * /turtle1/cmd_vel [unknown type]

Services: 
 * /clear
 * /kill
 * /reset
 * /spawn
 * /turtle1/set_pen
 * /turtle1/teleport_absolute
 * /turtle1/teleport_relative
 * /turtlesim/get_loggers
 * /turtlesim/set_logger_level


contacting node http://172.17.0.1:38981/ ...
Pid: 6614
Connections:
 * topic: /rosout
    * to: /rosout
    * direction: outbound
    * transport: TCPROS
  • rostopic type /turtle1/cmd_vel
geometry_msgs/Twist
  • rosmsg show -r geometry_msgs/Twist
# This expresses velocity in free space broken into its linear and angular parts.
Vector3  linear
Vector3  angular
  • rosmsg show geometry_msgs/Twist
geometry_msgs/Vector3 linear
  float64 x
  float64 y
  float64 z
geometry_msgs/Vector3 angular
  float64 x
  float64 y
  float64 z

実装

フォルダ構成

├── CMakeLists.txt
└── my_joystick
    ├── CMakeLists.txt
    ├── launch
    │   └── my_joystick.launch
    ├── package.xml
    └── src
        └── my_joystick.cpp

my_joystick.cpp

f:id:robonchu:20180221211513p:plain

Aボタンで速直進

Bボタンで速回転

#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
#include <sensor_msgs/Joy.h>

// class definition
class TeleopTurtle
{
  public:
        //constructor
    TeleopTurtle();

  //hiding 
  private:
    void joyCallback(const sensor_msgs::Joy::ConstPtr& joy);
    ros::NodeHandle nh;
    int vel_linear, vel_angular;
    int lin_boost, ang_boost;
    double l_scale_, a_scale_;
    ros::Publisher  vel_pub_;
    ros::Subscriber joy_sub_;
};

//constructor
TeleopTurtle::TeleopTurtle(): vel_linear(1), vel_angular(0), a_scale_(3.2),l_scale_(2.0), lin_boost(0), ang_boost(1) //initialize
{
    nh.param("axis_linear"  , vel_linear , vel_linear); //set parameter
    nh.param("axis_angular" , vel_angular, vel_angular);
    nh.param("angular_boost" , ang_boost, ang_boost);
    nh.param("linear_boost" , lin_boost, lin_boost);
    nh.param("scale_angular", a_scale_, a_scale_);
    nh.param("scale_linear" , l_scale_, l_scale_);

    //subsucriber,use template
        //this pointa is object pointa
    joy_sub_ = nh.subscribe<sensor_msgs::Joy>("joy", 10, &TeleopTurtle::joyCallback, this);
    vel_pub_ = nh.advertise<geometry_msgs::Twist>("turtle1/cmd_vel", 1);
}

void TeleopTurtle::joyCallback(const sensor_msgs::Joy::ConstPtr& joy)
{
    geometry_msgs::Twist twist;
    if (joy->buttons[ang_boost] > 0){
      twist.angular.z = 2 * a_scale_*joy->axes[vel_angular];
    }else{
      twist.angular.z = a_scale_*joy->axes[vel_angular];
    }

    if (joy->buttons[lin_boost] > 0){
      twist.linear.x = 2 * l_scale_*joy->axes[vel_linear];
    }else{
      twist.linear.x = l_scale_*joy->axes[vel_linear];
    }
    
    ROS_INFO_STREAM("(" << joy->axes[vel_linear] << " " << joy->axes[vel_angular] << ")");
    vel_pub_.publish(twist);
}

int main(int argc, char** argv)
{
    ros::init(argc, argv, "teleop_turtle");
    TeleopTurtle teleop_turtle;

    ros::spin();
    return 0;
}

launch

<launch>

    <!-- Turtlesim Node -->
    <node pkg="turtlesim" type="turtlesim_node" name="sim"/>

    <!-- joy node -->
    <node pkg="joy" type="joy_node" name="teleopJoy" output="screen">
      <param name="dev" type="string" value="/dev/input/js0" />
      <param name="deadzone"          value="0.12" />
      <param name="autorepeat_rate"   value="1.0" />
      <param name="coalesce_interval" value="0.0" />
    </node>

    <!-- define buttons & Axes for fami con Pad-->
    <param name="axis_linear"   value="1"   type="int" />
    <param name="axis_angular"  value="0"   type="int" />
    <param name="linear_boost"   value="0"   type="int" />
    <param name="angular_boost"   value="1"   type="int" />
    <param name="scale_linear"  value="3.0" type="double" />
    <param name="scale_angular" value="2.0" type="double" />

    <node pkg="my_joystick" type="my_joystick" name="my_joystick" 
    launch-prefix="xterm -font r14 -bg darkblue -geometry 113x30+503+80 -e"
    output="screen" required="true" 
    />    

</launch>

CMakeList.txt

cmake_minimum_required(VERSION 2.8.3)
project(my_joystick)

find_package(catkin REQUIRED COMPONENTS
  geometry_msgs
  roscpp
  sensor_msgs
  std_msgs
)

catkin_package(
  CATKIN_DEPENDS geometry_msgs roscpp sensor_msgs std_msgs
)

include_directories(
  include
  ${catkin_INCLUDE_DIRS}
)

add_executable(my_joystick src/my_joystick.cpp)
target_link_libraries(my_joystick
  ${catkin_LIBRARIES}
)

package.xml

  <buildtool_depend>catkin</buildtool_depend>

  <build_depend>geometry_msgs</build_depend>
  <build_depend>roscpp</build_depend>
  <build_depend>sensor_msgs</build_depend>
  <build_depend>std_msgs</build_depend>
  <build_depend>turtlesim</build_depend>
  <build_depend>sensor_msgs</build_depend>
  <build_depend>joy</build_depend>

  <run_depend>geometry_msgs</run_depend>
  <run_depend>roscpp</run_depend>
  <run_depend>sensor_msgs</run_depend>
  <run_depend>std_msgs</run_depend>
  <run_depend>turtlesim</run_depend>
  <run_depend>sensor_msgs</run_depend>
  <run_depend>joy</run_depend>

systemdで処理を自動起動してみる

やりたいこと

PCを起動するときにいくつかの処理を自動起動にする

手順

注意

GUIなどを立ち上げたりエラーが出続けている場合は、正しい挙動にならないので、要注意!

その際は$ journalctl -b | lessなどでlogをチェック!!

シェルスクリプトの作成

boot_all.sh

#!/bin/sh

pass="pass"
echo $pass | sudo /home/nvidia/jetson_clocks.sh
cd '/home/nvidia/src'
/home/nvidia/openpose/bin/python /home/nvidia/src/track_face.py

systemdにservice用スクリプトを作る

  • cd /etc/systemd/system

  • emacs -nw test.service

[Unit]
Description = test service

[Service]
ExecStart = /home/nvidia/boot_all.sh
Restart = always
Type = simple

[Install]
WantedBy = multi-user.target

自動起動にserviceを登録

  • sudo systemctl enable test

disable で解除

確認

  • sudo systemctl start test

  • sudo systemctl status test

参考

クラップセンサで遊んでみる

やりたいこと

クラップセンサであそんでみる

Hand Clap Sensor VM-Clap1 - Elmwood Electronics

f:id:robonchu:20180211100212j:plain

クラップセンサを動かしてみる

Clap on 👏👏, Clap off 👏👏 - Elmwood Electronics

ここにすべてのっていた👆

f:id:robonchu:20180211100158p:plain

抜粋

/*
    Verbal Machines VM-CLAP1 sensor test
    clap twice within ¼ second to turn Arduino LED on or off

    by  scruss - 2017-06
    for Elmwood Electronics - https://elmwood.to/

    Sensor wiring:

      OUT   → Arduino Pin 2 (use INPUT_PULLUP)
      PWR   → 5V or 3.3V
      GND   → GND
*/

#define CLAPIN  2             // pin must be interrupt-capable
#define CLAP_DELAY   250      // max gap between claps to trigger

volatile boolean clap = false;                // clap detected state
boolean led_state = false;                    // LED on/off state
unsigned long clap_time, last_clap_time = 0;  // clap time records

void setup() {
  pinMode(CLAPIN, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);     // control built-in LED by clapping
  Serial.begin(9600);
  Serial.println("# Clap sensor test ...");
  attachInterrupt(                  // register Interrupt Service Routine (ISR):
    digitalPinToInterrupt(CLAPIN),  //   pin to watch for interrupt
    heard_clap,                     //   void function to call on interrupt
    FALLING                         //   trigger interrupt on HIGH → LOW change
  );
}

void loop() {
  digitalWrite(LED_BUILTIN, led_state);  // set LED based on clap status
  if (clap) {                       // we heard a clap from ISR
    clap = false;                   // make sure we don't trigger again too soon
    last_clap_time = clap_time;     // store old clap time
    clap_time = millis();           // note current clap time
    if (clap_time - last_clap_time < CLAP_DELAY) {  // if two claps heard in ¼ s:
      Serial.println("clap clap!");                 //   notify
      led_state = !led_state;                       //   and switch LED state
    }
    else {
      Serial.println("clap!");                      // notify of only one clap
    }
  }
}

void heard_clap() {
  clap = true;      // just set clap state in ISR
}

シリアルモニタで確認

f:id:robonchu:20180211100802p:plain

雑音にも強くて、いい感じ♪

PC側の実装

空ならempty、clapが発生したらclapを表示するスクリプト

スレッドにして、mainで処理をしつつ、clapが来たらQueueに値が入りmainに渡している。

#!/usr/bin/env python
# coding: utf-8

import serial
import time
import threading
from Queue import Queue, Empty

class Thread_cls(threading.Thread):
    def run(self):
        while True: 
            read_ser = ser.readline().rstrip()
            q.put(read_ser)

if __name__ == '__main__':
    print "hello serial!"
    ser = serial.Serial()
    ser.port = '/dev/ttyUSB0'
    ser.baudrate = 9600
    ser.open()
    time.sleep(3.0)

    q = Queue()

    th = Thread_cls()
    th.daemon = True
    th.start()

    while True:
        try:
            task = q.get(False)
            print task
        except Empty:
            print "empty"

マイコン x Linux のお勉強

やりたいこと

UbuntuやTX2からマイコンを操作し、いろいろなデバイスを動かしたい

TX2での準備

以下を実行し、ACMを使えるようにする必要がある

Build Kernel and ttyACM Module – NVIDIA Jetson TX2 | JetsonHacks

Arduinoの設定

  1. UbuntuにArduino IDE(1.8.5)インストール | ねこめも を参考に64bitをダウンロード

  2. sudo chmod a+rw /dev/ttyACM0

  3. ~/arduino-1.8.5/arduinoを実行

  4. ビルド -> arduinoに転送

ToDo: /dev/ttyACM0に権限をつけておく方法調査

mbedの設定

OSにあまり関係しない

  1. ブラウザで開発

  2. ビルドでできたバイナリをmbedフォルダに移動

マイコンとの通信

マイコンとUSB接続してACM経由で通信する

準備

  • pip install pyserial

PC側実装

標準入力のコマンドをマイコンに送る

注意!

  • serialのopen()してからtime.sleep(2.0)のように2秒以上いれないと通信できないことがあった! Debugの時は念の為長くとっておこう。

  • python3では文字のエンコードがpython2と異なるため、serial writeする時に<送りたい文字列>.encode()とする必要がある。

#!/usr/bin/env python
# coding: utf-8

import serial
import time

if __name__ == '__main__':
  print "hello serial!"
  ser = serial.Serial()
  ser.port = '/dev/ttyACM0'
  ser.baudrate = 9600
  ser.open()
  time.sleep(2.0)

  while(1):
    position = bytes(raw_input())
    ser.write(position)

    # for Debug 
    read_ser = ser.readline().rstrip()
    print read_ser

Arduino側実装

#include "enum.h"

const char RX_FOOT = ';';

void setup() {
  // settings
  Serial.begin(9600);
  Serial.setTimeout(1500);
}

void loop() {

  // シリアル受信
  if(Serial.available() > 0)
  {
    // 受信しきるまでのバッファ時間
    delay(5);
    // データ受信
    String buf = Serial.readStringUntil(RX_FOOT);
    Serial.println(buf);
  }
  delay(1);
}

mbed側実装

#include "mbed.h"
#include "MODSERIAL.h"
#include <list>
#include <string>
#include <cstdlib>

int main() 
{
    MODSERIAL pc(USBTX, USBRX, 8192);
    pc.baud(9600); 
    
    char data[1100];
    int data_index = 0;
  
    while(1) 
    {
        if (!pc.readable()) continue;
        char c = pc.getc();
        data[data_index++] = c;
        if (c == '\n')
        {
            data[data_index] = '\0';
            string command = data;
            pc.puts(command.c_str());
            data_index = 0;
        }
    }

mbedの遠隔リセット

ROSの基礎(通信編)

やりたいこと

ROSの復習がてら、基礎の部分で理解が浅い部分をまとめる

ピアツーピア設計方針

f:id:robonchu:20180126100735p:plain

参考:ROS/Technical Overview - ROS Wiki

「ピアツーピア」ではコンピュータさんの役割が決まっていません。 すべてのコンピュータさんが必要に応じてサーバにもなればクライアントにもなるのです。

参考:http://wa3.i-3-i.info/word1306.html

ピアアドレスの確認

$ rosnode list -a

-> http://localhost:58031/ /rosout

これによってlocalhost:58031というピアアドレスが使用されていることがわかる

XML-RPC(eXtensible Markup Language Remote Procedure Cal)

まぁ「XML-RPC」って単語が出てきたら「XML形式のデータをHTTPでやり取りするんだな~」と、お考えください。

point通信プロトコルだよ

pointXML形式のデータをやり取りするよ

pointHTTPでやり取りするよ

pointPOSTメソッドを使ってやり取りするよ

参考:http://wa3.i-3-i.info/word15419.html

POSTメソッド

まぁ「POSTメソッド」って単語が出てきたら「見えない所に隠してサーバに送るんだな~」と、お考えください。

参考:http://wa3.i-3-i.info/word1496.html

TCP/IP通信のpython実装

milestone-of-se.nesuke.com

参考:Pythonによる通信処理 - Qiita

client.py

# -*- coding:utf-8 -*-
import socket

host = "192.168.xxx.xxx" 
port = 2222 # 適当 

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #オブジェクトの作成をします

client.connect((host, port)) #これでサーバーに接続します

client.send("from robonchu") #適当なデータを送信します(届く側にわかるように)

response = client.recv(4096) #レシーブは適当な2の累乗にします(大きすぎるとダメ)

print response

server.py

# -*- coding:utf-8 -*-
import socket

host ="192.168.xxx.xxx" 
port = 2222 # クライアントと揃える

serversock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serversock.bind((host,port)) #IPとPORTを指定してバインドします
serversock.listen(10) #接続の待ち受けをします(キューの最大数を指定)

print 'Waiting for connections...'
clientsock, client_address = serversock.accept() #接続されればデータを格納

while True:
    rcvmsg = clientsock.recv(1024)
    print 'Received -> %s' % (rcvmsg)
    if rcvmsg == '':
      break
    print 'Type message...'
    s_msg = raw_input()
    if s_msg == '':
      break
    print 'Wait...'

    clientsock.sendall(s_msg) #メッセージを返します
clientsock.close()

UDP通信のpython実装

参考:Pythonでネットワークプログラミング | saito's memo

TODO

トピックの平均転送速度を求める

$ rostopic bw /turtle1/pose

トピックの配布速度を求める

$ rostopic hz /turtle1/pose

サービスの引数リストの表示

$ rosservice args /turtle1/set_pen

新しいLinux PCの設定方法まとめ (ラズパイやTX2など)

やりたいこと

新しいLinuxPCをこさえた時に、英語キーボードだったり、sshしにくかったりするので、最低限必要な設定をまとめる。

今後ガンガン追記していく予定

ラズパイ

  1. SSH

    • raspi-configでsshの設定をonに
  2. キーボード:

TX2

  1. キーボード

  2. リモートデスクトップ

参考

TX2全般

リモートデスクトップ

sshでXを飛ばす方法

raspi x rosメモ

いつものまにかROSWIKIにraspiでのインストール方法書いてる。最高。

raspi3用 Ubuntu16 + rosのイメージ

raspi zerp用 rosのイメージ

参考

Installing ROS Indigo on the Raspberry Pi | Learning Robotics Using ROS

ラズパイで動くロボット「GoPiGo」をつかって遠隔見守りロボットを作ろう(1) 開発準備編 (1/6):CodeZine(コードジン)