Linux 内核源码can相关配置项

ops/2025/2/7 20:03:43/

目录

  • 一、配置项解释(kernel源码/net/can/Makefile)
    • 1. CONFIG_CAN
    • 2. CONFIG_PROC_FS
    • 3. CONFIG_CAN_RAW
    • 4. CONFIG_CAN_BCM
    • 5. CONFIG_CAN_GW
    • 6. CONFIG_CAN_J1939
    • 7. CONFIG_CAN_ISOTP
    • 8. 总结
  • 二、Linux can协议栈部分功能细究
    • 1. CAN Gateway
      • 1.1 原理及使用场景
      • 1.2 使用方法
      • 1.3 为什么需要 CAN Gateway
    • 2. CAN Broadcast Manager (BCM)
      • 2.1 功能
      • 2.2 的实现文件
      • 2.3 **BCM 的使用示例**
        • 2.3.1 **创建 BCM 套接字**
        • 2.3.2 **配置周期性发送**
        • 2.3.3 **接收 BCM 消息**
      • 2.4 **BCM 的调用流程**
  • 二、can应用层编程
    • 1. 基本的can数据收发
      • 1.1 **包含头文件**
      • 1.2 **定义 CAN 接口名称**
      • 1.3 **创建 CAN 套接字并设置过滤规则**
      • 1.4 **接收和发送 CAN 帧**
      • 1.5 **主函数**
      • 1.6 **编译和运行**
      • 1.7 **解释**
    • 2. 使用can fd进行数据收发
      • 2.1 示例程序
      • 2.2 编译和运行
      • 2.3 解释
      • 2.4 注意事项
    • 3. can设置接收所有的错误帧到app层
  • 三、内核源码中can相关文档

一、配置项解释(kernel源码/net/can/Makefile)

在这里插入图片描述

1. CONFIG_CAN

  • 含义:启用通用CAN(Controller Area Network)支持。
  • 作用:CAN是一种用于实时数据交换的串行通信协议,常用于汽车、工业自动化等领域。启用这个选项后,内核将提供基本的CAN网络支持。
  • 相关文件
    • can.o:包含CAN核心模块的实现。
    • af_can.o:实现CAN地址族(Address Family)支持。
    • proc.o:如果配置中启用了CONFIG_PROC_FS,则提供与CAN相关的/proc文件系统支持。

2. CONFIG_PROC_FS

  • 含义:启用/proc文件系统支持。
  • 作用/proc文件系统是一个虚拟文件系统,提供内核状态和统计信息的接口。启用这个选项后,可以在/proc目录下看到与CAN相关的文件和目录。
  • 相关文件
    • proc.o:实现与CAN相关的/proc文件系统支持。

3. CONFIG_CAN_RAW

  • 含义:启用CAN RAW socket支持。
  • 作用:CAN RAW socket允许用户空间程序直接发送和接收CAN帧,而不需要经过更高级的协议处理。
  • 相关文件
    • can-raw.o:包含CAN RAW socket模块的实现。
    • raw.o:实现CAN RAW socket的具体功能。

4. CONFIG_CAN_BCM

  • 含义:启用CAN BCM(Bridge CAN Manager)支持。
  • 作用:CAN BCM提供了一种机制,可以在多个CAN接口之间桥接CAN帧,或者在多个应用之间共享CAN帧。这对于复杂的CAN网络配置非常有用。
  • 相关文件
    • can-bcm.o:包含CAN BCM模块的实现。
    • bcm.o:实现CAN BCM的具体功能。

5. CONFIG_CAN_GW

  • 含义:启用CAN GW(Gateway)支持。
  • 作用:CAN GW用于在不同的CAN网络之间进行路由和数据转发。这对于多网络拓扑的CAN系统非常有用。
  • 相关文件
    • can-gw.o:包含CAN GW模块的实现。
    • gw.o:实现CAN GW的具体功能。

6. CONFIG_CAN_J1939

  • 含义:启用CAN J1939协议支持。
  • 作用:J1939是一种基于CAN的高级协议,广泛用于重型车辆和工业设备的通信。启用这个选项后,内核将提供对J1939协议的支持。
  • 相关文件
    • j1939/:包含J1939协议相关模块的实现。

7. CONFIG_CAN_ISOTP

  • 含义:启用CAN ISOTP(ISO Transport Protocol)支持。
  • 作用:ISOTP是一种用于CAN网络的传输层协议,用于传输超过8字节的数据。这对于需要传输较大数据包的应用非常有用。
  • 相关文件
    • can-isotp.o:包含CAN ISOTP模块的实现。
    • isotp.o:实现CAN ISOTP的具体功能。

