系列文章目录

目录需要自己手动添加


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

目录

系列文章目录

文章目录

前言

1.设计(design)

2.基本前端(basic front-ends)

2.1.最简使用例程

2.1.1状态图

2.1.2 图例说明

2.1.3源代码解释:

2.2事件定义

2.3状态转换表

2.4定义包含入口和出口(entry/exit)的状态

2.5转换行为和守护条件实际行为中传递数据

2.6定义基本状态机

2.6.1定义和声明基本状态机的前端

2.6.2建立前端与后端连接

2.6.3测试

总结



前言

由于本人在开发一个新的视频模式识别产品中,会涉及到较多的各种状态变化,例如视频源经常由于网络原因或者视频源本生原因引起视频无法读取,读取完成后识别会涉及到多种场景,这些对应场景有些需要保存留作凭证,并同时上传第三方平台等,原有的状态管理太过庞杂造成而难于维护,故开始研究boost下的两个有限状态机以解决当前项目中问。理论上的材料完全依照官网英文材料(由于本人翻译及英文能力有限错误之处请读者谅解)。此文只要根据官网文档翻译而来,并结合笔者本人实际项目中的应用提供部分代码示例。Meta State Machine后续简称元状态机或者(MSM)——版权备注,此文档,未取得原作者授权,此文只记录本人学习理解过程。Chapter 3. Tutorial - 1.79.0https://www.boost.org/doc/libs/1_79_0/libs/msm/doc/HTML/ch03.html#d0e325


1.设计(design)

元状态机(以下简称状态机或者MSM)分为前端(front-ends)和后端(back-ends)。目前只有一个后端,前端有三个状态机语言(模型)前后端接口详见(前后端接口)。

首先前端采用动作适配(MPL book)连接状态和状态机。第二采用仿函数。第三采用boost的eUML——boost针对UML的验证性项目,使用了boost.Proto、Boost.Type和元编程技术增加可读性。eUML和仿函数的前端为用户提供各种动作列表方式。

2.基本前端(basic front-ends)

这个基本前端从MPL book项目延申而来,其提供一个用行方式提供状态转换表,动作(Actions)和状态转换条件(guards)采用方法实现(转换实体指针)。基本前端提供简单接口确保简单定义状态机的hi使用,但要实现更复杂的状体机比较困难。

2.1.最简使用例程

2.1.1状态图

转自原文

2.1.2 图例说明

1、此例演示一个播放器的操作按钮作为一个状态机实例。此状态表示当前CD播放器按钮有:1)打开/关闭——放置cd的抽屉;2)停止——停止播放;3)暂停——暂停播放;4)播放;

原教程代码请参见libs/msm/doc/HTML/examples/SimpleTutorial.cpp - 1.79.0

2.1.3源代码解释:

1、事件定义(详细见2.2事件定义

1)首先定义操作事件结构体:play(播放)、end_pause(结束暂停(继续))、 stop(停止)、pause(暂停)、open_close(开关cd盒);2)定义一个内部事件:cd_detected(cd歌曲信息检查,通过这个事件可以定义检查后,显示这些歌曲信息)内部事件,包含事件本身信息,可以为后续事件使用和处理,且可以通过结构构造函数初始化(如下):

struct play {};
.....
struct cd_detected {cd_detected(std::string name, DiskTypeEnum diskType): name(name),disc_type(diskType) {}std::string name;DiskTypeEnum disc_type;
};

2、定义前端状态机类从源代码--player_  如下

struct player_ : public msm::front::state_machine_def<player_>
{.......void no_transition(Event const& e, FSM&,int state){std::cout << "no transition from state " << state<< " on event " << typeid(e).name() << std::endl;}
};

在这个基本前端中包含以下要素代码:

1)状态定义:状态Empty\Open\Stoped\Playing状态包含出入口状态on_entry()/on_exit();其中stopped状态还包含状态机指针方法。Paused状态没有出入口(on_entry()/on_exit();)(详细见2.4)Stoped状态采用set_sm_ptr(),目前这个方法被废弃,不建议使用,建议用后续仿函数方式实现。

