microPython的源码解析之 vstr.c

本文深入解析MicroPython中vstr.c的源码,这是一个C语言实现的可变字符串库,用于处理Unicode字符串。介绍了vstr结构体的初始化、内存管理、字符串操作等函数,旨在帮助读者理解Python解析器的核心实现逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MicroPython 是一种适用于微控制器和其他受限环境的 Python 编程语言的实现。它旨在提供与 Python 3 语言的紧密兼容,同时考虑到内存和计算资源的限制。MicroPython 库是这门语言的核心组成部分,提供了一系列的模块和函数,使得开发者能够在硬件上执行各种任务。
下面将通过系列文章,逐一解读microPython,以便让读者了解掌握microPython的整个核心逻辑.,以便读者可以透过这个Python的最小内核,掌握Python解析器的核心实现逻辑,学习世界上最优秀的源码设计之一.


microPython Python最小内核源码解析
NI-motion运动控制c语言示例代码解析
python编程示例系列 python编程示例系列二
python的Web神器Streamlit


#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <assert.h>

// 定义 MicroPython 配置和运行时环境的头文件
#include "py/mpconfig.h"
#include "py/runtime.h"
#include "py/mpprint.h"

// 定义一个宏,用于分配内存时向上取整到8的倍数,同时至少比原值大1
#define ROUND_ALLOC(a) (((a) & ((~0U) - 7)) + 8)

// 初始化 vstr 结构体,分配指定大小的内存,初始长度设置为0
void vstr_init(vstr_t *vstr, size_t alloc) {
   
   
    if (alloc < 1) {
   
   
        alloc = 1; // 如果分配的内存小于1,重置为1
    }
    vstr->alloc = alloc; // 设置分配的内存大小
    vstr->len = 0; // 初始化字符串长度为0
    vstr->buf = m_new(char, vstr->alloc); // 分配内存
    vstr->fixed_buf = false; // 标记是否为固定缓冲区
}

// 初始化 vstr 结构体,分配刚好足够容纳给定长度字符串的内存,并设置长度
void vstr_init_len(vstr_t *vstr, size_t len) {
   
   
    vstr_init(vstr, len + 1); // 分配内存,长度加1为了容纳字符串结束符'\0'
    vstr->len = len; // 设置字符串的实际长度
}

// 使用给定的固定大小的缓冲区初始化 vstr 结构体
void vstr_init_fixed_buf(vstr_t *vstr, size_t alloc, char *buf) {
   
   
    vstr->alloc = alloc; // 设置分配的内存大小
    vstr->len = 0; // 初始化字符串长度为0
    vstr->buf = buf; // 设置字符串缓冲区
    vstr->fixed_buf = true; // 标记为固定缓冲区
}

// 初始化 vstr 结构体,并设置打印函数,用于后续的字符串添加操作
void vstr_init_print(vstr_t *vstr, size_t alloc, mp_print_t *print) {
   
   
    vstr_init(vstr, alloc); // 初始化 vstr
    print->data = vstr; // 设置打印数据为 vstr 结构体
    print->print_strn = (mp_print_strn_t)vstr_add_strn; // 设置打印字符串的函数指针
}

// 清空 vstr 结构体,释放内存
void vstr_clear(vstr_t *vstr) {
   
   
    if (!vstr->fixed_buf) {
   
    // 如果不是固定缓冲区,则释放内存
        m_del(char, vstr->buf, vstr->alloc);
    }
    vstr->buf = NULL; // 清空缓冲区指针
}

// 创建并初始化一个新的 vstr 结构体,分配指定大小的内存
vstr_t *vstr_new(size_t alloc) {
   
   
    vstr_t *vstr = m_new_obj(vstr_t); // 创建新的 vstr 结构体对象
    vstr_init(vstr, alloc); // 初始化 vstr
    return vstr; // 返回新创建的 vstr 对象
}

// 释放 vstr 结构体占用的内存
void vstr_free(vstr_t *vstr) {
   
   
    if (vstr != NULL) {
   
    // 检查 vstr 是否为空
        if (!vstr->fixed_buf) {
   
    // 如果不是固定缓冲区,则释放内存
            m_del(char, vstr->buf, vstr->alloc);
        }
        m_del_obj(vstr_t, vstr); // 释放 vstr 结构体对象
    }
}

// 扩展 vstr 结构体的内存,增加指定大小的内存空间,并返回新扩展的内存地址
char *vstr_extend(vstr_t *vstr, size_t size) {
   
   
    if (vstr->fixed_buf) {
   
    // 如果是固定缓冲区,则不能重新分配内存
        // 抛出运行时错误
        mp_raise_msg(&mp_type_RuntimeError, NULL);
    }
    char *new_buf = m_renew(char, vstr->buf, vstr->alloc, vstr->alloc + size); // 重新分配内存
    char *p = new_buf + vstr->alloc; // 获取新扩展的内存地址
    vstr->alloc += size; // 更新分配的内存大小
    vstr->buf = new_buf; // 更新缓冲区地址
    return p; // 返回新扩展的内存地址
}

