做一个能和你互动玩耍的智能机器人之五-app代码设计

openbot使用手机做大脑,这使得我们可以做很多的AI算法来实现诸如循迹,避障,跟随,寻光,人体感应等传感器,使得整个项目的设计更简单和更经济,减少的很多的传感器都会降低硬件DIY的难度和花费。缺点是需要学习一下caffee或tensorFolw Lite,根据我的感觉,也可以使用这些芯片减少来开发的难度,国内网店上这些芯片价格并不高,几块钱就能搞定,如果没有手机,使用传感器是个很好的选择。连无线充电模块都能买到,那些不使用手机的机器人应该是使用了这些芯片。

openbot的android代码位于根目录的android目录下,包含两个模块,一个控制,一个机器人app。

IOS目录是IOS 的app

doc目录是说明文档。

opencode很有意思,是一个在线开发固件的平台,通过拖放就可以,这可以节省很多写代码的时间。

policy是一个驾驶策略训练的平台,需要有比较强劲的电脑和tensorflow知识,主要是采集吧

python手机控制训练。

不要害怕动手。自己买很便宜,购买时自己可以购买一些杜邦线和arduino的扩展板,我也是后来才知道,arduino uno有Arduino sensor shield V5.0的扩展板,arduino有cnc shield v4的扩展板,结合杜邦线,都是几块钱,就能让接入更加轻松,这样调试会更方便,而且省掉很多焊接。

android代码需要的android studio版本比较新,老版需要自己适配gradle,安装好以后就可以插上arduino测试。

然后就可以自己自己开发加一些表情集,为了提高复用性,我倾向于不使用控件而是基本的绘图api来实现,这可以使之十分方便的移植到其他语言和平台。

一个简单的自定义VIEW:

public class FaceView extends View{
    private static final String TAG = "FaceView";
    private Paint mPaint;
    private RectF oval;
    //是否圆眼
    private boolean m_bCircle=true ;
    //是否显示嘴
    private boolean isShowMouth=false;
    //表情索引
    private int mFace=0;
    //view Width
    private int m_VW = 0;
    //view height
    private int m_VH = 0;
    //X轴中心
    private float mRadiusX = 0;
    //Y轴中心
    private float mRadiusY = 0;

    //圆角半径
    private float m_rx = 0;
    private float m_ry = 0;

    //左眼矩形位置
    private float m_left_Leye = 0;
    private float m_left_Teye = 0;
    private float m_left_Reye = 0;
    private float m_left_Beye = 0;
    //左眼中心
    private float m_left_Xeye = 0;
    private float m_left_Yeye = 0;
    //圆眼的半径
    private float m_eye_R = 0;
    //右眼位置
    private float m_right_Leye = 0;
    private float m_right_Teye = 0;
    private float m_right_Reye = 0;
    private float m_right_Beye = 0;
    //右眼中心
    private float m_right_Xeye = 0;
    private float m_right_Yeye = 0;

    private RectF m_l_oval;
    private RectF m_r_oval;

    //表情添加过渡动画
    private ValueAnimator va;
    private float mCurrentSingle = 0f;
    private boolean isRun = false;

view初始化,为了调试方便,我在view加入了一个单击切换表情的操作。

 private void initView() {
        mPaint= new Paint();
        mPaint.setAntiAlias(true);
        oval = new RectF();

        mPaint.setColor(Color.YELLOW);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);

        this.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mFace++;
                if (mFace > 20){
                    mFace = 0;
                }
                isRun = false;
                invalidate();
            }
        });
        m_l_oval = new RectF();
        m_r_oval = new RectF();

        initAni();
    }
private void initAni() {
        Log.i("jiaXXX", "enter drawAni");
        if (va == null) {
            Log.i("jiaXXX", "enter drawAni========");
            va = ObjectAnimator.ofFloat(0f, 1);
            va.setDuration(300);
            //va.setRepeatCount(ValueAnimator.INFINITE);
            //va.setRepeatCount(0);
            va.setInterpolator(new AccelerateInterpolator());
            //va.start();
            va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mCurrentSingle = (float) animation.getAnimatedValue();
                    invalidate();
                    Log.i("jiaXXX", "enter drawAni========, onAnimationUpdate");
                }
            });

            va.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {

                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    Log.i("jiaXXX", "enter drawAni========, onAnimationEnd");
                    if (va != null) {
                        Log.i("jiaXXX", "enter drawAni========, onAnimationEnd----");
                        //va.end();
                        va.cancel();
                        //va = null;
                        //isRun = false;
                    }
                }

                @Override
                public void onAnimationCancel(Animator animation) {
                    Log.i("jiaXXX", "enter drawAni======== onAnimationCancel");
                    if (va != null) {
                        Log.i("jiaXXX", "enter drawAni======== onAnimationCancel----");
                        //va = null;
                        //isRun = false;
                    }
                }

                @Override
                public void onAnimationRepeat(Animator animation) {

                }
            });
        }
    }