8. 总结

  这些配置项主要用于控制Linux内核中CAN相关模块的编译和功能启用。根据你的硬件和软件需求,你可以选择性地启用这些选项。以下是一些常见的使用场景:

  • 基本CAN支持:启用CONFIG_CANCONFIG_CAN_RAW,用于基本的CAN通信。
  • 高级CAN功能:启用CONFIG_CAN_BCMCONFIG_CAN_GW,用于复杂的CAN网络拓扑。
  • 特定协议支持:启用CONFIG_CAN_J1939CONFIG_CAN_ISOTP,用于特定的工业和汽车通信协议。

二、Linux can协议栈部分功能细究

1. CAN Gateway

1.1 原理及使用场景

  CAN Gateway(GW)的功能并不是指在同一个物理总线上转发消息,而是指在不同的物理总线之间转发消息。这在多总线系统中非常有用,例如:

  • 多总线系统:在复杂的系统中,可能会有多个CAN总线,每个总线负责不同的功能或不同的子系统。CAN Gateway 可以将一个总线上的消息转发到另一个总线,从而实现不同总线之间的通信。

  • 隔离和路由:CAN Gateway 可以用于隔离不同的CAN网络,防止一个网络中的问题影响到另一个网络。它还可以根据特定的规则路由消息,只转发特定的消息到特定的总线。

  • 协议转换:CAN Gateway 不仅限于CAN总线之间的转发,还可以用于不同协议之间的转换,例如CAN到以太网、CAN到LIN等。

CAN Gateway 的具体应用场景:
  假设你有一个系统,包含两个CAN总线:can0can1,即一个soc拥有两路can。这两个总线分别连接不同的子系统,但有时需要在这两个子系统之间传递消息。这时,你可以使用CAN Gateway来实现这个功能。

1.2 使用方法

  可使用 cangw 工具配置 CAN Gateway,下述命令表示将 can0 接口的消息转发到 can1 接口,并启用错误帧转发。

sudo cangw -A -s can0 -d can1 -e

  也可以自己利用Linux API实现该功能,示例代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>#define CAN_INTERFACE1 "can0"
#define CAN_INTERFACE2 "can1"void setup_can_socket(int *sock, const char *ifname) {struct sockaddr_can addr;struct ifreq ifr;*sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);if (*sock < 0) {perror("Socket");exit(EXIT_FAILURE);}strcpy(ifr.ifr_name, ifname);ioctl(*sock, SIOCGIFINDEX, &ifr);addr.can_family = AF_CAN;addr.can_ifindex = ifr.ifr_ifindex;if (bind(*sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {perror("Bind");close(*sock);exit(EXIT_FAILURE);}
}void forward_messages(int src_sock, int dst_sock) {struct can_frame frame;while (1) {if (read(src_sock, &frame, sizeof(struct can_frame)) < 0) {perror("Read");continue;}if (write(dst_sock, &frame, sizeof(struct can_frame)) < 0) {perror("Write");continue;}}
}int main() {int can0_sock, can1_sock;setup_can_socket(&can0_sock, CAN_INTERFACE1);setup_can_socket(&can1_sock, CAN_INTERFACE2);printf("CAN Gateway started: %s <-> %s\n", CAN_INTERFACE1, CAN_INTERFACE2);// Start forwarding messages from can0 to can1forward_messages(can0_sock, can1_sock);close(can0_sock);close(can1_sock);return 0;
}

  确保 CAN 接口已经启动并配置好。你可以使用 ip 命令来配置 CAN 接口,例如:

sudo ip link set can0 up type can bitrate 500000
sudo ip link set can1 up type can bitrate 500000

  然后运行编译后的程序:

 ./can_gateway

  你可以使用 candump 和 cansend 工具来测试 CAN 网关是否正常工作。例如,发送一个 CAN 消息到 can0 接口,并检查 can1 接口是否收到该消息:

# 发送消息到 can0
cansend can0 123#DEADBEEF# 监听 can1 接口
candump can1

  如果一切正常,你应该在 can1 接口中看到 123#DEADBEEF 消息。上述代码是一个非常基础的 CAN 网关实现。你可以根据需要扩展其功能,例如:

  • 添加过滤规则,只转发特定 ID 的消息。
  • 支持更多的 CAN 接口。 添加日志记录和错误处理。
  • 提供配置文件或命令行参数来动态配置转发规则。