// The list of FSM states
struct Empty : public msm::front::state<> {// every (optional) entry/exit methods get the event passed.template <class Event,class FSM>void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;}template <class Event,class FSM>void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} };struct Open : public msm::front::state<> {   template <class Event,class FSM>void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;}template <class Event,class FSM>void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;}};// sm_ptr still supported but deprecated as functors are a much better way to do the same thing
struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr>
{    template <class Event,class FSM>void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;}template <class Event,class FSM>void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;}void set_sm_ptr(player_* pl)   {m_player=pl;
}
player_* m_player;
};struct Playing : public msm::front::state<>
{template <class Event,class FSM>void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;}template <class Event,class FSM>void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;}
};// state not defining any entry or exit
struct Paused : public msm::front::state<>{
};

3、定义初始状态

// the initial state of the player SM. Must be defined
typedef Empty initial_state;

4、状态转换函数;守护(条件函数)(具体定义见2.5

 // transition actionsvoid start_playback(play const&)       { std::cout << "player::start_playback\n"; }void open_drawer(open_close const&)    { std::cout << "player::open_drawer\n"; }void close_drawer(open_close const&)   { std::cout << "player::close_drawer\n"; }void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; }void stop_playback(stop const&)        { std::cout << "player::stop_playback\n"; }void pause_playback(pause const&)      { std::cout << "player::pause_playback\n"; }void resume_playback(end_pause const&)      { std::cout << "player::resume_playback\n"; }void stop_and_open(open_close const&)  { std::cout << "player::stop_and_open\n"; }void stopped_again(stop const&)  {std::cout << "player::stopped_again\n";}// guard conditionsbool good_disk_format(cd_detected const& evt){// to test a guard condition, let's say we understand only CDs, not DVDif (evt.disc_type != DISK_CD){std::cout << "wrong disk, sorry" << std::endl;return false;}return true;}// used to show a transition conflict. This guard will simply deactivate one transition and thus// solve the conflictbool auto_start(cd_detected const&){return false;}

4、定义前端对象,以情况状态和提供状态转换表的状态转换函数和条件函数

typedef player_ p; // makes transition table cleaner

5、定义状态转换表(详细见状态2.3状态转换表

 // Transition table for playerstruct transition_table : mpl::vector<//    Start     Event         Next      Action                Guard//  +---------+-------------+---------+---------------------+----------------------+a_row < Stopped , play        , Playing , &p::start_playback                         >,a_row < Stopped , open_close  , Open    , &p::open_drawer                            >,_row < Stopped , stop        , Stopped                                              >,//  +---------+-------------+---------+---------------------+----------------------+a_row < Open    , open_close  , Empty   , &p::close_drawer                           >,//  +---------+-------------+---------+---------------------+----------------------+a_row < Empty   , open_close  , Open    , &p::open_drawer                            >,row < Empty   , cd_detected , Stopped , &p::store_cd_info   ,&p::good_disk_format  >,row < Empty   , cd_detected , Playing , &p::store_cd_info   ,&p::auto_start        >,//  +---------+-------------+---------+---------------------+----------------------+a_row < Playing , stop        , Stopped , &p::stop_playback                          >,a_row < Playing , pause       , Paused  , &p::pause_playback                         >,a_row < Playing , open_close  , Open    , &p::stop_and_open                          >,//  +---------+-------------+---------+---------------------+----------------------+a_row < Paused  , end_pause   , Playing , &p::resume_playback                        >,a_row < Paused  , stop        , Stopped , &p::stop_playback                          >,a_row < Paused  , open_close  , Open    , &p::stop_and_open                          >//  +---------+-------------+---------+---------------------+----------------------+> {};

5、自动定义无状态转换函数

  template <class FSM,class Event>void no_transition(Event const& e, FSM&,int state){std::cout << "no transition from state " << state<< " on event " << typeid(e).name() << std::endl;}};

6、定义状态机即连接前后端

 typedef msm::back::state_machine<player_> player;

7、显示当前状态即状体名称

 static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };void pstate(player const& p){std::cout << " -> " << state_names[p.current_state()[0]] << std::endl;}

