Raspberry Pi で戦車を作る(拡張編)

ひと通り、Raspberry Pi で動く戦車ができたのでおしまい…という訳ではなくて、これを拡張/派生させていきます。RasPi 戦車の最大の目的は、

  • リモコンで動くこと
  • モーターが制御できること

の2点なので、有線ケーブル(USBケーブル接続など)は対象外です。あと、戦車自体を「動かす」ことが私の目的なので、Raspberry Pi に外部電源を乗せています。これは、拡張先である Arduino なども同じです。メインチップに供給する電源とは違うところから取ってくるってのが必須。

image

以前、LEGO Mindstorms EV3 で作った時のノウハウを使っているので、PS3 Dualshock3 のコントローラーで Bluetooth 接続させています。モータードライバは手早く済まそうと思って購入してしまいました(本当は自作する予定だった)。制御フローでいえば、5つのブロックに分かれているわけで、これを切り替えることで、拡張できますよね。というか、もともとそのつもりでプログラムの作っています。

ざっと、今考えているところを書き出すとこれだけあります。

image

Bluetooth 付きのコントローラーとして Wii コントローラーを使う方法があります。また、Android のスマートフォンを使って RFCOMM で繋ぐ方法もありますよね。通信方法も、Bluetooth よりも WiFi のほうが手軽です。更に、赤外線を使うと非常に安くできます。Dualshock3 が 4,000円を超えますが、赤外線だと500円ぐらいでできそうです。

また、本体は、Raspberry Pi で動かしてますが、Arduino を使ってもできるハズです。これもシリアル通信にすれば結構いろんなことができるし、赤外線通信も可能です。ライントレーサーのような自動で動く仕組みでもいいんですが、ここはコントローラーで動かしていきたいですね。

モータ制御のところも、現在の ON/OFF だけではなくて、PWM 制御にすれば高速/低速で動かせます。このあたりはラジコンカーの制御も合わせると色々種類があります。また、コントローラ自体が多様(スマートフォンからの動かせるし)なので、従来のラジコンよりも色々な操作をひとつのコントローラに詰め込めます。動力のモーターと、サーボを同時に動かすこともできるだろうし、LEDや音を鳴らすこともできるでしょう。そのあたりのギミックが無線でもやりやすくなっています。

Raspberry Pi 2 自体は 5,000円近くするので、数台用意して実験するのは大変ですが、Arduino のボードを使って半額ぐらいで済ますことも可能です。更に言えば、Arduino で使っているチップを取り出して、必要な部分だけ基板で組めばもっと安くできそうです。このあたりはもうちょっと探っていきたいところです。

Arduinoブートローダ書き込み済みATMega328P-PU(内蔵CR発振8MHz版) – スイッチサイエンス
https://www.switch-science.com/catalog/1414/
Arduinoブートローダ書き込み済みATMega328P-PU(内蔵CR発振8MHz版)

こんな感じで、ブートローダーが書き込んだものが 400円弱で手に入るのだから、モーター制御だけの専用基板に組み込めば、かなり小さく&安くできそうな感じです。そうすると、1台だけじゃなくて、5,6台は一気に作れそうですよね。5台ぐらいあると、個々に赤外線センサーや距離センサーを乗せて、相互に自動で動かしたりかなりおもしろいことができそうです。まあ、1万円/台で作ってもいいんでしょうが、そこは創意工夫と頭を使って製作費を下げる努力をするということで。

カテゴリー: RaspberryPi | Raspberry Pi で戦車を作る(拡張編) はコメントを受け付けていません

Raspberry Pi で戦車を作る(リモコン編)

ゲームコントローラーで戦車のモーターを制御するということで、以下の2つをくっつけます。

Raspberry Pi で戦車を作る(Bluetooth/PS3 Dualshock3編)
http://www.moonmile.net/blog/archives/6898
Raspberry Pi で戦車を作る(モーターシールド編)
http://www.moonmile.net/blog/archives/6910

ジョイスティックをイベント化する

https://github.com/moonmile/RaspiTank/blob/master/RaspiRobot/BPiJoystick.cs

– ジョイスティックの変更イベント OnChangedJoystick
– ボタン状態の変更イベント OnChangedButton

を作成します。ジョイスティックの場合は、XY軸がちょっとずれただけでイベントが発生するので、ボタンのON/OFFとは別にイベントを発生させます。

protected void OnLoop()
{
    while (this.IsLoop)
    {
        js_event js;
        js.time = _br.ReadUInt32();
        js.value = _br.ReadInt16();
        js.type = _br.ReadByte();
        js.number = _br.ReadByte();
        // Console.WriteLine(js.ToString());

		bool changedJoystick = false;
		bool changedButton = false;
        if (BPiJoystickData.IsAxis(js.type))
        {
            if (this.Joystick.GetAxisValue(js.number) != js.value)
            {
                Joystick.SetValue(js.type, js.number, js.value);
				changedJoystick = true;
            }
        }
        else if (BPiJoystickData.IsButton(js.type))
        {
            if (Joystick.GetButtonValue(js.number) != (js.value != 0))
            {
                Joystick.SetValue(js.type, js.number, js.value);
				changedButton = true;
            }
        }
        // filter
        if (BPiJoystickData.IsAxis(js.type) && js.number >= 6) changedJoystick = false;
        if (BPiJoystickData.IsButton(js.type) && js.number >= 16) changedButton = false;

        // raise value change event
		if (changedJoystick)
        {
            if (OnChangedJoystick != null)
            {
				OnChangedJoystick(this, new JoystickEventArgs()
                {
                    Joystick = this.Joystick,
                    Raw = js
                });
            }
            // Console.Write("{0} {1} {2} ", js.type, js.number, js.value );
            // Console.WriteLine(j.ToString());
        }
		if (changedButton)
		{
			if (OnChangedButton != null)
			{
				OnChangedButton(this, new JoystickEventArgs()
				{
					Joystick = this.Joystick,
					Raw = js
				});
			}
			// Console.Write("{0} {1} {2} ", js.type, js.number, js.value );
			// Console.WriteLine(j.ToString());
		}
    }
    this._br.Close();
}

