在构建移动机器人系统的过程中,经常需要将传感器的测量数据可视化。本文将介绍如何使用Android系统创建一个雷达屏幕,以实现这一目的。
在开发移动机器人系统时,需要可视化来自传感器的测量数据。在互联网上寻找类似项目的过程中,发现了Larry的主页()。但那个示例对并无帮助,因为它是基于PC的代码,使用了Processing编译器。因此,决定开发一个新的数据可视化程序,基于Android系统,因为计划通过移动设备远程控制机器人系统。Larry的主页对帮助很大,在此向Larry先生表示感谢。
要将这段代码用于项目,首先需要将RadarView.java添加到项目中。然后,编辑想要显示的activity文件的资源文件。
enum radarColor {LAY, PREVSWEEP, CURSWEEP, AVRSWEEP}
这是一个枚举值,用于设置扫描值的颜色。
void setDisplayColor(radarColor what, int color)
通过Color.argb()设置颜色变量,它具有alpha、red、green、blue属性。
void setDrawMode(boolean bPreSweep, boolean bCurSweep, boolean bAvrSweep)
这是选项,用于决定三种数据的每个值是否可见。
bPreSweep:是否启用以前的数据可视化
bCurSweep:是否启用当前数据可视化
bAvrSweep:是否启用平均数据可视化 [平均值 = (以前的数据 + 当前数据) / 2]
void setHalfSweep(boolean ck)
如果输入变量为true,则显示屏幕将是半圆形,否则将是全圆形。半圆形的显示数据范围为0到180度,全圆形为0到360度。
void setMaxDistance(int val)
这个函数用于设置检测距离的最大指示值。
void setValue(int index, int val)
这是一个设置当前值的函数。
index:扫描角度,以度为单位
val:检测到的距离值
在layout.xml文件中添加以下代码资源。需要更改这个包名(com.example.bskim)。
雷达数据通过从原点到感应点的实线描述。线的长度是传感器和任何物体之间的距离,线的角度是从基线(x轴)检测到的方向。
检测物体的扫描运动是通过扇形实现的。
以下是RadarView类的实现代码。
public class RadarView extends View {
......
void DrawDisplay(Canvas canvas, int radius, int centerx, int centery) {
float x, y;
boolean clockwise = false;
float ratio = (float)radius / (float)widthp * 3.0f;
int textsize = (int)(25.0 * ratio);
if ((dectAngle - oldDectAngle) >= 0) {
clockwise = false;
} else {
clockwise = true;
}
Paint paint=new Paint();
paint.setStyle(Paint.Style.FILL_AND_STROKE);
// previous sweep
if (mbPrevSweep == true) {
paint.setARGB(64, 0, 32, 0);
Path path1st = new Path();
path1st.reset();
path1st.moveTo(centerx, centery);
for (int i = 0; i < sweepAngle; i++) {
x = centerx + (int) (Math.cos(Math.toRadians((-i))) * ((float) oldDistValue[i]));
y = centery + (int) (Math.sin(Math.toRadians((-i))) * ((float) oldDistValue[i]));
path1st.lineTo(x, y);
}
canvas.drawPath(path1st, paint);
}
// current sweep
if (mbCurSweep == true) {
paint.setARGB(128, 0, 200, 0);
Path path2nd = new Path();
path2nd.reset();
path2nd.moveTo(centerx, centery);
for (int i = 0; i < sweepAngle; i++) {
x = centerx + (int) (Math.cos(Math.toRadians((-i))) * ((float) distValue[i]));
y = centery + (int) (Math.sin(Math.toRadians((-i))) * ((float) distValue[i]));
path2nd.lineTo(x, y);
}
canvas.drawPath(path2nd, paint);
}
// average
if (mbAvrSweep == true) {
paint.setStyle(Paint.Style.STROKE);
paint.setARGB(255, 0, 0, 255);
Path pathavg = new Path();
pathavg.reset();
pathavg.moveTo(centerx, centery);
for (int i = 0; i < sweepAngle; i++) {
x = centerx + (int) (Math.cos(Math.toRadians((-i))) * ((float) ((distValue[i] + oldDistValue[i]) / 2)));
y = centery + (int) (Math.sin(Math.toRadians((-i))) * ((float) ((distValue[i] + oldDistValue[i]) / 2)));
pathavg.lineTo(x, y);
}
canvas.drawPath(pathavg, paint);
}
// sweep motion
if (mbSweepMotion == true) {
// sweep motion
paint.setAntiAlias(true);
paint.setStrokeWidth(7);
int gradationAngle = 30;
if (dectAngle < gradationAngle) gradationAngle = dectAngle;
if (dectAngle > (sweepAngle-gradationAngle)) gradationAngle = sweepAngle-dectAngle;
// scan motion drawing
if (clockwise == false) {
for (int i = gradationAngle; i >= 0; i--) {
if (i == 0)
paint.setColor(mLayColor);
else
paint.setARGB(128, 150+(100/gradationAngle * i), 150+(100/gradationAngle * i), 150+(100/gradationAngle * i));
canvas.drawLine(centerx, centery, centerx + (int)(Math.cos(Math.toRadians(-dectAngle + (i))) * (float)radius), centery + (int) (Math.sin(Math.toRadians(-dectAngle + (i))) * (float)radius), paint);
}
}
else {
for (int i = 0; i <= gradationAngle; i++) {
if (i == 0)
paint.setColor(mLayColor);
else
paint.setARGB(128, 150+(100/gradationAngle * i), 150+(100/gradationAngle * i), 150+(100/gradationAngle * i));
canvas.drawLine(centerx, centery, centerx + (int) (Math.cos(Math.toRadians(-dectAngle + (-i))) * (float) radius), centery + (int) (Math.sin(Math.toRadians(-dectAngle + (-i))) * (float) radius), paint);
}
}
}
}
}
创建RadarView的对象实例并配置其属性。例如,全圆形模式,扫描线颜色为绿色。扫描距离范围由常量MAX_DISTANCE设置,可以使用预定义的XML文件中的资源,通过findViewById()获取。
RadarView mRadarView;
mRadarView = (RadarView) findViewById(R.id.RadarView);
mRadarView.setHalfSweep(false);
mRadarView.setDisplayColor(RadarView.radarColor.LAY, Color.argb(255, 0, 255, 0));
mRadarView.setMaxDistance(MAX_DISTANCE);
mRadarView.setSweepMotion(true);
以下代码展示了这个类的使用。要绘制的数据缓冲区通过setValue(..)填充,并且使用handler以安全的方式更新视图。
TimerTask mTask;
Timer mTimer;
mTask = new TimerTask() {
@Override
public void run() {
if (mRadarView.isHalfMode() == true) {
if (angle > 180) flag = 0;
if (angle <= 0) flag = 1;
if (flag == 0) angle--;
else angle++;
}
else {
if (angle > 360) angle = 0;
else angle++;
}
int distance = mRandVal.nextInt(MAX_DISTANCE);
mRadarView.setValue(angle, distance);
RadarViewInvalidate();
}
};
mTimer = new Timer();
mTimer.schedule(mTask, 500, 100);
// every 100ms after 500ms.
在使用setValue(..)输入新的传感器数据后,应该调用RadaViewInvalidate()来更新视图。
Handler mDrawHandler = new Handler();
private void RadarViewInvalidate() {
mDrawHandler.post(new Runnable() {
@Override
public void run() {
mRadarView.invalidate();
}
});
}