1.3 为什么需要 CAN Gateway

  • 物理隔离:不同的CAN总线可以物理隔离,防止一个总线上的问题影响到另一个总线。
  • 消息路由:可以根据特定的规则路由消息,只转发特定的消息到特定的总线。
  • 协议转换:CAN Gateway 可以用于不同协议之间的转换,例如CAN到以太网、CAN到LIN等。

2. CAN Broadcast Manager (BCM)

2.1 功能

  Linux 内核中的 CAN Broadcast Manager (BCM) 是一个用于 CAN 总线的高级协议,主要用于处理周期性和非周期性的数据帧(如周期性发送、过滤、应答等),BCM 是 CAN 协议栈的一部分。

  BCM 的主要功能是管理 周期性的 CAN 帧非周期性的 CAN 帧

  • 周期性帧

    • BCM 可以配置为定期发送相同的 CAN 数据帧,例如周期性地发送心跳包或传感器数据。
    • 用户可以配置发送频率、帧内容以及发送次数。
  • 非周期性帧

    • BCM 可以处理非周期性的 CAN 帧,例如配置过滤规则,接收特定的 CAN 帧并触发回调。
    • BCM 支持向接收到的 CAN 帧发送应答帧。

  BCM 支持删除周期性发送任务或过滤规则。BCM 提供了一个用户空间接口,允许应用程序通过 socket API 与 BCM 进行交互。

2.2 的实现文件

BCM 的实现代码位于 Linux 内核的以下文件中:

  • net/can/bcm.c

    • 这是 BCM 的核心实现文件,包含了周期性发送、过滤、应答等功能的实现。
    • 定义了 BCM 的协议处理逻辑和用户空间接口。
  • include/uapi/linux/can/bcm.h

    • 定义了 BCM 的头文件,包含 BCM 相关的结构体和常量。
      在这里插入图片描述

2.3 BCM 的使用示例

2.3.1 创建 BCM 套接字
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/bcm.h>int sock = socket(PF_CAN, SOCK_DGRAM, CAN_BCM);
if (sock < 0) {perror("socket");return -1;
}
2.3.2 配置周期性发送
struct bcm_msg_head msg_head;
struct can_frame frames[2];// 设置 BCM 消息头
msg_head.opcode = TX_SETUP;
msg_head.flags = 0;
msg_head.count = 0; // 无限循环发送
msg_head.ival1.tv_sec = 1; // 间隔 1 秒
msg_head.ival1.tv_usec = 0;
msg_head.can_id = 0x123;
msg_head.nframes = 1;// 设置 CAN 帧
frames[0].can_id = 0x123;
frames[0].can_dlc = 2;
frames[0].data[0] = 0x11;
frames[0].data[1] = 0x22;// 发送配置
write(sock, &msg_head, sizeof(msg_head));
write(sock, frames, sizeof(frames));
2.3.3 接收 BCM 消息
struct can_frame recv_frame;
recv(sock, &recv_frame, sizeof(recv_frame), 0);

2.4 BCM 的调用流程

  1. 创建 BCM 套接字

    • 使用 socket(PF_CAN, SOCK_DGRAM, CAN_BCM) 创建 BCM 套接字。
  2. 配置 BCM 任务

    • 使用 write 向 BCM 发送配置消息,如 TX_SETUPRX_SETUP
  3. 启动 BCM 任务

    • BCM 根据配置执行周期性发送或过滤接收。
  4. 接收 BCM 消息

    • 使用 recv 接收 BCM 返回的消息,如接收到的 CAN 帧或应答帧。
  5. 删除 BCM 任务

    • 使用 TX_DELETERX_DELETE 删除 BCM 任务。

  通过 BCM,用户可以轻松实现复杂的 CAN 总线通信需求,例如心跳包、传感器数据采集和应答机制。

二、can应用层编程

1. 基本的can数据收发

  下面是一个完整的 Linux CAN 数据收发程序,其中包括设置过滤规则的功能。这个程序将创建一个 CAN 套接字,绑定到指定的 CAN 接口,并设置过滤规则来只接收特定的 CAN 帧。

1.1 包含头文件

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <sys/ioctl.h>

1.2 定义 CAN 接口名称

#define CAN_IFNAME "can0"

1.3 创建 CAN 套接字并设置过滤规则