コントローラーの動きでモーターを制御する

アナログジョイスティックやボタンを押したときのモーター制御やLED点灯を行います。

https://github.com/moonmile/RaspiTank/blob/master/RaspiRobot/Program.cs

class Program
{
	static void Main(string[] args)
	{
		new Program().main();
	}

	BPiJoystick js;
	RaspiRobotNet.RaspiRobot robot;
		
	public void main()
	{
		robot = new RaspiRobotNet.RaspiRobot();

		js = new BPiJoystick();
		js.OnChangedJoystick += js_OnChangedJoystick;
		js.OnChangedButton += js_OnChangedButton;
		js.Setup();
		Console.WriteLine("any key is stop.");
		var key = Console.ReadKey();
	}

	/// <summary>
	/// ボタンが変更された
	/// </summary>
	/// <param name="arg1"></param>
	/// <param name="arg2"></param>
	void js_OnChangedButton(object sender, JoystickEventArgs e)
	{
		if (e.Joystick.Up == true) robot.Forward();
		else if (e.Joystick.Down == true) robot.Back();
		else if (e.Joystick.Left == true) robot.Left();
		else if (e.Joystick.Right == true) robot.Right();
		else
		{
			if (e.Joystick.Up == false && e.Joystick.Down == false &&
				e.Joystick.Left == false && e.Joystick.Right == false)
				robot.Stop();
		}

		/// LED1,2 を点灯する
		robot.SetLED1( e.Joystick.Circle );
		robot.SetLED2( e.Joystick.Cross );
	}
	/// <summary>
	/// ジョイスティックを変更
	/// </summary>
	/// <param name="sender"></param>
	/// <param name="e"></param>
	void js_OnChangedJoystick(object sender, JoystickEventArgs e)
	{
		int ly = e.Joystick.LeftAxisY;
		int ry = e.Joystick.RightAxisY;
		Console.WriteLine("joystick {0} {1}", ly, ry);
		int sp1 = ly / (32767 / 200);
		int sp2 = ry / (32767 / 200);
		if (e.Joystick.Up == false && e.Joystick.Down == false &&
			e.Joystick.Left == false && e.Joystick.Right == false)
		{
			move_bot(sp1, sp2);
		}
	}
	void move_bot(int sp1, int sp2)
	{
		if (sp1 > 0 && sp2 > 0) { robot.Forward(); }
		if (sp1 < 0 && sp2 > 0) { robot.Right(); }
		if (sp1 > 0 && sp2 < 0) { robot.Left(); }
		if (sp1 < 0 && sp2 < 0) { robot.Reverse(); }
		// 停止
		if (sp1 == 0 && sp2 == 0) { 
			robot.Stop(); 
		}
		// 右だけ動かす
		if (sp1 == 0)
		{
			if ( sp2 > 0 )
				robot.SetMotors(false, false, true, false);
			if (sp2 < 0)
				robot.SetMotors(false, false, true, true);
		}
		// 左だけ動かす
		if (sp2 == 0)
		{
			if (sp1 > 0)
				robot.SetMotors(true, false, false, false);
			if (sp1 < 0)
				robot.SetMotors(true, true, false, false);
		}
	}
}

このプログラム自体はターミナルで起動することを想定しているので、最終的には /etc/ini.d を利用して初期起動させます。WiFi のドングルが結構な電力を食っているので、これを外すだけでもかなり RasPi の動作が安定します。ただし、外側から RasPi の状態が見えなくなってしまうので、何らかのモニタリングが必要ですよね。あとで、RasPi にミニ液晶を付けて状態を監視できるようにします。

上記ではジョイスティックといくつかの○×ボタンを単独で感知していますが、同時押しとかR/Lボタンも取得ができます。ちなみに、ジョイスティックの傾きも取れるので、スマートフォンを傾けたときのような動作も可能です。このあたりは、RasPi 戦車に限らず、RasPi 自体を媒介させることで、ゲームコントローラー -> RasPi -> Windows PC というパターンが組めます。Windows 自体はゲームコントローラを認識するのですが、なぜか 8.1 から PS3 Dualshock3 は認識しないんですよね。

カテゴリー: C#, RaspberryPi | Raspberry Pi で戦車を作る(リモコン編) はコメントを受け付けていません

Raspberry Pi で戦車を作る(モーターシールド編)

Raspberry Pi用モータードライバボード(キット) – スイッチサイエンス
https://www.switch-science.com/catalog/1239/

今度は、モータードライバーを使ってモーターを動かします。今回使っているのは Raspberry Pi 用のモータードライバーシールドで、4,000円弱という RasPi が買えるかもしれません、というお値段で結構します。慣れれば自作できるようなんですが、手始めにひとつ買ってためしています。ちなみに、このキットは v2 が出ていて adafrutit などから購入できます。

RasPi Robot Board v2 by MonkMakes ID: 1940 – $29.95 : Adafruit Industries, Unique & fun DIY electronics and kits
http://www.adafruit.com/product/1940

動作させるためのサンプルコードは以下にあります。

simonmonk/raspirobotboard2
https://github.com/simonmonk/raspirobotboard2

9V(乾電池6個)を給電すると、RasPi に 5V 給電してくれるので、RasPi 本体への USB 給電がいらなくなるみたいです。私の場合は、両方に 5V ずつ給電しているので、今度試してみましょう。

サンプルコードを追っていくと

– ボード上の LED1,2 を光らせる
– ボード上の SW1,2 を ON/OFF できる
– モーター1,2 を回転/逆回転できる

ことが分かります。残念ながらスピード制御はできないので、戦車のようなキャタピラ式のものとか、ギア比を変えてアームを動かすとかに使えますね。PWM制御(パルス制御)のほうは、別途回路を組んで実験していきます。

