⑤【从0制作自己的ros导航小车:上、下位机通信篇】上、下位机串口DMA通信

news2024/9/24 1:27:25

从0制作自己的ros导航小车

    • 前言
    • 一、准备工作
    • 二、下位机端(STM32)
    • 三、上位机端(旭日x3派)
    • 四、测试

系列文章
①【从0制作自己的ros导航小车:介绍及准备】
②【从0制作自己的ros导航小车:下位机篇】01、工程准备_标准库移植freertos
③【从0制作自己的ros导航小车:下位机篇】02、电机驱动、转速读取、PID控制
④【从0制作自己的ros导航小车:下位机篇】03、mpu6050偏航角获取
⑤【从0制作自己的ros导航小车:上、下位机通信篇】上、下位机串口DMA通信
⑥【从0制作自己的ros导航小车:上位机篇】01、里程计与坐标变换发布
⑦【从0制作自己的ros导航小车:上位机篇】02、ros1多机通讯与坐标变换可视化
⑧【从0制作自己的ros导航小车:上位机篇】03、添加urdf模型(发布各传感器与小车基坐标系之间的静态坐标变换)
⑨【从0制作自己的ros导航小车:上位机篇】04、使用gmapping建图
⑩【从0制作自己的ros导航小车:上位机篇】05、导航!


前言

下位机的电机驱动、轮速读取、偏航角读取都已经完成,接下来就是上下位机的桥梁:串口通信。
使用USB转TTL模块连接上位机(旭日x3派)和下位机(stm32),只需要连接RX、TX、GND即可。


一、准备工作

确认上位机是否安装了ch340驱动(如果使用的USB转TTL模块使用的是cp210x,那就安装对应的驱动)
远程登录开发板之后,插入USB转TTL模块,在命令行输入:

ls -l /dev/ttyUSB*

如果显示如下:在这里插入图片描述
那就不需要再安装驱动了,如果没有显示的话,那就说明板子没有识别到此串口,那就需要安装驱动:这个我也没有安装过,因为这块板子插上直接就能识别,需要安装的驱动的可以参考这篇博客。

二、下位机端(STM32)

以下代码在此博主开源的代码上进行修改使用(感谢博主的开源),将代码集成到一个c文件中。
serial.c:

#include "Serial.h"
#include "stm32f10x.h"   
#include <stdio.h>  
#include <string.h>  
#include <stdbool.h>

unsigned char JudgeReceiveBuffer[reBiggestSize*2];//接受最大内存
unsigned char JudgeSend[SendBiggestSize];//发送最大内存

//通信协议常量
const unsigned char header[2]  = {0x55, 0xaa};
const unsigned char ender[2]   = {0x0d, 0x0a};

uint8_t   num_buff[3],think_flag=0;
int8_t    fan;
int8_t    bianliang;

unsigned char SaveBuffer[100];//接受双缓存区

Ctrl_information chassis_ctrl ={0,0,0,1,1}; //接受上位机控制信息
//extern Chassis F103RC_chassis;//底盘实时数据

//发送数据(左轮速、右轮速、角度)共用体(-32767 - +32768)
union sendData
{
	short d;
	unsigned char data[2];
}leftVelNow,rightVelNow,angleNow;

//左右轮速控制速度共用体
union receiveData
{
	short d;
	unsigned char data[2];
}leftVelSet,rightVelSet;