int create_can_socket_with_filter(const char *ifname, canid_t id, canid_t mask) {int sock;struct sockaddr_can addr;struct ifreq ifr;struct can_filter rfilter[1];// 创建 CAN 套接字sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);if (sock < 0) {perror("socket");return -1;}// 获取 CAN 接口索引strcpy(ifr.ifr_name, ifname);if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) {perror("ioctl");close(sock);return -1;}// 绑定 CAN 套接字addr.can_family = AF_CAN;addr.can_ifindex = ifr.ifr_ifindex;if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {perror("bind");close(sock);return -1;}// 设置过滤规则rfilter[0].can_id = id;rfilter[0].can_mask = mask;setsockopt(sock, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));return sock;
}

1.4 接收和发送 CAN 帧

void receive_and_send_can_frame(int sock) {struct can_frame frame;ssize_t nbytes;// 从 CAN 接口读取数据nbytes = read(sock, &frame, sizeof(struct can_frame));if (nbytes < 0) {perror("read");return;}// 打印接收到的 CAN 帧printf("Received CAN frame: ID=0x%03X DLC=%d Data=", frame.can_id, frame.can_dlc);for (int i = 0; i < frame.can_dlc; i++) {printf("%02X ", frame.data[i]);}printf("\n");// 将接收到的 CAN 帧重新发送回同一个 CAN 接口if (write(sock, &frame, sizeof(struct can_frame)) != sizeof(struct can_frame)) {perror("write");}
}

1.5 主函数

  在主函数中创建 CAN 套接字,设置过滤规则,并进入循环接收和发送 CAN 帧:

int main() {int sock;canid_t filter_id = 0x123;  // 过滤的 CAN IDcanid_t filter_mask = 0x7FF; // 过滤掩码// 创建 CAN 套接字并设置过滤规则sock = create_can_socket_with_filter(CAN_IFNAME, filter_id, filter_mask);if (sock < 0) {fprintf(stderr, "Failed to create CAN socket\n");return -1;}printf("CAN socket created and filter set. Waiting for frames...\n");while (1) {receive_and_send_can_frame(sock);}close(sock);return 0;
}

1.6 编译和运行

  保存上述代码到一个文件中,例如 can_example.c,然后使用以下命令编译和运行:

gcc -o can_example can_example.c
./can_example

1.7 解释

  • 创建 CAN 套接字:使用 socket() 函数创建一个 CAN 套接字。
  • 绑定 CAN 接口:使用 ioctl()bind() 函数将套接字绑定到指定的 CAN 接口。
  • 设置过滤规则:使用 setsockopt() 函数设置 CAN 帧的过滤规则。
  • 接收和发送 CAN 帧:使用 read()write() 函数在 CAN 接口上接收和发送 CAN 帧。

2. 使用can fd进行数据收发

2.1 示例程序

  以下是一个完整的 Linux CAN FD 数据收发程序,其中包括设置过滤规则的功能。这个程序将创建一个 CAN FD 套接字,绑定到指定的 CAN 接口,并设置过滤规则来只接收特定的 CAN 帧。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <sys/ioctl.h>#define CAN_IFNAME "can0"int create_canfd_socket_with_filter(const char *ifname, canid_t id, canid_t mask) {int sock;struct sockaddr_can addr;struct ifreq ifr;struct can_filter rfilter[1];int enable_canfd = 1;// 创建 CAN FD 套接字sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);if (sock < 0) {perror("socket");return -1;}// 启用 CAN FD 支持if (setsockopt(sock, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &enable_canfd, sizeof(enable_canfd)) < 0) {perror("setsockopt CAN_RAW_FD_FRAMES");close(sock);return -1;}// 获取 CAN 接口索引strcpy(ifr.ifr_name, ifname);if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) {perror("ioctl");close(sock);return -1;}// 绑定 CAN FD 套接字addr.can_family = AF_CAN;addr.can_ifindex = ifr.ifr_ifindex;if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {perror("bind");close(sock);return -1;}// 设置过滤规则rfilter[0].can_id = id;rfilter[0].can_mask = mask;setsockopt(sock, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));return sock;
}void receive_and_send_canfd_frame(int sock) {struct canfd_frame frame;ssize_t nbytes;// 从 CAN FD 接口读取数据nbytes = read(sock, &frame, sizeof(struct canfd_frame));if (nbytes < 0) {perror("read");return;}// 打印接收到的 CAN FD 帧printf("Received CAN FD frame: ID=0x%03X DLC=%d Data=", frame.can_id, frame.len);for (int i = 0; i < frame.len; i++) {printf("%02X ", frame.data[i]);}printf("\n");// 将接收到的 CAN FD 帧重新发送回同一个 CAN 接口if (write(sock, &frame, sizeof(struct canfd_frame)) != sizeof(struct canfd_frame)) {perror("write");}
}int main() {int sock;canid_t filter_id = 0x123;  // 过滤的 CAN IDcanid_t filter_mask = 0x7FF; // 过滤掩码// 创建 CAN FD 套接字并设置过滤规则sock = create_canfd_socket_with_filter(CAN_IFNAME, filter_id, filter_mask);if (sock < 0) {fprintf(stderr, "Failed to create CAN FD socket\n");return -1;}printf("CAN FD socket created and filter set. Waiting for frames...\n");while (1) {receive_and_send_canfd_frame(sock);}close(sock);return 0;
}