// 确保 vstr 结构体有足够的额外空间来存储指定大小的数据
static void vstr_ensure_extra(vstr_t *vstr, size_t size) {
   
   
    if (vstr->len + size > vstr->alloc) {
   
    // 如果当前分配的内存不足以存储额外的数据
        if (vstr->fixed_buf) {
   
    // 如果是固定缓冲区,则不能重新分配内存
            // 抛出运行时错误
            mp_raise_msg(&mp_type_RuntimeError, NULL);
        }
        size_t new_alloc = ROUND_ALLOC((vstr->len + size) + 16); // 计算新的内存分配大小
        char *new_buf = m_renew(char, vstr->buf, vstr->alloc, new_alloc); // 重新分配内存
        vstr->alloc = new_alloc;
from __future__ import print_function, with_statement import sys, time import os import string import re import subprocess import distutils.version from optparse import OptionParser import rospy from tf.srv import * import tf def listen(duration): rospy.init_node("view_frames", anonymous=True) tf_listener = tf.TransformListener() print("Listening to /tf for {} seconds".format(duration)) time.sleep(duration) print("Done Listening") return tf_listener.allFramesAsDot(rospy.Time.now()) def poll(node_name): print("Waiting for service {}/tf_frames".format(node_name)) rospy.wait_for_service('{}/tf_frames'.format(node_name)) try: print("Polling service") tf_frames_proxy = rospy.ServiceProxy('{}/tf_frames'.format(node_name), FrameGraph) output = tf_frames_proxy.call(FrameGraphRequest()) except rospy.ServiceException as e: print("Service call failed: {}".format(e)) return output.dot_graph def generate(dot_graph): with open('frames.gv', 'w') as outfile: outfile.write(dot_graph) try: # Check version, make postscript if too old to make pdf args = ["dot", "-V"] try: vstr = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[1] except OSError as ex: print("Warning: Could not execute `dot -V`. Is graphviz installed?") sys.exit(-1) v = distutils.version.StrictVersion('2.16') r = re.compile(".*version (\d+\.?\d*)") print(vstr) vstr = str(vstr) m = r.search(vstr.decode('utf-8') if not m or not m.group(1): print('Warning: failed to determine your version of dot. Assuming v2.16') else: version = distutils.version.StrictVersion(m.group(1)) print('Detected dot version {}'.format(version)) if version > distutils.version.StrictVersion('2.8'): subprocess.check_call(["dot", "-Tpdf", "frames.gv", "-o", "frames.pdf"]) else: subprocess.check_call(["dot", "-Tps2", "frames.gv", "-o", "frames.ps"]) print("frames.ps generated") except subprocess.CalledProcessError: print("failed to generate frames.pdf", file=sys.stderr) if __name__ == '__main__': parser = OptionParser(usage="usage: %prog [options]", prog='viewFrames.py') parser.add_option("--node", metavar="node name", type="string", help="Node to get frames from", dest="node") options, args = parser.parse_args() dot_graph = '' if not options.node: dot_graph = listen(5.0) else: print("Generating graph for node: ", options.node) dot_graph = poll(options.node) generate(dot_graph) 什么问题
最新发布
07-01
<think>我们正在处理的是ROSNoetic中的`view_frames`工具。根据引用[1][2][3][5]中的信息,用户遇到的错误是`TypeError:cannot usea stringpattern ona bytes-like object`。这个错误通常发生在Python3中,因为Python3严格区分了字符串(str)和字节(bytes)类型。###问题分析在ROSNoetic中,默认使用Python3。而`view_frames`脚本(位于`/opt/ros/noetic/lib/tf/view_frames`)中,有一行代码:```pythonm= r.search(vstr)``` 这里`vstr`是字节类型(bytes),而`r`是一个字符串模式(str),因此导致类型不匹配错误。 ###解决方案根据引用[1][2][3]的建议,解决方案是安装`ros-noetic-tf2-tools`并使用其中的`view_frames.py`工具。这是因为`tf`包中的`view_frames`脚本可能已经过时,而`tf2_tools`包中的`view_frames.py`是更新后的版本,兼容Python3。 ####具体步骤:1.**安装`ros-noetic-tf2-tools`**:```bashsudoapt installros-noetic-tf2-tools``` 2. **运行`tf2_tools`中的`view_frames.py`**:```bashrosrun tf2_tools view_frames.py```运行后,该工具会监听`/tf`话题5秒钟,然后生成一个名为`frames.pdf`的文件,其中包含TF树的可视化。###替代方案:修改原脚本如果你希望继续使用原来的`view_frames`脚本,可以按照以下步骤修改(但推荐使用上述官方解决方案): 1.打开文件:```bashsudo nano/opt/ros/noetic/lib/tf/view_frames``` 2.找到第89行左右(具体行号可能略有不同):```pythonvstr =subprocess.check_output(['dot', '-V'],stderr=subprocess.STDOUT)```这行代码获取`dot`命令的版本信息,返回的是字节字符串(bytes)。 3.修改为将字节字符串解码为字符串:```pythonvstr =subprocess.check_output(['dot', '-V'],stderr=subprocess.STDOUT).decode('utf-8')```或者,如果遇到解码错误,可以尝试:```pythonvstr =subprocess.check_output(['dot', '-V'],stderr=subprocess.STDOUT).decode('ascii',errors='ignore')```4.保存并退出,然后再次运行`rosruntf view_frames`。###代码功能解析`view_frames`工具的主要功能是生成TF树的可视化图。它通过以下步骤实现:1.**监听TF数据**:监听`/tf`话题一段时间(默认为5秒),收集所有TF变换。 2. **构建变换树**:根据收集到的变换数据构建坐标系之间的树状结构。 3. **生成Graphviz描述**:使用DOT语言(Graphviz的描述语言)描述TF树。4.**调用Graphviz生成PDF**:使用`dot`命令(Graphviz的一部分)将DOT描述转换为PDF文件。 ###如何运行1.确保ROS正在运行,并且有节点发布TF变换(例如,运行一个机器人仿真或SLAM节点)。 2.安装Graphviz(如果尚未安装):```bashsudo aptinstall graphviz```3.按照上述解决方案运行`view_frames.py`:```bashrosrun tf2_tools view_frames.py```或者,如果修改了原脚本,则运行:```bashrosruntf view_frames``` ###注意事项-如果运行`view_frames.py`时出现权限问题,可以尝试给生成的PDF文件设置可写权限,或者使用`chmod`更改输出目录的权限。-确保在运行工具之前,有TF数据正在发布(可以通过`rostopicecho /tf`检查)。###引用说明根据引用[1][2][3],安装`tf2_tools`并使用其中的`view_frames.py`是官方推荐的解决方案,因为该版本已经修复了Python3兼容性问题[^1][^2][^3]。§§相关问题§§1.如何在ROS中调试TF变换问题?2.Graphviz在ROS中有哪些其他应用?3.如何自定义`view_frames`生成的PDF样式? 4. ROSNoetic中还有哪些工具需要特别注意Python2/3兼容性?</think>### ROS Noetic view_frames.py 功能解析与解决方案 #### 1. **功能解析** `view_frames.py` 是 ROS 中用于可视化 TF 坐标系的工具,主要功能: - **监听 TF 数据**:订阅 `/tf` 话题,收集坐标系变换关系 [^2] - **生成树状结构**:将坐标系之间的父子关系构建为树状结构 - **创建可视化 PDF**:使用 Graphviz 的 `dot` 工具生成 PDF 文件(默认名为 `frames.pdf`) - **调试辅助**:帮助开发者验证坐标系设置是否正确(如 base_link → laser → map 等关系) #### 2. **正确运行方法** 根据引用 [1][2][3],在 ROS Noetic 中应使用新版工具: ```bash # 1. 安装 tf2_tools sudo apt install ros-noetic-tf2-tools # 2. 运行新版工具 rosrun tf2_tools view_frames.py ``` 成功运行时将输出: ```bash [INFO] [时间戳]: Listening to tf data during 5 seconds... [INFO] [时间戳]: Generating graph in frames.pdf file... ``` #### 3. **解决常见错误** ##### 错误 1:`TypeError: cannot use a string pattern on a bytes-like object` **原因**:Python 3 严格区分字节(byte)和字符串(str)类型,旧版脚本未兼容 [^2][^5] **修复方案**: ```bash # 方案A:使用官方推荐的新工具(首选) rosrun tf2_tools view_frames.py # 方案B:手动修改旧脚本(不推荐) sudo sed -i 's/vstr = subprocess.check_output(/vstr = subprocess.check_output(.decode("utf-8")/' /opt/ros/noetic/lib/tf/view_frames ``` ##### 错误 2:`SyntaxError: invalid syntax` **原因**:Python 2/3 语法不兼容(如 print 语句缺少括号)[^1] **修复示例**: ```python # 修改前(错误) print "Generating graph..." # 修改后(正确) print("Generating graph...") ``` #### 4. **依赖检查** 确保安装必要组件: ```bash # 检查 Graphviz 安装 dpkg -l | grep graphviz # 若未安装,执行: sudo apt install graphviz ``` #### 5. **结果验证** 成功运行后: 1. 当前目录生成 `frames.pdf` 文件 2. PDF 显示坐标系树形结构,例如: ``` world (root) └── base_link ├── camera_link └── lidar_link ``` #### 6. **高级用法** ```bash # 自定义监听时长(默认5秒) rosrun tf2_tools view_frames.py _timeout:=10 # 指定输出文件名 rosrun tf2_tools view_frames.py _frames_file:=my_tf_tree.pdf ``` > **注意**:修改系统文件可能被 ROS 更新覆盖,建议优先使用 `tf2_tools` 版本 [^1][^2][^3]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

openwin_top

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值