void JudgeBuffReceive(unsigned char ReceiveBuffer[])
{
	//uint16_t cmd_id;//识别信息内容,暂未开启
	short k=0;
	short PackPoint;
	memcpy(&SaveBuffer[reBiggestSize],&ReceiveBuffer[0],reBiggestSize);		//把ReceiveBuffer[0]地址拷贝到SaveBuffer[13], 依次拷贝13个, 把这一次接收的存到数组后方
	for(PackPoint=0;PackPoint<reBiggestSize;PackPoint++)		//先处理前半段数据(在上一周期已接收完成)
	{
		if(SaveBuffer[PackPoint]==header[0] && SaveBuffer[PackPoint + 1]== header[1]) //包头检测
		{	
			short dataLength  = SaveBuffer[PackPoint + 2]    ;
			unsigned char checkSum = getCrc8(&SaveBuffer[PackPoint], 3 + dataLength);
				// 检查信息校验值
			if (checkSum == SaveBuffer[PackPoint +3 + dataLength]) //SaveBuffer[PackPoint开始的校验位]
			{
				//说明数据核对成功,开始提取数据
				 for(k = 0; k < 2; k++)
					{
						leftVelSet.data[k]  = SaveBuffer[PackPoint + k + 3]; //SaveBuffer[3]  SaveBuffer[4]
						rightVelSet.data[k] = SaveBuffer[PackPoint + k + 5]; //SaveBuffer[5]  SaveBuffer[6]
					}				
					
					//速度赋值操作
					chassis_ctrl.leftSpeedSet  = (int)leftVelSet.d;
					chassis_ctrl.rightSpeedSet = (int)rightVelSet.d;
					
					//ctrlFlag
					chassis_ctrl.crtlFlag = SaveBuffer[PackPoint + 7];                //SaveBuffer[7]
				
			}
		}	
	memcpy(&SaveBuffer[0],&SaveBuffer[reBiggestSize],reBiggestSize);		//把SaveBuffer[13]地址拷贝到SaveBuffer[0], 依次拷贝13个,把之前存到后面的数据提到前面,准备处理
	}
}


void usartSendData(short leftVel, short rightVel,short angle,unsigned char ctrlFlag)
{
	int i, length = 0;

	// 计算左右轮期望速度
	leftVelNow.d  = leftVel;
	rightVelNow.d = rightVel;
	angleNow.d    = angle;
	
	// 设置消息头
	for(i = 0; i < 2; i++)
		JudgeSend[i] = header[i];                      // buf[0] buf[1] 
	
	// 设置机器人左右轮速度、角度
	length = 7;
	JudgeSend[2] = length;                             // buf[2]
	for(i = 0; i < 2; i++)
	{
		JudgeSend[i + 3] = leftVelNow.data[i];         // buf[3] buf[4]
		JudgeSend[i + 5] = rightVelNow.data[i];        // buf[5] buf[6]
		JudgeSend[i + 7] = angleNow.data[i];           // buf[7] buf[8]
	}
	// 预留控制指令
	JudgeSend[3 + length - 1] = ctrlFlag;              // buf[9]
	
	// 设置校验值、消息尾
	JudgeSend[3 + length] = getCrc8(JudgeSend, 3 + length);  // buf[10]
	JudgeSend[3 + length + 1] = ender[0];              // buf[11]
	JudgeSend[3 + length + 2] = ender[1];              // buf[12]
	
	DMA1_Channel4->CNDTR = SendBiggestSize; 
	DMA_Cmd(DMA1_Channel4,ENABLE);
}


unsigned char getCrc8(unsigned char *ptr, unsigned short len)
{
	unsigned char crc;
	unsigned char i;
	crc = 0;
	while(len--)
	{
		crc ^= *ptr++;
		for(i = 0; i < 8; i++)
		{
			if(crc&0x01)
                crc=(crc>>1)^0x8C;
			else 
                crc >>= 1;
		}
	}
	return crc;
}