両輪のギアーボックスを使うと、右左の車輪を別々に制御できるので、車体を右左に曲げることができます。普通の車のようにハンドル+後輪駆動みたいにしたい場合は、別途サーボ制御が必要になるのですが、キャタピラ式はこれで十分だったりします。まあ、普通のラジコンカーを作るのも今後の課題ですね。

ピンを調べる

さて、ON/OFF のピンは、サンプルの python スクリプトを見ると以下のようになっています。

https://github.com/simonmonk/raspirobotboard/blob/master/raspirobotboard.py

LEFT_GO_PIN = 17
LEFT_DIR_PIN = 4
RIGHT_GO_PIN = 10
RIGHT_DIR_PIN = 25
SW1_PIN = 11
SW2_PIN = 9
LED1_PIN = 7
LED2_PIN = 8
OC1_PIN = 22
OC2_PIN = 21

RasPi 専用のシールドなので、GPIO のピンは RasPi に合わせてあるわけですが、この GPIO の番号と Arduino のそれとを合わせてやれば、Arduino でも使うことができると思います。まあ、Arduino 専用のものを買ったほうが早かったり、自作したほうがいい気もしますが。

C# で使えるように書き直す

後で整理して git にアップしますが、python のコードを C# に書き直したのが次のものです。
内部的に

AlexSartori/RaspberryGPIOManager
https://github.com/AlexSartori/RaspberryGPIOManager

をちょっと改変して使っています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RaspberryGPIOManager;
using System.Threading;

namespace RaspiRobotNet
{
    public class RaspiRobot
    {
		const int LEFT_GO_PIN = 17;
		const int LEFT_DIR_PIN = 4;
		const int RIGHT_GO_PIN = 10;
		const int RIGHT_DIR_PIN = 25;
		const int SW1_PIN = 11;
		const int SW2_PIN = 9;
		const int LED1_PIN = 7;
		const int LED2_PIN = 8;
		const int OC1_PIN = 22;
		const int OC2_PIN = 21;

		public GPIOPinDriver LED1 { get; set; }
		public GPIOPinDriver LED2 { get; set; }
		public GPIOPinDriver OC1 { get; set; }
		public GPIOPinDriver OC2 { get; set; }
		public GPIOPinDriver LEFT_GO { get; set; }
		public GPIOPinDriver LEFT_DIR { get; set; }
		public GPIOPinDriver RIGHT_GO { get; set; }
		public GPIOPinDriver RIGHT_DIR { get; set; }

		public RaspiRobot()
		{
			this.LED1 = new GPIOPinDriver(GPIOPinDriver.Pin.GPIO7, GPIOPinDriver.GPIODirection.Out, GPIOPinDriver.GPIOState.Low);
			this.LED2 = new GPIOPinDriver(GPIOPinDriver.Pin.GPIO8, GPIOPinDriver.GPIODirection.Out, GPIOPinDriver.GPIOState.Low);
			this.OC1 = new GPIOPinDriver(GPIOPinDriver.Pin.GPIO22, GPIOPinDriver.GPIODirection.Out, GPIOPinDriver.GPIOState.Low);
			this.OC2 = new GPIOPinDriver(GPIOPinDriver.Pin.GPIO21, GPIOPinDriver.GPIODirection.Out, GPIOPinDriver.GPIOState.Low);
			this.LEFT_GO = new GPIOPinDriver(GPIOPinDriver.Pin.GPIO17, GPIOPinDriver.GPIODirection.Out, GPIOPinDriver.GPIOState.Low);
			this.LEFT_DIR = new GPIOPinDriver(GPIOPinDriver.Pin.GPIO4, GPIOPinDriver.GPIODirection.Out, GPIOPinDriver.GPIOState.Low);
			this.RIGHT_GO = new GPIOPinDriver(GPIOPinDriver.Pin.GPIO10, GPIOPinDriver.GPIODirection.Out, GPIOPinDriver.GPIOState.Low);
			this.RIGHT_DIR = new GPIOPinDriver(GPIOPinDriver.Pin.GPIO25, GPIOPinDriver.GPIODirection.Out, GPIOPinDriver.GPIOState.Low);
		}

		public void SetMotors( int left_go, int left_dir, int right_go, int right_dir )
		{
			this.SetMotors(left_go != 0, left_dir != 0, right_go != 0, right_dir != 0);
		}
		public void SetMotors( bool left_go, bool left_dir, bool right_go, bool right_dir )
		{
			this.LEFT_GO.State = left_go  ? GPIOPinDriver.GPIOState.High : GPIOPinDriver.GPIOState.Low;
			this.LEFT_DIR.State = left_dir ? GPIOPinDriver.GPIOState.High : GPIOPinDriver.GPIOState.Low;
			this.RIGHT_GO.State = right_go ? GPIOPinDriver.GPIOState.High : GPIOPinDriver.GPIOState.Low;
			this.RIGHT_DIR.State = right_dir ? GPIOPinDriver.GPIOState.High : GPIOPinDriver.GPIOState.Low;
		}

		public void Stop()
		{
			this.SetMotors(false, false, false, false);
		}
		public void Forward(float sec = 0.0f)
		{
			this.SetMotors(true, false, true, false);
			if (sec > 0.0f)
			{
				Thread.Sleep((int)(sec * 1000.0));
				this.Stop();
			}
		}
		public void Left(float sec = 0.0f)
		{
			this.SetMotors(true, false, true, true);
			if (sec > 0.0f)
			{
				Thread.Sleep((int)(sec * 1000.0));
				this.Stop();
			}
		}
		public void Right(float sec = 0.0f)
		{
			this.SetMotors(true, true, true, false);
			if (sec > 0.0f)
			{
				Thread.Sleep((int)(sec * 1000.0));
				this.Stop();
			}
		}
		public void Reverse(float sec = 0.0f)
		{
			this.SetMotors(true, true, true, true);
			if (sec > 0.0f)
			{
				Thread.Sleep((int)(sec * 1000.0));
				this.Stop();
			}
		}
		public void Back(float sec = 0.0f)
		{
			this.Reverse(sec);
		}