显示表情:

 protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.BLACK);
        m_VW = getWidth();
        m_VH = getHeight();
        Log.i("jia", "w="+m_VW);
        Log.i("jia", "h="+m_VH);
        //view 中心
        mRadiusX = m_VW / 2;
        mRadiusY = m_VH / 2;

        //眼睛半径
        if ( m_VW > m_VH){
            m_eye_R = m_VH / 4;
        }else {
            m_eye_R = m_VW / 5;
        }
        //左眼中心
        if ( m_VW > m_VH){
            m_left_Xeye = mRadiusX-m_eye_R-(m_eye_R/3);
        }else {
            m_left_Xeye = m_VW / 4;
        }
        //右眼中心
        m_left_Yeye = m_VH / 2;
        if ( m_VW > m_VH) {
            m_right_Xeye = mRadiusX+m_eye_R+(m_eye_R/3);
        }else {
            m_right_Xeye = m_VW * 3 / 4;
        }
        m_right_Yeye = m_VH / 2;

        //默认方眼的四个顶点位置,为了好看,方眼y方向做一放缩
        m_left_Leye = m_left_Xeye - m_eye_R;
        m_left_Teye = m_left_Yeye - m_eye_R*1.2f;
        m_left_Reye = m_left_Xeye + m_eye_R;
        m_left_Beye = m_left_Yeye + m_eye_R*1.2f;
        //当眼睛为方的时,圆角半径
        m_rx = m_eye_R/10;
        m_ry = m_eye_R/10;

        m_right_Leye = m_right_Xeye - m_eye_R;
        m_right_Teye = m_right_Yeye - m_eye_R*1.2f;
        m_right_Reye = m_right_Xeye + m_eye_R;
        m_right_Beye = m_right_Yeye + m_eye_R*1.2f;

        //m_left_Xeye = m_left_Xeye+(float) Math.sin(mCurrentSingle * 50) * 8;
        //m_left_Yeye = m_left_Xeye+(float) Math.sin(mCurrentSingle * 50) * 8;
        if (mFace == 0) {
            drawFace(canvas);
        }else if (mFace == 1) {
            drawFace1(canvas);
        }else if (mFace == 2) {
            drawFace2(canvas);
        }else if (mFace == 3) {
            drawFace3(canvas);
        }else if (mFace == 4) {
            drawFace4(canvas);
        }else if (mFace == 5) {
            drawFace5(canvas);
        }else if (mFace == 6) {
            drawFace6(canvas);
        }else if (mFace == 7) {
            drawFace7(canvas);
        }else if (mFace == 8) {
            drawFace8(canvas);
        }else if (mFace == 9) {
            drawFace9(canvas);
        }else if (mFace == 10) {
            drawFace10(canvas);
        }else if (mFace == 11) {
            drawFace11(canvas);
        }else if (mFace == 12) {
            drawFace12(canvas);
        }else if (mFace == 13) {
            drawFace13(canvas);
        }else if (mFace == 14) {
            drawFace14(canvas);
        }else if (mFace == 15) {
            drawFace15(canvas);
        }else if (mFace == 16) {
            drawFace16(canvas);
        }else{
            drawFace(canvas);
        }
        //invalidate();
        //drawAni(canvas);

    }

初始化的眼睛从小到大一个睁眼的动作

