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 パーマリンク