/**********************************************************************************************************
*函 数 名: UART1_DMA_Init
*功能说明: uart1初始化(速度收发)
*形    参: 无
*返 回 值: 无
**********************************************************************************************************/
void UART2_DMA_Init(void)
{
    //GPIO端口设置
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);    //使能USART1,GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 注意这里的时钟使能

    //USART1_TX   GPIOA.9
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA9
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    //复用推挽输出
    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9

    //USART1_RX	  GPIOA.10初始化
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

    //USART 初始化设置
    USART_InitStructure.USART_BaudRate = 115200;//串口波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
    USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;    //收发模式

    USART_Init(USART1, &USART_InitStructure); //初始化串口1
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
    USART_Cmd(USART1, ENABLE);                    //使能串口1

    //RX DMA1 5通道
    USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); 
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    {
        DMA_InitTypeDef dma;

        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

        DMA_DeInit(DMA1_Channel5);

        dma.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);
        dma.DMA_MemoryBaseAddr = (uint32_t)JudgeReceiveBuffer;
        dma.DMA_DIR = DMA_DIR_PeripheralSRC;
        dma.DMA_BufferSize = reBiggestSize;
        dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
        dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
        dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
        dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
        dma.DMA_Mode = DMA_Mode_Circular;
        dma.DMA_Priority = DMA_Priority_VeryHigh;
        dma.DMA_M2M = DMA_M2M_Disable;

        DMA_Init(DMA1_Channel5, &dma);
        DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
        DMA_Cmd(DMA1_Channel5, ENABLE);
    }
  
    //TX DMA1 4通道
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
    {
        DMA_InitTypeDef dma;
        DMA_DeInit(DMA1_Channel4);
        dma.DMA_PeripheralBaseAddr = (uint32_t)&(USART1->DR);
        dma.DMA_MemoryBaseAddr = (uint32_t)JudgeSend;
        dma.DMA_DIR = DMA_DIR_PeripheralDST;
        dma.DMA_BufferSize = SendBiggestSize;
        dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
        dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
        dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
        dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
        dma.DMA_Mode = DMA_Mode_Normal;
        dma.DMA_Priority = DMA_Priority_VeryHigh;
        dma.DMA_M2M = DMA_M2M_Disable;

        DMA_Init(DMA1_Channel4, &dma);
        DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);
        DMA_ITConfig(DMA1_Channel4, DMA1_FLAG_GL4, ENABLE);
        DMA_Cmd(DMA1_Channel4, DISABLE);
    }       
}

//rx满中断
void DMA1_Channel5_IRQHandler(void)
{	
	if(DMA_GetFlagStatus(DMA1_FLAG_TC5) == SET)
	{
		DMA_ClearFlag(DMA1_FLAG_TC5);
		//接收处理函数
		JudgeBuffReceive(JudgeReceiveBuffer);
	}
}

//Tx满中断
void DMA1_Channel4_IRQHandler(void)
{
	if(DMA_GetITStatus(DMA1_IT_TC4)!=RESET)
	{
		DMA_ClearFlag(DMA1_FLAG_TC4 | DMA1_FLAG_GL4);
		DMA_ClearITPendingBit(DMA1_IT_TC4 | DMA1_FLAG_GL4);
		
		DMA_Cmd(DMA1_Channel4,DISABLE);
	}	
}

serial.h

#ifndef USART_H
#define USART_H

#define START   0X11
#define reBiggestSize 13
#define SendBiggestSize  13
//控制位
#define Reset 0x01
#define Stop  0x02

#include "stm32f10x.h"   

typedef union 
{
	short d;
	unsigned char data[2];
}sendData;

//左右轮速控制速度共用体
typedef union
{
	short d;
	unsigned char data[2];
}receiveData;

//控制车体结构体
typedef struct{
	int leftSpeedSet;
	int rightSpeedSet;
	unsigned char crtlFlag;
	char left_move;//为1时方向为正
	char right_move;
}Ctrl_information;

//void DATA_DISPOSE_task(void *pvParameters);
	
//串口接受中断中接收函数
void usartReceiveData(void);

//从linux接收并解析数据到参数地址中
extern int usartReceiveOneData(int *p_leftSpeedSet,int *p_rightSpeedSet,unsigned char *p_crtlFlag);   

//封装数据,调用USART1_Send_String将数据发送给linux
extern void usartSendData(short leftVel, short rightVel,short angle,unsigned char ctrlFlag); 

//发送指定字符数组的函数
void USART_Send_String(unsigned char *p,unsigned short sendSize); 

//计算八位循环冗余校验,得到校验值,一定程度上验证数据的正确性
unsigned char getCrc8(unsigned char *ptr, unsigned short len); 

void JudgeBuffReceive(unsigned char ReceiveBuffer[]);

void UART2_DMA_Init(void);
#endif

代码简单介绍:
程序中使用共用体和结构体组织发送的数据包,存放下发的经过解析的数据包。提供了接收函数和发送函数,很好理解,对于想要发送更多数据或者更少数据的小伙伴需要更改发送和接收函数、结构体、公用体等等。
UART2_DMA_Init是初始化串口以DMA的形式,这样可以不占用系统资源,会在后台运行(将数据存到Ctrl_information结构体对象中),后面只需要操作Ctrl_information结构体对象即可获取数据。
上传的数据存放在F103RC_chassis结构体中,可以在定时器中断回调函数中组织此结构体数据,以固定频率发送给上位机。


三、上位机端(旭日x3派)