private void drawFace(Canvas canvas){
        mPaint.setColor(Color.WHITE);
        if (m_bCircle){
            //canvas.drawCircle(m_left_Xeye, m_left_Yeye, m_eye_R, mPaint);
            //canvas.drawCircle(m_right_Xeye, m_right_Yeye, m_eye_R, mPaint);
            m_l_oval.set(m_left_Leye, m_left_Yeye-m_eye_R*mCurrentSingle, m_left_Reye, m_left_Yeye+m_eye_R*mCurrentSingle);
            //绘制圆弧  圆弧所在的椭圆对象 圆弧的起始角度 圆弧的角度 是否显示半径连线
            canvas.drawArc(m_l_oval, 0, 360, true, mPaint);

            m_r_oval.set(m_right_Leye, m_right_Yeye-m_eye_R*mCurrentSingle, m_right_Reye, m_right_Yeye+m_eye_R*mCurrentSingle);
            //绘制圆弧  圆弧所在的椭圆对象 圆弧的起始角度 圆弧的角度 是否显示半径连线
            canvas.drawArc(m_r_oval, 0, 360, true, mPaint);
            if (!isRun) {
                Log.i("jiaXXX", "drawAni");
                isRun = true;
                //drawAni(canvas);
                va.start();
            }
        }
        else
        {
            canvas.drawRoundRect(m_left_Leye, m_left_Teye, m_left_Reye, m_left_Beye, m_rx, m_ry, mPaint);
            canvas.drawRoundRect(m_right_Leye, m_right_Teye, m_right_Reye, m_right_Beye, m_rx, m_ry, mPaint);
        }
    }
  //闭眼,叹气
    private void drawFace1(Canvas canvas){
        mPaint.setColor(Color.WHITE);
        if (m_bCircle){

            m_l_oval.set(m_left_Leye, m_left_Teye+m_eye_R*mCurrentSingle, m_left_Reye, m_left_Beye-m_eye_R*mCurrentSingle);
            //绘制圆弧  圆弧所在的椭圆对象 圆弧的起始角度 圆弧的角度 是否显示半径连线
            canvas.drawArc(m_l_oval, 0, 360, false, mPaint);

            m_r_oval.set(m_right_Leye, m_right_Teye+m_eye_R*mCurrentSingle, m_right_Reye, m_right_Beye-m_eye_R*mCurrentSingle);
            //绘制圆弧  圆弧所在的椭圆对象 圆弧的起始角度 圆弧的角度 是否显示半径连线
            canvas.drawArc(m_r_oval, 0, 360, false, mPaint);
            if (!isRun) {
                Log.i("jiaXXX", "drawAni");
                isRun = true;
                //drawAni(canvas);
                va.start();
            }
        }
        else
        {
            canvas.drawRoundRect(m_left_Leye, m_left_Teye+m_eye_R, m_left_Reye, m_left_Beye-m_eye_R, m_rx, m_ry, mPaint);
            canvas.drawRoundRect(m_right_Leye, m_right_Teye+m_eye_R, m_right_Reye, m_right_Beye-m_eye_R, m_rx, m_ry, mPaint);
        }
    }
    //下半睁
    private void drawFace2(Canvas canvas){
        mPaint.setColor(Color.WHITE);
        if (m_bCircle){
            m_l_oval.set(m_left_Leye, m_left_Yeye-m_eye_R, m_left_Reye, m_left_Yeye+m_eye_R);
            //绘制圆弧  圆弧所在的椭圆对象 圆弧的起始角度 圆弧的角度 是否显示半径连线
            canvas.drawArc(m_l_oval, 0, 180, true, mPaint);

            RectF r1= new RectF();
            r1.set(m_left_Leye, m_left_Yeye-(m_eye_R/5)-(m_eye_R*4/5)*(1-mCurrentSingle), m_left_Reye, m_left_Yeye+(m_eye_R/5)+(m_eye_R*4/5)*(1-mCurrentSingle));
            //绘制圆弧  圆弧所在的椭圆对象 圆弧的起始角度 圆弧的角度 是否显示半径连线
            Paint p = new Paint();
            p.setColor(Color.BLACK);
            canvas.drawArc(r1, 0, -180, true, mPaint);

            m_r_oval.set(m_right_Leye, m_right_Yeye-m_eye_R, m_right_Reye, m_right_Yeye+m_eye_R);
            //绘制圆弧  圆弧所在的椭圆对象 圆弧的起始角度 圆弧的角度 是否显示半径连线
            canvas.drawArc(m_r_oval, 0, 180, true, mPaint);

            RectF r2= new RectF();
            r2.set(m_right_Leye, m_right_Yeye-(m_eye_R/5)-(m_eye_R*4/5)*(1-mCurrentSingle), m_right_Reye, m_right_Yeye+(m_eye_R/5)+(m_eye_R*4/5)*(1-mCurrentSingle));
            //绘制圆弧  圆弧所在的椭圆对象 圆弧的起始角度 圆弧的角度 是否显示半径连线
            canvas.drawArc(r2, 0, -180, true, mPaint);

            if (!isRun) {
                Log.i("jiaXXX", "drawAni");
                isRun = true;
                va.start();
            }
        }
        else
        {
            canvas.drawRoundRect(m_left_Leye, m_left_Teye+m_eye_R, m_left_Reye, m_left_Beye, m_rx, m_ry, mPaint);
            canvas.drawRoundRect(m_right_Leye, m_right_Teye+m_eye_R, m_right_Reye, m_right_Beye, m_rx, m_ry, mPaint);
        }
    }
    //上半睁
    private void drawFace3(Canvas canvas){
        mPaint.setColor(Color.WHITE);
        if (m_bCircle){
            canvas.drawCircle(m_left_Xeye, m_left_Yeye, m_eye_R, mPaint);
            canvas.drawCircle(m_right_Xeye, m_right_Yeye, m_eye_R, mPaint);
            Paint p = new Paint();
            p.setColor(Color.BLACK);
            canvas.drawRect(m_left_Leye, mRadiusY+m_eye_R/3+(m_eye_R*2/3)*(1-mCurrentSingle), m_right_Reye, m_right_Beye, p);
            if (!isRun) {
                Log.i("jiaXXX", "drawAni");
                isRun = true;
                va.start();
            }
        }
        else
        {
            canvas.drawRoundRect(m_left_Leye, m_left_Teye, m_left_Reye, m_left_Beye-m_eye_R, m_rx, m_ry, mPaint);
            canvas.drawRoundRect(m_right_Leye, m_right_Teye, m_right_Reye, m_right_Beye-m_eye_R, m_rx, m_ry, mPaint);
        }
    }

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值