Agile Modbus移植教程--基于GD32F103C8T6+RT-Thread+mdk5

news2024/9/22 17:23:17

主机移植

0.下载源码

开源地址:GitHub - loogg/agile_modbus

1.复制源码

1.2、目录结构

名称说明
doc文档
examples例子参考示例
figures素材
inc头文件移植需要
src源代码移植需要
util提供简单实用的组件移植需要

本次移植需要的有

  • 参考demo

  • 头文件

  • 源码

  • 从机辅助文件


在这里插入图片描述

2.添加源码

在这里插入图片描述

3.头文件

在这里插入图片描述

4.接口实现

1.串口发送接口

​ 串口的发送时基于发送缓冲为空的基础实现

2.接收一帧完整数据接口

基于RT-Thread前提下,本次移植使用的串口中断方式接收数据,每次收到一个数据就开启一个单次定时器,在定时器未超时之前收到数据立即重置定时器,当定时器超时时表示一帧数据接收完毕,完毕后使用信号量通知主线程

3.本次实现基于GD32F103C8T6+RT-Thread+mdk5

5.主机使用示例

#include "agile_modbus.h"
#include <stdio.h>
#include <stdlib.h>

#include "bsp_uart.h"
#include "main.h"
#include "bsp_timer_base.h"
#include "rtthread.h"


static uint8_t ctx_send_buf[AGILE_MODBUS_MAX_ADU_LENGTH];//发送缓存
static uint8_t ctx_read_buf[AGILE_MODBUS_MAX_ADU_LENGTH];//接收缓存
static uint16_t ctx_read_buf_index=0;
static rt_timer_t timer1;
/* 指向信号量的指针 */
static rt_sem_t dynamic_sem = RT_NULL;
/* 定时器 1 超时函数 */
static void timeout1(void *parameter)
{
    //通知数据接收完毕
    rt_timer_stop(timer1);
    rt_sem_release(dynamic_sem);

}

static void modbus_recv_callback(uint8_t ch)
{

    /* 重置定时器 */
    if (timer1 != RT_NULL)
    {
        rt_timer_stop(timer1);
        rt_timer_start(timer1);
    }
    //保存数据
    ctx_read_buf[ctx_read_buf_index++%AGILE_MODBUS_MAX_ADU_LENGTH]=ch;
}

void  modbus_cycle_entry(void)
{

    uint16_t hold_register[10];
    uint8_t coils[10];

    agile_modbus_rtu_t ctx_rtu;
    agile_modbus_t *ctx = &ctx_rtu._ctx;
    //rtu初始化
    agile_modbus_rtu_init(&ctx_rtu, ctx_send_buf, sizeof(ctx_send_buf), ctx_read_buf, sizeof(ctx_read_buf));
    //设置从机地址
    agile_modbus_set_slave(ctx, 1);

    rt_kprintf("agile_modbus master runing...\n");
    //设置串口回调函数
    bsp_uart_set_recv_callback(BSP_UART_ID_0,modbus_recv_callback);
    /* 创建定时器 1 周期定时器 */
    timer1 = rt_timer_create("timer1", timeout1,
                             RT_NULL, 5,
                             RT_TIMER_FLAG_PERIODIC);
    /* 创建一个动态信号量,初始值是 0 ,用于通知一帧数据接收完毕*/
    dynamic_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_PRIO);
    while (1) {

        rt_thread_delay(1000*5);
        //构建线圈读取指令
        int send_len = agile_modbus_serialize_read_bits(ctx, 0, 10);
        //清空数据
        ctx_read_buf_index=0;
        //发送数据
        bsp_uart_send_datas(BSP_UART_ID_0,ctx->send_buf, send_len);
        //等待数据接收完毕
        int  result = rt_sem_take(dynamic_sem, 1000);//RT_WAITING_FOREVER

        if (RT_EOK ==result)
        {
            //解析数据
            int rc = agile_modbus_deserialize_read_bits(ctx, ctx_read_buf_index, coils);

            if (rc >0)
            {
                rt_kprintf("Coils:[");
                for (int i = 0; i < 10; i++)
                {
                    rt_kprintf(" %d", coils[i]);

                }
                rt_kprintf(" ]\n");
            }
            else {
                rt_kprintf("exp code %d\n",-128 - rc);//得到异常码
            }

        }


        /**************************/
        //构建保持寄存器读取指令
        send_len = agile_modbus_serialize_read_registers(ctx, 0, 10);
        //清空缓存
        ctx_read_buf_index=0;
        //发送
        bsp_uart_send_datas(BSP_UART_ID_0,ctx->send_buf, send_len);
        //等待从机回复
        result = rt_sem_take(dynamic_sem, 1000);


        if (RT_EOK ==result)
        {
            //解析保持寄存器数据
            int   rc = agile_modbus_deserialize_read_registers(ctx, ctx_read_buf_index, hold_register);
            if (rc >0 )
            {
                rt_kprintf("Hold Registers:[");
                for (int i = 0; i < 10; i++)
                {
                    rt_kprintf(" 0x%04X", hold_register[i]);

                }
                rt_kprintf(" ]\n");
            }
            else {
                rt_kprintf(" exp code %d\n",-128 - rc);
            }
        }



    }


}

