解决 “warning C4819- 该文件包含不能在当前代码页(936)中表示的字符” 问题指南

起因是因为我在对我的 C++ 项目进行跨平台适配,从 macOS 平台移植到 Windows 平台时,在使用 Cmake + MSVC 编译后,出现了这个问题。 问题原因这是由于 Windows 平台默认使用的是 GBK 编码,而 macOS 平台上使用的是 UTF-8 编码。 解决方法方法一在 CMakeLists.txt 文件中添加如下代码: add_compile_options("$<$<C_COMPILER_ID:MSVC>:/source-charset:utf-8>") 方法二在 CMakeLists.txt 文件中添加如下代码: if(MSVC) target_compile_options(<你的项目名> PRIVATE "/utf-8") endif()

2024年06月16日 · 1 分钟 · Cassius0924

C++ 中位运算的妙用:与运算和或运算的合并标志位

当我们处理程序中的多个标志位时,将它们合并成一个整数是一种常见的技巧。这种做法不仅有助于提高代码的可读性,还使得对多个标志位进行操作更为方便。 在C++中,可以使用按位或(|)运算符来实现这一功能。 合并标志位的基本思想在C++中,合并标志位的基本思想是将多个标志位的二进制表示中的相应位进行按位或运算,形成一个包含所有标志位信息的整数。这样,就能够用一个整数表示多个不同的状态或选项。 代码示例#include <iostream> int main() { // 定义三个标志位 const int FLAG_A = 0b0001; const int FLAG_B = 0b0010; const int FLAG_C = 0b0100; // 合并标志位 int flags = FLAG_A | FLAG_C; // 检查是否设置了某个标志位 if (flags & FLAG_A) { // True std::cout << "FLAG_A is set." << std::endl; } if (flags & FLAG_B) { // False std::cout << "FLAG_B is set." << std::endl; } if (flags & FLAG_C) { // True std::cout << "FLAG_C is set." << std::endl; } return 0; } 在这个示例中,首先定义了三个标志位 FLAG_A、FLAG_B 和 FLAG_C,它们分别对应二进制的 0001、0010 和 0100。然后,使用 | 运算符将 FLAG_A 和 FLAG_C 合并成 flags。 接下来,使用按位与运算符 & 来检查 flags 中是否设置了每个标志位。如果某个标志位被设置,相应的条件语句将会执行。 优点与用途 可读性提高: 将多个标志位合并成一个整数可以提高代码的可读性。通过使用有意义的常量名称,可以更清晰地了解整数表示的含义。 便于操作: 合并后的整数可以方便地进行各种按位操作,如按位与、按位或等,而无需单独处理每个标志位。 节省内存: 在某些情况下,将多个标志位合并成一个整数可能节省内存,特别是当标志位数量较多时。 在实际编码中,合并标志位是一种常见的编程技巧,适用于需要处理多个二进制状态的情况。这种技术不仅简化了代码,还使得代码更易维护和扩展。

2024年06月16日 · 1 分钟 · Cassius0924

基于 L2CAP 协议的蓝牙 BLE 设备通信指南