上位机使用ros1开发,这里默认已经熟悉了ros1的开发流程。
①创建工作空间及功能包

mkdir my_nav_car_ws
cd my_nav_car_ws
mkdir src
cd src
catkin_init_workspace
cd ..
catkin_make
catkin_make install
cd src
catkin_create_pkg uart_tf geometry_msgs nav_msgs roscpp rospy sensor_msgs tf2 tf2_ros cv_bridge
cd ..
catkin_make
cd src/uart_tf/src
touch uartandtf.cpp

②uart节点代码

#include <ros/ros.h>
#include <ros/time.h>
#include <geometry_msgs/TransformStamped.h>
#include <geometry_msgs/Twist.h>
#include <std_msgs/String.h>
#include <std_msgs/Float32.h>
#include <nav_msgs/Odometry.h>
#include <boost/asio.hpp>
#include "tf2_ros/transform_broadcaster.h"
#include <geometry_msgs/Quaternion.h>
#include "tf2_geometry_msgs/tf2_geometry_msgs.h"
#include "tf2/LinearMath/Quaternion.h"
#include <array>
#include <thread>

using namespace std;
using namespace boost::asio;

bool ttt;

std_msgs::String msg;
std::stringstream ss;

class MyNode {

public:
    std::string Node;
    MyNode() 
      : Node("node"), 
        sp(iosev, "/dev/ttyUSB0"), 
        
        odom_pose_covariance_({
        {1e-9, 0, 0, 0, 0, 0, 
        0, 1e-3,1e-9, 0, 0, 0, 
        0, 0, 1e6, 0, 0, 0,
        0, 0, 0, 1e6, 0, 0, 
        0, 0, 0, 0, 1e6, 0, 
        0, 0, 0, 0, 0, 1e-9}}),
        
        odom_twist_covariance({
        {1e-9, 0, 0, 0, 0, 0, 
        0, 1e-3,1e-9, 0, 0, 0, 
        0, 0, 1e6, 0, 0, 0, 
        0, 0, 0, 1e6, 0, 0, 
        0, 0, 0, 0, 1e6, 0, 
        0, 0, 0, 0, 0, 1e-9}})
    
    {
        serialInit();
        
        // Create Odometry publisher
        odom_publisher = nh_.advertise<nav_msgs::Odometry>("odom", 1);
        
        // Create TransformBroadcaster
        tf_broadcaster = std::make_shared<tf2_ros::TransformBroadcaster>();
        
    }
    
    void controlLoop();

private:
    boost::asio::io_service iosev;
    boost::asio::serial_port sp;    
    ros::NodeHandle nh_; 
    ros::Timer timer;
    ros::Publisher odom_publisher;
    std::shared_ptr<tf2_ros::TransformBroadcaster> tf_broadcaster;    

    const unsigned char header[2]  = {0x55, 0xaa};
    const unsigned char ender[2]   = {0x0d, 0x0a};

    union sendData {
        short d;
        unsigned char data[2];
    } leftVelSet, rightVelSet;

    union receiveData {
        short d;
        unsigned char data[2];
    } leftVelNow, rightVelNow, angleNow;

    unsigned char getCrc8(unsigned char *ptr, unsigned short len) {
        unsigned char crc;
        unsigned char i;
        crc = 0;
        while (len--) {
            crc ^= *ptr++;
            for (i = 0; i < 8; i++) {
                if (crc & 0x01)
                    crc = (crc >> 1) ^ 0x8C;
                else
                    crc >>= 1;
            }
        }
        return crc;
    }

    void serialInit() {
        sp.set_option(serial_port::baud_rate(115200));
        sp.set_option(serial_port::flow_control(serial_port::flow_control::none));
        sp.set_option(serial_port::parity(serial_port::parity::none));
        sp.set_option(serial_port::stop_bits(serial_port::stop_bits::one));
        sp.set_option(serial_port::character_size(8));    
    }