从机移植

从机的移植和主机一样,需要提供串口的发送接口,一帧数据接收接口

从机使用流程

#include "agile_modbus.h"
#include <stdio.h>
#include <stdlib.h>
#include "agile_modbus_slave_util.h"
#include "bsp_uart.h"
#include "main.h"
#include "bsp_timer_base.h"
#include "rtthread.h"
//这个定义在bits.c中
extern const agile_modbus_slave_util_map_t bit_maps[1];
//这个定义在input_bits.c中
extern const agile_modbus_slave_util_map_t input_bit_maps[1];
//这个定义在registers.c中
extern const agile_modbus_slave_util_map_t register_maps[1];
//这个定义在input_registers.c中
extern const agile_modbus_slave_util_map_t input_register_maps[1];
 //发送缓存
static     uint8_t ctx_send_buf[AGILE_MODBUS_MAX_ADU_LENGTH];
//接收缓存
static    uint8_t ctx_read_buf[AGILE_MODBUS_MAX_ADU_LENGTH];
//接收数据长度
static  uint16_t ctx_read_buf_index=0;
static rt_timer_t timer1;
/* 指向信号量的指针 */
static rt_sem_t dynamic_sem = RT_NULL;
/* 定时器 1 超时函数 */
static void timeout1(void *parameter)
{

    rt_timer_stop(timer1);
    rt_sem_release(dynamic_sem);

}

static void modbus_recv_callback(uint8_t ch)
{

    /* 启动定时器 1 */
    if (timer1 != RT_NULL)
    {
        rt_timer_stop(timer1);
        rt_timer_start(timer1);
    }

    ctx_read_buf[ctx_read_buf_index++%AGILE_MODBUS_MAX_ADU_LENGTH]=ch;
}




static int addr_check(agile_modbus_t *ctx, struct agile_modbus_slave_info *slave_info)
{
    int slave = slave_info->sft->slave;
    if ((slave != ctx->slave) && (slave != AGILE_MODBUS_BROADCAST_ADDRESS) && (slave != 0xFF))
        return -AGILE_MODBUS_EXCEPTION_UNKNOW;

    return 0;
}

const agile_modbus_slave_util_t slave_util = {
    bit_maps,
    sizeof(bit_maps) / sizeof(bit_maps[0]),
    input_bit_maps,
    sizeof(input_bit_maps) / sizeof(input_bit_maps[0]),
    register_maps,
    sizeof(register_maps) / sizeof(register_maps[0]),
    input_register_maps,
    sizeof(input_register_maps) / sizeof(input_register_maps[0]),
    addr_check,
    NULL,
    NULL};

 void rtu_slave_entry(void )
{


    agile_modbus_rtu_t ctx_rtu;
    agile_modbus_t *ctx = &ctx_rtu._ctx;
    agile_modbus_rtu_init(&ctx_rtu, ctx_send_buf, sizeof(ctx_send_buf), ctx_read_buf, sizeof(ctx_read_buf));
    agile_modbus_set_slave(ctx, 1);
	
	
	    bsp_uart_set_recv_callback(BSP_UART_ID_0,modbus_recv_callback);
    /* 创建定时器 1 周期定时器 */
    timer1 = rt_timer_create("timer1", timeout1,
                             RT_NULL, 10,
                             RT_TIMER_FLAG_PERIODIC);
    /* 创建一个动态信号量,初始值是 0 */
    dynamic_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_PRIO);

    rt_kprintf("Running.");

    while (1) {
      
				ctx_read_buf_index=0;
        int  result = rt_sem_take(dynamic_sem, RT_WAITING_FOREVER);//
			  if (RT_EOK ==result) 
				{
					 int send_len = agile_modbus_slave_handle(ctx, ctx_read_buf_index, 0, agile_modbus_slave_util_callback, &slave_util, NULL);
					if(send_len>0)
					{
						bsp_uart_send_datas(BSP_UART_ID_0,ctx->send_buf, send_len);
					}
				}

      
    }

}