		public void SetLED1( bool sw ) {
			this.LED1.State = sw ? GPIOPinDriver.GPIOState.High : GPIOPinDriver.GPIOState.Low;
		}
		public void SetLED2(bool sw)
		{
			this.LED2.State = sw ? GPIOPinDriver.GPIOState.High : GPIOPinDriver.GPIOState.Low;
		}
		public void SetOC1(bool sw)
		{
			this.OC1.State = sw ? GPIOPinDriver.GPIOState.High : GPIOPinDriver.GPIOState.Low;
		}
		public void SetOC2(bool sw)
		{
			this.OC2.State = sw ? GPIOPinDriver.GPIOState.High : GPIOPinDriver.GPIOState.Low;
		}
	}
}

でもって、F# でテストコードを書いてみます。

open System
open RaspiRobotNet
open RaspberryGPIOManager

[<EntryPoint>]
let main argv = 
    printfn "Hello RaspiRobotNet"

    let robot = new RaspiRobot()
    let mutable loop = true
    while loop do
      let key = Console.ReadKey()
      match key.KeyChar with
      | 'e' | 'q' -> loop <- false
      | 'f' -> robot.Forward(1.0f)
      | 'b' -> robot.Back(1.0f)
      | 'r' -> robot.Right(1.0f)
      | 'l' -> robot.Left(1.0f)
      | 's' -> robot.Stop()
      | '1' -> robot.SetLED1( robot.LED1.State = GPIOPinDriver.GPIOState.Low )
      | '2' -> robot.SetLED2( robot.LED2.State = GPIOPinDriver.GPIOState.Low )
      | '3' -> robot.SetOC1( robot.OC1.State = GPIOPinDriver.GPIOState.Low )
      | '4' -> robot.SetOC2( robot.OC2.State = GPIOPinDriver.GPIOState.Low )
      | _ -> ()
    0 // 整数の終了コードを返します

Tera Term で接続して、WiFi 経由で制御ができます。
F# のインストールは、以下を参考にしてください。

Raspberry Pi 2 に F# をインストールする | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/6870

これで、モータードライバーの制御は作れたので、これをゲームコントローラーで制御できるようにしていきます。

カテゴリー: C#, F#, RaspberryPi | Raspberry Pi で戦車を作る(モーターシールド編) はコメントを受け付けていません

MySQL の謎なアップデータを止める方法

正月あたりから、真夜中に謎の黒い画像が出てきて、なんかやって止まるという現象が起きていました。謎な黒い画面ってのは cmd の画面で、なんか流れて1秒経たずに消えてしまうのでなんだか分からなかったんですよね。

それを昨晩、やっとこさ捕まえました。

image

どのプロセスか分からなかったのですが、

C:Program Files (x86)MySQLMySQL Installer for WindowsMySQLInstallerConsole.exe

です。MySQL の何かのアップデートのようで、検索していくと

MySQL :: MySQL Installer Guide :: 2.1 MySQL Product Catalog
http://dev.mysql.com/doc/mysql-installer/en/mysql-installer-catalog.html

なところでカタログのスケジューラが動いているとのことです。左上のアイコンを押して、MySQL Installer Options でスケジュールのチェックを外せば ok ですね。どうやら、MySQL 関連のバージョンアップデートしたかどうか毎日チェックするツールなようです。ええ、毎日チェックしたって、そうそうバージョンアップするものではないし、止めておいてもよいでしょう。

以前はなかったものなので、たぶん最新の MySQL Workbench を入れたときのような気がします。サーバー系ではチェックが必要なんでしょうが、開発用 PC で一瞬だけでるのはやめて欲しいところですね。

ちなみにコマンドプロンプトで schtasks と打っても出てきます。at コマンドってなくなったんですね、ってに今更気づきました。

image

そんな訳で MySQL のカタログスケジューラを止めると、真夜中の謎な黒い画面(cmd)が消えます。カタログ自体は手動でアップデートすればよいので、これで十分です。

カテゴリー: MySQL | MySQL の謎なアップデータを止める方法 はコメントを受け付けていません

Raspberry Pi で戦車を作る(Bluetooth/PS3 Dualshock3編)

RasPi 戦車は、PS3 Dualshock3 コントローラーで操作できるようにしてあります。スマートフォンで WiFi 経由とか Bluetooth 経由が多い中で、コントローラーを使っているのは、まあ、ラジコンと同じ風に扱いたかったからです。これだと5歳児でも動かせますからね。PS3 Dualshock3 には、アナログスティックが2つと、上下ボタンと○×ボタン4個と…色々ついているので、車輪だけじゃない制御がしやすいそうってのもあります。サーボを制御してロボットアームを動かす予定もあって、これを使っています。

が、去年買ってみてあまり汎用的ではないので、どうせならば Bluetooth 対応のコントローラーを自作したほうがいいのですが…まあ、手元にあるということで。ちなみ、PS3 のコントローラは持っているけど、PS3 本体は持っていません。PS4 も持ってません。PS4 用の DualShock4 の場合はどうなるのか?は分からないので、持っている方は試してみてください。

Bluetooth の準備

基本は、

BrickPi + LOGO Mindstorms EV3 をゲームコントローラーでコントロールする(前編) | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/5997

に書いてある手順の通りなのですが、おさらいも兼ねて、もう一度書き連ねておきます。RasPi のレポジトリも新しくなっているので。

apt-get で bluetooth 関連をインストール

sudo apt-get install bluetooth bluez-utils bluez-compat bluez-hcidump
sudo apt-get install libusb-dev libbluetooth-dev

基本は bluetooth だけでいいはずですが、後の QtSixA のために libusb-dev あたりが必要です。