8、测试状态机

 void test(){        player p;// needed to start the highest-level SM. This will call on_entry and mark the start of the SMp.start(); // go to Open, call on_exit on Empty, then action, then on_entry on Openp.process_event(open_close()); pstate(p);p.process_event(open_close()); pstate(p);// will be rejected, wrong disk typep.process_event(cd_detected("louie, louie",DISK_DVD)); pstate(p);p.process_event(cd_detected("louie, louie",DISK_CD)); pstate(p);p.process_event(play());// at this point, Play is active      p.process_event(pause()); pstate(p);// go back to Playingp.process_event(end_pause());  pstate(p);p.process_event(pause()); pstate(p);p.process_event(stop());  pstate(p);// event leading to the same state// no action method called as it is not present in the transition tablep.process_event(stop());  pstate(p);std::cout << "stop fsm" << std::endl;p.stop();}

2.2事件定义

事件定义简单,也可以复杂,在简单状态转换中可以定能一一个不含任何属性和方法的结构体如struct paly{},也可以定义含有自身属性的结构体例如cd_detected如下。

struct play {};
struct cd_detected {cd_detected(std::string name, DiskTypeEnum diskType): name(name),disc_type(diskType) {}std::string name;DiskTypeEnum disc_type;
};

2.3状态转换表

转换表是状态机的灵魂,其用一行表示一个状态转换,习惯上需要每个状态下能转换到的各种状态集中相邻描述。实际上转换表是一个MPL vector组件内的vector。

转换表注意事项:1)并且这个veector默认大小可以容纳20个转表表,如果需要使用更大的状态转换表需要牢记定义一下宏。2)定义转换表是需要实例化前端实例typedef player_ p; 且在前端类中定义。

修改运行允许转换表最大状态转换项:

#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
#define BOOST_MPL_LIMIT_VECTOR_SIZE 30 //or whatever you need
#define BOOST_MPL_LIMIT_MAP_SIZE 30 //or whatever you need     

状态转换表:

typedef player_ p; // makes transition table cleaner// Transition table for player
struct transition_table : mpl::vector<
//    Start     Event         Next      Action               Guard
//  +---------+-------------+---------+---------------------+----------------------+
a_row < Stopped , play        , Playing , &p::start_playback                         >,
a_row < Stopped , open_close  , Open    , &p::open_drawer                            >,_row < Stopped , stop        , Stopped                                              >,
//  +---------+-------------+---------+---------------------+----------------------+
a_row < Open    , open_close  , Empty   , &p::close_drawer                           >,
//  +---------+-------------+---------+---------------------+----------------------+
a_row < Empty   , open_close  , Open    , &p::open_drawer                            >,row < Empty   , cd_detected , Stopped , &p::store_cd_info   ,&p::good_disk_format  >,row < Empty   , cd_detected , Playing , &p::store_cd_info   ,&p::auto_start        >,
//  +---------+-------------+---------+---------------------+----------------------+
a_row < Playing , stop        , Stopped , &p::stop_playback                          >,
a_row < Playing , pause       , Paused  , &p::pause_playback                         >,
a_row < Playing , open_close  , Open    , &p::stop_and_open                          >,
//  +---------+-------------+---------+---------------------+----------------------+
a_row < Paused  , end_pause   , Playing , &p::resume_playback                        >,
a_row < Paused  , stop        , Stopped , &p::stop_playback                          >,
a_row < Paused  , open_close  , Open    , &p::stop_and_open                          >//  +---------+-------------+---------+---------------------+----------------------+
> {};

转换表中描述状态转换的row总共有五个模板参数——开始状态-->触发事件-->到达的状态-->行为(软件内部需要完成的业务操作)-->守护条件(个人理解是要发生这种转化是存在的内部条件),对应上述转换有4种基本的*row分别定义:

1)row----5个组成部分(start state, event, target state, action and guard)全部需要采用row方式,例如

row< Empty   , cd_detected,  Stopped  , &player_::store_cd_info   , &player_::good_disk_format >

2)a_row (“a” for action)定义一个行为但省略了守护条件(guard)

a_row< Stopped , play       ,  Playing  , &player_::start_playback           >

3)g_row (“g” for guard)定义一个没有行为,但有守护条件的转换表

4)_row定义一个没有行为和守护条件的状态转换表,常用在在一个状态下,再触发对应进入到次状态下事件的行为,也就是不做任何操作和状态转换,例如在停止状态下按了停止按钮。

