在VSCode下利用PlateFormIO开发Arduino的MicroROS遇到的一些问题

文章目录

  • 简介
  • 1.左下角没有platformio的相关按钮
  • 2.vscode没有串行监视器(Serial Monitor)
  • 3.关于trajectory_msgs/msg/joint_trajectory.hpp的相关问题
  • 4.关于control_msgs::action::FollowJointTrajectory的相关问题
    • 4.1.方法一
    • 4.2.方法二
  • 5.关于moveit_msg的相关问题
  • 6.publisher以及service不能同时实现的问题
  • 7.创建action失败的原因
  • 8.关于action_name的字符长度被限制在29的问题
    • 8.1 原因分析
    • 8.2.解决办法
  • 9.关于代码调试、信息打印
  • 10.关于action_server无法接收goal的问题
    • 10.1.现象
    • 10.2.查找资料及分析
    • 10.2.解决方案
  • 11.关于action_server接收了goal,却无法Goal finished with status: xxx的问题
  • 12.Controller is taking too long to execute trajectory (the expected upper 。。。问题
  • 总结

简介

我是按照鱼香ROS的教程【3.搭建PlateFormIO开发环境】进行的,但是在进行的过程中,遇到了一些问题,这里记录下来,供有同样问题的同学进行参考。其实只要你使用的板子的MCU是ESP32,都可以按照他这个教程进行操作。

1.左下角没有platformio的相关按钮

在第四节编译工程中,教程使用的vscode的左下角是有编译、上传的按钮的。但是我的没有。
教程的:
在这里插入图片描述我的:
在这里插入图片描述
不过没关系,点击那个小蚂蚁,执行RROJECT TASKS–》General下面的命令,具有同样的效果。
在这里插入图片描述
后来又发现在右上角:
在这里插入图片描述

2.vscode没有串行监视器(Serial Monitor)

在【6.串口通信-接收实验】中,教程中是有这个串行监视器(Serial Monitor)的,但是我这边没有。
在这里插入图片描述
其实这个需要是个独立的拓展,需要自己独立安装。
在这里插入图片描述

3.关于trajectory_msgs/msg/joint_trajectory.hpp的相关问题

首先我们要明确,microros这个环境基本是基于c开发的,而不是cpp。所以,trajectory_msgs/msg/joint_trajectory.hpp这个cpp下的目录是没有的,取而代之的是trajectory_msgs/msg/joint_trajectory.h
在这里插入图片描述

4.关于control_msgs::action::FollowJointTrajectory的相关问题

假如我们想让我们的microros节点直接接入到moveit中,一种比较好的办法是直接在此节点上实现一个action_server,类型为control_msgs::action::FollowJointTrajectory。
但是目前鱼香ros的microros不支持该类型,而官方的也是不支持的(当使用官方的microros时,需要梯子或者fastgithub)。
不知道怎么回事,我看官方的【micro_ros_arduino】是有这个模块的。
在这里插入图片描述
我在上面发起了个提问,到时候再看看。【I couldn’t find the control_msg module in the compiled include folder】
实在没办法的话,可以暂时在电脑端创建个action_server,然后通过用service传给下位机吧。

4.1.方法一

20230612:他们今天回答了。
在这里插入图片描述
意思大概是假如这个库没有control_msgs这个模块,需要自己按照 https://github.com/micro-ROS/micro_ros_platformio#extra-packages 这里的说明来进行添加到编译步骤中。
在这里插入图片描述
ok,那我们自己操作一下。
呃,但是,在哪个路径下搞这个extra_packages.repos呢?😶
问了一下chatgpt
在这里插入图片描述
也就是在platformio.ini所在的目录下。
但是,extra_packages.repos应该如何写?
在 /home/yong/Desktop/arduino/hello_microros/.pio/libdeps/featheresp32/micro_ros_platformio/ci/extra_packages(你需要根据你自己的项目名字来决定这个路径,其实也就是你当前项目下的) 这个目录下是有一个extra_packages.repos。
在这里插入图片描述
我们用的是humble,他这个是galactic-devl与humble可能不匹配。我们去 https://github.com/ros-controls/control_msgs 看看
在这里插入图片描述有humble的,那我们手动改一下:
在这里插入图片描述ok,那就按照它的写法,自己在platformio.ini所在目录下,建立一个extra_packages文件夹,然后在里面新建一个extra_packages.repos文件,内容如下

repositories:control_msgs:type: giturl: https://ghproxy.com/https://github.com/ros-controls/control_msgsversion: humble

在这里插入图片描述修改之后,手动将libmicroros文件夹删除,然后重新编译。
编译的时候,可能会发生类似这样的错误:

control_msgs clone failed: 
fatal: destination path '/home/yong/Desktop/arduino/hello_microros/.pio/libdeps/featheresp32/micro_ros_platformio/build/mcu/src/control_msgs' already exists and is not an empty directory.

这是因为在原来的 microros_utils/library_builder.py文件中,对extra_packages重复下载了(鱼香ros的:75行,139行;官方的:65行,123行 )。需要删除其中一个才行。针对这个问题,我提交了一个push request:【 Update library_builder.py #105 】 😂
这个错误是鱼香ros这边才有,官方那边是没有的。原因在于第75行这个,与139行重复了。
在这里插入图片描述

4.2.方法二

另外分享一个我发现的一个办法吧(不太建议使用,相当于改人家的源代码,不好):
直接修改 repositories.py
在这里插入图片描述

  +          Repository("control_msgs", "https://ghproxy.com/https://github.com/ros-controls/control_msgs", "humble"),

注意我目前使用的是鱼香ros的仓库,所以加上的网址是带【https://ghproxy.com/】的(起到加速、梯子的作用),假如你是用官方的,没必要加这个前缀(因为你都能够使用官方的了,应该就不存在墙的问题)。
修改之后,手动将libmicroros文件夹删除,
在这里插入图片描述
然后重新编译
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qa6iQrQs-1686555231137)(null)]
顺利的话,等编译完成后,你就可以看到control_msgs了。
在这里插入图片描述