定义一些从机相关的寄存器

bits.c

#include "slave.h"
/*

	01指令 读取线圈寄存器
*/
static uint8_t _tab_bits[10] = {0, 0, 0, 1, 0, 1, 0, 1, 1, 0};//0xA801 

static int get_map_buf(void *buf, int bufsz)
{
    uint8_t *ptr = (uint8_t *)buf;

   // pthread_mutex_lock(&slave_mtx);
    for (int i = 0; i < sizeof(_tab_bits); i++) {
        ptr[i] = _tab_bits[i];
    }
   // pthread_mutex_unlock(&slave_mtx);

    return 0;
}

static int set_map_buf(int index, int len, void *buf, int bufsz)
{
    uint8_t *ptr = (uint8_t *)buf;

   // pthread_mutex_lock(&slave_mtx);
    for (int i = 0; i < len; i++) {
        _tab_bits[index + i] = ptr[index + i];
    }
   // pthread_mutex_unlock(&slave_mtx);

    return 0;
}
/*
	初始化线圈,
	1.设置开始结束地址
	2.设置get和set接口
*/
const agile_modbus_slave_util_map_t bit_maps[1] = {
    {50, 59, get_map_buf, set_map_buf}};

input_bits.c

#include "slave.h"
/*

	02指令 读取离散输入寄存器
	
	响应各离散输入寄存器状态,分别对应数据区中的每位值,1 代表 ON;0 代表 OFF。
	第一个数据字节的 LSB(最低字节)为查询的寻址地址,其他输入口按顺序在该字节中由低字节向高字节排列,直到填充满 8 位。下一个字节中的 8 个输入位也是从低字节到高字节排列。若返回的输入位数不是 8
的倍数,则在最后的数据字节中的剩余位至该字节的最高位使用 0 填充
*/
static uint8_t _tab_input_bits[10] = {1, 1, 1, 0, 0, 1, 1, 0, 1, 1};//0x6703  

static int get_map_buf(void *buf, int bufsz)
{
    uint8_t *ptr = (uint8_t *)buf;

//    pthread_mutex_lock(&slave_mtx);
    for (int i = 0; i < sizeof(_tab_input_bits); i++) {
        ptr[i] = _tab_input_bits[i];
    }
//    pthread_mutex_unlock(&slave_mtx);

    return 0;
}
/*
	初始化输入离散数据,
	1.设置开始结束地址
	2.设置get和set接口
*/
const agile_modbus_slave_util_map_t input_bit_maps[1] = {
    {10, 19, get_map_buf, NULL}};

register.c

#include "slave.h"