_row< Stopped , stop       ,  Stopped              >

3动作定义(action 函数定义),这个函数形式为void anction_name(event const & ),这个函数,如果不需event附带信息,则可以定义一个没有行参名字的event,这可以提升效率(个人理解为,在调用时由于传入的实参,不需要使用信息,编译可以避免使用内存分配或者及时销毁甚至优化掉)如:void stop_playback(stop const&)。定义action的几个注意事项:1)函数无返回值;2)不可修改应勇(const &);3)如果不需要事件本省信息,可以省略形参名,根据编译器可能会优化效率;4)不必用一个action对应多个事件;

4守护条件(Guards)也是采用函数定义方式实现,且需要返回值必须是bool值; 例如:bool good_disk_format(cd_detected const& evt)。

2.4定义包含入口和出口(entry/exit)的状态

定义一个状态目前允许定义一个类或者结构,并可包含数据,以方便出入口函数中使用完成具体业务,并且需要继承自简单状态,例如。定义一个空闲状态struct Empty : public msm::front::state<> {};状态的出入口根据是否需要可选,如果选择在进入和退出时完成具体业务,则必须按照如下格式定义on_entry(),on_exit()

truct Empty : public msm::front::state<>
{template <class Event, class Fsm> void on_entry(Event const&, Fsm& ) {std::cout <<"entering: Empty" << std::endl;} template <class Event, class Fsm> void on_exit(Event const&, Fsm& ) {std::cout <<"leaving: Empty" << std::endl;}
};

此次简单定义出入口行为,至于其他UML中其他状态行为(终止、中断、伪装态等等)下一章节介绍。

2.5转换行为和守护条件实际行为中传递数据

在状态机种定义一个结构体作为事件和完成转换是非常重要的一环,但不是全部,在实际应用中需要赋予行为转换中更多功能。例如需要发送一个火箭到“半人头马阿法星”,定义的转换行为(action)如下:

//事件
struct Fire{};
template <class Fire> void send_rocket(Fire const&)
{fire_rocket();
}

这个最简单的状态转换,只有事件结构体本身,其本身可以不含任何实体对象,只是实现了发射,但如果我们需要明确发送到“半人头马阿法星”,如何明确让“fire_rock()”发送到指定位置呢,可以有两种方法,第一个在fire_rocket()明确,但这种方式发送到其他方向时,有需要重新修改,这种方式不易重用和修改。要实现可重用,我们可以修改定义struct Fire{Direction direction;};再修改fire_rocket(evt.direction);这个修改实现了外部传入方向。

// Event
struct Fire {Direction direction;};
template <class Fire> void send_rocket(Fire const& evt)
{fire_rocket(evt.direction);
}

上述方法中,我们可以获取外部数据,但我们也希望前端保存自己的内部状态或者数据,则可以安装如下定义前端,在laucher_内部保存一个Data,然后在调用 “fire_rocket(evt.direction, current_calculation)”使用它。

// Event
struct Fire {Direction direction;};
//front-end definition, see down
struct launcher_ : public msm::front::state_machine_def<launcher_>{
Data current_calculation;
template <class Fire> void send_rocket(Fire const& evt)
{fire_rocket(evt.direction, current_calculation);
}
...
};

对于一个状态状态的进入和退出是一个通用行为,不管用何种方式进入和退出,要求状态的可重用,那么可以把所需要的自定义数据放入到状态机中,为不是事件中,这样可以实现最大可重用这个数据和更可读性。出入口访问状态数据同时可以访问事件、状态机,这种转换行为的事件和状态机的模板如下

struct Launching : public msm::front::state<>
{template <class Event, class Fsm> void on_entry(Event const& evt, Fsm& fsm) {fire_rocket(evt.direction, fsm.current_calculation);}
};

另外一种让入口行为传递数据给子状态或者之状态机,Launching是Laucher_的子状态(子状态机),入口(on_entry(Event const &evt,Fsm & fsm))可以直接访问到自定义的数据current_calculation,并放入要进入的子状态(Lauching)s.data中,实现数据在各种状态的传递。

