`
liguanyi11111
  • 浏览: 62967 次
  • 性别: Icon_minigender_1
  • 来自: 黑龙江
社区版块
存档分类
最新评论

备忘(View显示流程1-View draw的准备工作)

阅读更多

      这是一篇写给自己用于备忘的文字。所以内容上比较跳跃,不建议作为参考。使用代码版本Android4.4.

      首先Activity的加载不归我们管辖,所以View的展示可以说是从Activity的setContentView()开始的,这个方法最终会走到PhoneWindow(继承与Window)类中的setContentView()方法。

public void setContentView(View view, ViewGroup.LayoutParams params) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mContentParent.addView(view, params);
       ....
    }

       这里涉及了一个重要的对象,mContentParent对象,这是个ViewGroup类型对象。也就是我们activity的内容部分(用于放置我们加进去的View)的根View,这是由installDecor()方法创建的。这个方法首先需要创建整个Activity的根DecorView对象,这是个FrameLayout的子类。是整个的Activity的框架(包含标题栏与ActionBar)。然后在通过generateLayout()方法创建mContentParent对象,与其说创建不如说获取,因为这个对象时从DecorView中实例化xml布局文件中根据ID得到的。

protected ViewGroup generateLayout(DecorView decor) {
        .......
        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        .......
        return contentParent;
}

 最后,回到PhoneWindow中,mContentParent对象使用addView()方法,加载我们要加入的view。这样我们要展示的View就完全准备好了。但是这里并没有开始绘制,只是单纯的准备工作完成。

       之后,由于Activity自己加载过程中onCreate()方法执行之后即上述加载完View结束后,随着Activity的加载,ActivityManagerService(AMS)开始调用ActivtyThread的handleResumeActivity()的方法,并把mDecorView设置为可见(setVisibility(View.VISIBLE))。

 

final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
            boolean reallyResume) {
       .......
       ActivityClientRecord r = performResumeActivity(token, clearHide);
       .......
       final Activity a = r.activity;   //获取activity对象
       .......
       decor.setVisibility(View.INVISIBLE);    //显示decor
       ViewManager wm = a.getWindowManager();
       WindowManager.LayoutParams l = r.window.getAttributes();
       a.mDecor = decor;
       l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
       l.softInputMode |= forwardBit;
       if (a.mVisibleFromClient) {
             a.mWindowAdded = true;
              wm.addView(decor, l);     //加载了我们的View
        }
        .......
}

 这里最重要的代码就是wm.addView(decor, l);但是这里要先说一下wm,看上述代码可知只是一个ViewManager类型,实际上是一个WindowManager(实现了ViewManager接口)。他通过activity的getWindowManager()获得,获得的WindowManager对象最初来自于Window类的getWindowManager()方法,由setWindowManager()创建。

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
       .......
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

 这里可以看到,首先我们通过mContext对象获取系统的WindowManagerService。然后通过createLocalWindowManager方法复制了一份。所以说对于WindowManagerService对象,每一个Activity程序都会有一个自己的mWindowManager。下面就是通过这个WindowManager将我们的View加载并显示出来。回到addView()方法,这个方法的实现在WindowManagerImpl里面,发现他只是一个代理,这个方法最终交给了WindowManagerGlobal类的一个单例来执行。

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();   //单例对象
...............
public void addView(View view, ViewGroup.LayoutParams params) {
    mGlobal.addView(view, params, mDisplay, mParentWindow);
}

 单例的addView()方法如下:

 public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
         .........
        ViewRootImpl root;
         .........
         root = new ViewRootImpl(view.getContext(), display);
         view.setLayoutParams(wparams);
         mViews.add(view);
         mRoots.add(root);
         .........
         root.setView(view, wparams, panelParentView);
         .........
}

 这里我们首次看到了ViewRoot,这是非常重要的类。在ViewRoot的构造函数中可见,

 public ViewRootImpl(Context context, Display display) {
        .......
        mWindowSession = WindowManagerGlobal.getWindowSession();
        ......
}

 这里取得了一个WindowSession的对象,这是一个aidl的接口,用于跨进程取得Session对象。流程如下:

public static IWindowSession getWindowSession() {
        .....
        sWindowSession = windowManager.openSession(
                  imm.getClient(), imm.getInputContext());
        .....
        return sWindowSession;
}

 这里调用了类WindowManagerService里实现的openSession方法。

public IWindowSession openSession(IInputMethodClient client,
            IInputContext inputContext) {
        ......
        Session session = new Session(this, client, inputContext);
        return session;
}

 用此可见,Session是在WMS中直接创建的,如名字一样,这是一个WMS与ViewRoot的一个会话,由此ViewRoot的功能就非常清楚:是View与WMS通信的桥梁,在ViewRoot中使用WMS。我们了解了ViewRoot的功能,让我们看看他是如何执行的。我们回到WindowManagerImpl的addView()。ViewRoot使用setView方法将我们需要显示View放入ViewRoot内进行操作。

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
                ..........
                requestLayout();
                ..........
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mInputChannel);
                .........
}

     首先是requestLayout()方法,经过scheduleTraversals()方法的传递,使用Choreographer的postCallback()方法异步执行了一个Runnable。这里的Choreographer就是一个消息处理器,里面包含一个handler来处理几种信号,这个与主题关系不大暂时不做介绍。这个Runnable中调用了一个doTraversal()方法,最终指向performTraversals()方法。这个方法相信有些人非常了解,因为我们所用View的树结构就是在这里被绘制展开的,包含一系列的measure,Layout等测量大小计算位置等方法。但是现在先不介绍他。因为怎么画还不急着去研究,我们先要去拿到我们的画布。虽然绘制代码逻辑在前,但是别忘了他是个消息队列形式执行的方法,在我们的activity没启动完毕时,这个绘制过程是不会被触发的。  这时我们回到setView来看Session的addToDisplay方法。

public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets,
            InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outInputChannel);
    }

 这里代理调用了mService的addWindow()方法,其中传入的参数window是ViewRoot内的一个内部类通过binder机制放入到WMS中用于WMS反向调用ViewRoot,即与Session相反。这样ViewRoot与WMS建立了双向链接。这时,让我们进入WMS看看addWindow()方法。

    public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, InputChannel outInputChannel) {
            ..............
            win = new WindowState(this, session, client, token,
                    attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
            .............
            win.attach();
            mWindowMap.put(client.asBinder(), win);
            .............            
}

 这里实例化了一个WindowState对象,里面保存了了一个SurfaceSession对象。这个SurfaceSession的实例化是通过win的attach()方法,最终调用到Session中的windowAddedLocked()方法。

    void windowAddedLocked() {
       ........
       mSurfaceSession = new SurfaceSession();
       ........
       mService.mSessions.add(this);
    }

 最终SurfaceSession被放入了Service的HashSet<Session>集合里面,对于Surface的实例化这里简单说一下,他调用了自己的native方法,与SurfaceFlinger建立了一个链接,SurfaceFlinger是用来绘制surface的一个服务,SurfaceSession就是WMS与SurfaceFlinger的通信桥梁。至此Activity的启动消息被执行完成,但是我们还是没有获取到画布,所以我们继续进入到performTraversals()方法。先无视掉View绘制的代码,我们直接看我们需要的代码 :relayoutWindow()方法(在performTraversals()直接调用

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
           boolean insetsPending) throws RemoteException {
           .........
           int relayoutResult = mWindowSession.relayout(,......, mSurface);
           .........
}

 看着有点乱,就是讲mSurface传入了Session的方法中。mSurface是ViewRoot自己建立的一个Surface对象,但是是个空构造函数没什么意义。这里传入Session就是要利用WMS充实这个Surface对象,这个Surface就是我们要绘制的屏幕。在Session中,他直接把Surface对象传入了mService即WMS对象中。

 

    public int relayout(IWindow window, ..., Surface outSurface) {
        ......
        int res = mService.relayoutWindow(this, window, ..., outSurface);
       ....
    }

 

这里的outSurface就是从客户端ViewRoot中一步步传入WMS内的Surface对象了。下面来开WMS对Surface对象的处理过程

    public int relayoutWindow(Session session, IWindow client, ......, Surface outSurface) {
    WindowState win = windowForClientLocked(session, client, false);
     .......
    WindowStateAnimator winAnimator = win.mWinAnimator;
      ........
        SurfaceControl surfaceControl =  winAnimator.createSurfaceLocked();
        if (surfaceControl != null) {
                outSurface.copyFrom(surfaceControl);
        }
}

 win就是通过我们上文中被放入map中的WindowState对象。这里首先通过win获取到WindowStateAnimator对象,再通过这个对象通过createSurfaceLocked()方法创建一个SurfaceControl对象(在早期的版本要简单些,直接就通过win创建出surface了)。

SurfaceControl createSurfaceLocked() {
              ........
              mSurfaceControl = new SurfaceControl(
                        mSession.mSurfaceSession,
                        attrs.getTitle().toString(),
                        w, h, format, flags);
               ........
}

 在SurfaceControl的构造函数中传入了SurfaceSession对象。然后回到上面,通过surface的copyFrom方法,从SurfaceControl对象中取得Surface。这里获取的方式仍然是native方法。到此,其实我们已经在java层面看到了surface的创建于传递的过程。其实surface还远没有结束,现在关于它我们只了解了三个部分,ViewRoot的创建(只是简单实例化一个对象,没有意义),SurfaceFlinger创建真正的Surface,WMS获得它。至于Surface怎样传回ViewRoot,还有其他对它的一系列处理,其中大量的代码是在native函数中进行的,这里先不研究native的C++函数。我们优先搞定上层处理。所以我们回到ViewRoot中继续看View的创建于绘制流程。

 

performTraversals() {
       .....
       performDraw();     //开始绘制了
       .....
}

 再继续看performDraw()

 

private void performDraw() {
      ......
      draw(fullRedrawNeeded);
      ......
}

 继续。

private void draw(boolean fullRedrawNeeded) {
        Surface surface = mSurface;    //我们千辛万苦获得的surface
        ........
        if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) {
               return;
        }
        .......
}

 这里的drawSoftware()方法就是绘制了

    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff,
            boolean scalingRequired, Rect dirty) {
       .......
       canvas = mSurface.lockCanvas(dirty);    //在指定区域拿到canvas画笔
       .......
       mView.draw(canvas);  //开始绘制我们的View
       .......
       surface.unlockCanvasAndPost(canvas);    //回收surface
       .......
}

 终于,我们看到了mView.draw(canvas);利用画笔开始调用View的draw方法。具体的View的绘制,我们以后再看。

分享到:
评论
2 楼 麦田的设计者 2017-01-06  
“所以我们继续进入到performTraversals()方法。先无视掉View绘制的代码,我们直接看我们需要的代码 :relayoutWindow()方法(在performTraversals()直接调用),”你是从哪个function进入到这里的
1 楼 yefengxue 2016-01-07  

相关推荐

    Android实现截屏方式整理(总结)

    dView.draw(canvas); ``` 二、截取带导航栏的整个屏幕 截取带导航栏的整个屏幕可以使用 adb 命令来实现。在 AndroidManifest.xml 文件中添加 `&lt;uses-permission android:name="android.permission.READ_FRAME_...

    闭路头钉铆合机sw16可编辑_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    闭路头钉铆合机sw16可编辑_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    级联H桥储能系统中的相间与相内SOC均衡及解耦控制技术研究

    内容概要:本文深入探讨了级联H桥储能系统中的关键技术和挑战,特别是在相间和相内SOC均衡方面。文中详细介绍了相间SOC均衡和相内SOC均衡的重要性及其对系统性能的影响。为了实现高效的SOC均衡,文章讨论了多种控制技术,包括解耦控制、单极倍频载波移相调制、零序电压注入法和负序电压注入法。此外,还涉及了蓄电池充放电控制的具体方法和技术细节,如恒流-恒压充电和防止过充过放的措施。通过这些技术的应用,能够显著提高储能系统的效率和稳定性。 适合人群:从事电力电子、储能系统设计和控制领域的研究人员、工程师和技术爱好者。 使用场景及目标:适用于需要深入了解级联H桥储能系统的工作原理和控制策略的研究人员和工程师。主要目标是掌握相间和相内SOC均衡的方法,以及解耦控制和其他关键技术的实际应用,以优化储能系统的性能。 其他说明:文章提供了大量的伪代码和公式推导,帮助读者更好地理解和实现这些复杂的控制技术。同时,强调了实际调试中的注意事项,如参数整定和避免系统振荡等问题。

    c语言奔跑的火柴人游戏源码.zip

    c语言奔跑的火柴人游戏源码.zip

    tini-0.19.0-1.el8.x64-86.rpm.tar.gz

    1、文件说明: Centos8操作系统tini-0.19.0-1.el8.rpm以及相关依赖,全打包为一个tar.gz压缩包 2、安装指令: #Step1、解压 tar -zxvf tini-0.19.0-1.el8.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm

    书本胶订切边设备sw18_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    书本胶订切边设备sw18_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    编程语言TypeScript核心特性解析:静态类型检查与面向对象编程助力大型应用开发及团队协作

    内容概要:TypeScript是由微软开发的开源编程语言,作为JavaScript的超集,它在JS基础上增加了静态类型定义和对ES6+特性的支持,旨在提升大型应用开发的效率与代码质量。TypeScript具有静态类型检查、类型推断、接口和类、装饰器、模块系统五大特点。其中,静态类型检查能在编译期捕捉类型错误,增强代码健壮性;类型推断让开发者无需频繁声明变量类型;接口和类使代码结构更模块化;装饰器提供了一种灵活的元编程手段;模块系统支持代码按需加载。此外,TypeScript最终编译为JavaScript,确保了广泛的环境兼容性。 适合人群:前端开发工程师,尤其是参与大型项目或团队协作的开发者。 使用场景及目标:①适用于开发大型应用程序,保证代码的可维护性;②在团队协作中,提高代码可读性和减少bug;③借助静态类型检查,在开发阶段发现并解决潜在性能问题。 其他说明:推荐学习官方文档、入门教程等资源,GitHub仓库也有助于深入理解TypeScript的功能和应用场景。

    草莓采摘机器人sw22可编辑+仿真视频_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    草莓采摘机器人sw22可编辑+仿真视频_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    基于Comsol的激光熔覆熔池流动数值模拟:探究马兰戈尼对流及S活性元素影响

    内容概要:本文详细探讨了利用Comsol软件对激光熔覆过程中熔池流动行为进行数值模拟的方法和技术。主要内容涵盖熔池内主要驱动力如马兰戈尼对流、表面张力、重力和浮力的作用机理及其相互影响,特别关注了S活性元素对表面张力系数的影响,导致马兰戈尼对流方向发生变化,从而使熔池表面形成凸起。文中提供了具体的MATLAB和Python代码片段用于定义表面张力系数随温度变化的关系,并介绍了如何在Comsol中设置相关物理场和边界条件。此外,还讨论了网格处理方法和一些有趣的模拟现象,如熔池表面波动结构和涡量场特征。 适合人群:从事材料科学、激光加工技术研究的专业人士,以及对多物理场耦合仿真感兴趣的科研工作者。 使用场景及目标:适用于希望通过数值模拟深入了解激光熔覆过程中熔池内部复杂流动行为的研究人员。目标是优化激光熔覆工艺参数,提高涂层质量和性能。 其他说明:文中提到的模型和方法不仅有助于理论研究,还可以指导实际工程应用中的工艺改进。同时强调了参数校验的重要性,避免因错误设置而导致不合理的结果。

    Java项目源码带论文(jsp+servlet+javabean)l.zip

    Java项目源码带论文(jsp+servlet+javabean)l.zip

    各院校专业录取分数线.zip

    各院校专业录取分数线.zip

    openssl-1.0.2k-fips

    windows版本的openssl-1.0.2k-fips,用openssl-1.0.2k和openssl-fips-2.0.16和集成编译的,下载之后环境变量配置bin目录就可用

    螺母压装方案sw16可编辑_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    螺母压装方案sw16可编辑_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    基于组态王6.53的3层立体车库(9车位)自动化仿真程序设计与实现

    内容概要:本文详细介绍了使用组态王6.53开发的3层立体车库(9车位)仿真程序。首先概述了车库的结构特点,接着讲解了如何在组态王中创建工程、绘制图形界面以及定义和关联变量。随后重点阐述了通过命令语言实现车辆进出逻辑的方法,包括车位状态判断、平移和升降控制等关键步骤。此外,还探讨了调试过程中遇到的问题及其解决方案,如变量同步、动画速度调整等。最后分享了一些实用技巧,如虚拟PLC配置、状态机设计和报警机制等。 适合人群:从事自动化控制系统开发的技术人员,尤其是对组态王有一定了解的工程师。 使用场景及目标:适用于希望深入了解组态王应用开发、掌握自动化控制逻辑实现方法的学习者。目标是能够独立完成类似的自动化仿真项目。 其他说明:文中提供了大量实例代码片段,帮助读者更好地理解和实践相关知识点。同时提醒注意一些常见的陷阱和技术难点,如PLC通信延迟、脚本执行优先级等问题。

    基于LabVIEW的模拟采集卡与互相关定位算法的气体管道泄漏检测系统

    内容概要:本文详细介绍了利用LabVIEW平台构建的气体管道泄漏检测系统的实现方法和技术细节。系统采用NI的模拟采集卡进行信号采集,并运用互相关算法计算泄漏点位置。文中涵盖了硬件选型、采样率设定、互相关算法配置、噪声处理以及系统优化等多个方面的内容。通过实际案例展示了系统的应用效果,强调了生产者/消费者模式在提高系统稳定性和响应速度方面的作用。 适合人群:从事工业自动化、管道安全监控领域的工程师和技术人员。 使用场景及目标:适用于需要实时监测气体管道泄漏情况的企业和机构,旨在提供一种低成本、高效的解决方案,减少因泄漏带来的安全隐患和经济损失。 其他说明:文中提到的一些具体技术和实现细节,如互相关算法的具体配置、噪声处理方法等,对于理解和实施类似项目非常有帮助。同时,作者分享了许多实践经验,有助于避免常见的错误和陷阱。

    10车位三层四列立体车库:组态王6.53与西门子200PLC仿真实现及应用

    内容概要:本文详细介绍了10车位三层四列立体车库的自动化控制系统的设计与实现。主要内容涵盖西门子200PLC的IO分配表设计、PLC编程要点以及组态王6.53的工程创建、设备定义、变量定义、画面设计和动画连接等方面。文中不仅提供了具体的IO分配表和梯形图逻辑代码,还深入探讨了PLC与组态王之间的交互细节,如动画绑定、定时器设置、互锁逻辑等。此外,作者还分享了一些调试经验和常见问题解决方案,如防止传感器抖动、优化画面流畅度等。 适合人群:从事工业自动化控制领域的工程师和技术人员,特别是对PLC编程和HMI设计有一定基础的学习者。 使用场景及目标:适用于需要理解和掌握立体车库自动化控制系统的人员,旨在帮助他们熟悉PLC与HMI的联合调试方法,提高实际项目的开发效率和稳定性。 其他说明:文末附有完整的工程文件下载链接,包括组态王HMI文件、S7-200程序和IO表说明文档,方便读者进行实践操作。

    【新能源汽车电子电气】基于功能域划分的动力系统通信网络与控制器需求分析:电子电气架构设计要点

    内容概要:本文详细介绍了新能源汽车电子电气系统功能需求及其架构特点。首先,文章阐述了新能源汽车的分类及其与传统汽车控制系统的主要差异,特别是动力系统的转变对电驱动系统、电池管理系统及电控系统提出的更高要求。接着,重点分析了新能源汽车动力域的通信网络,对比了CAN、CANFD、FlexRay和车载以太网四种网络技术的优缺点,指出CANFD和FlexRay是当前动力域内部通信网络的主流选择。此外,文章还探讨了动力域控制器的功能要求,强调了其在通信支持、软件实时性、安全等级和信息安全方面的关键作用。最后,文章讨论了新能源汽车底盘系统的特点,特别是线控底盘作为未来发展方向的重要性,以及底盘域控制器在智能驾驶中的核心地位。 适用人群:从事汽车电子工程及相关领域的工程师、研究人员和技术人员,尤其是关注新能源汽车发展的专业人士。 使用场景及目标:①帮助工程师理解新能源汽车电子电气架构的设计思路和技术难点;②为研发人员提供动力域通信网络选型的参考依据;③指导技术人员掌握动力域控制器和底盘域控制器的功能需求及发展趋势。 阅读建议:本文内容涵盖广泛,建议读者根据自身专业背景和兴趣点进行有针对性的阅读。对于初学者,可以从新能源汽车的基本概念入手,逐步深入到具体的通信网络和技术细节;对于有一定经验的研发人员,可以重点关注动力域控制器和线控底盘的相关内容,以获取最新的技术和应用趋势。

    飞机起落架落下试验塔sw10可编辑_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    飞机起落架落下试验塔sw10可编辑_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    基于Matlab/Simulink的模块化多电平换流器(MMC)仿真与控制优化

    内容概要:本文详细介绍了如何在Matlab/Simulink环境下构建和优化模块化多电平换流器(MMC)的仿真模型。主要内容涵盖子模块电容电压均衡、环流抑制、调制策略以及仿真参数设置等方面的技术细节。文中提供了具体的代码示例,如子模块参数初始化、电容排序控制、环流抑制控制器配置等,并分享了许多实用的经验和技巧,帮助读者避免常见的错误。 适合人群:从事电力电子研究和技术开发的专业人士,尤其是对MMC仿真感兴趣的工程师和研究人员。 使用场景及目标:适用于希望深入了解MMC内部机制及其控制方法的研究人员,旨在提高仿真效率和准确性,确保最终设计能够稳定可靠地运行。 其他说明:文章不仅提供了详细的理论解释,还包括大量实战经验和故障排除指南,有助于读者更好地理解和应用相关技术。同时强调了在实际硬件部署前进行充分仿真的重要性。

    轮腿式机器人sw21可编辑_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

    轮腿式机器人sw21可编辑_三维3D设计图纸_包括零件图_机械3D图可修改打包下载_三维3D设计图纸_包括零件图_机械3D图可修改打包下载.zip

Global site tag (gtag.js) - Google Analytics