PX4模块设计之三十一:ManualControl模块

1. ManualControl模块简介

从帮助的角度,该模块主要处理manual_control_inputs消息,发布one manual_control_setpoint消息。

### Description
Module consuming manual_control_inputs publishing one manual_control_setpoint.

manual_control <command> [arguments...]
 Commands:
   start

   stop

   status        print status info

注:print_usage函数是具体对应实现。

class ManualControl : public ModuleBase<ManualControl>, public ModuleParams, public px4::ScheduledWorkItem

注:ManualControl模块采用了ModuleBaseWorkQueue设计。

2. 模块入口函数

2.1 主入口manual_control_main

同样继承了ModuleBase,由ModuleBase的main来完成模块入口。

manual_control_main
 └──> return ManualControl::main(argc, argv)

2.2 自定义子命令custom_command

模块仅支持start/stop/status命令,不支持其他自定义命令。

ManualControl::custom_command
 └──> return print_usage("unknown command");

3. ManualControl模块重要函数

3.1 task_spawn

这里主要初始化了ManualControl对象,后续通过WorkQueue来完成进行轮询。

ManualControl::task_spawn
 ├──> ManualControl *instance = new ManualControl()
 ├──> <instance>
 │   ├──> _object.store(instance);
 │   ├──> _task_id = task_id_is_work_queue;
 │   ├──> <instance->init()>
 │   │   └──> return PX4_OK
 │   └──> <else>
 │       └──> PX4_ERR("alloc failed")
 ├──> delete instance
 ├──> _object.store(nullptr)
 ├──> _task_id = -1
 └──> return PX4_ERROR

3.2 instantiate

注:鉴于该模块不采用任务(线程),所以ModuleBase::run_trampoline无需执行,所以可以不实现。

3.3 init

在task_spawn中使用,直接调用SubscriptionCallbackWorkItem::ScheduleNow,ManualControl::Run的初次调度任务。

ManualControl::init
 ├──> ScheduleNow()
 └──> return true

3.4 Run

根据RC遥控器控制输入,发布不同switch和setpoint消息。

ManualControl::Run
 ├──> [优雅退出处理]
 ├──> <_vehicle_status_sub.updated()><_vehicle_status_sub.copy(&vehicle_status)>  //飞行器状态:system_id/rotary_wing/vtol
 │   ├──> _system_id = vehicle_status.system_id;
 │   ├──> _rotary_wing = (vehicle_status.vehicle_type == vehicle_status_s::VEHICLE_TYPE_ROTARY_WING);
 │   └──> _vtol = vehicle_status.is_vtol;
 ├──> <_parameter_update_sub.updated()>
 │   ├──> _parameter_update_sub.copy(&param_update)
 │   ├──> updateParams()
 │   ├──> _stick_arm_hysteresis.set_hysteresis_time_from(false, _param_com_rc_arm_hyst.get() * 1_ms)
 │   ├──> _stick_disarm_hysteresis.set_hysteresis_time_from(false, _param_com_rc_arm_hyst.get() * 1_ms)
 │   ├──> _button_hysteresis.set_hysteresis_time_from(false, _param_com_rc_arm_hyst.get() * 1_ms)
 │   ├──> _selector.setRcInMode(_param_com_rc_in_mode.get());
 │   ├──> _selector.setTimeout(_param_com_rc_loss_t.get() * 1_s);
 │   └──> [MAN_ARM_GESTURE处理场景:1)RC_MAP_ARM_SW & MAN_ARM_GESTURE: disable arm gesture if an arm switch is configured;2)MC_AIRMODE & MAN_ARM_GESTURE: check for unsafe Airmode settings: yaw airmode requires disabling the stick arm gesture]
 ├──> _selector.updateValidityOfChosenInput(now) //检查RC输入有效性
 ├──> <for (int i = 0; i < MAX_MANUAL_INPUT_COUNT; i++)><_manual_control_setpoint_subs[i].update(&manual_control_input)>
 │   └──>_selector.updateWithNewInputSample(now, manual_control_input, i);
 ├──> switches_updated = _manual_control_switches_sub.update(&switches);
 ├──> <_selector.setpoint().valid> //setpoint有效
 │   ├──> _published_invalid_once = false
 │   ├──> processStickArming(_selector.setpoint())
 │   ├──> [ 更新_selector.setpoint().sticks_moving ]
 │   ├──> <switches_updated><_previous_switches_initialized>
 │   │   ├──> <switches.mode_slot != _previous_switches.mode_slot>
 │   │   │   └──> evaluateModeSlot(switches.mode_slot)
 │   │   ├──> [message: Arming button/switch]  // _action_request_pub发布
 │   │   ├──> [message:Return switch] // _action_request_pub发布
 │   │   ├──> [message:Loiter switch] // _action_request_pub发布
 │   │   ├──> [message:Offboard switch] // _action_request_pub发布
 │   │   ├──> [message:Kill switch] // _action_request_pub发布
 │   │   ├──> [message:Gear switch]        // _landing_gear_pub发布
 │   │   ├──> [message:Transition switch] // _action_request_pub发布
 │   │   ├──> [message:Photo switch] // _action_request_pub发布
 │   │   ├──> [message:Video switch] // _action_request_pub发布
 │   │   ├──> _previous_switches_initialized = true;
 │   │   └──>  _previous_switches = switches;
 │   ├──> <else>
 │   │   └──> _previous_switches_initialized = false
 │   ├──> _selector.setpoint().timestamp = now;
 │   ├──> _manual_control_setpoint_pub.publish(_selector.setpoint());  //有效setpoint发布
 ├──> <!_selector.setpoint().valid> //setpoint无效
 │   ├──> <!_published_invalid_once>
 │   │   ├──> _published_invalid_once = true
 │   │   └──> _manual_control_setpoint_pub.publish(_selector.setpoint())  //无效setpoint发布
 │   ├──> _x_diff.reset();
 │   ├──> _y_diff.reset();
 │   ├──> _z_diff.reset();
 │   ├──> _r_diff.reset();
 │   ├──> _stick_arm_hysteresis.set_state_and_update(false, now);
 │   ├──> _stick_disarm_hysteresis.set_state_and_update(false, now);
 │   └──> _button_hysteresis.set_state_and_update(false, now);
 ├──> _last_time = now
 └──> ScheduleDelayed(200_ms)