    void writeSpeed(double Left_v, double Right_v,unsigned char ctrlFlag) {
        unsigned char buf[13] = {0};//
        int i, length = 0;
    
        leftVelSet.d  = Left_v;//mm/s
        rightVelSet.d = Right_v;
    

        for(i = 0; i < 2; i++)
            buf[i] = header[i];             //buf[0]  buf[1]
        
    
        length = 5;
        buf[2] = length;                    //buf[2]
        for(i = 0; i < 2; i++) {
            buf[i + 3] = leftVelSet.data[i];  //buf[3] buf[4]
            buf[i + 5] = rightVelSet.data[i]; //buf[5] buf[6]
        }

        buf[3 + length - 1] = ctrlFlag;       //buf[7]
    

        buf[3 + length] = getCrc8(buf, 3 + length);//buf[8]
        buf[3 + length + 1] = ender[0];     //buf[9]
        buf[3 + length + 2] = ender[1];     //buf[10]
    

        boost::asio::write(sp, boost::asio::buffer(buf));
    }

    bool readSpeed(double &Left_v,double &Right_v,double &Angle,unsigned char &ctrlFlag) {
        unsigned char i, length = 0;
        unsigned char checkSum;
        unsigned char buf[1024]={0};
        boost::system::error_code err; 

        try {
            boost::asio::streambuf response;
            boost::asio::read_until(sp, response, "\r\n",err);  
            copy(istream_iterator<unsigned char>(istream(&response)>>std::noskipws), istream_iterator<unsigned char>(), buf); 
        } catch(boost::system::system_error &err) {
            ROS_INFO("read_until error");
            return false;
        } 
    

        if (buf[0]!= header[0] || buf[1] != header[1]) {
            ROS_INFO("Received message header error!");
            return false;
        }

        length = buf[2];                                
    

        checkSum = getCrc8(buf, 3 + length);             
        if (checkSum != buf[3 + length]) {                
            ROS_INFO("Received data check sum error!");
            return false;
        }    
    

        for(i = 0; i < 2; i++) {
            leftVelNow.data[i]  = buf[i + 3]; //buf[3] buf[4]
            rightVelNow.data[i] = buf[i + 5]; //buf[5] buf[6]
            angleNow.data[i]    = buf[i + 7]; //buf[7] buf[8]
        }
    

        ctrlFlag = buf[9];
        
        Left_v  =leftVelNow.d;
        Right_v =rightVelNow.d;
        Angle   =angleNow.d;
     
        return true;
    }

    boost::array<double, 36> odom_pose_covariance_;
    boost::array<double, 36> odom_twist_covariance;
};

void MyNode::controlLoop() {
        double left_speed_now = 0.0;
        double right_speed_now = 0.0;
        double angle = 0.0;
        unsigned char testRece4 = 0x00;


        readSpeed(left_speed_now, right_speed_now, angle, testRece4);
        ROS_INFO("left_speed_now=%.2f,right_speed_now =%.2f,angle = %.2f", left_speed_now,right_speed_now,angle  );

    }

int main(int argc, char **argv) {
    ros::init(argc, argv, "Mynode");
    ros::NodeHandle nh;
    /*rclcpp::init(argc, argv);*/

    auto node = std::make_shared<MyNode>();
    
    std::thread control_thread([&]() {
        ros::Rate loop_rate(20);
        /*rclcpp::Rate loop_rate(20);*/
        while (ros::ok()) {
        /*while (rclcpp::ok()) {*/
            node->controlLoop();
            loop_rate.sleep();
        }
    });
    
    ros::spin();
    /*rclcpp::spin(node);*/

    ros::shutdown();
    /*rclcpp::shutdown();*/

    return 0;
}

③cmakelists.txt
添加如下代码,根据自己的情况进行修改。

catkin_package(
  CATKIN_DEPENDS roscpp geometry_msgs nav_msgs tf2 cv_bridge sensor_msgs
)

add_executable(uartandtf src/uartandtf.cpp)
target_link_libraries(uartandtf 
  ${catkin_LIBRARIES}
)

install(TARGETS
  uartandtf
  DESTINATION lib/${PROJECT_NAME}
)

四、测试

代码都准备就绪,可以开始测试了,stm32端大致测试过程如下,由于我代码都已经集成好了,懒得修改,所以以文字形式给出:首先初始化uart、mpu6050、定时器中断、pwm等需要用到的,定时器中断回调函数中不断读取当前左右轮速,每隔20ms串口发送一次数据,样例如下:

usartSendData(left_speed_now,right_speed_now,Yaw,num_buff[1]);

