副标题:从QSensor抽象层到平台后端插件的源码级架构剖析,掌握移动端传感器开发的终极密码
一、为什么Qt Sensors值得深挖?
在桌面开发时代,传感器是个遥远的概念。但当你用Qt开发车载中控、工业平板、医疗设备时------加速度计、陀螺仪、磁力计、距离传感器、环境光传感器,这些就成了刚需。Qt Sensors模块不是简单的API封装,它是一个跨平台抽象层 + 插件式后端架构的教科书级设计,理解它的源码,能让你在任何平台上快速适配新传感器。
源码路径:qtsensors/src/sensors/
二、架构全景:三层分离设计
Qt Sensors的架构分为三层:
┌─────────────────────────────────┐
│ Application Layer │ ← QML / C++ API
├─────────────────────────────────┤
│ Qt Sensors Framework │ ← QSensor, QSensorReading
├─────────────────────────────────┤
│ Platform Backend Plugins │ ← sensorfw, android, winrt, generic
└─────────────────────────────────┘
2.1 核心类层次
QObject
└── QSensor
├── QAccelerometer
├── QGyroscope
├── QMagnetometer
├── QProximitySensor
├── QLightSensor
├── QPressureSensor
├── QRotationSensor
├── QOrientationSensor
├── QTiltSensor
├── QHumiditySensor
└── QAmbientTemperatureSensor
QSensorReading
├── QAccelerometerReading
├── QGyroscopeReading
├── QMagnetometerReading
├── QProximityReading
├── QLightReading
├── QPressureReading
├── QRotationReading
├── QOrientationReading
├── QTiltReading
├── QHumidityReading
└── QAmbientTemperatureReading
关键设计:QSensor负责生命周期和数据获取策略,QSensorReading负责数据承载,二者分离使得同一Reading可被不同策略的Sensor复用。
三、QSensor核心源码解析
3.1 QSensor的初始化链路
源码路径:qtsensors/src/sensors/qsensor.cpp
cpp
QSensor::QSensor(const QByteArray &type, QObject *parent)
: QObject(*new QSensorPrivate, parent)
{
// type是传感器的唯一标识,如"QAccelerometer"
d->type = type;
// 注册到QSensorManager,触发后端发现
QSensorManager::registerSensor(this);
}
QSensorManager是整个模块的调度中心,源码路径:qtsensors/src/sensors/qsensormanager.cpp
cpp
void QSensorManager::registerSensor(QSensor *sensor)
{
QMutexLocker locker(&mutex());
// 首次注册时加载后端插件
if (!pluginsLoaded) {
loadPlugins();
pluginsLoaded = true;
}
// 为sensor绑定可用的后端
sensor->d->backend = createBackendForSensor(sensor);
}
3.2 后端插件的发现与加载机制
Qt Sensors使用Qt的插件系统,每个平台后端是一个独立的QSensorPluginInterface实现:
cpp
// 源码路径:qtsensors/src/sensors/qsensorplugin.h
class Q_SENSOR_EXPORT QSensorPluginInterface
{
public:
virtual ~QSensorPluginInterface() {}
virtual void registerSensors() = 0;
};
后端加载链路:
QSensorManager::loadPlugins()
→ QFactoryLoader::instance(QSensorPluginInterface_iid)
→ 扫描 {QT_INSTALL_PLUGINS}/sensors/ 目录
→ 加载 .dll / .so 插件
→ 调用 registerSensors()
以Android后端为例(qtsensors/src/plugins/sensors/android/android.json):
json
{
"Keys": ["android"],
"Provider": "AndroidSensors",
"Features": ["QAccelerometer", "QGyroscope", "QMagnetometer",
"QProximitySensor", "QLightSensor", "QPressureSensor"]
}
3.3 数据读取链路:从硬件到QML
当调用QSensor::start()后,数据流动路径:
硬件传感器中断
→ 平台后端读取数据 (QSensorBackend::sensorEvent)
→ 调用 QSensorBackend::newReading()
→ QSensorPrivate::readingChanged() 信号
→ QML property binding 更新
核心源码片段(qsensorbackend.cpp):
cpp
void QSensorBackend::newReading(QSensorReading *reading)
{
// 更新时间戳
reading->d->timestamp = QDateTime::currentMSecsSinceEpoch();
// 标记reading已更新
reading->d->timestampChanged = true;
// 通知sensor有新数据
if (d->sensor)
d->sensor->d->newReadingAvailable();
}
cpp
void QSensorPrivate::newReadingAvailable()
{
Q_Q(QSensor);
// 属性变化通知
emit q->readingChanged();
// 如果设置了buffering,先缓存
if (maxSpeed > 0) {
bufferCount++;
if (bufferCount >= maxSpeed) {
emit q->bufferFull();
bufferCount = 0;
}
}
}
四、实战:自建传感器后端插件
当你的硬件平台没有现成的Qt Sensors后端时,需要自己写。以下是一个完整的Linux IIO(Industrial I/O)子系统后端实现。
4.1 插件类骨架
cpp
// iioplugin.h
class IioSensorPlugin : public QObject, public QSensorPluginInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QSensorPluginInterface" FILE "iio.json")
Q_INTERFACES(QSensorPluginInterface)
public:
void registerSensors() override;
};
// iioplugin.cpp
void IioSensorPlugin::registerSensors()
{
// 扫描 /sys/bus/iio/devices/ 发现可用传感器
QDir iioDir(QStringLiteral("/sys/bus/iio/devices"));
const auto devices = iioDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
for (const auto &dev : devices) {
QString type = detectSensorType(iioDir.absoluteFilePath(dev));
if (type == "accel")
QSensorManager::registerBackend(QAccelerometer::type,
QByteArray("iio.accel." + dev.toLatin1()),
new IioBackendFactory<IioAccelerometer>(iioDir.absoluteFilePath(dev)));
else if (type == "gyro")
QSensorManager::registerBackend(QGyroscope::type,
QByteArray("iio.gyro." + dev.toLatin1()),
new IioBackendFactory<IioGyroscope>(iioDir.absoluteFilePath(dev)));
}
}
4.2 后端实现------加速度计
cpp
// ioaccelerometer.h
class IioAccelerometer : public QSensorBackend
{
Q_OBJECT
public:
IioAccelerometer(QSensor *sensor, const QString &devicePath)
: QSensorBackend(sensor), m_devicePath(devicePath)
{
// 初始化Reading
m_reading = new QAccelerometerReading(this);
setReading(m_reading);
// 打开IIO字符设备
m_fd = open(QString(m_devicePath + "/dev").toUtf8().constData(), O_RDONLY | O_NONBLOCK);
if (m_fd < 0) {
// 回退到sysfs轮询
m_pollTimer = new QTimer(this);
connect(m_pollTimer, &QTimer::timeout, this, &IioAccelerometer::pollSysfs);
} else {
// 使用IIO事件通道(更高效)
m_notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
connect(m_notifier, &QSocketNotifier::activated,
this, &IioAccelerometer::readIioEvent);
}
}
void start() override
{
if (m_notifier)
m_notifier->setEnabled(true);
else if (m_pollTimer)
m_pollTimer->start(sensor()->dataRate() > 0 ? 1000 / sensor()->dataRate() : 100);
}
void stop() override
{
if (m_notifier)
m_notifier->setEnabled(false);
else if (m_pollTimer)
m_pollTimer->stop();
}
private slots:
void readIioEvent()
{
struct iio_event_data event;
if (read(m_fd, &event, sizeof(event)) > 0) {
// 解析IIO事件,提取x/y/z
qreal x = extractChannelValue(event, 0);
qreal y = extractChannelValue(event, 1);
qreal z = extractChannelValue(event, 2);
m_reading->setValues(x, y, z);
newReading(m_reading);
}
}
void pollSysfs()
{
qreal x = readSysfsChannel("in_accel_x_raw");
qreal y = readSysfsChannel("in_accel_y_raw");
qreal z = readSysfsChannel("in_accel_z_raw");
// 将原始值转换为m/s²
qreal scale = readSysfsChannel("in_accel_scale").toReal();
m_reading->setValues(x * scale, y * scale, z * scale);
newReading(m_reading);
}
private:
QAccelerometerReading *m_reading;
QString m_devicePath;
int m_fd = -1;
QSocketNotifier *m_notifier = nullptr;
QTimer *m_pollTimer = nullptr;
};
4.3 注册后端到QSensorManager
cpp
// 后端工厂模板
template <typename BackendClass>
class IioBackendFactory : public QSensorBackendFactory
{
public:
IioBackendFactory(const QString &devicePath) : m_devicePath(devicePath) {}
QSensorBackend *createBackend(QSensor *sensor) override
{
return new BackendClass(sensor, m_devicePath);
}
private:
QString m_devicePath;
};
五、性能优化:数据流的关键瓶颈
5.1 数据速率控制
源码路径:qsensor.cpp
cpp
void QSensor::setDataRate(int rate)
{
if (d->dataRate == rate) return;
d->dataRate = rate;
// 通知后端调整采样率
if (d->backend)
d->backend->setDataRate(rate);
emit dataRateChanged();
}
后端的setDataRate直接影响硬件采样频率。关键优化点:不要设置比实际需求更高的数据速率。一个UI动画只需要30fps的更新频率,却把加速度计开到500Hz,是典型的电量杀手。
5.2 缓冲机制------减少信号发射频率
Qt Sensors支持批量读取模式:
cpp
QAccelerometer *accel = new QAccelerometer(this);
accel->setAlwaysOn(false); // 应用后台时暂停
accel->setSkipDuplicates(true); // 过滤重复数据
accel->setMaxBufferSize(50); // 缓冲50个样本后一次性发射
accel->start();
源码中的过滤逻辑(qsensor.cpp):
cpp
bool QSensorPrivate::filter(QSensorReading *reading)
{
Q_Q(QSensor);
// skipDuplicates检查
if (q->skipDuplicates() && d->lastReading == *reading) {
return false; // 过滤掉重复reading
}
d->lastReading = *reading;
return true;
}
5.3 线程模型与零拷贝优化
Qt Sensors的默认线程模型:
Main Thread (GUI)
└── QSensor 信号/槽
↑
Sensor Thread (QSensorGlobal::instance()->thread())
└── QSensorBackend::newReading()
所有后端的newReading()在sensor线程中调用,通过信号槽自动切换到主线程。如果你需要高频数据采集(如IMU 1kHz+),不要让每个样本都经过信号槽,而是用共享内存环形缓冲区:
cpp
// 高频传感器数据采集优化方案
class SharedRingBuffer {
public:
struct Sample {
qint64 timestamp;
qreal x, y, z;
};
bool init(const QString &name, int capacity = 4096)
{
// POSIX共享内存
int fd = shm_open(name.toUtf8().constData(), O_CREAT | O_RDWR, 0666);
size_t size = sizeof(Header) + sizeof(Sample) * capacity;
ftruncate(fd, size);
m_data = static_cast<char*>(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
m_header = reinterpret_cast<Header*>(m_data);
m_samples = reinterpret_cast<Sample*>(m_data + sizeof(Header));
m_header->capacity = capacity;
m_header->writeIndex = 0;
m_header->readIndex = 0;
close(fd);
return true;
}
void write(const Sample &s)
{
// 无锁写入------单生产者场景
int idx = m_header->writeIndex.load(std::memory_order_relaxed);
m_samples[idx % m_header->capacity] = s;
m_header->writeIndex.store(idx + 1, std::memory_order_release);
}
private:
struct Header {
int capacity;
std::atomic<int> writeIndex;
std::atomic<int> readIndex;
};
char *m_data = nullptr;
Header *m_header = nullptr;
Sample *m_samples = nullptr;
};
六、QML集成与数据绑定
Qt Sensors的QML绑定是自动的,但理解其背后的属性系统很重要:
qml
// Sensors.qml
import QtSensors 5.15
Item {
Accelerometer {
id: accel
dataRate: 50
active: true
onReadingChanged: {
// 每次reading变化自动触发
// reading.x, reading.y, reading.z 是Q_PROPERTY
rotationCalculator.update(reading.x, reading.y, reading.z);
}
}
// 低通滤波器------去除高频噪声
QtObject {
id: rotationCalculator
property real alpha: 0.8
property real filteredX: 0
property real filteredY: 0
function update(x, y, z) {
filteredX = alpha * filteredX + (1 - alpha) * x;
filteredY = alpha * filteredY + (1 - alpha) * y;
}
}
}
QML属性绑定路径的源码级追踪:
QAccelerometer::reading() → QAccelerometerReading*
→ Q_PROPERTY(x READ x NOTIFY xChanged)
→ QML引擎自动绑定 onReadingChanged
七、跨平台差异------你必须知道的坑
7.1 Android平台特殊处理
源码路径:qtsensors/src/plugins/sensors/android/
Android后端通过JNI调用android.hardware.SensorManager:
cpp
// androidsensors.cpp
void AndroidSensors::registerSensors()
{
// 通过JNI获取Android SensorManager
QJniObject activity = QJniObject::callStaticObjectMethod(
"org/qtproject/qt/android/sensors/QtSensors",
"getSensorManager",
"(Landroid/content/Context;)Ljava/lang/Object;",
QJniObject::callStaticObjectMethod(
"org/qtproject/qt/android/sensors/QtSensors",
"getContext", "()Landroid/content/Context;").object());
// 遍历Android传感器列表
QJniObject sensorList = activity.callObjectMethod(
"getSensorList", "(I)Ljava/util/List;",
QJniObject::getStaticField<jint>(
"android/hardware/Sensor", "TYPE_ALL"));
}
关键坑:Android传感器坐标系与Qt不同 。Android使用手机自然方向坐标系,而Qt定义了QAccelerometer::AxesOrientationMode:
cpp
accel->setAxesOrientationMode(QAccelerometer::AutomaticOrientation);
// 自动处理屏幕旋转时的坐标变换
7.2 Windows平台(WinRT后端)
源码路径:qtsensors/src/plugins/sensors/winrt/
WinRT后端使用C++/WinRT API:
cpp
// winrtaccelerometer.cpp
void WinRtAccelerometer::start()
{
auto accelerometer = WinRT::GetActivationFactory<
ABI::Windows::Devices::Sensors::IAccelerometerStatics,
WinRT::HStringReference(RuntimeClass_Windows_Devices_Sensors_Accelerometer)>();
ComPtr<IAccelerometer> sensor;
accelerometer->GetDefault(&sensor);
// 注册ReadingChanged事件
EventRegistrationToken token;
sensor->add_ReadingChanged(
Callback<ITypedEventHandler<Accelerometer*, AccelerometerReadingChangedEventArgs*>>(
this, &WinRtAccelerometer::onReadingChanged).Get(),
&token);
}
7.3 Generic后端------仿真传感器
源码路径:qtsensors/src/plugins/sensors/generic/
当平台没有真实传感器时,Generic后端提供模拟数据,非常适合开发和测试:
cpp
// genericrotation.cpp
void GenericRotation::start()
{
// 使用QAccelerometer数据计算旋转角度
if (!m_accelerometer) {
m_accelerometer = new QAccelerometer(this);
connect(m_accelerometer, &QAccelerometer::readingChanged,
this, &GenericRotation::updateRotation);
m_accelerometer->start();
}
}
void GenericRotation::updateRotation()
{
QAccelerometerReading *reading = m_accelerometer->reading();
qreal x = reading->x();
qreal y = reading->y();
qreal z = reading->z();
// 从加速度向量计算欧拉角
qreal pitch = qAtan2(y, qSqrt(x*x + z*z)) * 180.0 / M_PI;
qreal roll = qAtan2(-x, z) * 180.0 / M_PI;
m_reading->setEuler(pitch, roll, 0);
newReading(m_reading);
}
八、传感器融合------从单一传感器到姿态估计
单个传感器的数据总是有噪声和偏差的。工程实践中,传感器融合是必经之路。
8.1 互补滤波器
cpp
class ComplementaryFilter : public QObject
{
Q_OBJECT
public:
ComplementaryFilter(QObject *parent = nullptr) : QObject(parent)
{
m_accelerometer = new QAccelerometer(this);
m_gyroscope = new QGyroscope(this);
connect(m_accelerometer, &QAccelerometer::readingChanged,
this, &ComplementaryFilter::accelerometerUpdate);
connect(m_gyroscope, &QGyroscope::readingChanged,
this, &ComplementaryFilter::gyroscopeUpdate);
m_accelerometer->setDataRate(50);
m_gyroscope->setDataRate(100);
m_accelerometer->start();
m_gyroscope->start();
}
private slots:
void accelerometerUpdate()
{
QAccelerometerReading *r = m_accelerometer->reading();
// 加速度计提供稳定的长期方向,但受运动加速度干扰
qreal accelPitch = qAtan2(r->y(), qSqrt(r->x()*r->x() + r->z()*r->z()));
qreal accelRoll = qAtan2(-r->x(), r->z());
// 互补滤波:短期信任陀螺仪,长期信任加速度计
const qreal alpha = 0.98;
m_pitch = alpha * m_pitch + (1 - alpha) * accelPitch;
m_roll = alpha * m_roll + (1 - alpha) * accelRoll;
emit orientationChanged(m_pitch, m_roll, m_yaw);
}
void gyroscopeUpdate()
{
QGyroscopeReading *r = m_gyroscope->reading();
// 陀螺仪积分得到短期的角度变化
qreal dt = (r->timestamp() - m_lastGyroTimestamp) / 1000.0;
m_lastGyroTimestamp = r->timestamp();
m_pitch += r->x() * dt;
m_roll += r->y() * dt;
m_yaw += r->z() * dt;
}
signals:
void orientationChanged(qreal pitch, qreal roll, qreal yaw);
private:
QAccelerometer *m_accelerometer;
QGyroscope *m_gyroscope;
qreal m_pitch = 0, m_roll = 0, m_yaw = 0;
qint64 m_lastGyroTimestamp = 0;
};
8.2 卡尔曼滤波器进阶
互补滤波虽然简单,但在快速运动时精度不够。完整的卡尔曼滤波实现:
cpp
// 6轴卡尔曼滤波器(3轴加速度 + 3轴陀螺仪)
class KalmanFilter6DoF {
public:
void init()
{
// 状态向量: [pitch, roll, yaw, pitch_rate, roll_rate, yaw_rate]
x.setZero(6);
// 状态转移矩阵
F = Eigen::MatrixXf::Identity(6, 6);
// 观测矩阵(加速度计观测角度,陀螺仪观测角速度)
H = Eigen::MatrixXf::Zero(6, 6);
H(0, 0) = 1; H(1, 1) = 1; H(2, 2) = 1; // 角度观测
H(3, 3) = 1; H(4, 4) = 1; H(5, 5) = 1; // 角速度观测
// 协方差矩阵
P = Eigen::MatrixXf::Identity(6, 6) * 0.1f;
// 过程噪声
Q = Eigen::MatrixXf::Identity(6, 6) * 0.01f;
// 观测噪声(加速度计噪声大于陀螺仪)
R = Eigen::MatrixXf::Identity(6, 6);
R(0, 0) = 0.5f; R(1, 1) = 0.5f; R(2, 2) = 0.5f; // accel噪声大
R(3, 3) = 0.01f; R(4, 4) = 0.01f; R(5, 5) = 0.01f; // gyro噪声小
}
void predict(float dt, const Eigen::Vector3f &gyro)
{
// 更新状态转移矩阵
F(0, 3) = dt; F(1, 4) = dt; F(2, 5) = dt;
// 预测
x = F * x;
x(3) = gyro(0); x(4) = gyro(1); x(5) = gyro(2);
// 预测协方差
P = F * P * F.transpose() + Q;
}
void update(const Eigen::Vector3f &accelAngles, const Eigen::Vector3f &gyroRates)
{
Eigen::VectorXf z(6);
z << accelAngles(0), accelAngles(1), accelAngles(2),
gyroRates(0), gyroRates(1), gyroRates(2);
Eigen::VectorXf y = z - H * x;
Eigen::MatrixXf S = H * P * H.transpose() + R;
Eigen::MatrixXf K = P * H.transpose() * S.inverse();
x = x + K * y;
P = (Eigen::MatrixXf::Identity(6, 6) - K * H) * P;
}
Eigen::Vector3f getEulerAngles() const {
return Eigen::Vector3f(x(0), x(1), x(2));
}
private:
Eigen::VectorXf x;
Eigen::MatrixXf F, H, P, Q, R;
};
九、实战案例:工业设备振动监测
cpp
class VibrationMonitor : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal rmsLevel READ rmsLevel NOTIFY rmsLevelChanged)
Q_PROPERTY(qreal peakFrequency READ peakFrequency NOTIFY peakFrequencyChanged)
Q_PROPERTY(AlertLevel alertLevel READ alertLevel NOTIFY alertLevelChanged)
public:
enum AlertLevel { Normal, Warning, Critical };
Q_ENUM(AlertLevel)
VibrationMonitor(QObject *parent = nullptr) : QObject(parent)
{
m_accelerometer = new QAccelerometer(this);
m_accelerometer->setDataRate(200); // 200Hz采样
m_accelerometer->setSkipDuplicates(false);
connect(m_accelerometer, &QAccelerometer::readingChanged,
this, &VibrationMonitor::processSample);
m_fftTimer = new QTimer(this);
m_fftTimer->setInterval(500); // 每500ms做一次FFT
connect(m_fftTimer, &QTimer::timeout, this, &VibrationMonitor::computeFFT);
m_accelerometer->start();
m_fftTimer->start();
}
private slots:
void processSample()
{
QAccelerometerReading *r = m_accelerometer->reading();
m_samples.append(QVector3D(r->x(), r->y(), r->z()));
// 保持滑动窗口为1024个样本
if (m_samples.size() > 1024)
m_samples.removeFirst();
// 在线计算RMS
qreal sumSq = 0;
for (const auto &s : m_samples) {
sumSq += s.lengthSquared();
}
m_rmsLevel = qSqrt(sumSq / m_samples.size());
emit rmsLevelChanged();
// 告警判定
if (m_rmsLevel > 5.0) {
m_alertLevel = Critical;
} else if (m_rmsLevel > 2.0) {
m_alertLevel = Warning;
} else {
m_alertLevel = Normal;
}
emit alertLevelChanged();
}
void computeFFT()
{
if (m_samples.size() < 256) return;
// 对z轴做FFT(假设z为振动主方向)
std::vector<double> signal(m_samples.size());
for (int i = 0; i < m_samples.size(); ++i)
signal[i] = m_samples[i].z();
// 使用KissFFT或FFTW
int n = signal.size();
kiss_fft_cfg cfg = kiss_fft_alloc(n, 0, nullptr, nullptr);
std::vector<kiss_fft_cpx> in(n), out(n);
for (int i = 0; i < n; ++i) {
in[i].r = signal[i];
in[i].i = 0;
}
kiss_fft(cfg, in.data(), out.data());
// 找到峰值频率
int peakBin = 1;
double peakMag = 0;
for (int i = 1; i < n / 2; ++i) {
double mag = qSqrt(out[i].r * out[i].r + out[i].i * out[i].i);
if (mag > peakMag) {
peakMag = mag;
peakBin = i;
}
}
m_peakFrequency = qreal(peakBin) * 200.0 / n; // 采样率200Hz
emit peakFrequencyChanged();
kiss_fft_free(cfg);
}
signals:
void rmsLevelChanged();
void peakFrequencyChanged();
void alertLevelChanged();
private:
QAccelerometer *m_accelerometer;
QTimer *m_fftTimer;
QList<QVector3D> m_samples;
qreal m_rmsLevel = 0;
qreal m_peakFrequency = 0;
AlertLevel m_alertLevel = Normal;
};
十、总结
Qt Sensors模块的架构精髓在于三层分离:应用层只关心QSensor和QSensorReading的API,框架层负责后端调度和信号传递,平台层通过插件机制适配不同硬件。这种设计让你可以:
- 在同一套API下切换不同平台的传感器后端------Android、iOS、WinRT、Linux IIO,应用层代码零修改
- 用Generic后端在无传感器设备上开发和测试
- 自建后端插件适配私有硬件------工业传感器、车载IMU等
- 通过数据速率、缓冲、去重等机制精细控制性能
性能优化的核心原则:降低采样率到需求最低值、启用skipDuplicates减少无效信号、高频场景用共享内存替代信号槽。传感器融合则从互补滤波起步,追求精度时升级到卡尔曼滤波。
当你下次面对一个需要"感知物理世界"的Qt项目时,不要只是调API------理解QSensorManager的插件加载链路、QSensorBackend的newReading时序、以及平台后端的差异,才能写出既高性能又跨平台的传感器应用。
《注:若有发现问题欢迎大家提出来纠正》