当前位置: 首页 > news >正文

PX4从放弃到精通(二十九):传感器冗余机制

文章目录

  • 前言
  • 一、parametersUpdate
  • 二、imuPoll
  • 三、 put
  • 四、 confidence
  • 五、 get_best

前言

PX4 1.13.2
一个人可以走的更快,一群人才能走的更远,可加文章底部微信名片

代码的位置如下
PX4冗余机制主要通过传感读数错误计数和传感器的优先级进行选优
在这里插入图片描述

一、parametersUpdate

这个函数主要是初始化每个imu传感器的优先级,PX4传感器在经过校准后,会给每个同类的传感器生成一个优先级,这个优先级在冗余机制中有着重要的作用

void VotedSensorsUpdate::parametersUpdate()
{_parameter_update = true;updateParams();// run through all IMUsfor (uint8_t uorb_index = 0; uorb_index < MAX_SENSOR_COUNT; uorb_index++) {uORB::SubscriptionData<vehicle_imu_s> imu{ORB_ID(vehicle_imu), uorb_index};imu.update();if (imu.advertised() && (imu.get().timestamp != 0)&& (imu.get().accel_device_id != 0) && (imu.get().gyro_device_id != 0)) {// find corresponding configured accel priorityint8_t accel_cal_index = calibration::FindCurrentCalibrationIndex("ACC", imu.get().accel_device_id);if (accel_cal_index >= 0) {// found matching CAL_ACCx_PRIOint32_t accel_priority_old = _accel.priority_configured[uorb_index];_accel.priority_configured[uorb_index] = calibration::GetCalibrationParamInt32("ACC", "PRIO", accel_cal_index);if (accel_priority_old != _accel.priority_configured[uorb_index]) {if (_accel.priority_configured[uorb_index] == 0) {// disabled_accel.priority[uorb_index] = 0;} else {// change relative priority to incorporate any sensor faultsint priority_change = _accel.priority_configured[uorb_index] - accel_priority_old;_accel.priority[uorb_index] = math::constrain(_accel.priority[uorb_index] + priority_change, static_cast<int32_t>(1),static_cast<int32_t>(100));}}}// find corresponding configured gyro priorityint8_t gyro_cal_index = calibration::FindCurrentCalibrationIndex("GYRO", imu.get().gyro_device_id);if (gyro_cal_index >= 0) {// found matching CAL_GYROx_PRIOint32_t gyro_priority_old = _gyro.priority_configured[uorb_index];_gyro.priority_configured[uorb_index] = calibration::GetCalibrationParamInt32("GYRO", "PRIO", gyro_cal_index);if (gyro_priority_old != _gyro.priority_configured[uorb_index]) {if (_gyro.priority_configured[uorb_index] == 0) {// disabled_gyro.priority[uorb_index] = 0;} else {// change relative priority to incorporate any sensor faultsint priority_change = _gyro.priority_configured[uorb_index] - gyro_priority_old;_gyro.priority[uorb_index] = math::constrain(_gyro.priority[uorb_index] + priority_change, static_cast<int32_t>(1),static_cast<int32_t>(100));}}}}}
}

二、imuPoll

这个函数里首先是对传感器的数据进行循环订阅,然后赋值到_last_sensor_data中,通过put方法将数据放入链表中进行处理。PX4通过单向链表DataValidator对传感器的数据进行存储和处理,put函数调用了DataValidator的put函数,在里面计算了数据的均方根误差还有错误密度,然后通过错误密度计算出每个传感器的confidence,根据confidence和优先级,通过get_best得出目前的最优传感器,然后把最优传感器的数据通过形参raw返回,这个raw最后会通过sensor_combine话题发布。
在这里插入图片描述