2.2 编译和运行

gcc -o canfd_example canfd_example.c
./canfd_example

2.3 解释

  1. 创建 CAN FD 套接字

    • 使用 socket(PF_CAN, SOCK_RAW, CAN_RAW) 创建一个 CAN 套接字。
    • 通过 setsockopt 启用 CAN FD 支持。
  2. 绑定 CAN 接口

    • 使用 ioctl 获取 CAN 接口的索引。
    • 使用 bind 将套接字绑定到指定的 CAN 接口。
  3. 设置过滤规则

    • 使用 setsockopt 设置 CAN 帧的过滤规则。
  4. 接收和发送 CAN FD 帧

    • 使用 read 从 CAN FD 接口读取数据。
    • 打印接收到的 CAN FD 帧。
    • 使用 write 将接收到的 CAN FD 帧重新发送回同一个 CAN 接口。

2.4 注意事项

  • CAN FD 支持:确保你的硬件和驱动程序支持 CAN FD。
  • DLC 和 Length:在 CAN FD 中,len 字段表示实际数据长度,而 dlc 字段表示数据长度代码(DLC)。
  • 过滤规则:过滤规则可以根据需要进行调整,以匹配特定的 CAN ID 和掩码。

3. can设置接收所有的错误帧到app层

#include <linux/can.h>
#include <linux/can/error.h>
#include <linux/can/raw.h>
#include <net/if.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>#define CAN_INTERFACE "can0"// 打印错误帧的具体类型
void print_error_type(struct can_frame *frame)
{uint32_t error_code = frame->data[0]; // 获取错误代码的第 0 字节printf("  - Error Code: 0x%02X\n", error_code);if (error_code & CAN_ERR_TX_TIMEOUT){printf("    - TX Timeout Error (CAN_ERR_TX_TIMEOUT)\n");}else if (error_code & CAN_ERR_LOSTARB){printf("    - Lost Arbitration Error (CAN_ERR_LOSTARB)\n");}else if (error_code & CAN_ERR_CRTL){printf("    - Controller problems (CAN_ERR_CRTL)\n");}else if (error_code & CAN_ERR_PROT){printf("    - Protocol Violation (CAN_ERR_PROT)\n");}else if (error_code & CAN_ERR_TRX){printf("    - transceiver status (CAN_ERR_TRX)\n");}else if (error_code & CAN_ERR_ACK){printf("    - ACK Error (CAN_ERR_ACK)\n");}else if (error_code & CAN_ERR_BUSOFF){printf("    - Bus Off (CAN_ERR_BUSOFF)\n");}else if (error_code & CAN_ERR_BUSERROR){printf("    - Bus Error (CAN_ERR_BUSERROR)\n");}else if (error_code & CAN_ERR_RESTARTED){printf("    - Controller Restarted (CAN_ERR_RESTARTED)\n");}else{printf("    - Unknown Error Type\n");}
}int main()
{int s = -1;struct ifreq ifr;struct sockaddr_can addr;struct can_frame frame;char *ifname = CAN_INTERFACE;// 创建一个 CAN 原始套接字if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0){perror("Socket creation failed");return -1;}// 获取接口索引strcpy(ifr.ifr_name, ifname);ioctl(s, SIOCGIFINDEX, &ifr);// 设置套接字地址addr.can_ifindex = ifr.ifr_ifindex;addr.can_family = AF_CAN;// 绑定套接字到指定的 CAN 接口if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0){perror("Bind failed");return -2;}// 设置 CAN 过滤规则,接收所有 CAN 错误帧uint32_t err_mask = 0xFFFFFFFF; // 接收所有错误帧setsockopt(s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &err_mask, sizeof(err_mask));printf("Listening for CAN error frames on %s...\n", ifname);while (1){int nbytes;struct sockaddr_can sender;// 接收数据nbytes = recvfrom(s, &frame, sizeof(struct can_frame), 0,(struct sockaddr *)&sender, NULL);if (nbytes < 0){perror("recvfrom");continue;}// 检查接收到的帧是否为 CAN 错误帧if (frame.can_id & CAN_ERR_FLAG){printf("Received CAN error frame: ID=0x%03X, DLC=%d\n", frame.can_id,frame.can_dlc);print_error_type(&frame);}}// 关闭套接字close(s);return 0;
}