蓝牙 BLE 是什么蓝牙BLE,即蓝牙低功耗 (Bluetooth Lower Energy)是一种蓝牙通信标准,设计用于短距离通信和低功耗应用。 相比经典蓝牙,BLE 更加节能,传输距离更远,连接更快。BLE 主要用于健身设备、医疗设备、家居自动化等场景。 蓝牙 BLE 设备的连接信道L2CAP的基本概念是信道(Signaling Channel)。信道是个抽象概念,表示两个设备某个协议层之间的通道。每个信道分配一个2字节的信道ID——CID(Channel ID),每个信道功用不同,比如CID=0x0004的信道表示属性协议(Attribute Protocol)专用信道。对于BLE协议,L2CAP共有三个信道ID: 0x0004 – 属性协议 0x0005 – 低功耗信令信道 0x0006 – 安全管理协议 其他信道则用于经典蓝牙。协议复用可以理解为,不同的协议走不同的信道,互不干扰。 代码#define ATT_CID 4; // 创建 L2CAP socket int s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); // 绑定 L2CAP socket struct sockaddr_l2 bind_addr = {0}; bind_addr.l2_family = AF_BLUETOOTH; bind_addr.l2_cid = htobs(ATT_CID); // ATT 信道 CID bdaddr_t any_addr = {{0, 0, 0, 0, 0, 0}}; bacpy(&bind_addr.l2_bdaddr, &any_addr); bind_addr.l2_bdaddr_type = BDADDR_LE_PUBLIC; int err = bind(s, (struct sockaddr *)&bind_addr, sizeof(bind_addr)); if (err) { return -1; } // 连接 L2CAP socket struct sockaddr_l2 conn_addr = {0}; conn_addr.l2_family = AF_BLUETOOTH; conn_addr.l2_cid = htobs(ATT_CID); // ATT CID str2ba(mac_address.c_str(), &conn_addr.l2_bdaddr); conn_addr.l2_bdaddr_type = BDADDR_LE_PUBLIC; err = connect(s, (struct sockaddr *)&conn_addr, sizeof(conn_addr)); if (err) { exit(-1); } L2CAP 数据包解析L2CAP(Logical Linked Control and Adaptation Protocol 逻辑链路控制与适配协议)工作在链路层,为上层协议提供数据通道。它支持数据分片与重组,确保数据完整可靠地传输。 ...

2024年06月16日 · 3 分钟 · Cassius0924

解决VScode远程开发C++项目时无代码提示问题

简单粗暴,给本地主机和远程主机都安装上C/C++插件即可。

2024年06月16日 · 1 分钟 · Cassius0924

三维重建笔记——从变换矩阵中提取并分析平移量

在编写点云变换和姿态估计代码时,我们经常需要从结果的变换矩阵中提取平移信息,并分析平移量的大小,帮助判断结果的准确性。 本文将介绍如何从变换矩阵中提取平移向量,并计算其欧几里得长度。 提取平移向量假设我们通过某种姿态估计算法得到了一个4x4的变换矩阵 result.transformation_ ,其中包含了旋转和平移信息。 我们可以通过以下代码提取出平移部分:(以Open3D为例) core::Tensor translation = result.transformation_.Slice(0, 0, 3).Slice(1, 3, 4); Slice(0,0,3);表示从矩阵的第0维(行),第0行开始,取3行,即前3行;Slice(1,3,4);表示从矩阵的第1维(列),第3列开始,取1列,即第4列。 最后获得一个3x1的平移向量,包含x、y、z三个方向的平移量。 计算欧几里得长度有了平移向量后,我们可以计算其欧几里得长度,表示平移量的大小: double translation_norm = std::sqrt((translation * translation).Sum({0, 1}).Item<double>()); std::sqrt计算平方根,translation * translation 计算向量每个元素的平方,Sum 聚合,最终得到平移向量的L2范数,即欧几里得长度。 分析平移量通过提取并计算平移向量的模长,我们可以分析相机或物体的移动距离,以及姿态估计结果的准确性。 如果 translation_norm 的值过大,可能表示估计存在误差,需要反复优化算法。 设置不同的阈值可以根据实际场景需求,判断平移量是否在允许的范围内。 以上是从变换矩阵中提取和利用平移信息的简单示例,可以提供点云变换和姿态估计任务有用的辅助分析。

2024年06月16日 · 1 分钟 · Cassius0924

C++ Copy&Swap 惯用法指南