注:ManualControl::Run的主要途径是Init/Run和下面两个WorkItem内部的ScheduleNow触发的。

uORB::SubscriptionCallbackWorkItem _manual_control_setpoint_subs[MAX_MANUAL_INPUT_COUNT] {
	{this, ORB_ID(manual_control_input), 0},
	{this, ORB_ID(manual_control_input), 1},
	{this, ORB_ID(manual_control_input), 2},
};
uORB::SubscriptionCallbackWorkItem _manual_control_switches_sub{this, ORB_ID(manual_control_switches)};

4. 总结

从代码的角度

  • 输入
  uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s};
  uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)};
  uORB::SubscriptionCallbackWorkItem _manual_control_setpoint_subs[MAX_MANUAL_INPUT_COUNT] {{this, ORB_ID(manual_control_input), 0},{this, ORB_ID(manual_control_input), 1},{this, ORB_ID(manual_control_input), 2},};
  uORB::SubscriptionCallbackWorkItem _manual_control_switches_sub{this, ORB_ID(manual_control_switches)};
  • 输出
  uORB::Publication<action_request_s> _action_request_pub{ORB_ID(action_request)};
  uORB::Publication<landing_gear_s> _landing_gear_pub{ORB_ID(landing_gear)};
  uORB::Publication<manual_control_setpoint_s> _manual_control_setpoint_pub{ORB_ID(manual_control_setpoint)};

5. 参考资料

【1】PX4开源软件框架简明简介
【2】PX4模块设计之十一:Built-In框架
【3】PX4模块设计之十二:High Resolution Timer设计
【4】PX4模块设计之十三:WorkQueue设计
【5】PX4模块设计之十七:ModuleBase模块
【6】PX4模块设计之三十:Hysteresis类
【7】PX4 modules_main

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个基本的Arduino控制水泵自动浇水的代码示例,它包括手动和自动两种控制模式,并根据土壤湿度传感器的输入判断是否自动启动水泵: ``` // 引入所需库 #include <LiquidCrystal.h> #include <Wire.h> #include <Adafruit_Sensor.h> #include <DHT.h> #include <OneWire.h> #include <DallasTemperature.h> // 定义土壤湿度传感器引脚 #define soilMoisturePin A0 // 定义温湿度传感器引脚 #define DHTPIN 2 #define DHTTYPE DHT11 DHT dht(DHTPIN, DHTTYPE); // 定义水泵控制引脚 #define pumpPin 10 // 定义LCD引脚 LiquidCrystal lcd(12, 11, 5, 4, 3, 1); // 定义一根数据线连接的温度传感器引脚 #define ONE_WIRE_BUS 6 OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); // 定义水泵状态和控制模式 bool pumpOn = false; bool autoMode = true; // 定义手动模式下水泵开关控制函数 void manualControl() { if (digitalRead(pumpPin) == LOW) { digitalWrite(pumpPin, HIGH); pumpOn = true; } else { digitalWrite(pumpPin, LOW); pumpOn = false; } } void setup() { // 初始化LCD lcd.begin(16, 2); lcd.print("Auto Mode: ON"); lcd.setCursor(0, 1); lcd.print("Soil Moisture:"); // 初始化温湿度传感器 dht.begin(); // 初始化温度传感器 sensors.begin(); // 初始化水泵控制引脚 pinMode(pumpPin, OUTPUT); // 初始化手动控制按钮引脚 pinMode(7, INPUT_PULLUP); } void loop() { // 读取土壤湿度传感器值 int soilMoisture = analogRead(soilMoisturePin); // 读取温湿度传感器值 float humidity = dht.readHumidity(); float temperature = dht.readTemperature(); // 读取温度传感器值 sensors.requestTemperatures(); float temperatureC = sensors.getTempCByIndex(0); // 判断是否手动控制 if (digitalRead(7) == LOW) { autoMode = false; manualControl(); lcd.setCursor(0, 0); lcd.print("Manual Mode: "); lcd.setCursor(0, 1); if (pumpOn) { lcd.print("Pump: ON "); } else { lcd.print("Pump: OFF"); } } else { autoMode = true; // 判断是否需要浇水 if (soilMoisture < 500) { digitalWrite(pumpPin, HIGH); pumpOn = true; } else { digitalWrite(pumpPin, LOW); pumpOn = false; } lcd.setCursor(0, 0); lcd.print("Auto Mode: ON "); lcd.setCursor(0, 1); lcd.print("Soil Moisture:"); lcd.print(soilMoisture); lcd.print(" "); } // 输出温湿度传感器值 lcd.setCursor(0, 0); lcd.print("Temp:"); lcd.print(temperatureC); lcd.print("C"); lcd.setCursor(9, 0); lcd.print("Humidity:"); lcd.print(humidity); lcd.print("%"); // 延时1秒后重新循环 delay(1000); } ``` 这个代码使用了一个土壤湿度传感器、一个温湿度传感器、一根数据线连接的温度传感器和一个水泵。它通过手动控制按钮和自动控制来控制水泵的开关,当土壤湿度低于500时,自动模式下会启动水泵进行浇水。在LCD显示屏上,它会显示当前的温度、湿度、土壤湿度和水泵状态。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值