三、内核源码中can相关文档

https://linuxkernel.org.cn/doc/html/latest/networking/can.html

http://www.ppmy.cn/ops/156528.html

相关文章

阿里云不同账号vpc对等连接

目录 一&#xff0c;VPC对等连接介绍 1&#xff0c;VPC功能介绍 2&#xff0c;使用场景 二&#xff0c;准备vpc,和ECS服务器 1,第一个账号vpc网络/网段 ​编辑 2&#xff0c;第一个账号下的ECS实例 ip:172.19.45.29 ​编辑 3&#xff0c; 第二个账号vpc网络/网段 4&…

亚博microros小车-原生ubuntu支持系列:18 Cartographer建图

Cartographer简介 Cartographer是Google开源的一个ROS系统支持的2D和3D SLAM&#xff08;simultaneous localization and mapping&#xff09;库。基于图优化&#xff08;多线程后端优化、cere构建的problem优化&#xff09;的方法建图算法。可以结合来自多个传感器&#xff0…

Spring Boot 自动装配原理与优化实践

在 Java 开发领域&#xff0c;Spring Boot 以其 “约定优于配置” 的理念&#xff0c;极大地简化了 Spring 应用的开发和部署过程&#xff0c;成为了众多开发者的首选框架。它通过自动装配机制&#xff0c;让开发者能够快速搭建一个功能完备的应用&#xff0c;而无需进行繁琐的…

js-使用dayjs库常用方法以及vue中使用计算两个日期间的天数差

1.前言 dayjs是一个轻量级的JavaScript日期库&#xff0c;可以方便地处理日期和时间。 2.dayjs常用方法 2.1创建dayjs对象 dayjs(); dayjs(2023-10-01); dayjs(1664582400000); // 时间戳2.2获取日期和时间 dayjs().year(); // 获取年份 dayjs().month(); // 获取月份&…

C# 数组、索引器与集合介绍

.NET学习资料 .NET学习资料 .NET学习资料 在 C# 编程中&#xff0c;数组、索引器与集合是常用的数据结构&#xff0c;它们各自有着独特的功能和用途。 一、C# 数组 数组是一种用于存储固定数量同类型元素的数据结构。在 C# 中&#xff0c;数组的声明方式较为特殊&#xff0…

【SQL技术】不同数据库引擎 SQL 优化方案剖析

一、引言 在数据处理和分析的世界里&#xff0c;SQL 是不可或缺的工具。不同的数据库系统&#xff0c;如 MySQL、PostgreSQL&#xff08;PG&#xff09;、Doris 和 Hive&#xff0c;在架构和性能特点上存在差异&#xff0c;因此针对它们的 SQL 优化策略也各有不同。这些数据库中…

图像增广:用OpenCV实现的6个自动图像增强策略

目录 1. 调整对比度和亮度 2. 直方图均衡化 3. 自动白平衡 4. 高斯模糊去噪 5. 自适应阈值 6. 锐化滤波器 7. 实战案例分析与技巧 本文将探讨如何使用OpenCV来自动增强图像,让照片看起来更加生动和吸引人。下面,我们将一步步介绍六种图像增强策略,并通过Python代码示…

计算机视觉-边缘检测

一、边缘 1.1 边缘的类型 ①实体上的边缘 ②深度上的边缘 ③符号的边缘 ④阴影产生的边缘 不同任务关注的边缘不一样 1.2 提取边缘 突变-求导&#xff08;求导也是一种卷积&#xff09; 近似&#xff0c;1&#xff08;右边的一个值-自己可以用卷积做&#xff09; 该点f(x,y)…