struct launcher_ : public msm::front::state_machine_def<launcher_>{
Data current_calculation;
// state machines also have entry/exit actions
template <class Event, class Fsm>
void on_entry(Event const& evt, Fsm& fsm)
{launcher_::Launching& s = fsm.get_state<launcher_::Launching&>();s.data = fsm.current_calculation;
}
...
};

除了上述方法外,用set_states后端方法也可以实现上述方式。

2.6定义基本状态机

2.6.1定义和声明基本状态机的前端

首先通过继承方式定义一个具体的前端也就是状态机定义。这个player_继承自msm::front::state_machine_def<plaer_>。在这个结构体内,需要定义action、guards的方法(函数)。

struct player_ : public msm::front::state_machine_def<player_>{   /* see below */}

结构体体内部需要定义:

1)出入口即on_entry()、on_exit();   详细见2.4部分的on_entry()、on_exit();

struct player_ : public msm::front::state_machine_def<player_> {template <class Event,class FSM>void on_entry(Event const& ,FSM&) {std::cout << "entering: Player" << std::endl;}template <class Event,class FSM>void on_exit(Event const&,FSM& ) {std::cout << "leaving: Player" << std::endl;}
.....

2)定义各种状态结构体,可以定义在状态机内部,且每个状态必须实现on_entry()、on_exit();   详细见2.4部分的on_entry()、on_exit();——备注各种状态可以在任何地方什么,但为了方便阅读,习惯性定义到状态结构体内部——这类似内嵌类

 struct Empty : public msm::front::state<> {// every (optional) entry/exit methods get the event passed.template <class Event,class FSM>void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;}template <class Event,class FSM>void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;}};

3)声明起始状态——必须声明一个初始状态

typedef Empty initial_state;

4)定义状态转换函数——状态转换函数是状态转换表中的要素之一

void start_playback(play const&) { std::cout << "player::start_playback\n"; }

5)定义一个自定义的事件没有对应的状态转表表的转换以替换默认转换函数,可以通过这个函数中实现异常处理;

 void no_transition(Event const& e, FSM&,int state)   {std::cout << "no transition from state " << state<< " on event " << typeid(e).name() << std::endl;
}

6)申明一个前端即此定义的实例,用于对应7)状态转换表的转换函数引用;

typedef player_ p; // makes transition table cleaner

7)根据状态转换图(2.1.1状态图)初始化状态转换表(详细设置见2.3状态转换表);

2.6.2建立前端与后端连接

typedef msm::back::state_machine<player_> player;

2.6.3测试

通过以上2.6.1和2.6.2一个基本前端的简单状态即的过程和组成。可以通过以下 测试状态机的工作机制和实现情况。

1、方便查看测试情况,首先定义一个函数

void pstate(player const& p)  {std::cout << " -> " << state_names[p.current_state()[0]] << std::endl;
}

2、测试函数

void test_playerofmsm(){
player p;   //------------L1
// needed to start the highest-level SM. This will call on_entry and mark the start of the SM
p.start();   //------------L2
// go to Open, call on_exit on Empty, then action, then on_entry on Open
p.process_event(open_close()); pstate(p);//------------L3
p.process_event(open_close()); pstate(p);//------------L4
// will be rejected, wrong disk type
p.process_event(cd_detected("louie, louie",DISK_DVD)); pstate(p);//------------L5
p.process_event(cd_detected("louie, louie",DISK_CD)); pstate(p);------------L6
p.process_event(play());pstate(p);//------------L7// at this point, Play is active
p.process_event(pause()); pstate(p);//------------L8
// go back to Playing
p.process_event(end_pause());  pstate(p);//------------L9
p.process_event(pause()); pstate(p);//------------L10
p.process_event(stop());  pstate(p);//------------L11
// event leading to the same state
// no action method called as it is not present in the transition table
p.process_event(stop());  pstate(p);//------------L12
std::cout << "stop fsm" << std::endl;//------------L13
p.stop();//------------L14
}

运行上述代码后可以见到如下输出