void VotedSensorsUpdate::imuPoll(struct sensor_combined_s &raw)
{const hrt_abstime time_now_us = hrt_absolute_time();for (int uorb_index = 0; uorb_index < MAX_SENSOR_COUNT; uorb_index++) {vehicle_imu_s imu_report;if ((_accel.priority[uorb_index] > 0) && (_gyro.priority[uorb_index] > 0)&& _vehicle_imu_sub[uorb_index].update(&imu_report)) {// copy corresponding vehicle_imu_status for accel & gyro error countsvehicle_imu_status_s imu_status{};_vehicle_imu_status_subs[uorb_index].copy(&imu_status);_accel_device_id[uorb_index] = imu_report.accel_device_id;_gyro_device_id[uorb_index] = imu_report.gyro_device_id;// convert the delta velocities to an equivalent accelerationconst float accel_dt_inv = 1.e6f / (float)imu_report.delta_velocity_dt;Vector3f accel_data = Vector3f{imu_report.delta_velocity} * accel_dt_inv;// convert the delta angles to an equivalent angular rateconst float gyro_dt_inv = 1.e6f / (float)imu_report.delta_angle_dt;Vector3f gyro_rate = Vector3f{imu_report.delta_angle} * gyro_dt_inv;_last_sensor_data[uorb_index].timestamp = imu_report.timestamp_sample;_last_sensor_data[uorb_index].accelerometer_m_s2[0] = accel_data(0);_last_sensor_data[uorb_index].accelerometer_m_s2[1] = accel_data(1);_last_sensor_data[uorb_index].accelerometer_m_s2[2] = accel_data(2);_last_sensor_data[uorb_index].accelerometer_integral_dt = imu_report.delta_velocity_dt;_last_sensor_data[uorb_index].accelerometer_clipping = imu_report.delta_velocity_clipping;_last_sensor_data[uorb_index].gyro_rad[0] = gyro_rate(0);_last_sensor_data[uorb_index].gyro_rad[1] = gyro_rate(1);_last_sensor_data[uorb_index].gyro_rad[2] = gyro_rate(2);_last_sensor_data[uorb_index].gyro_integral_dt = imu_report.delta_angle_dt;_last_sensor_data[uorb_index].accel_calibration_count = imu_report.accel_calibration_count;_last_sensor_data[uorb_index].gyro_calibration_count = imu_report.gyro_calibration_count;_last_accel_timestamp[uorb_index] = imu_report.timestamp_sample;_accel.voter.put(uorb_index, imu_report.timestamp, _last_sensor_data[uorb_index].accelerometer_m_s2,imu_status.accel_error_count, _accel.priority[uorb_index]);_gyro.voter.put(uorb_index, imu_report.timestamp, _last_sensor_data[uorb_index].gyro_rad,imu_status.gyro_error_count, _gyro.priority[uorb_index]);}}// find the best sensorint accel_best_index = _accel.last_best_vote;int gyro_best_index = _gyro.last_best_vote;if (!_parameter_update) {// update current accel/gyro selection, skipped on cycles where parameters update_accel.voter.get_best(time_now_us, &accel_best_index);_gyro.voter.get_best(time_now_us, &gyro_best_index);if (!_param_sens_imu_mode.get() && ((_selection.timestamp != 0) || (_sensor_selection_sub.updated()))) {// use sensor_selection to find bestif (_sensor_selection_sub.update(&_selection)) {// reset inconsistency checks against primaryfor (int sensor_index = 0; sensor_index < MAX_SENSOR_COUNT; sensor_index++) {_accel_diff[sensor_index].zero();_gyro_diff[sensor_index].zero();}}for (int i = 0; i < MAX_SENSOR_COUNT; i++) {if ((_accel_device_id[i] != 0) && (_accel_device_id[i] == _selection.accel_device_id)) {accel_best_index = i;}if ((_gyro_device_id[i] != 0) && (_gyro_device_id[i] == _selection.gyro_device_id)) {gyro_best_index = i;}}} else {// use sensor voter to find best if SENS_IMU_MODE is enabled or ORB_ID(sensor_selection) has never publishedcheckFailover(_accel, "Accel", events::px4::enums::sensor_type_t::accel);checkFailover(_gyro, "Gyro", events::px4::enums::sensor_type_t::gyro);}}// write data for the best sensor to output variablesif ((accel_best_index >= 0) && (accel_best_index < MAX_SENSOR_COUNT) && (_accel_device_id[accel_best_index] != 0)&& (gyro_best_index >= 0) && (gyro_best_index < MAX_SENSOR_COUNT) && (_gyro_device_id[gyro_best_index] != 0)) {raw.timestamp = _last_sensor_data[gyro_best_index].timestamp;memcpy(&raw.accelerometer_m_s2, &_last_sensor_data[accel_best_index].accelerometer_m_s2,sizeof(raw.accelerometer_m_s2));memcpy(&raw.gyro_rad, &_last_sensor_data[gyro_best_index].gyro_rad, sizeof(raw.gyro_rad));raw.accelerometer_integral_dt = _last_sensor_data[accel_best_index].accelerometer_integral_dt;raw.gyro_integral_dt = _last_sensor_data[gyro_best_index].gyro_integral_dt;raw.accelerometer_clipping = _last_sensor_data[accel_best_index].accelerometer_clipping;raw.accel_calibration_count = _last_sensor_data[accel_best_index].accel_calibration_count;raw.gyro_calibration_count = _last_sensor_data[gyro_best_index].gyro_calibration_count;if ((accel_best_index != _accel.last_best_vote) || (_selection.accel_device_id != _accel_device_id[accel_best_index])) {_accel.last_best_vote = (uint8_t)accel_best_index;_selection.accel_device_id = _accel_device_id[accel_best_index];_selection_changed = true;}if ((_gyro.last_best_vote != gyro_best_index) || (_selection.gyro_device_id != _gyro_device_id[gyro_best_index])) {_gyro.last_best_vote = (uint8_t)gyro_best_index;_selection.gyro_device_id = _gyro_device_id[gyro_best_index];_selection_changed = true;// clear all registered callbacksfor (auto &sub : _vehicle_imu_sub) {sub.unregisterCallback();}for (int i = 0; i < MAX_SENSOR_COUNT; i++) {vehicle_imu_s report{};if (_vehicle_imu_sub[i].copy(&report)) {if ((report.gyro_device_id != 0) && (report.gyro_device_id == _gyro_device_id[gyro_best_index])) {_vehicle_imu_sub[i].registerCallback();}}}}}// publish sensor selection if changedif (_param_sens_imu_mode.get() || (_selection.timestamp == 0)) {if (_selection_changed) {// don't publish until selected IDs are validif (_selection.accel_device_id > 0 && _selection.gyro_device_id > 0) {_selection.timestamp = hrt_absolute_time();_sensor_selection_pub.publish(_selection);_selection_changed = false;}for (int sensor_index = 0; sensor_index < MAX_SENSOR_COUNT; sensor_index++) {_accel_diff[sensor_index].zero();_gyro_diff[sensor_index].zero();}}}
}

三、 put

这个函数计算了错误密度_error_density,这个将在计算confidence时用到,这个_error_density取决于_error_count,而_error_count实在传感器驱动部分赋值的,也就是说这里的错误计数是根据数据的读取错误来确定的,而数据本身的对错是不关注的,个人觉得这个地方还需要改进,例如气压计被堵住导致数据不准,应该加一些这方面的判断。
在这里插入图片描述
除了_error_density的计算,还计算了均方根误差_rms

void DataValidator::put(uint64_t timestamp, const float val[dimensions], uint32_t error_count_in, uint8_t priority_in)
{_event_count++;if (error_count_in > _error_count) {_error_density += (error_count_in - _error_count);} else if (_error_density > 0) {_error_density--;}_error_count = error_count_in;_priority = priority_in;for (unsigned i = 0; i < dimensions; i++) {if (PX4_ISFINITE(val[i])) {if (_time_last == 0) {_mean[i] = 0;_lp[i] = val[i];_M2[i] = 0;} else {float lp_val = val[i] - _lp[i];float delta_val = lp_val - _mean[i];_mean[i] += delta_val / _event_count;_M2[i] += delta_val * (lp_val - _mean[i]);_rms[i] = sqrtf(_M2[i] / (_event_count - 1));if (fabsf(_value[i] - val[i]) < 0.000001f) {_value_equal_count++;} else {_value_equal_count = 0;}}// XXX replace with better filter, make it auto-tune to update rate_lp[i] = _lp[i] * 0.99f + 0.01f * val[i];_value[i] = val[i];}}_time_last = timestamp;
}

四、 confidence

前面是一些错误判断以及错误密度抗饱和,没问题的话就根据错误密度_error_density计算confidence。
公式如下。
ret = 1.0f - (_error_density / ERROR_DENSITY_WINDOW);、
_error_density是在上面put函数里根据传感器的_error_count计算的,ERROR_DENSITY_WINDOW是常数100.

float DataValidator::confidence(uint64_t timestamp)
{float ret = 1.0f;/* check if we have any data */if (_time_last == 0) {_error_mask |= ERROR_FLAG_NO_DATA;ret = 0.0f;} else if (timestamp > _time_last + _timeout_interval) {/* timed out - that's it */_error_mask |= ERROR_FLAG_TIMEOUT;ret = 0.0f;} else if (_value_equal_count > _value_equal_count_threshold) {/* we got the exact same sensor value N times in a row */_error_mask |= ERROR_FLAG_STALE_DATA;ret = 0.0f;} else if (_error_count > NORETURN_ERRCOUNT) {/* check error count limit */_error_mask |= ERROR_FLAG_HIGH_ERRCOUNT;ret = 0.0f;} else if (_error_density > ERROR_DENSITY_WINDOW) {/* cap error density counter at window size */_error_mask |= ERROR_FLAG_HIGH_ERRDENSITY;_error_density = ERROR_DENSITY_WINDOW;}/* no critical errors */if (ret > 0.0f) {/* return local error density for last N measurements */ret = 1.0f - (_error_density / ERROR_DENSITY_WINDOW);if (ret > 0.0f) {_error_mask = ERROR_FLAG_NO_ERROR;}}return ret;
}

五、 get_best

这个函数就是根据confidence和传感器优先级来确定最优的传感器,判断如下,max_confidence是目前最优传感器的confidence,max_priority是目前最优的传感器的优先级,confidence是当前的传感器的confidence,根据这两个confidence 还有优先级确定当前传感器是否要取代最优传感器。
可以看到,((max_confidence < MIN_REGULAR_CONFIDENCE) && (confidence >=
MIN_REGULAR_CONFIDENCE)) ,这个判断一般是在目前最优传感器失效的情况下才会触发,所以这个判断是没有考虑优先级的,这很好理解,级别你优先级再高,如果你失效了,我只能往低优先级的传感器切换。实际上这个条件一般不会触发,一个稳定的硬件很少会出现传感器损坏的情况。
大多数时候会在后面两个判断里面进行判断,只有在优先级比目前最优传感器高或者相等的情况下,才有可能取代目前的最优传感器,否则即使confidence高也没用,因此,我们可以手动的给一些质量好的传感器设置高的优先级。否则的话,飞控是有可能一直在使用低质量的传感器的(即便精度较差)

if ((((max_confidence < MIN_REGULAR_CONFIDENCE) && (confidence >=
MIN_REGULAR_CONFIDENCE)) ||
(confidence > max_confidence && (next->priority() >= max_priority)) ||
(fabsf(confidence - max_confidence) < 0.01f && (next->priority() > max_priority))) &&
(confidence > 0.0f)) {

float *DataValidatorGroup::get_best(uint64_t timestamp, int *index)
{DataValidator *next = _first;// XXX This should eventually also include votingint pre_check_best = _curr_best;float pre_check_confidence = 1.0f;int pre_check_prio = -1;float max_confidence = -1.0f;int max_priority = -1000;int max_index = -1;DataValidator *best = nullptr;int i = 0;while (next != nullptr) {float confidence = next->confidence(timestamp);if (i == pre_check_best) {pre_check_prio = next->priority();pre_check_confidence = confidence;}/** Switch if:* 1) the confidence is higher and priority is equal or higher* 2) the confidence is less than 1% different and the priority is higher*/if ((((max_confidence < MIN_REGULAR_CONFIDENCE) && (confidence >= MIN_REGULAR_CONFIDENCE)) ||(confidence > max_confidence && (next->priority() >= max_priority)) ||(fabsf(confidence - max_confidence) < 0.01f && (next->priority() > max_priority))) &&(confidence > 0.0f)) {max_index = i;max_confidence = confidence;max_priority = next->priority();best = next;}next = next->sibling();i++;}/* the current best sensor is not matching the previous best sensor,* or the only sensor went bad */if (max_index != _curr_best || ((max_confidence < FLT_EPSILON) && (_curr_best >= 0))) {bool true_failsafe = true;/* check whether the switch was a failsafe or preferring a higher priority sensor */if (pre_check_prio != -1 && pre_check_prio < max_priority &&fabsf(pre_check_confidence - max_confidence) < 0.1f) {/* this is not a failover */true_failsafe = false;/* reset error flags, this is likely a hotplug sensor coming online late */if (best != nullptr) {best->reset_state();}}/* if we're no initialized, initialize the bookkeeping but do not count a failsafe */if (_curr_best < 0) {_prev_best = max_index;} else {/* we were initialized before, this is a real failsafe */_prev_best = pre_check_best;if (true_failsafe) {_toggle_count++;/* if this is the first time, log when we failed */if (_first_failover_time == 0) {_first_failover_time = timestamp;}}}/* for all cases we want to keep a record of the best index */_curr_best = max_index;}*index = max_index;return (best) ? best->value() : nullptr;
}

相关文章:

PX4从放弃到精通(二十九):传感器冗余机制

文章目录 前言一、parametersUpdate二、imuPoll三、 put四、 confidence五、 get_best 前言 PX4 1.13.2 一个人可以走的更快&#xff0c;一群人才能走的更远&#xff0c;可加文章底部微信名片 代码的位置如下 PX4冗余机制主要通过传感读数错误计数和传感器的优先级进行选优 …...

vue 设置数组

手写获取数据 <el-form-item label"缴纳方"><el-select v-model"form.invoiceCategoryName" placeholder"请选择缴纳方"><el-optionv-for"item in kplmList":key"item.value":label"item.label":v…...

9.NIO非阻塞式网络通信入门

highlight: arduino-light Selector 示意图和特点说明 一个 I/O 线程可以并发处理 N 个客户端连接和读写操作&#xff0c;这从根本上解决了传统同步阻塞 I/O 一连接一线程模型。架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。 服务端流程 1、当客户端连接服务端时&…...

QT基于TCP协议实现数据传输以及波形绘制

这个玩意我做了两个&#xff0c;一个是安卓app&#xff0c;一个是Windows程序。代码并非全部都是由我从无到有实现&#xff0c;只是实现了我想要的功能。多亏了巨人的肩膀&#xff0c;开源万岁&#xff01;&#xff01;&#xff01; 我把程序放到GitHub上&#xff0c;需要的可…...

苹果safari浏览器播放不了video标签视频

今天遇到了个神奇的问题&#xff0c;视频文件在pc端和安卓手机上播放都没问题&#xff0c;但是在ios上就是播放不了&#xff0c;大概代码如下&#xff1a; 前端代码&#xff1a; <video id"video" width"350" height"500" controls><s…...

【粒子群算法和蝴蝶算法组合】粒子群混沌混合蝴蝶优化算法研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

Java设计模式之单例模式详解(懒汉式和饿汉式)

在开发工作中&#xff0c;有些类只需要存在一个实例&#xff0c;这时就可以使用单例模式。Java中的单例模式是一种常见的设计模式&#xff0c;它确保一个类只有一个实例&#xff0c;并提供全局访问点。下面来介绍一下两种常见的单例模式&#xff1a;懒汉式和饿汉式。 一、懒汉式…...

软件测试基本知识

安全测试 安全防护策略&#xff1f;&#xff08;漏洞扫描、入侵检查、安全日志、隔离防护&#xff09; 安全日志&#xff1a;用于记录非法用户的登录名称、操作时间及内容等信息&#xff0c;以便发现问题并提出解决措施&#xff1b;安全日志仅记录相关信息&#xff0c;不对非…...

Vue项目中强制刷新页面的方法

我们在动态切换组件的过程中&#xff0c;导航栏和底栏不动&#xff0c;动态切换中间区域的情况&#xff0c;在首页可以进行跳转任意组件&#xff0c;在组件与组件之间不能相互跳转&#xff0c;路由发生了变化&#xff0c;但是页面未改变&#xff0c;这时我们就需要强制刷新页面…...

文件按关键字分组-切割-染色-写入excel

1. 背景 针对下面的文件data.csv&#xff0c;首先根据fid进行排序&#xff0c;然后分组&#xff0c;使相同fid的记录放到同一个excel文件中&#xff0c;并对每列重复的数据元素染上红色。 fid,user_id -1000078398032092029,230410010036537520 -1000078398032092029,23042301…...

爬虫的基本原理:爬虫概述及爬取过程

前言 随着互联网的不断发展和普及&#xff0c;我们的生活越来越离不开网络。而网络世界中有着海量的信息和数据&#xff0c;这些信息和数据对于我们的工作和生活都有很大的帮助。但是&#xff0c;如何高效地获取这些数据呢&#xff1f;这时候&#xff0c;爬虫这个工具就派上用…...

cocos2D插件转3D插件

cocos2D插件转3D插件 use strict;/*** 3d插件api映射,兼容2d插件* */let fs require("fs");let path require("path");let baseDir ;const prsPath (Editor.Project && Editor.Project.path ? Editor.Project.path : Editor.remote.projectP…...

[Angular] 主从表结构,从表记录在主表固定栏位上呈现

Background 主从表结构&#xff0c;有时为了方便数据呈现&#xff0c;在UI上不显示从表资料&#xff0c;那么需要动态把从表的资料加载到主表的固定栏位上。 例如&#xff1a;主表是人员信息&#xff0c;从表是银行卡信息&#xff0c;一个人在同一家银行可能有多张银行卡&…...

Kotlin Multiplatform 创建多平台分发库

目标&#xff1a;通过本教程学习如何使用 Kotlin Multiplatform Library 创建多平台分发库(iOS&#xff0c;安卓)。 创建一个项目 1、本教程使用的是Android Studio创建 2、选择 新建工程&#xff0c;选择 Kotlin Multiplatform Library 3、点击next 输入需要创建的项目名称以…...

[SQL挖掘机] - union/union all 使用注意事项

因为当使用union和union all操作符时&#xff0c;有一些注意事项需要考虑&#xff1a; 1. 列数和数据类型匹配&#xff1a; 要使用union或union all合并结果集&#xff0c;两个或多个查询的 select 语句必须返回相同数量和类型的列。确保每个查询返回相同的列数&#xff0c;并…...

php 单例模式

1&#xff0c;单例模式&#xff0c;属于创建设计模式&#xff0c;简单来说就是一个类只能有一个实例化对象&#xff0c;并提供一个当前类的全局唯一可访问入口&#xff1b; 2&#xff0c;例子 <?phpclass Singleton {private static $instance null;// 禁止被实例化priva…...

【数据结构】实验二:顺序表

实验二 顺序表 一、实验目的与要求 1&#xff09;熟悉顺序表的类型定义&#xff1b; 2&#xff09;熟悉顺序表的基本操作&#xff1b; 3&#xff09;灵活应用顺序表解决具体应用问题。 二、实验内容 1&#xff09;在一个整数序列a1,a2,…,an中&#xff0c;若存在一个数&…...

【高级数据结构】线段树

目录 最大数&#xff08;单点修改&#xff0c;区间查询&#xff09; 线段树1&#xff08;区间修改&#xff0c;区间查询&#xff09; 最大数&#xff08;单点修改&#xff0c;区间查询&#xff09; 洛谷&#xff1a;最大数https://www.luogu.com.cn/problem/P1198 题目描述 …...

qt简易闹钟

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);ui->stopBtn->setDisabled(true);this->setFixedSize(this->size()); //设置固定大小this->s…...

python和c加加有什么区别,c和c++和python先学哪个

本篇文章给大家谈谈c加加编程和python编程有什么区别&#xff0c;以及python和c加加有什么区别&#xff0c;希望对各位有所帮助&#xff0c;不要忘了收藏本站喔。 1、python和c学哪个好 学C好。 C通常比Python更快&#xff0c;因为C是一种编译型语言&#xff0c;而Python则是…...

别再混淆了!一文搞懂SM2双证书(签名/加密)与P10请求的完整关系链

解密SM2双证书体系&#xff1a;从密钥生成到HTTPS安全通信的全链路解析 当你第一次在国密算法体系中遇到"双证书"这个概念时&#xff0c;脑海中可能会浮现出这样的疑问&#xff1a;为什么一个身份认证需要两套证书&#xff1f;签名证书和加密证书究竟有何不同&#x…...

智能体开发路线:从 Demo 到生产环境完整路径

文章目录前言一、起点&#xff1a;清醒认知——Demo与生产的天壤之别1.1 三大核心差异&#xff1a;从理想照进现实&#xff08;1&#xff09;环境与数据&#xff1a;从"无菌室"到"野生丛林"&#xff08;2&#xff09;性能与稳定性&#xff1a;从"跑一…...

AVR智能充电器PID控制程序(基于ATmega16/ATmega328)

一、系统硬件架构 1. 硬件连接方案 AVR ATmega16 ├── ADC0 (PC0) → 电池电压采样&#xff08;分压电阻&#xff09; ├── ADC1 (PC1) → 充电电流采样&#xff08;分流电阻运放&#xff09; ├── OC1A (PB1) → PWM输出 → MOSFET驱动 → 充电控制 ├── INT0 (PD2) →…...

Mac上IDEA的PlantUML插件报错‘找不到Graphviz’?手把手教你用Homebrew搞定(附阿里云镜像避坑)

Mac上IDEA的PlantUML插件报错‘找不到Graphviz’&#xff1f;手把手教你用Homebrew搞定&#xff08;附阿里云镜像避坑&#xff09; 最近在Mac上使用IntelliJ IDEA的PlantUML插件时&#xff0c;不少开发者遇到了一个经典问题&#xff1a;插件报错提示"找不到Graphviz"…...

SQL注入攻击与防御实战:手把手教你挖漏洞

三、防御方案。1.参数化查询&#xff1a;用Prepared Statements&#xff0c;用户输入当数据处理。PHP用PDO&#xff0c;Java用PreparedStatement。2.输入验证&#xff1a;白名单过滤危险字符单引号、分号等。3.使用ORM框架&#xff1a;Laravel、Hibernate等内置防注入。4.最小权…...

工业现场设备的监控系统(有完整资料)

资料查找方式&#xff1a;特纳斯电子&#xff08;电子校园网&#xff09;&#xff1a;搜索下面编号即可编号&#xff1a;T1532310M设计简介&#xff1a;本设计是工业现场设备的监控系统&#xff0c;主要实现以下功能&#xff1a;通过温湿度传感器检测温湿度&#xff0c;湿度过高…...

UE5 Lumen性能调优实战:从30帧到60帧,我的项目优化踩坑全记录

UE5 Lumen性能调优实战&#xff1a;从30帧到60帧的完整优化指南 当你的UE5项目终于实现了梦寐以求的Lumen全局光照效果&#xff0c;却发现帧率卡在30帧无法突破时&#xff0c;那种挫败感每个开发者都深有体会。去年我们的开放世界项目就遭遇了这个典型困境——在PS5上开启Lumen…...

Windows右键菜单终极清理指南:ContextMenuManager高效管理完整教程

Windows右键菜单终极清理指南&#xff1a;ContextMenuManager高效管理完整教程 【免费下载链接】ContextMenuManager &#x1f5b1;️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager 你是否厌倦了每次右键点击文件时&…...

通过C#编程开发西门子PLC系统的诊断与故障排查工具

在工业自动化领域&#xff0c;PLC&#xff08;可编程逻辑控制器&#xff09;是自动化控制系统的核心&#xff0c;广泛应用于各类生产线、设备及工厂的管理控制中。西门子作为全球领先的自动化控制系统提供商&#xff0c;其PLC产品&#xff08;如S7-1200、S7-1500系列&#xff0…...

Qt Creator项目里集成工业相机SDK,手把手教你配置.pro文件(附避坑点)

Qt Creator工业相机SDK集成实战&#xff1a;从配置到团队协作的最佳实践 工业视觉系统的开发往往需要将硬件厂商提供的相机SDK与Qt框架深度整合。不同于普通的第三方库集成&#xff0c;工业相机SDK通常涉及复杂的设备通信、图像采集和内存管理机制。本文将分享在Qt Creator中高…...