在while循环或者freertos任务中不断读取偏航角,并且打印上位机串口下发的数据:

mpu_dmp_get_data(&Pitch,&Roll,&Yaw);
OLED_ShowSignedNum(2, 7, left_speed_now, 2);
OLED_ShowSignedNum(3, 7, right_speed_now, 2);
我这里用的oled显示,没有的话可以使用串口打印,注意这个串口不能是前面用来与上位机通信的串口。

上下位机都运行之后效果如下:
上位机:
在这里插入图片描述
下位机
在这里插入图片描述
注:下位机我是用oled可视化,也可以使用串口,oled的代码随便网上搜一搜移植一下就好了。

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

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

相关文章

一站式解决方案:打造无缝连接的跨渠道客户服务体验

在当今这个数字化时代&#xff0c;客户与企业之间的互动已不再局限于单一渠道。从社交媒体、在线聊天、电子邮件到电话热线&#xff0c;甚至是实体店面&#xff0c;客户期望能够随时随地、无缝切换地获得一致且高效的服务体验。因此&#xff0c;构建一站式解决方案&#xff0c;…

商城系统审计代码审计

1 开源组件通用性漏洞审计 1.1 fastjson漏洞审计与验证 1.1.1 相关知识 Fastjson是Alibaba开发的Java语言编写的高性能JSON库,用于将数据在JSON和Java对 象之间相互转换。 Fastjson反序列化漏洞简单来说是出现在将JSON数据反序列化过程中出现的漏洞。 攻击者可以传入一个恶…

算法小白的进阶之路(力扣6~8)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…

对象属性值对比(支持复杂对象)

文章目录 前言一、如何对比二、开始编码三、使用结果示例总结 前言 需求如下&#xff1a; 对比两个bean中的内容,返回其中属性的值不一致的完整信息,包括: 属性 新值 旧值 一、如何对比 例如我有一个这的类型: public class Tel {private String name;private String tel; …

学习笔记第十七天

1.链表 1.1链表尾插 void push_back(struct Node *pHead,int n)//尾插 {if(isEmpty(pHead)){push_front(pHead,n);}else{struct Node *p pHead->next; while(p->next !NULL){p p->next;}struct Node *pNew malloc(sizeof(struct Node));p->nextpNew;pNew->n…

C++ bind复杂回调逻辑分析

回调函数基本知识回顾 回调函数是什么 函数指针或者函数对象作为参数传递给另一个函数的机制&#xff0c;当某个事件发生的时候&#xff0c;系统会自动的调用这些函数进行处理事件驱动模型中作用&#xff0c;回调函数则被用于处理I/O事件&#xff0c;通常用来读写异常等事件 bi…

本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——2Yolo使用之ONNX模型准备

本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——2Yolo使用之ONNX模型准备 ​ 大家好&#xff0c;因为板端BPU环境&#xff0c;可以加速目标检测的速度&#xff0c;所以今天在此先给大家带来如何准备一个模型&#xff0c;下一期会给大家带来如何在板端部…

如何做一个惊艳领导和客户的原型?

在产品开发过程中&#xff0c;原型设计是验证设计想法、提升用户体验的重要环节。Axure作为一款业界领先的原型设计工具&#xff0c;凭借其强大的交互设计和丰富的功能&#xff0c;赢得了全球设计师和开发者的信赖。而Axure的高效交互元件库&#xff0c;则如同一本字典或说明书…

将YOLOv8模型从PyTorch的.pt格式转换为OpenVINO支持的IR格式

OpenVINO是Open Visual Inference & Neural Network Optimization工具包的缩写&#xff0c;是一个用于优化和部署AI推理模型的综合工具包。OpenVINO支持CPU、GPU和NPU设备。 OpenVINO的优势: (1).性能&#xff1a;OpenVINO利用英特尔CPU、集成和独立GPU以及FPGA的强大功能提…

原生IP节点是什么意思?和socks5节点有什么区别?

在了解这两种代理节点前&#xff0c;我们首先要了解&#xff1a;节点是什么&#xff1f; 首先&#xff0c;在电信网络当中&#xff0c;一个节点是一个连接点。表示一个再分发点又或者是一个通信端点。节点的定义依赖于所提及的网络和协议层。一个物理网络节点是一个连接到网络…