/*

	03指令 读取保持寄存器
*/
static uint16_t _tab_registers[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

static int get_map_buf(void *buf, int bufsz)
{
    uint16_t *ptr = (uint16_t *)buf;

   
    for (int i = 0; i < sizeof(_tab_registers) / sizeof(_tab_registers[0]); i++) {
        ptr[i] = _tab_registers[i];
    }
   

    return 0;
}

static int set_map_buf(int index, int len, void *buf, int bufsz)
{
    uint16_t *ptr = (uint16_t *)buf;

   // pthread_mutex_lock(&slave_mtx);
    for (int i = 0; i < len; i++) {
        _tab_registers[index + i] = ptr[index + i];
    }
   // pthread_mutex_unlock(&slave_mtx);

    return 0;
}
/*
	初始化保持寄存器
	1.设置开始结束地址
	2.设置get和set接口
*/
const agile_modbus_slave_util_map_t register_maps[1] = {
    {30, 39, get_map_buf, set_map_buf}};

input_register.c

#include "slave.h"
/*

	04指令  读取输入寄存器
*/ 
static uint16_t _tab_input_registers[10] = {0, 1, 2, 3, 4, 9, 8, 7, 6, 5};

static int get_map_buf(void *buf, int bufsz)
{
    uint16_t *ptr = (uint16_t *)buf;

    //pthread_mutex_lock(&slave_mtx);
    for (int i = 0; i < sizeof(_tab_input_registers) / sizeof(_tab_input_registers[0]); i++) {
        ptr[i] = _tab_input_registers[i];
    }
   // pthread_mutex_unlock(&slave_mtx);

    return 0;
}
/*
	初始化输入寄存器
	1.设置开始结束地址
	2.设置get和set接口
*/
const agile_modbus_slave_util_map_t input_register_maps[1] = {
    {20, 29, get_map_buf, NULL}};

官方Agile Modbus文档

开源地址:GitHub - loogg/agile_modbus

Agile Modbus 即:轻量型 modbus 协议栈

在这里插入图片描述

1.1、特性

  1. 支持 rtu 及 tcp 协议,使用纯 C 开发,不涉及任何硬件接口,可在任何形式的硬件上直接使用。
  2. 由于其使用纯 C 开发、不涉及硬件,完全可以在串口上跑 tcp 协议,在网络上跑 rtu 协议。
  3. 支持符合 modbus 格式的自定义协议。
  4. 同时支持多主机和多从机。
  5. 使用简单,只需要将 rtu 或 tcp 句柄初始化好后,调用相应 API 进行组包和解包即可。

1.2、目录结构

名称说明
doc文档
examples例子
figures素材
inc头文件
src源代码
util提供简单实用的组件

1.3、许可证

Agile Modbus 遵循 Apache-2.0 许可,详见 LICENSE 文件。

2、使用 Agile Modbus

帮助文档请查看 doc/doxygen/Agile_Modbus.chm

2.1、移植

  • 用户需要实现硬件接口的 发送数据等待数据接收结束清空接收缓存 函数

    对于 等待数据接收结束,提供如下几点思路:

    1. 通用方法

      每隔 20 / 50 ms (该时间可根据波特率和硬件设置,这里只是给了参考值) 从硬件接口读取数据存放到缓冲区中并更新偏移,直到读取不到或缓冲区满,退出读取。

      本次移植使用的串口中断方式接收数据,每次收到一个数据就开启一个单次定时器,在定时器未超时之前收到数据立即重置定时器,当定时器超时时表示一帧数据接收完毕,完毕后使用信号量通知主线程

      这对于裸机或操作系统都适用,操作系统可通过 select信号量 方式完成阻塞。

    2. 串口 DMA + IDLE 中断方式

      配置 DMA + IDLE 中断,在中断中使能标志,应用程序中判断该标志是否置位即可。

      但该方案容易出问题,数据字节间稍微错开一点时间就不是一帧了。推荐第一种方案。

  • 主机:

    1. agile_modbus_rtu_init / agile_modbus_tcp_init 初始化 RTU/TCP 环境
    2. agile_modbus_set_slave 设置从机地址
    3. 清空接收缓存
    4. agile_modbus_serialize_xxx 打包请求数据
    5. 发送数据
    6. 等待数据接收结束
    7. agile_modbus_deserialize_xxx 解析响应数据
    8. 用户处理得到的数据
  • 从机:

    1. 实现 agile_modbus_slave_callback_t 类型回调函数
    2. agile_modbus_rtu_init / agile_modbus_tcp_init 初始化 RTU/TCP 环境
    3. agile_modbus_set_slave 设置从机地址
    4. 等待数据接收结束
    5. agile_modbus_slave_handle 处理请求数据
    6. 清空接收缓存 (可选)
    7. 发送数据
  • 特殊功能码

    需要调用 agile_modbus_set_compute_meta_length_after_function_cbagile_modbus_set_compute_data_length_after_meta_cb API 设置特殊功能码在主从模式下处理的回调。

    • agile_modbus_set_compute_meta_length_after_function_cb

      msg_type == AGILE_MODBUS_MSG_INDICATION: 返回主机请求报文的数据元长度(uint8_t 类型),不是特殊功能码必须返回 0。

      msg_type == MSG_CONFIRMATION: 返回从机响应报文的数据元长度(uint8_t 类型),不是特殊功能码必须返回 1。

    • agile_modbus_set_compute_data_length_after_meta_cb

      msg_type == AGILE_MODBUS_MSG_INDICATION: 返回主机请求报文数据元之后的数据长度,不是特殊功能码必须返回 0。

      msg_type == MSG_CONFIRMATION: 返回从机响应报文数据元之后的数据长度,不是特殊功能码必须返回 0。

  • agile_modbus_rtu_init / agile_modbus_tcp_init

    初始化 RTU/TCP 环境时需要用户传入 发送缓冲区接收缓冲区,建议这两个缓冲区大小都为 AGILE_MODBUS_MAX_ADU_LENGTH (260) 字节。特殊功能码 情况用户根据协议自行决定。

    但对于小内存 MCU,这两个缓冲区也可以设置小,所有 API 都会对缓冲区大小进行判断:

    发送缓冲区设置:如果 预期请求的数据长度预期响应的数据长度 大于 设置的发送缓冲区大小,返回异常。

    接收缓冲区设置:如果 主机请求的报文长度 大于 设置的接收缓冲区大小,返回异常。这个是合理的,小内存 MCU 做从机肯定是需要对某些功能码做限制的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2037019.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【中等】 猿人学web第一届 第5题 js混淆-乱码增强

请求流程 打开 调试工具&#xff0c;查看数据接口 https://match.yuanrenxue.cn/api/match/5 请求参数 请求参数携带了 page, m, f 3个字段&#xff0c; page为页数&#xff0c; m 为时间戳 像是 new Date().getTIme() 生成的 f 为时间戳 像是 Date.parse(new Date()) 生成的 …

Spring boot敏感参数加密配置

一&#xff0c;背景 在项目中很多参数会被配置到配置文件中&#xff0c;比如说密钥&#xff0c;用户名&#xff0c;数据库连接&#xff0c;账号密码之类的&#xff0c;如果用明文配置&#xff0c;会有一定的安全风险。为了减小风险&#xff0c;增加对敏感配置数据的加密配置。…

Gerrit 使用教程

一、Gerrit简介 Gerrit&#xff0c;一种开放源代码的代码审查软件&#xff0c;使用网页界面。利用网页浏览器&#xff0c;同一个团队的程序员&#xff0c;可以相互审阅彼此修改后的代码&#xff0c;决定是否能够提交&#xff0c;退回或是继续修改。它使用版本控制系统Git作为底…

LabVIEW光伏微网实验系统

开发了一个基于LabVIEW的光伏微网实验系统&#xff0c;系统主要服务于工程教育和技术研究&#xff0c;以提高学生对分布式电力系统的理解和操作能力。该实验系统能够模拟光伏微网的各种运行状态&#xff0c;包括能量的生成、存储和消费等&#xff0c;特别是在无电网状态下的独立…

Datawhale AI夏令营-大模型技术(微调)Task2打卡

1 输出结果要求 input&#xff1a;阅读文章本体与完成QAG的prompt target&#xff1a;题干、题目选项及答案 2 数据处理 2.1 Python 正则表达式 需要将文件中的数据读取出来&#xff0c;将语文数据与英语数据整理好后存储成可以微调的数据格式&#xff08;csv与jsonl类型&a…

el-checkbox 状态不更新

文章目录 数据处理代码片段 &#x1f330; 大概举例原因解决方法 - 深拷贝forceUpdate - 强制更新 今天遇到了checkbox不更新的问题&#xff0c;相同的功能在其他地方正常使用&#xff0c;有些地方不能用。数据处理代码片段 &#x1f330; 大概举例 从现有数据中过滤出新的数据…

MySQL学习[4] ——MySQL锁

四、MySQL锁 4.1 MySQL有哪些锁&#xff1f; 4.1.1 全局锁 全局锁就是**对整个数据库实例加锁&#xff0c;主要用于全库逻辑备份**等场景。 flush tables with read lock # 加全局锁unlock tables # 解锁加上全局&#xff08;读&#xff09;锁后&#xff0c;整个数据库都…

网络安全-第二阶段-linux操作系统01

一. linux介绍: windows,mac,linux都是由unix系统发展而来。 linux:类unix系统; 二. Centos系统的安装: 可以去清华大学开源软件镜像站下载: 输入ip addr: 可以看到自己电脑的ip地址; 1. ssh远程连接linux: 使用windows或者linux的物理机或者虚拟机都可以连接上它,…

MPU6050+OLED读取姿态角(超级细讲)

STM32F103C8T6读取陀螺仪MPU6050的角度数据&#xff0c;使用6050自带DMP库姿态解算出各个方向的角度&#xff0c;并使用OLED实时刷新显示&#xff0c;同时可以将数据通过串口发送到计算机&#xff0c;每一组数据50ms。本操作过程简单&#xff0c;方便移植&#xff0c;显示屏接P…

ppt中添加页码(幻灯片编号)及问题解决方案

在幻灯片母版中&#xff0c;选择插入 幻灯片编号 右下角显示幻灯片编号 问题一&#xff1a;母版中没有显示编号 原因可能是母版版式中没有设置显示&#xff0c;勾选即可。 问题二&#xff1a;子母版中没有显示幻灯片 将母版中的编号复制到子母版中。 问题三&#xff1a;应用…

Element-UI自学实践

概述 Element-UI 是由饿了么前端团队推出的一款基于 Vue.js 2.0 的桌面端 UI 组件库。它为开发者提供了一套完整、易用、美观的组件解决方案&#xff0c;极大地提升了前端开发的效率和质量。本文为自学实践记录&#xff0c;详细内容见 &#x1f4da; ElementUI官网 1. 基础组…

2024年7月文章一览

2024年7月编程人总共更新了5篇文章&#xff1a; 1.2024年6月文章一览 2.《Programming from the Ground Up》阅读笔记&#xff1a;p19-p48 3.《Programming from the Ground Up》阅读笔记&#xff1a;p49-p74 4.《Programming from the Ground Up》阅读笔记&#xff1a;p75…

深入理解Kafka核心设计与实践原理_03

深入理解Kafka核心设计与实践原理_03 03_消费者3.1消费者与消费者组3.2客户端开发3.2.1 必要的参数配置3.2.2 订阅主题与分区 草稿 03_消费者 与生产者对应的是消费者&#xff0c;应用程序可以通过KafkaConsumer来订阅主题&#xff0c;并从订阅的主题中拉取消息。不过在使用Ka…

Redis17-服务端优化

目录 持久化配置 慢查询 什么是慢查询 如何查看慢查询 命令及安全配置 内存配置 集群优化 持久化配置 Redis的持久化虽然可以保证数据安全&#xff0c;但也会带来很多额外的开销&#xff0c;因此持久化请遵循下列建议&#xff1a; 用来做缓存的Redis实例尽量不要开启持…

springboot项目打包jar 并打包为exe启动

springboot项目打包jar 并打包为exe启动&#xff08;在无jdk环境下运行&#xff09; 环境 SpringBoot Windows IDEA 实现 1.springboot打包为可执行jar&#xff08;这里使用maven install&#xff09; maven工具栏选择项目->Plugins ->install 注&#xff1a;如果…

Golang | Leetcode Golang题解之第332题重新安排行程

题目&#xff1a; 题解&#xff1a; func findItinerary(tickets [][]string) []string {var (m map[string][]string{}res []string)for _, ticket : range tickets {src, dst : ticket[0], ticket[1]m[src] append(m[src], dst)}for key : range m {sort.Strings(m[key])…

python对接vertx中踩的坑

需求 因为我们的硬件sdk只提供了python的版本&#xff0c;故需要python作为采集端来获取数据&#xff0c;然后将数据发送给java作为数据中心处理。 分析 这里就涉及到跨语言跨进程的数据的中转。有以下的几种解决方法 tcp:基于tcp自己拆包粘包&#xff0c;做心跳。一看就pa…

PV 与 PVC 状态迁移

文章目录 一、概述1、PV2、PVC 二、状态变化三、实例1、单独创建 PV1.1、创建并应用 PV1.2、查看刚创建的 PV 状态 2、单独创建 PVC2.1、创建并应用 PV2.2、查看刚创建的 PVC 状态 3、等待绑定4、删除 PV4.1、查看 PV&#xff0c;PVC 状态4.2、真正删除 PV4.3、查看PV PVC 状态…

PaLM-E: An Embodied Multimodal Language Model

发表时间&#xff1a;arXiv 6 Mar 2023 作者单位&#xff1a;Robotics at Google Motivation&#xff1a;大型语言模型已被证明可以执行复杂的任务。然而&#xff0c;在现实世界中启用一般推理&#xff0c;例如对于机器人问题&#xff0c;提出了落地的挑战。 解决方法&#…

探索全新AI编码代理框架:Agent Zero

引言 在科技的不断进步中&#xff0c;人工智能&#xff08;AI&#xff09;正越来越多地融入我们的日常生活。今天&#xff0c;我要为大家介绍一款全新的AI编码代理框架——Agent Zero。这款框架不仅可以自动化处理编码任务&#xff0c;还能操作文本、应用程序前端等&#xff0…