Androidで端末の傾きセンサの値を取得し、それを速度に変換し、rosjavaを使ってtopicとして出力します。
おまけで掃除機能のOn/Off、ドックへの合体指示も出せるようにボタンをつけました。
ただし、この機能はotl_roombaパッケージのroomba_twist_node.pyを想定しています。
システム図は↓のような感じです。
Roomba制御のためにPCを経由しています。本当はAndroidからBluetoothで直接Roombaを操作可能なので、実はこのアプリはかなり冗長です。
でもこのノードは/cmd_vel (geometry_msgs/Twist)を出力するので、制御対象がルンバだろうが、PR2だろうが、動かすことができるというメリットもあります。
シミュレータでの動作画面は↓のようになっています。
スライダで最大動作速度を変えたり、ボタンでクリーナーのOn/Off等ができます。
一番上のでかいロゴに意味はありません。
リポジトリに上げましたが、
ソースもさらしておきます。まずはルンバ操作用クラス
- /**
- * @license New BSD License
- *
- */
- package com.ogutti.ros.android.roomba;
- import org.ros.message.geometry_msgs.Twist;
- import org.ros.message.std_msgs.Bool;
- import org.ros.namespace.GraphName;
- import org.ros.node.NodeMain;
- import org.ros.node.topic.Publisher;
- import org.ros.node.Node;
- /**
- * class for Controlling a roomba by Twist msg
- *
- */
- public class RoombaControllerNode implements NodeMain {
- private Publisher<Twist> velPublisher;
- private Publisher<Bool> cleanPublisher;
- private Publisher<Bool> dockPublisher;
- @Override
- public void onShutdown(Node arg0) {
- }
- @Override
- public void onShutdownComplete(Node arg0) {
- }
- @Override
- public void onStart(Node node) {
- velPublisher = node.newPublisher("/cmd_vel", "geometry_msgs/Twist");
- cleanPublisher = node.newPublisher("/roomba/clean", "std_msgs/Bool");
- dockPublisher = node.newPublisher("/roomba/dock", "std_msgs/Bool");
- }
- @Override
- public GraphName getDefaultNodeName() {
- return new GraphName("roomba_controller");
- }
- /**
- * start/stop cleaning motor
- * @param isOn if true start cleaning, false stop.
- */
- public void publishClean(boolean isOn) {
- if (cleanPublisher != null) {
- Bool msg = new Bool();
- msg.data = isOn;
- cleanPublisher.publish(msg);
- }
- }
- /**
- * Publish roomba's velocity (Vx, Vtheta)
- * @param linearX twist.linear.x forward speed [m/s]
- * @param angularZ twist.angular.z rotational speed [rad/s]
- */
- public void publishVelocity(double linearX, double angularZ) {
- if (velPublisher != null) {
- Twist vel = new Twist();
- vel.linear.x = linearX;
- vel.angular.z = angularZ;
- velPublisher.publish(vel);
- }
- }
- /**
- * start docking
- */
- public void publishDock() {
- if (dockPublisher != null) {
- Bool msg = new Bool();
- msg.data = true;
- dockPublisher.publish(msg);
- }
- }
- }
つぎはメインのActivityのクラス
- /**
- * @license New BSD License
- */
- package com.ogutti.ros.android.roomba;
- import java.text.DecimalFormat;
- import java.util.List;
- import android.os.Bundle;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.CompoundButton;
- import android.widget.CompoundButton.OnCheckedChangeListener;
- import android.widget.SeekBar;
- import android.widget.SeekBar.OnSeekBarChangeListener;
- import android.widget.TextView;
- import android.widget.ToggleButton;
- import org.ros.address.InetAddressFactory;
- import org.ros.android.MessageCallable;
- import org.ros.android.RosActivity;
- import org.ros.android.views.RosTextView;
- import org.ros.node.NodeConfiguration;
- import org.ros.node.NodeMainExecutor;
- import android.hardware.Sensor;
- import android.hardware.SensorEvent;
- import android.hardware.SensorEventListener;
- import android.hardware.SensorManager;
- import com.ogutti.ros.android.roomba.R;
- import org.ros.message.geometry_msgs.Twist;
- /**
- * MainActivity of this App (roomba controller)
- *
- */
- public class MainActivity extends RosActivity implements SensorEventListener {
- private RosTextView<Twist> rosTextView;
- private SensorManager sensorManager;
- private RoombaControllerNode controllerNode;
- private static final double LINEAR_VELOCITY_RATE = 0.05;
- private static final double ANGULAR_VELOCITY_RATE = 0.1;
- /**
- * 1.0 means max speed, 0.0 means stop always.
- */
- private double speedRate;
- public MainActivity() {
- super("Roomba Controller", "Roomba Controller");
- speedRate = 0.5;
- }
- @SuppressWarnings("unchecked")
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
- rosTextView = (RosTextView<Twist>) findViewById(R.id.text);
- rosTextView.setTopicName("/cmd_vel");
- rosTextView.setMessageType("geometry_msgs/Twist");
- rosTextView
- .setMessageToStringCallable(new MessageCallable<String, Twist>() {
- @Override
- public String call(Twist message) {
- DecimalFormat df = new DecimalFormat();
- df.setMaximumFractionDigits(2);
- return "vel_x:\n" + df.format(message.linear.x) + "\n"
- + "vel_theta:\n" + df.format(message.angular.z);
- }
- });
- }
- @Override
- protected void init(NodeMainExecutor nodeMainExecutor) {
- // create ROS nodes
- controllerNode = new RoombaControllerNode();
- NodeConfiguration nodeConfiguration = NodeConfiguration
- .newPublic(InetAddressFactory.newNonLoopback().getHostName());
- nodeConfiguration.setMasterUri(this.getMasterUri());
- nodeMainExecutor.execute(controllerNode, nodeConfiguration);
- nodeMainExecutor.execute(rosTextView, nodeConfiguration);
- // set callback for accelerometer
- List<Sensor> sensors = sensorManager
- .getSensorList(Sensor.TYPE_ACCELEROMETER);
- if (sensors.size() > 0) {
- Sensor accelerometer = sensors.get(0);
- sensorManager.registerListener(this, accelerometer,
- SensorManager.SENSOR_DELAY_FASTEST);
- } else {
- android.util.Log.v("MainActivity", "NOT found sensor!");
- }
- // set toggle button's callback
- ToggleButton tb = (ToggleButton) findViewById(R.id.toggleButton1);
- tb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView,
- boolean isChecked) {
- controllerNode.publishClean(isChecked);
- }
- });
- // set seekbar callback.
- SeekBar seekBar = (SeekBar) findViewById(R.id.seekBar1);
- final TextView tv1 = (TextView) findViewById(R.id.textView1);
- seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
- public void onProgressChanged(SeekBar seekBar, int progress,
- boolean fromUser) {
- tv1.setText("Speed:" + progress + "%");
- speedRate = progress * 0.01f;
- }
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
- });
- Button dockButton = (Button) findViewById(R.id.button1);
- dockButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(android.view.View v) {
- controllerNode.publishDock();
- }
- });
- }
- @Override
- protected void onResume() {
- super.onResume();
- }
- @Override
- protected void onStop() {
- super.onStop();
- sensorManager.unregisterListener(this);
- }
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
- /**
- * callback of sensor change(accelerometer)
- */
- @Override
- public void onSensorChanged(SensorEvent event) {
- if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
- double linearX = event.values[1] * -LINEAR_VELOCITY_RATE * speedRate;
- double angularZ = event.values[0] * ANGULAR_VELOCITY_RATE * speedRate;
- if (Math.abs(linearX) < LINEAR_VELOCITY_RATE) {
- linearX = 0.0d;
- }
- if (Math.abs(angularZ) < ANGULAR_VELOCITY_RATE) {
- angularZ = 0.0d;
- }
- controllerNode.publishVelocity(linearX, angularZ);
- }
- }
- }
ちょっと長いですが以上になります。
Androidアプリは作るのが結構簡単なのでチャレンジしてみてはどうでしょうか。
Androidアプリは作るのが結構簡単なのでチャレンジしてみてはどうでしょうか。
0 件のコメント:
コメントを投稿