不是很懂为什么官方不直接加进去,难道是出于单片机资源紧张的考虑?
通过对比好像发现,control_msgs是属于第三方团队https://github.com/ros-controls的,并不是ros团队(https://github.com/ros2、https://github.com/micro-ROS)的亲儿子。所以默认没有包含进去?

5.关于moveit_msg的相关问题

moveit_msgs也是第三方的,默认是没有包含的。因此也需要自己加进去

  +          Repository("moveit_msgs", "https://ghproxy.com/https://github.com/ros-planning/moveit_msgs", "humble"),

直接加这个还是不得行的,因为它还需要其他的msg
在这里插入图片描述因此,需要把extra_packages.repos搞成这样(关于extra_packages.repos,请查看本文4.1):

repositories:control_msgs:type: giturl: https://ghproxy.com/https://github.com/ros-controls/control_msgsversion: humbleobject_recognition_msgs:type: giturl: https://ghproxy.com/https://github.com/wg-perception/object_recognition_msgsversion: ros2octomap_msgs:type: giturl: https://ghproxy.com/https://github.com/OctoMap/octomap_msgsversion: ros2moveit_msgs:type: giturl: https://ghproxy.com/https://github.com/ros-planning/moveit_msgsversion: humble

需要注意的是,上面的【version】 是填ros2还是填humble要根据人家仓库的分支命名来决定,不能随便填。
然后就有了:
在这里插入图片描述

6.publisher以及service不能同时实现的问题

按照教程,假如你想把发布话题、实现服务这两个功能一起放到同一个工程中:

...// 执行器添加服务rclc_executor_add_service(&executor, &service, &req, &res, service_callback);
...// 给执行器添加定时器rclc_executor_add_timer(&executor, &timer);
...

会发现只能实现其中一个功能,另外一个会被忽略。
这个其实只要改一下执行器的初始化语句就行:

  // 创建执行器
-  rclc_executor_init(&executor, &support.context, 1, &allocator);
+  rclc_executor_init(&executor, &support.context, 2, &allocator);

也就是把number_of_handles从1改成2.这个参数是控制执行器允许处理的对象数量,可以自己看一下源码。这也是对寸土寸金的单片机的妥协,也没办法,要控制好每一份资源。

7.创建action失败的原因

假如我们直接利用类似以下的代码创建action的话,大概率会返回RCL_RET_ERROR:

...
auto ret = rclc_action_server_init_default(&mActionServer, node, support,ROSIDL_GET_ACTION_TYPE_SUPPORT(control_msgs, FollowJointTrajectory),// "/my_group_controller/follow_joint_trajectory" // 不支持有效字符长度超过29的名字// "my/follow_joint_trajectory" // 双层也是支持的"follow_joint_trajectory");if (ret != RCL_RET_OK) {return 10000 + ret;// return -1;}// microros没有AcceptedCallback ?ret = rclc_executor_add_action_server(executor, &mActionServer, 1, &mGoalReq, sizeof(mGoalReq),action_server_handle_goal,action_server_handle_cancel, nullptr);if (ret != RCL_RET_OK) {return 20000 + ret;// return -2;}...

倒不是我们的代码有问题,而是,创建一个action,需要创建好几个service,而能创建的service数量,在编译时microros时已经被限制了,具体请查看:【 Unable to create two servers in micro_ros_platformio #103 】
解决办法是:
在这里插入图片描述也就是修改colcon.meta、colcon_lowmem.meta、colcon_verylowmem.meta(因为我不知道它用哪个,所以我全改了) ,改成你觉得合适的数据。然后重新编译。
在这里插入图片描述 不过 假如看一下 extra_script.py 这个文件,里面有各个文件的对应。也就是我们改colcon.meta这个文件就行了。在这里插入图片描述
同时也要注意第6点提到的number_of_handles问题。
处理好之后,你的action自然出现了。
在这里插入图片描述

8.关于action_name的字符长度被限制在29的问题

8.1 原因分析

本文第7节中的代码提到,action_name不支持有效字符长度超过29的名字。我是怎么知道的?我是一个个字符增加测试出来的😣。
那为啥是29?
因为创建action时会创建三个service,其中一个名叫xxx/_action/cancel_goal。
但是,查看创建service的init函数,假如你一层一层地查看源代码,会在一个叫 rmw_service.c 的文件中的第60行看到这玩意。
在这里插入图片描述总之就是这句话限制了service_name的长度。
然后再继续深入,知道了这玩意:RMW_UXRCE_TOPIC_NAME_MAX_LENGTH
在这里插入图片描述这玩意的值为60.
在这里插入图片描述那 30 + 19 + 1 = 50,那也没超过啊。。。
哦,漏了一段
在这里插入图片描述
继续追查到 expand_topic_name.c ,可以看到,service的最终名字是会混合命名空间、节点名称之类的。我之前的节点名称为【hello_microros】14个字节…好像又超太多了。唉,算了,总之,最终的service_name会比你原来的名字长一段,而且和节点名称、命名空间有关。
在这里插入图片描述好累。

8.2.解决办法

通过查找,在 【/home/yong/Desktop/arduino/hello_microros/.pio/libdeps/featheresp32/micro_ros_platformio/build/mcu/src/rmw-microxrcedds/rmw_microxrcedds_c/CMakeLists.txt】 这个文件中出现了可以配置这个宏定义的地方。直接在这里修改肯定不行,因为这个是在编译时才下载下来的。
在这里插入图片描述办法是回到metas/colcon.meta,修改成这样(修改的数字我暂时只是随意修改的,可能会导致一些未知的错误。但是在新的错误未出现之前,先用着)(已经出现问题了,详细请看10):
在这里插入图片描述

{"names": {"rmw_microxrcedds": {"cmake-args": ["-DRMW_UXRCE_MAX_NODES=10","-DRMW_UXRCE_MAX_PUBLISHERS=10","-DRMW_UXRCE_MAX_SUBSCRIPTIONS=10","-DRMW_UXRCE_MAX_SERVICES=10","-DRMW_UXRCE_MAX_CLIENTS=10","-DRMW_UXRCE_MAX_HISTORY=10","-DRMW_UXRCE_TRANSPORT=custom","-DRMW_UXRCE_NODE_NAME_MAX_LENGTH=128","-DRMW_UXRCE_TOPIC_NAME_MAX_LENGTH=128","-DRMW_UXRCE_TYPE_NAME_MAX_LENGTH=128",]},"microxrcedds_client":{"cmake-args": ["-DUCLIENT_CUSTOM_TRANSPORT_MTU=1024",]}}
}

慎重设置上面的 RMW_UXRCE_MAX_NODES,保持1就好了。否则非常耗资源。
编译,运行,长名字的action也就出现了。完美。
在这里插入图片描述后来发现,别人已经发现过这个问题:【 Long action name does not work for action server #1138 】

9.关于代码调试、信息打印

相信很多人都希望micro_ros这边能够类似电脑端直接调用 RCLCPP_INFO() 之类的函数来打印信息、或者获取什么last_error之类来显示函数错误的原因(这一点在我查找实现action初始化失败的原因时深有体会😣)。
但是,遗憾的是,貌似暂时还没有很好的解决办法,哪怕你修改common.meta里面的
“-DRCUTILS_AVOID_DYNAMIC_ALLOCATION=OFF”,也是不行的(agent直接无法初始化micro_ros,然后单片机不断重启);
目前只能解决printf的问题,办法肯定很多人想到了,就是创建一个string的话题发布者,然后在需要的地方发布信息就行:

'''
// 声明明话题发布者
rcl_publisher_t publisher;
// 声明消息结构体
std_msgs__msg__String pub_msg;
// 缓冲器
char strBuffer[256] = {};
...// 发布者初始化rclc_publisher_init_default(&publisher, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, String),"microros_log");
...// 填充自己的消息sprintf(strBuffer, "robot init ret = %d\r\n", ret);pub_msg.data.data = strBuffer;pub_msg.data.size = strlen(strBuffer);pub_msg.data.capacity = pub_msg.data.size + 1;...// 派发消息rcl_ret_t ret = rcl_publish(&publisher, &pub_msg, NULL);

然后,你可以在上面的基础上写出下面这个很骚的用法:

// 用一个函数,直接sprinf+发布
int my_ros_log(char *formattedStr, ...)
{va_list args;// 初始化可变参数列表va_start(args, formattedStr);// 将格式字符串和可变参数列表转换为字符串vsnprintf(strBuffer, 256, formattedStr, args);// 结束可变参数列表的使用va_end(args);// // 这样是自己初始化的方式,下面的官方提供的方式也行,不过会复制多一份内存,感觉有点浪费pub_msg.data.data = strBuffer;pub_msg.data.size = strlen(strBuffer);pub_msg.data.capacity = pub_msg.data.size + 1;// // 需要 #include <micro_ros_utilities/string_utilities.h>// pub_msg.data = micro_ros_string_utilities_init(strBuffer);// 派发消息rcl_ret_t ret = rcl_publish(&publisher, &pub_msg, NULL);return ret;
}

这时候,你就可以在需要的地方直接使用了:

  my_ros_log("the traj points count:%d", points.size);

效果还是不错的:
在这里插入图片描述

10.关于action_server无法接收goal的问题

ESP32的资源:

HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash

10.1.现象

如前面所述,我创建了一个control_msgs/action/FollowJointTrajectory类型的action: follow_joint_trajectory ,然后尝试通过下面的命令从电脑发送指令给esp32
这里稍微提一下,moveit发送和接受的角度数据都是以弧度表示的,要记住这一点。

ros2 action send_goal /my_group_controller/follow_joint_trajectory control_msgs/action/FollowJointTrajectory "{trajectory: {joint_names: [joint1, joint2, joint3, joint4, joint5],points: [{ positions: [0.1, 0.1, 0.1, 0.1, 0.1], time_from_start: { sec: 0, nanosec: 500 } },{ positions: [0.2, 0.5, 0.2 ,0.2, 0.2], time_from_start: { sec: 5, nanosec: 500 } },{ positions: [0.3, 0.3, 0.7, -0.5, 0.3], time_from_start: { sec: 7, nanosec: 500 } },{ positions: [0.4, 0.4, 0.9, 0.4, 0.4], time_from_start: { sec: 8, nanosec: 500 } }]}
}"

成功了,但是并没有完全成功。貌似已经发送了出去,但是没有收到返回,而且下位机的回调函数也没有被调用。

10.2.查找资料及分析

看了好几遍代码(参考【 Action server example #1129 】),觉得我的代码应该没啥问题,那到底是怎么回事?
再查找一下资料,发现有人讨论过类似的问题:【 Handling large action request gets failed #1145 】
里面提到RMW_UXRCE_STREAM_HISTORY、timer、超时的问题。
RMW_UXRCE_STREAM_HISTORY,这个参数貌似会影响数据通讯的缓存空间?
不能直接用32,会报错
region `dram0_0_seg’ overflowed by 316440 bytes
在这里插入图片描述经过一番测试(优化代码、把不必要的东西去掉等等),最后可以只能用8了,这个可能和其他参数设置有关系,最好自己动手试试(但是要注意是要2的n次方,比如4、8、16、32、64等等)。
此外,还要控制好超时的问题。

10.2.解决方案

总的来说,是因为整个action goal的数据量比较大,一方面留给数据传输缓存的空间要大一点,另外ros的超时时间也要留长一点。也就是空间、时间的问题。
时间方面:减少或者取消loop函数里面的delay、增加rclc_executor_spin_some的超时时间、其它。

其实我们对比一下【 Action server example #1129 】、【 Handling large action request gets failed #1145 】这两篇讨论的代码,就会发现,前一篇对ros_goal_request声明之后,就可以直接拿来用了,而后面一篇还需要用一个函数来申请ros_goal_request的空间:
在这里插入图片描述
而恰恰就是我没有调用这个函数来初始化,才导致出现了我上面所述的问题。
加上这个函数,就ok了。
在这里插入图片描述

另外,这是我目前的colcon.meta:

{"names": {"rmw_microxrcedds": {"cmake-args": ["-DRMW_UXRCE_MAX_NODES=1","-DRMW_UXRCE_MAX_PUBLISHERS=10","-DRMW_UXRCE_MAX_SUBSCRIPTIONS=5","-DRMW_UXRCE_MAX_SERVICES=6","-DRMW_UXRCE_MAX_CLIENTS=1","-DRMW_UXRCE_MAX_HISTORY=4","-DRMW_UXRCE_TRANSPORT=custom","-DRMW_UXRCE_TOPIC_NAME_MAX_LENGTH=100","-DRMW_UXRCE_STREAM_HISTORY=8",]},"microxrcedds_client":{"cmake-args": ["-DUCLIENT_CUSTOM_TRANSPORT_MTU=1024",]}}
}

为啥有时候需要申请空间,有时候不用呢?

11.关于action_server接收了goal,却无法Goal finished with status: xxx的问题

在按照本文第10小节,操作后,应该是能够发送了goal给下位机,而且下位机的【rclc_action_goal_handle_t】函数也被成功调用了。
但是,假如你在【rclc_executor_add_action_server】时,设置【handles_number】为n的话(并且【ros_goal_request】也正确地申请了空间),那么,反复发送了n次后,就无法再发送了,发送程序就一直卡在那里,不会出现 【Goal accepted with ID: 72718b32773f4a0583cb543b8f33d113】之类的信息。
这是因为没有调用【rclc_action_send_result】来告知系统该goal已经被处理,所以这个goal就一直占用着内存。等你申请的空间被占用完了,那自然无法再继续接收(受)goal了。
在这里插入图片描述而且这个函数还不是一次调用就会成功,还需要反复调用,直到确认调用成功才行。
另外,这个函数也不应该在【rclc_action_goal_handle_t】就调用。在【rclc_action_goal_handle_t】里应该是判断是否接受该goal,接受的话,先记录好,然后返回接受值【RCL_RET_ACTION_GOAL_ACCEPTED】(不接受的话,直接返回【RCL_RET_ACTION_GOAL_REJECTED】,后面吊事都不用处理);接着再在另外的线程、函数中执行电机的操作,然后再返回结果。
处理好的话,应该是可以得到类似的结果:

RCL_RET_ACTION_GOAL_REJECTED
在这里插入图片描述

RCL_RET_ACTION_GOAL_ACCEPTED
在这里插入图片描述

12.Controller is taking too long to execute trajectory (the expected upper 。。。问题

对于本人的项目来说,这个主要是因为moveit给我规划的路径的执行时间太短了,我的机械臂跑不了这么快。
针对这个问题,只需要修改config里面的joint_limits.yaml文件就行。具体数值要看实际需求。
在这里插入图片描述

总结

在单片机下面操作,一来要先申请空间,再操作;二来真的是1bit、1byte都要考虑清楚。

其他暂时没问题,遇到再补充。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

进程管理(笔记)

如果对内存寻址熟悉的话, 或者认真看过上一节的内容: 内存管理之内存寻址: https://blog.csdn.net/qq_40482358/article/details/130868188. 那么对linux系统中的进程管理应该已经有一个初步的认识了: cr3作为一个控制寄存器, 描述当前进程的页目录的物理内存基地址, 当进程切换…

chatgpt赋能python:Python数据读写——技术大杂烩

Python数据读写——技术大杂烩 介绍 Python作为一种高级的编程语言&#xff0c;被广泛应用于数据科学领域。Python提供了多种实用工具来读取和写入数据&#xff0c;包括CSV文件、Excel文件、JSON文件、SQL数据库等。本文将介绍Python常用的数据读写技术和应用&#xff0c;并探…

chatgpt赋能Python-libreoffice_python扩展

LibreOffice Python扩展: 提升办公效率的利器 如果你一直在寻找一种提高办公效率的方法&#xff0c;那么你肯定会喜欢LibreOffice Python扩展。作为LibreOffice的一个特性&#xff0c;它可以让你使用Python编写宏程序自动化你的日常办公任务。 什么是LibreOffice Python扩展&…

【Linux操作系统】【综合实验二 vi应用与shell脚本编辑】【浅试编辑命令】

文章目录 一、实验目的二、实验要求三、实验内容⭐&#xff08;1&#xff09;继续练习Linux系统的文件类、目录类、进程管理类与磁盘操作类常用命令&#xff0c;并使用常见的选择项&#xff1b;⭐&#xff08;2&#xff09;了解ed、ex行编辑器与Emacs全屏幕编辑器的工作模式、基…

chatgpt赋能python:使用Python获取句柄和发送消息

使用Python获取句柄和发送消息 什么是句柄&#xff1f; 在计算机中&#xff0c;句柄是指一个唯一的标识符&#xff0c;用于引用正在执行的进程或程序。在Python中&#xff0c;我们可以使用win32api模块获取Windows操作系统中的句柄。使用句柄&#xff0c;我们可以与Windows中…

chatmol:将chatgpt应用于pymol

文章目录 前言一、源文件二、安装二、应用示例总览示例1(方法查询)示例2(直接运行上述方法)示例三(结合口袋展示) 前言 Chatmol将chatgpt内置到了pymol当中&#xff0c;采用对话的方式调用pymol进行绘图。 一、源文件 https://github.com/JinyuanSun/ChatMol/blob/main/chatm…

开源问卷项目分享-TDUCK填鸭表单

前言 自TDUCK填鸭表单开源以来&#xff0c;收到许许多多微信社群朋友的反馈&#xff0c;其中关心最多的莫非就是部署的相关问题&#xff0c;对于初学者或者行业小白来说&#xff0c;非常希望能够简化部署方式。为了满足伙伴们的需求&#xff0c;现在我们推出简化部署教程&…

chatgpt赋能python:Python的排列组合生成工具——实现高效SEO

Python的排列组合生成工具——实现高效SEO 排列组合是算法中非常常见的模块&#xff0c;也是搜索引擎优化&#xff08;SEO&#xff09;中常用的工具。举例来说&#xff0c;如果某个电商平台需要为某个页面展示的商品进行排列组合&#xff0c;那么Python就是一个非常有效的选择…

chatgpt赋能python:Python就近捕捉原理及其在开发中的应用

Python 就近捕捉原理及其在开发中的应用 Python 作为一门高级编程语言&#xff0c;拥有着丰富的库和工具&#xff0c;广泛应用于数据分析、机器学习、自然语言处理等领域。同时&#xff0c;Python 作为一种主流的 Web 编程语言&#xff0c;也有着众多优秀的 Web 框架和库。在 …

ChatGPT刷力扣面试题 01.05.一次编辑

题目描述 字符串有三种编辑操作:插入一个英文字符、删除一个英文字符或者替换一个英文字符。 给定两个字符串&#xff0c;编写一个函数判定它们是否只需要一次(或者零次)编辑。示例 1:输入: first "pale" second "ple" 输出: True示例 2:输入: first …

【直播回放】ChatGPT刷力扣面试题 02.03. 删除中间节点

直播截图 题目描述 若链表中的某个节点&#xff0c;既不是链表头节点&#xff0c;也不是链表尾节点&#xff0c;则称其为该链表的「中间节点」。假定已知链表的某一个中间节点&#xff0c;请实现一种算法&#xff0c;将该节点从链表中删除。例如&#xff0c;传入节点 c&#x…

用完即走!设计师必备的浏览器工具!

设计师们都知道&#xff0c;谁都不想在设计过程中被一堆繁琐的操作搞得焦头烂额。 浏览器中一些有趣但实用的工具&#xff0c;如颜色拾取器、像素标尺和抠图、在线生成工具等&#xff0c;让我们的设计变得更加简单有趣。 但网站每次都要去收藏夹里找出来用&#xff0c;有时候…

chatgpt赋能python:Python截图库的全面介绍:了解它如何提高你的工作效率

Python截图库的全面介绍&#xff1a;了解它如何提高你的工作效率 在如今的数字化时代&#xff0c;截图已经成为了我们处理信息和交流的重要手段之一。特别是对于那些需要分享教程、演示演讲或者进行 bug 报告的人&#xff0c;截图所提供的直观性和高效性无疑是无法替代的。而在…

chatgpt赋能python:Python滚动截图:高效的网页截图工具

Python滚动截图&#xff1a;高效的网页截图工具 在如今数字化的时代&#xff0c;网站或应用程序的设计是至关重要的。无论是展示企业介绍还是在线购物&#xff0c;网站的设计和用户界面都是重要的。在这种情况下&#xff0c;颇具价值的内容应该能够呈现出来并能受到使用者的欣…

chatgpt赋能python:Python截图并保存——提高工作效率的利器

Python截图并保存——提高工作效率的利器 随着数字化时代的到来&#xff0c;屏幕截图在我们的日常工作中越来越常见&#xff0c;无论是在写作、设计、开发还是教育等各个领域中&#xff0c;截图都是必不可少的一环。而Python作为一门功能强大、易上手的编程语言&#xff0c;自…

chatgpt赋能python:Python指定区域截图:优化截图流程的最佳方式

Python指定区域截图&#xff1a;优化截图流程的最佳方式 在网络时代&#xff0c;图片作为一种重要的信息传播方式&#xff0c;扮演着举足轻重的角色。截图作为最常见的图片处理方式之一&#xff0c;也时常被我们用于记录屏幕内容、报告问题及展示操作流程。 若想提高截图的效…

chatgpt赋能python:Python区域截屏:简化工作流程的利器

Python区域截屏&#xff1a;简化工作流程的利器 在如今的数码时代&#xff0c;截屏已成为我们日常生活和工作中不可或缺的一环。在各类软件应用中&#xff0c;截图功能随处可见。但是对于高效率的工作&#xff0c;区域截屏则显得更为实用。本文将着重介绍Python语言中的区域截…

chatgpt赋能python:Python怎么截图速度快?

Python怎么截图速度快&#xff1f; 在现在这个数字时代&#xff0c;我们所有人都需要进行屏幕截图。无论是用于记录重要笔记&#xff0c;制作教程&#xff0c;或是用于软件质量控制&#xff0c;高速、高质量、高效的屏幕截图工具都非常必要。 在Python编程领域中&#xff0c;…

chatgpt赋能python:Python截取当前窗口图片的方法——简单实现Windows截图功能

Python截取当前窗口图片的方法——简单实现Windows截图功能 在SEO优化中&#xff0c;图片的质量与数量同样重要。而对于一些技术博客或者教程类文章&#xff0c;为了更好地展示代码或操作过程&#xff0c;需要截取窗口或屏幕截图。本文介绍了使用Python实现简单的Windows截图功…

chatgpt赋能python:使用Python轻松截取屏幕指定区域的方法

使用Python轻松截取屏幕指定区域的方法 在现代数字化时代&#xff0c;屏幕截图是相当普遍的需求。这些截图可以用作网站截图&#xff0c;学习指导或简单的记录目的。在Python中&#xff0c;有许多库可用于截取屏幕指定区域。在这篇文章中&#xff0c;我们将讨论如何使用Python…