/etc/init.d/bluetooth status

で、きちんと bluetooth デーモンが動いているか確認します。

lsusb

で、Bluetooth ドングルが認識できることを確認。ここでは、Bus 001 Device 004 がそれです。

QtSixA のインストール

PS3 Dualshock3 用のドライバーを入れます。

QtSixA – Browse /QtSixA 1.5.1 at SourceForge.net
http://sourceforge.net/projects/qtsixa/files/QtSixA%201.5.1/

tar -xvf QtSixA-1.5.1-src.tar.gz
cd QtSixA-1.5.1/sixad
make
sudo make install 

今の環境ではきちんとビルドできて、sixpair まで出来上がります。
中身は、bluetooth SPP を解析しているだけだから、C# で書き直すのはそんなに難しくはないと思うんですよね。これは、RasPi で直接 Bluetooth を扱うときに試してみます。

PS3 Dualshock3 とペアリングする

ゲームコントローラーを USB で RasPi につなげて

sudo sixpair

を動かします。

Bluetooth ドングルの MAC ID が表示されます。MAC ID 自体は Windows 8.1 の PC に繋いでデバイスマネージャから確認できます。

動作させる。

ペアリング自体は、最初だけなので、その後はゲームコントローラーの USB を外しても大丈夫です。確か、つなげないで直接 MAC ID を書き込む方法もあります。

sudo sixad -start &

させます。うまく起動したら、ゲームコントローラーの PS ボタンを押せば接続できます。Dualshock3 の MAC ID が表示されます。

通信情報は、/dev/input/js0 に流れてくるので、

sudo cat /dev/input/js0

接続を止める場合は、

sixad -stop

すれば大丈夫です。

コントローラーの通信を解析する

データ自体は以下の構造体で流れてきます。これをうまく読み取ってやれば、各種のボタンの判別がつきます。

public struct js_event
{
	public UInt32 time; /* event timestamp in milliseconds */
	public Int16 value; /* value */
	public byte type; /* event type */
	public byte number; /* axis/button number */
	public override string ToString()
	{
		return string.Format("time:{0} val:{1} type:{2} num:{3}", time, value, type, number);
	}
}

テストコード
https://gist.github.com/moonmile/7c44e5130617c7a76a87

ボタンの割り当てをしたのが上記になります。
このコードは以前書いたもので、ジョイスティックのチェックしかやっていませんが、今回の RasPi 戦車では上下左右ボタンにも対応させています。これは後述します。
同じものが飛んできたら捨てるような処理を入れればいいのです。

カテゴリー: RaspberryPi | Raspberry Pi で戦車を作る(Bluetooth/PS3 Dualshock3編) はコメントを受け付けていません

Raspberry Pi で戦車を作る(材料編)

モーターの制御…というか、モーターってどうやって駆動させるの?の理解に苦労しましたが、ひとまず、コントローラを使って戦車を動かすところまでが完成しました。

https://www.youtube.com/watch?v=LTFYI98lF3c

image

例のごとく Raspberry Pi が固定されていませんが、これはいずれ。土台を LEGO で組んだのですが、ギア部分ががたつくのと RasPi と電源が落ちそうになるので、これも固定式に変えないと駄目です。予定では、この上にアームを乗っけるので。

meArm を使ってサーボモーターの制御はできていたので、今度は足回りの制御です。前に進むだけならば GPIO の ON/OFF でいけるのですが、反転させないといけないので以前に買ったモーターシールドを使っています。RaspiRobot Board 自体は新しいバージョン v2 が出ていることと、もうちょっと頑張れば独自にモータードライバー DRV8830  を買って回路を組めば、もっと安く済みます。

ともあれ、戦車をリモートコントロールで動かす場合は、本体に Arudino/Netduino を使うこともできるし、通信に赤外線/Bluetooth/BLE/WiFi を選ぶことができます。これらの組み合わせは、いずれやっていく予定ですが、ひとまず手元にある/以前やったことがある Bluetooth で試しています。

材料

あとは、土台に LEGO を使っていますが、Amazon.co.jp | 楽しい工作シリーズ No.157 ユニバーサルプレート 2枚セット (70157) | ホビー 通販 を使って組み立てたほうがしっかり作れるでしょう。私ももう一台作るつもりなので、これを買う予定。

という訳で、材料が整ったのでお次は、Bluetooth のセッティングから。

カテゴリー: RaspberryPi | Raspberry Pi で戦車を作る(材料編) はコメントを受け付けていません

Windows で Raspberry Pi のバックアップを取る方法

Raspberry Pi 2 ではSDメモリカードが micro SD になりました。小さくなって飛び出し部分が少なくなった半面、micro SD を無くしやすい(苦笑)という罠が。
ただし、普通の PC とは違って、容量が小さい micro SD カードを丸ごとバックアップしておけば、他の micro SD カードにコピーできて便利です。

バックアップを取る理由

なんかやっているときに、micro SD カードが壊れる。パターンがあります、RPi の USB ケーブルをぶちっと抜いてシャットダウンしないと、壊れる可能性があります。まあ、初代から使っていますが、このパターンで壊れたことがないので、シャットダウンせずにぶちぶち切っていますが(苦笑)。あと、PC の USB ポートに繋いて試していると、PC がスリープして RPi に電源供給ができなくなってそのまま落ちる、ってのもありますけどね。micro SD メモリそのものよりも、中に入っている linux のファイルシステムが壊れる可能性もあってこれが結構面倒です。

実は、仮想環境 QEMU を PC 上で動かすためにも micro SD カードからのバックアップが必要です。VMWare とかの仮想環境の気分でスナップショット的なバックアップを取るとよいでしょう。ある程度、環境構築をしたあとにバックアップを取っておくと、同じ環境がさっくりと作れます。Win32DiskImager を使うと、SD カードの読み書きが簡単にできるので、複数の RPi 用の SD カードを作るときもさっくり作れます。
ちなみ、8GB の SD カードをバックアップしたあとに、16GB の SD カードに書き込みするということもできました。16GB のカードを RPi に差し込んで、raspi-config で SD メモリの全体を利用する設定をすれば、16GB 全てが使えるようになります。