深度强化学习:穿越智能迷雾,探索AI新纪元

近年来&#xff0c;深度强化学习成为关注的热点。在自动驾驶、棋牌游戏、分子重排和机器人等领域&#xff0c;计算机程序能够通过强化学习&#xff0c;理解以前被视为超级困难的问题&#xff0c;取得了令人瞩目的成果。在围棋比赛中&#xff0c;AlphaGo接连战胜樊麾、李世石和柯…

使用 SpringBoot + 虚拟线程将服务性能提升几百倍

虚拟线程简介 虚拟线程是 Java 平台的一项创新特性。虚拟线程是一种轻量级的线程实现,它在操作系统层面并不对应真实的内核线程,而是由 JVM 进行管理和调度。这使得可以在不消耗大量系统资源的情况下创建大量的线程,从而能够更高效地处理并发任务。 虚拟线程与普通线程的区…

【数学建模】——【A题 信用风险识别问题】全面解析

目录 1.题目 2.解答分析 问题1&#xff1a;指标筛选 1.1 问题背景 1.2 数据预处理 1.3 特征选择方法 1.4 多重共线性检测 1.5 实现步骤 问题2&#xff1a;信用评分模型 2.1 问题背景 2.2 数据分割 2.3 处理不平衡数据 2.4 模型选择与理由 问题3&#xff1a;模型对…

『 Linux 』线程池与 POSIX 线程的封装编码实现

文章目录 线程池概念线程池的编码实现线程池的测试参考代码 线程的封装使用测试封装后的线程参考代码 线程池概念 池化技术是一种资源管理方法,通过预先创建和管理一组资源以便在需要使用时快速分配这些资源; 线程池是池化技术的一种典型应用; 资源分配 在线程池中预先创建一定…

【python015】常见成熟AI-图像识别场景算法清单(已更新)

1.欢迎点赞、关注、批评、指正,互三走起来,小手动起来! 【python015】常见成熟AI-图像识别场景算法清单及代码【python015】常见成熟AI-图像识别场景算法清单及代码【python015】常见成熟AI-图像识别场景算法清单及代码文章目录 1.背景介绍2.`Python`版数据爬取、解析代码2.…

鸿蒙应用框架开发【画中画效果实现】 UI框架

画中画效果实现 介绍 本示例通过kit.ArkUI、kit.MediaKit等接口&#xff0c;实现了视频播放、手动和自动拉起画中画、画中画窗口控制视频播放和暂停等功能。 效果预览 使用说明 在主界面&#xff0c;可以点击对应视频按钮进入视频播放页面&#xff1b;视频播放页面点击开启…

三星One UI 7.0引入苹果的几大特色功能,iOS用户都羡慕哭了

在智能手机操作系统的创新之路上&#xff0c;苹果iOS系统的Dynamic Island和Live Activities功能无疑为用户带来了全新的交互体验。 现在&#xff0c;三星One UI 7.0系列的即将发布&#xff0c;似乎预示着安卓阵营也将迎头赶上&#xff0c;甚至可能在某些方面超越苹果。以下是…

AcWing-AcWing 837. 连通块中点的数量

在原来并查集的基础上增加一个Size数组&#xff0c;Size的初始化是必须先每个元素初始化为1 Size只对根节点有效&#xff0c;比如Size[find(1)]就是找1的祖先节点&#xff0c;然后访问祖先节点的个数。当我们联通a点和b点时&#xff0c;如果已经是联通状态了&#xff0c;那么无…

深度解读:等保测评标准与实践指南

在信息时代&#xff0c;数据安全与隐私保护成为企业和组织不可忽视的关键议题。等保测评&#xff0c;即信息安全等级保护测评&#xff0c;作为我国信息安全管理体系的重要组成部分&#xff0c;为各行业提供了标准化的安全评估与改进路径。本文旨在深度解读等保测评标准的核心要…

探索Python日期时间的宝藏:`dateutil`库的神秘面纱

文章目录 探索Python日期时间的宝藏&#xff1a;dateutil库的神秘面纱背景&#xff1a;为何选择dateutil&#xff1f;dateutil是什么&#xff1f;如何安装dateutil&#xff1f;简单函数介绍与使用parse函数&#xff1a;智能日期时间解析relativedelta&#xff1a;计算相对日期t…