---------1---------
entering: Player
entering: Empty
---------2---------
leaving: Empty
player::open_drawer
entering: Open-> Open
---------3---------
leaving: Open
player::close_drawer
entering: Empty-> Empty
---------4---------
wrong disk, sorry-> Empty
---------5---------
leaving: Empty
player::store_cd_info
entering: Stopped-> Stopped
---------6---------
leaving: Stopped
player::start_playback
entering: Playing-> Playing
---------7---------
leaving: Playing
player::pause_playback-> Paused
---------8---------
player::resume_playback
entering: Playing-> Playing
---------9---------
leaving: Playing
player::pause_playback-> Paused
---------10---------
player::stop_playback
entering: Stopped-> Stopped
---------11---------
leaving: Stopped
entering: Stopped-> Stopped
stop fsm
leaving: Stopped
leaving: Player

1) L1行初始化一个状态机;——调用的是默认函数即即2.6.2对应的后端状态机;

2)L2启动状态机——其会显示“1”和2之间内容,实际上实行了状态机的on_entry()函数和默认初始状态Empty状态的on_entry()状态转换;

3)L3相当于按了“开/关”CD抽屉的事件,根据状态转表“a_row < Empty   , open_close  , Open    , &p::open_drawer   >”同时会执行:1)Empty状态的on_exit();2)void open_drawer(open_close const&)    { std::cout << "player::open_drawer\n"; };3)Open状态的on_entry();可以看到如下

“2”~“3”之间的内容。

struct Open : public msm::front::state<> {template <class Event,class FSM>void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;}template <class Event,class FSM>void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;}
};

4)L4d代码会从打开状态转换为关闭状态,也会经历退出打开状态和进入关闭状态。L3、L4同一个事件出发,但会在两种状态切换,很方便管理了一个按钮或者一种事件的两种处理结果。

5)L5、L6演示了带内部属性的事件cd_detected和带有守护条件的状态转换表

带属性事件——详细见事件定义

     enum DiskTypeEnum{DISK_CD=0,DISK_DVD=1};struct cd_detected{cd_detected(std::string name, DiskTypeEnum diskType): name(name),disc_type(diskType){}std::string name;DiskTypeEnum disc_type;};

状态转换表:包含两个守护条件——是否为CD和是否直接运行,L5创建的事件cd_detected("louie, louie",DISK_DVD),DVD类型的事件,故其运行后运行了磁盘错误并返回到Empty状态,没有按照转换表进入存储磁盘信息状态,停留在Empty状态。但运行L6后由于外部事件属性是CD故其根据守护条件进入到了储存歌曲信息状态。由于 auto_start() 返回false,而无法进入到Play状态,实际上进入了停止状态   ——这一状态转换,没有地方详细介绍,个人猜测——由于需要auto_start()为真才能进入到Playing状态,根据状态转换表Playing-->stop-->stoped。采用

row < Empty   , cd_detected , Stopped , &p::store_cd_info   ,&p::good_disk_format  >,
row < Empty   , cd_detected , Playing , &p::store_cd_info   ,&p::auto_start        >,

总结

1、从分析测试程序中,程序代码按照状态转换表的转换状态正常转换;

2、但由于守护条件L6处,虽然事件列表的转换条件只有在cd信息正常且设置自动播放下,会直接在进入Palyinng状态即p.process_event(cd_detected("louie, louie",DISK_CD)); pstate(p);------------L6 如何守护条件返回false下,如何进入到Stoped,而没有保留在mpty状态