Win32DiskImager を利用する

Win32 Disk Imager プロジェクト日本語トップページ – SourceForge.JP
http://sourceforge.jp/projects/sfnet_win32diskimager/

から Win32DiskImager をダウンロードしてインストールします。

– Read : SD カードから読み込んでイメージファイルを作成
– Write : SD カードへイメージファイルを書き込み

適当なタイミングでバックアップを取っておいて、バージョン管理をしておくとよいでしょう。
SD カードを吸い上げると 8GB のイメージファイルができて結構大きいですが、zip で圧縮すると 2.5GB 位になります。

SDカードの容量を回復する

実は、Raspberry Pi を SD カードにインストールすると、Windows から見える容量が激減しています。これを回復して Windows からすべての容量をつ開けるように SDFormatter を使います。

SD/SDHC/SDXC用 SDフォーマッター4.0 – SD Association
https://www.sdcard.org/jp/downloads/formatter_4/

起動して、フォーマットオプションボタンをクリックして「論理サイズ設定」を ON にして実行をすると、Windows から見える容量が回復します。勿論、中身はフォーマットされてクリアされるので、再初期化したいときに使ってください。

RPi の初期設定

私の環境では、以下の設定だけ行った SD カードのバックアップがあります。

– wifi設定済み
– sudo apt-get install avahi-daemon
– sudo apt-get install xrdp
– sudo apt-get install samba

wifi は、GUI で設定したものを取っておきます。

vi /etc/network/interfaces

auto lo

iface lo inet loopback
iface eth0 inet dhcp

allow-hotplug wlan0
iface wlan0 inet manual
wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf
iface default inet dhcp

sudo vi /etc/wpa_supplicant/wpa_supplicant.conf

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
        ssid="SSID名"
        psk="パスワード"
        proto=WPA
        key_mgmt=WPA-PSK
        pairwise=TKIP
        auth_alg=OPEN
}

avahi-daemon は、Windows PC から rapi.local のように名前で見えるようにするサービスです。PC 側に iTunes を入れておけば ok です(単体のライブラリもあるけど iTunes を入れるのが手軽らしい)。

xrdp は、リモートデスクトップをするためのサービスです。リモートデスクトップで表示できるので、ディスプレイが無くても大丈夫です。が、欠点として、Scratch が動きません。たぶん、Remote Desktop プロトコルに対応していないみたいです。

samba は Windwos PC からファイル共有をするためのサービス

vi /etc/samba/smb.conf

 [shared]
 path = /home/masuda
 read only = No
 guest ok  = Yes
 force user = masuda

こんな風にすると、raspishared で /home/masuda のフォルダが見えるようなります。私の場合は masuda ユーザーを作っているのですが、pi ユーザーのままにして「force user = pi」のようにするのも良いです。

こういう風にしてバックアップを取っておくと、新しい SD メモリカードを買ってきて Win32DiskImager で書き込めば、さっくりと RPi が動くという状態になります。

# 追記
ただし、各社 SD カードのセクタ数に微妙に違いがあるらしく、書き込み時にセクタ数が足りなくて書き戻せないってことがあります。セクタ数の小さい方にあわせればいいんでしょうが。まあ、同じ会社の SD カード使えばよいんですが、そのあたりは気を付けて。

カテゴリー: RaspberryPi | 1件のコメント

Raspberry Pi 2 に F# をインストールする

初代 Raspi では、メモリが 256M しかなくて、Fsharp をソースからビルドしようとすると途中でこけてしまってうまく出来上がりませんした。今回の RPi2 はメモリが 1GB あるし CPU も早くなっているので、スムースに行くのではないか?と思って試してみました。

結論から言えば、ソースからビルドができません

結論から先に言うと、git から mono と fsharp のソースコードをダウンロードして来てビルドまではできるのですが、実行ができません。おそらく現時点(2015/02/26)の mono のバージョンに F# が対応していないだけだと思うので、適切な mono のバージョンを持って来れば実行ができるハズなのですが、結構面倒そうなのでパスします(苦笑)

mono や fsharp 自体のビルドは可能です。mono は 4時間弱、F# は 1時間ちょっとぐらい掛かるみたいですが、初代 RPi の時は 12時間以上かかっていたので「現実的な」時間になっています。

– mono 4.1.0 https://github.com/mono/mono
– fsharp 3.1 https://github.com/fsharp/fsharp

こんな感じで fsharpi の実行時に例外が発生しています。

ちなみに、fsharp4 のブランチを持ってきてビルドをしようとすると、コンパイルできなくてエラーになります。

レポジトリを追加して apt-get する

Use F# on Linux | The F# Software Foundation
http://fsharp.org/use/linux/

手順自体は、F# のサイトに書いてある「Option 1: Use the F# Debian/Ubuntu packages」の通りです。Rasberry pi のレポジトリには mono のバージョンが古いのと、fsharp 自体がありません。mono 自体がちょっと気になるところですが、sudo apt-key でレポジトリを追加した後に apt-get するとすんなり通ってくれます。

一応、下にコピーをしておきます。

レポジトリの追加

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list

mono と fsharp のインストール

sudo apt-get update
sudo apt-get install mono-complete fsharp

FSharp.Core.dll が見つからなくてエラーになる

たまに(?)、fsharpi を実行しようとすると、/usr/lib/mono/4.5 等に FSharp.Core.dll が見つからない、というエラーがでます。

F# on linux (mint/ubuntu) – dll path – Stack Overflow
http://stackoverflow.com/questions/27041080/f-on-linux-mint-ubuntu-dll-path