Copy&Swap 是什么Copy&Swap 是一种 C++ 中常用的编程技巧,用于实现类的赋值运算符(operator=)。 实现传统写法先看看未使用 Copy&Swap 的赋值运算符写法: #include <iostream> #include <vector> class OldAClass { private: int _count; std::string _str; std::vector<int> _vec; public: OldAClass() : _count(0), _vec(10) {} // 拷贝构造函数 和 拷贝赋值运算符 OldAClass(OldAClass &a) : _count(a._count), _str(a._str), _vec(a._vec) { std::cout << "Copy constructor called\n"; } OldAClass &operator=(OldAClass &a) { std::cout << "Copy Assignment operator called\n"; if (this != &a) { //判断传入的 a 是否是自己 _count = a._count; _str = a._str; _vec = a._vec; } return *this; } // 移动构造函数 和 移动赋值运算符 OldAClass(OldAClass &&a) noexcept : _count(a._count), _str(std::move(a._str)), _vec(std::move(a._vec)) { std::cout << "Move constructor called\n"; } OldAClass &operator=(OldAClass &&a) noexcept { std::cout << "Move Assignment operator called\n"; if (this != &a) { _count = a._count; _str = std::move(a._str); _vec = std::move(a._vec); } return *this; } }; 可以看到,这种写法需要重复写两次赋值运算符,并且每次都需要判断传入的参数是否是自己,而且代码重复度高。 Copy&Swap 写法class AClass { private: int _count; std::string _str; std::vector<int> _vec; public: AClass() : _count(0), _vec(10) {} static void swap(AClass &a, AClass &b) { std::swap(a._count, b._count); std::swap(a._str, b._str); std::swap(a._vec, b._vec); } // 拷贝构造函数 AClass(AClass &a) : _count(a._count), _str(a._str), _vec(a._vec) { std::cout << "Copy constructor called\n"; } // 移动构造函数 AClass(AClass &&a) noexcept { std::cout << "Move constructor called\n"; swap(*this, a); } // 赋值运算符 AClass &operator=(AClass a) { // 注意这里的参数是值传递,会调用拷贝构造函数 std::cout << "Assignment operator called\n"; swap(*this, a); return *this; } }; 这种写法只需要写一次赋值运算符,代码更简洁,而且不需要判断传入的参数是否是自己。 ...

2024年01月15日 · 2 分钟

基于 Open3D C++ 版实现点云数据的配准、相加和显示

本文介绍如何使用 Open3D C++ 库来进行点云的配准、相加和可视化显示。Open3D 是一个开源的多维数据处理工具箱,支持点云、图像和三维几何等多种类型的数据处理和可视化。 点云读取使用 ReadPointCloud() 函数可以轻松读取 pcd 和 ply 点云文件: // 读取 pcd 和 ply 点云文件 auto source = std::make_shared<open3d::geometry::PointCloud>(); auto target = std::make_shared<open3d::geometry::PointCloud>(); open3d::io::ReadPointCloud("pcd-data/1.pcd", *source); open3d::io::ReadPointCloud("pcd-data/2.pcd", *target); // ply 文件同理 // open3d::io::ReadPointCloud("pcd-data/1.ply", *source); // open3d::io::ReadPointCloud("pcd-data/2.ply", *target); 点云上色为两份点云上上不同的颜色: source->PaintUniformColor({1, 0.706, 0}); // source 为黄色 target->PaintUniformColor({0, 0.651, 0.929});// target 为蓝色 点云配准使用 Open3D 提供的 RegistrationICP() 函数进行点云配准。代码如下: // 为两个点云分别进行outlier removal //auto processed_source = source->RadiusOutlierRemoval(16, 0.5); //auto processed_target = target->RadiusOutlierRemoval(16, 0.5); double threshold = 1.0; // 移动范围的阀值 Eigen::Matrix4d trans_init = Eigen::Matrix4d::Identity(); // 运行ICP配准 open3d::pipelines::registration::RegistrationResult reg_p2p; // 这一步会返回点云的变换矩阵 reg_p2p = open3d::pipelines::registration::RegistrationICP( *source, *target, threshold, trans_init, open3d::pipelines::registration::TransformationEstimationPointToPoint()); // 将点云依照输出的变换矩阵进行变换 source->Transform(reg_p2p.transformation_); 点云相加这一步很简单,使用重载运算符 += 将两份点云数据相加: ...

2023年04月10日 · 1 分钟 · Cassius0924