BOOST元状态机用户手册之三教程(Meta State Machine (MSM))(1)——基本前端及例程相关推荐

  1. 关于Unity动画状态机Animator使用教程

    关于Unity动画状态机Animator使用教程 目录一.前言二.Animator组件三.AnimatorController文件四.AnimationClip文件五.状态机的状态(State)1.A ...

  2. 日本京都大学 77TB重要数据被删;元宇宙要凉?Meta停止开发 VR/AR系统。 | 一周 IT 资讯...

    惠普公司闯大祸!日本京都大学超算系统77TB重要数据被删:滴滴宣布裁员,幅度达20%:Meta 停止开发 VR/AR 操作系统,独立自主计划受挫:2021年编程语言排行榜出炉,Python稳居榜首:T ...

  3. 【翻译】What is State Machine Diagram(什么是状态机图)?

    [翻译]What is State Machine Diagram(什么是状态机图)? 写在前面 在上一篇学习类图的时候将这个网站上的类图的一篇文章翻译了出来,感觉受益良多,今天来学习UML状态机图, ...

  4. Meta-Weight-Net[NIPS‘2019]:关于元学习/域自适应(meta learning/domain adaptation)优化噪声标签与类别不平衡的问题

    目录 研究背景 一.为什么存在类别不平衡现象? 二. Meta-Weight-Net[NIPS'2019] 1.Focal Loss 2.self-pacd learning 3.Meta-Weigh ...

  5. Android系统自带的层次状态机StateMachine(Hierarchical State Machine)

    Android系统自带的层次状态机StateMachine(Hierarchical State Machine) Android在framework层自己实现一套层次状态机,总共有三个类:State ...

  6. Mina State machine状态机讲解

    原文地址:Mina State machine(Apache Mina User guide Chapter14 State machine) 如果您使用的是Mina与复杂网络开发应用程序交互,你可能 ...

  7. Android官方实现的层次状态机Hierarchical State Machine源代码

    Android官方内部的源代码中实现了一套层次状态机(Hierarchical State Machine),总共有三个代码文件:IState.java , State.java, StateMach ...

  8. [Dreamweaver教程]Meta标签详解

    [Dreamweaver教程]Meta标签详解 原作者:indian 出处:5D多媒体 发表时间:2004-12-8 关键词: 版权信息:传统媒体及商业网站禁止擅自转载:个人网站转载需经作者同意,并注 ...

  9. 烧掉300亿美元,小扎和元宇宙说再见!Meta转投AIGC,制定搞钱时间表

      视学算法报道   编辑:Aeneas 好困 [导读]Meta已经成立机构,专门研究生成式AI,并计划今年12月前完成商业化.看到小扎终于不在元宇宙上一条路走到黑,Meta的投资人们总算要松一口气了 ...

  10. 【Badusb】6元钱制作Badusb教程

    [Badusb]6元钱制作Badusb教程 在公司前一段时间了解了一些社工类产品,其中的U盘引起了我的注意,于是在网上查阅了许多资料完成制作.以下为关于Badusb的知识和制作教程: 1.概述 在20 ...

最新文章

  1. 京东姚霆:推理能力,正是多模态技术未来亟需突破的瓶颈!
  2. Linux网络编程实例详解
  3. windows 安装 python3
  4. mysql proxy yum_mysql 高可用架构 proxysql 之一 yum安装
  5. 老罗Android开发视频教程( android解析json数据 )4集集合
  6. Vue生命周期和钩子函数的一些理解
  7. SLAM_三维点优化时为什么使用逆深度误差而不是深度误差?
  8. 计算机速录水平考试,汉字速录水平测试(速录证考试报名网站)
  9. 【VS Code 神奇小插件】Code Runner
  10. 【Python】Pyside6简易版教程
  11. 华为的王炸黑科技鸿蒙系统,华为黑科技“cyberverse”发布,“鸿蒙系统”在其面前都不值一提...
  12. 阿里云 ACP 认证,分享下经验心得
  13. 用友U8 cloud再升级,为成长型集团注入数智新力量
  14. 详解人工智能(AI) 机器学习(ML) 深度学习(DL)
  15. 16.04编译android 7.0,[原创]使用ubuntu 16.04编译android-6.0.0_r1
  16. hive安装及整合hbase
  17. dz2.5 oday
  18. AD09 DXP保姆级教程系列——005、原理图生成PCB、裁剪板子、3维查看、封装PCB元件
  19. 芯片规格导致解码问题
  20. WWW2023推荐系统论文集锦,推荐系统方向占比高达72/365

热门文章

  1. wincc7.0显示无法访问服务器,WinCC 7.0 SP3 安装时提示网络连接不可用,无法安装...
  2. SAT数学解题方法总结
  3. Flutter - 图片/视频选择器(支持拍照及录制视频)
  4. 取消管理员取得所有权_win7怎么获取得管理员所有权
  5. 云服务器怎么存文件大小,云服务器怎么存文件大小
  6. 金三银四找工作,真没你想的那么难!
  7. 计算二叉树中各结点的最大元素的值
  8. 微信小程序一键连接已知wifi
  9. IT:如何把骨干留住
  10. Xcode6 打包报错 ITMS-90096