にある通り、gac に FSharp.Core.dll をインストールし直せば ok です。タイミング的に Fsharp 3.1 と 3.1.1 が混在しているときにおこるみたいです。

sudo gacutil -i /usr/lib/cli/FSharp.Core-4.3/FSharp.Core.dll

追記 2015/02/27
どうやら、上記の方法はだめみたいです。/usr/lib/cli/fsharp/ のパスから探しているので、シンボリックリンクを作ると動きます。

cd /usr/lib/cli/fsharp/ 
sudo ln -s /usr/lib/cli/FSharp.Core-4.3/FSharp.Core.* .

ただ、この方法も変なので、fsharp のインストーラーが何処かに書き忘れているような気もするのですが、よくわからず。

apt-get できるバージョン

– mono 3.12
– fsharp 3.1

以前、インストールしたときは fsharpi で実行するときにひどく待たされていたのですが、今回は大丈夫です。ひょっとすると、gac がうまくインストールされていなかっただけなのかもしれませんが。
そんな訳で、今回はさっくりと apt-get でインストールが可能です。というかソースからビルドできないのがアレなんですが、まあ良しとしましょう(笑)。

自分は BrickPi まで動かすぞ…と、近いうちに。

カテゴリー: 開発, F#, RaspberryPi | Raspberry Pi 2 に F# をインストールする はコメントを受け付けていません

VPN が繋がらなくなったので devcon 対処する

久しぶりに VPN につなげてみたら繋がらない…ので復旧させました。その作業のメモ書きです。

色々検索すると Windows XP/7 でもある現象で、突然 VPN に繋がらなくなります。手元の環境が Windows 8.1 なので結構探しました。

現象として、VPN が接続せずに、ネットワーク接続で「プロパティ」を見ようと思っても予期せぬエラーが出てプロパティウィンドウがでません。手元の2台の PC が同時になっていたので、先日入れた Bluetooth のドライバーかと思って復旧させてみたのですが、直りませんでした。

image

いくつか調べていくと、サービスで「Remote Access Connection Manager」が動いていないことが原因なのがわかりました。

image

説明にある通り「このコンピューターからインターネットや他のリモート ネットワークへのダイヤルアップ接続と仮想プライベート ネットワーク (VPN) 接続を管理します。このサービスを無効にすると、このサービスに明示的に依存しているサービスは開始できません。」とのことで、VPN に関わっています。これを、手動で開始しようとしたのですが、なかなか起動せずに、途中で「開始中」のまま落ちてしまっています。

image

別の PC で試してみたところ、すんなり立ち上げるので、この2台の PC がおかしいようです。

デバイスマネージャを見ると、「WAN ミニポート(PPTP)」などがあるのですが、これが正しく動いていません。ドライバーの更新をしてみるものの、なにかが引っ掛かってインストールができません。というか、先の Remote Access Connection Manager(Rasman) が悪さをしてインストールできないようです。

image

レジストリ等を弄ってやる方法が定番みたいなのですが、私のところでは直りませんでした。といいますか、WAN ミニポートが削除できない状態になって、どんどん増殖してしまいます。

そんな訳で、解決策としては、

  1. 「Remote Access Connection Manager」の設定を「無効」にして、PC を再起動する。
  2. devcon install を使って、新しく WAN ポートをインストールする(たぶん、IP と PPTP とネットワークモニタだけ有効であれば通じるはずです)
  3. 古い WAN ポートを「無効」にする。
  4. 「Remote Access Connection Manager」を「手動」に変更した後で「開始」させる。ひどく、時間がかかりますが、きちんと立ち上がってくれます。
  5. 「Remote Access Connection Manager」が立ち上がった状態で、VPN を再作成する。

ということで VPN が復旧しました。壊れてしまった WAN ポートが残骸として残っていますが、どうやっても消えないので、これはこのままです。なんだかなーという感じですが、仕方がないですね。

RasMan は、おそらく VPN に接続しようとしたときに自動的に立ち上がるはずですが、うまくいかないときは、あらかじめサービスで立ち上げておきます。

devcon のインストール

Windows Driver Kit (WDK) と Debugging Tools for Windows (WinDbg) のダウンロード にある「WDK 8.1 Update (Windows 8.1、8、7 ドライバー用)」の中に、devcon.exe があります。単体で動くようなので、どこかの PC にインストールして、c:\Program Files (x86)\Windows Kits\8.1\Tools\x64  の中身をコピーしてきても動きます。

devcon のコマンド

動かすコマンドは、以下のどれか。PPTP は、MS_PptpMiniport になります。

devcon.exe install c:\Windows\inf\netavpna.inf MS_AgileVpnMiniport
devcon.exe install c:\Windows\inf\netrasa.inf MS_NdisWanIp
devcon.exe install c:\Windows\inf\netrasa.inf MS_NdisWanIpv6
devcon.exe install c:\Windows\inf\netrasa.inf MS_NdisWanBh
devcon.exe install c:\Windows\inf\netrasa.inf MS_L2tpMiniport
devcon.exe install c:\Windows\inf\netrasa.inf MS_PppoeMiniport
devcon.exe install c:\Windows\inf\netrasa.inf MS_PptpMiniport
devcon.exe install c:\Windows\inf\netsstpa.inf MS_SstpMiniport

いくつか日本語の解説記事もあるので、それを参考にしてもよいでしょう。

ちなみに devcon remove でデバイスインスタンスを指定して削除ができるハズなのですが、WAN ミニポートの類は、ことごとく失敗します。よくわからないのですが、使い捨てみたいな感じで追加していきます。番号が #1, #2 と振られるので、後ろのほうのを残して古い WAN ミニポートは「無効」にしておくといいでしょう。あれこれレジストリを探って消そうとは思ったのですが、

レジストリで削除する

Windows 7 あたりでは、レジストリで削除できたらしいのですが、手もとの Windows 8.1 ではうまくいきませんでした。参考まで

