在之前的文章中我们讲了PX4的混动框架,控制信号由混控器输出执行机构的控制信号后,交由执行机构驱动器。
PX4支持多种驱动器类型,如fmu的pwmout驱动、io mcu驱动、dshot、DroneCAN(UAVCAN)、PCA9685等。
我们之前的分享中,以PX4 1.12版本为基础,今年6月发布的最新版本的1.13版本在混控和执行机构驱动做了较多的修改,比较重要的修改修改有
-
取消io单片机的mixer功能,全部由fmu实现mixer
相关修改说明:https://github.com/PX4/PX4-Autopilot/commit/089c962d9208b3616ba505ab927fa399554035d4
-
支持控制动态分配
相关修改说明:https://github.com/PX4/PX4-Autopilot/commit/089c962d9208b3616ba505ab927fa399554035d4
我们基于1.13版本,介绍一下PX4的执行机构驱动框架。
PX4启动时根据配置选择启动的执行机构驱动类型,px4io、pwm_out、dshot、uavcan_esc
#
# Start IO for PWM output or RC input if needed.
#
if [ $IO_PRESENT = yes ]
then
if ! px4io start
then
echo "PX4IO start failed"
tune_control play -t 18 # PROG_PX4IO_ERR
fi
fi
if [ $OUTPUT_MODE = $OUTPUT_CMD -o $OUTPUT_MODE = io ]
then
if param compare SYS_CTRL_ALLOC 1
then
pwm_out start
dshot start
else
if ! $OUTPUT_CMD start
then
echo "$OUTPUT_CMD start failed"
tune_control play error
fi
fi
fi
使用系统命令工具mixer,向指定的执行机构驱动配置主混控文件
set OUTPUT_DEV /dev/pwm_output0
if [ $OUTPUT_MODE = uavcan_esc ]
then
set OUTPUT_DEV /dev/uavcan/esc
fi
if mixer load ${OUTPUT_DEV} ${MIXER_FILE}
then
echo "INFO [init] Mixer: ${MIXER_FILE} on ${OUTPUT_DEV}"
else
echo "ERROR [init] Failed loading mixer: ${MIXER_FILE}"
tune_control play -t 18 # tune 18 = PROG_PX4IO_ERR
fi
PX4支持main和aux两个混控文件,即可以同时使用两个执行机构驱动,如fmu的pwm_out和px4io,或者uavcan_esc,可以同时支持更多的控制通道输出。
if [ -e $OUTPUT_AUX_DEV -a $OUTPUT_MODE != hil ]
then
if mixer load ${OUTPUT_AUX_DEV} ${MIXER_AUX_FILE}
then
echo "INFO [init] Mixer: ${MIXER_AUX_FILE} on ${OUTPUT_AUX_DEV}"
else
echo "ERROR [init] Failed loading mixer: ${MIXER_AUX_FILE}"
fi
else
echo "INFO [init] setting PWM_AUX_OUT none"
set PWM_AUX_OUT none
fi
启动执行机构驱动后,在驱动器任务中进行,混控更新和控制通道输出。以pwm_out驱动为例:
PX4可以非常灵活的配置PWM输出io,只需要在boards中配置即可,如boards/px4/fmu-v6x/src/timer_config.cpp
constexpr io_timers_t io_timers[MAX_IO_TIMERS] = {
initIOTimer(Timer::Timer5, DMA{DMA::Index1}),
initIOTimer(Timer::Timer4, DMA{DMA::Index1}),
initIOTimer(Timer::Timer12),
initIOTimer(Timer::Timer1),
initIOTimer(Timer::Timer2),
};
constexpr timer_io_channels_t timer_io_channels[MAX_TIMER_IO_CHANNELS] = {
initIOTimerChannel(io_timers, {Timer::Timer5, Timer::Channel4}, {GPIO::PortI, GPIO::Pin0}),
initIOTimerChannel(io_timers, {Timer::Timer5, Timer::Channel3}, {GPIO::PortH, GPIO::Pin12}),
initIOTimerChannel(io_timers, {Timer::Timer5, Timer::Channel2}, {GPIO::PortH, GPIO::Pin11}),
initIOTimerChannel(io_timers, {Timer::Timer5, Timer::Channel1}, {GPIO::PortH, GPIO::Pin10}),
initIOTimerChannel(io_timers, {Timer::Timer4, Timer::Channel2}, {GPIO::PortD, GPIO::Pin13}),
initIOTimerChannel(io_timers, {Timer::Timer4, Timer::Channel3}, {GPIO::PortD, GPIO::Pin14}),
initIOTimerChannel(io_timers, {Timer::Timer12, Timer::Channel1}, {GPIO::PortH, GPIO::Pin6}),
initIOTimerChannel(io_timers, {Timer::Timer12, Timer::Channel2}, {GPIO::PortH, GPIO::Pin9}),
initIOTimerChannelCapture(io_timers, {Timer::Timer1, Timer::Channel2}, {GPIO::PortE, GPIO::Pin11}),
};
constexpr io_timers_channel_mapping_t io_timers_channel_mapping =
initIOTimerChannelMapping(io_timers, timer_io_channels);
–
我的微信公众号,文章同步更新,欢迎关注。