Accent :: (編集中)WANミニポート(IP)!エラー修復メモ
http://www3.kcn.ne.jp/~accent/blosxom/blosxom.cgi/internet/091129100550.htm

のように HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}  配下から削除すると、デバイスドライバの更新ができるようなのですが。

カテゴリー: 開発 | VPN が繋がらなくなったので devcon 対処する はコメントを受け付けていません

Realsense でカメラを表示させる第一歩

RealSense 事始め | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/6751

事始めと言いつつ、コンテストも終わってそのままフェードアウトしてしまうのもアカンのです。手元のカメラは6か月ぐらい借りられる(らしい)ので、5月ぐらいまでは大丈夫なのかな。

Realsense はいくつかのサンプルが付いていますが、これが結構難しい。正月の頃にちら見はしていたのですが、ちょっとこれを使いこなすだけで「参加賞がもらえるのでは?」ってなぐらい難しいです。いや、私にとってはそうだった、というだけなのかもしれませんが、カメラの描画を OnPaint で表示させているところがもうなんとも、大変です。

へばりそうになったので、OpenCV 風に簡単にやってみました。

C#のWindowsフォームプロジェクトを作る

最終的には OpenCvSharp との組み合わせがしたかったので C# を使います。まあ、C++ でもいいんですが、ここは C# で(何かのはずみで F# も使えるかもしれないので)。

libpxcclr.cs.dll を参照設定する

C:Program Files (x86)IntelRSSDKbinx64libpxcclr.cs.dll を参照設定します。この中に諸々にライブラリが入っています。ちなみに、名前空間がありませんッ!!! 清い…

ビルドイベントを設定する

プロジェクトのプロパティからビルドイベントを開いて、ビルド後に実行するコマンドに以下を設定します。

if "$(Platform)" == "x86" ( copy /y "$(RSSDK_DIR)binwin32libpxccpp2c.dll" "$(TargetDir)" ) else ( copy /y "$(RSSDK_DIR)binx64libpxccpp2c.dll" "$(TargetDir)" )

c/c++ のライブラリのコピーですね。これは Realsense のサンプルから取ってきたもので、x86 もあるので 32bit版でも動きそうです。

ターゲットを「x64」にする

同じくプロジェクトのプロパティで、プラットフォームのターゲットを「x64」にします。

あまり高度なことはやらないので、「アンセーフコードの許可」は外したままでokです。

カメラを表示するだけのコード

これで準備が整ったので、RBGカメラを表示するだけのコードを書きます。

public partial class Form1 : Form
{
	public Form1()
	{
		InitializeComponent();
	}

	private PXCMSenseManager sm;
	Task _task;
	bool _task_flag = false;

	/// <summary>
	/// 撮影開始
	/// </summary>
	/// <param name="sender"></param>
	/// <param name="e"></param>
	private void button1_Click(object sender, EventArgs e)
	{
		sm = PXCMSenseManager.CreateInstance();
		// RGBの場合
		sm.EnableStream(PXCMCapture.StreamType.STREAM_TYPE_COLOR, 640, 480, 30);
		if (sm.Init() < pxcmStatus.PXCM_STATUS_NO_ERROR)
		{
			// エラー発生
			MessageBox.Show("PXCM_STATUS_NO_ERROR");
			return;
		}
			
		_task = new Task(() =>
		{
			_task_flag = true;
			while (_task_flag)
			{
				if (sm.AcquireFrame(true) < pxcmStatus.PXCM_STATUS_NO_ERROR) 
					return;
				PXCMCapture.Sample sample = sm.QuerySample();
				if (sample.color != null)
				{
					PXCMImage image = sample.color;
					PXCMImage.ImageData data;
					pxcmStatus sts = sample.color.AcquireAccess(PXCMImage.Access.ACCESS_READ, PXCMImage.PixelFormat.PIXEL_FORMAT_RGB32, out data);
					if (sts >= pxcmStatus.PXCM_STATUS_NO_ERROR)
					{
						Bitmap bmp = data.ToBitmap(0, image.info.width, image.info.height);
						image.ReleaseAccess(data);
						pictureBox1.Image = bmp;
					}
				}
				sm.ReleaseFrame();
				/// 30fps程度にあわせる
				Thread.Sleep(33);
			}
		});
		_task.Start();
	}

	/// <summary>
	/// 終了
	/// </summary>
	/// <param name="sender"></param>
	/// <param name="e"></param>
	private void button2_Click(object sender, EventArgs e)
	{
		_task_flag = false;
	}
}

詳しくはサンプルとリファレンスマニュアルを見ていけばいいのですが、なかなか単純な例が見つからないので、さらしておきます。

Intel RealSense SDK Documentation
https://software.intel.com/sites/landingpage/realsense/camera-sdk/2014gold/documentation/html/

別スレッドにしてぐるぐる回すほうが簡単なので、こっちを使っています。まあカメラの fps とずれるという欠点はありますが、結構なスピードで動いてくれるしコードが単純になります。

  1. PXCMSenseManager.CreateInstance() でカメラのインスタンスを作成
  2. sm.EnableStream() でキャプチャするカメラのサイズを決める。RGB と Depth は同時に撮影が可能。
  3. sm.QuerySample() で(たぶん)サンプリングして
  4. sample.color で RBG のデータを取り出す用意をして、
  5. sample.color.AcquireAccess で、PXCMImage 形式にコピーして
  6. data.ToBitmap で Bitmap 形式に直す
  7. pictureBox1.Image = bmp で、ピクチャボックスに貼り付け

という仕組みですね。

実行すると、こんな感じになります。

Bitmap 形式に直したら、そのまま OpenCvSharp を使って背景差分とか特徴量抽出とかができるので結構便利です。

サンプルコード

以下からどうぞ。
https://github.com/moonmile/RealsenseSample/tree/master/SimpleCamera

カテゴリー: 開発, C#, RealSense | Realsense でカメラを表示させる第一歩 はコメントを受け付けていません