Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RealFlight: Titan Cobra: add gimbal #140

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
CAM1_TYPE,7
MNT1_DEFLT_MODE,1
MNT1_NEUTRAL_Y,-15
MNT1_PITCH_MAX,45
MNT1_PITCH_MIN,-45
MNT1_RETRACT_Y,-180
MNT1_RETRACT_Z,-45
MNT1_ROLL_MAX,60
MNT1_ROLL_MIN,-60
MNT1_TYPE,9
SCR_ENABLE,1
SERVO10_FUNCTION,7
SERVO10_MAX,2000
SERVO10_MIN,1000
SERVO11_FUNCTION,94
SERVO11_MAX,2000
SERVO11_MIN,1000
SERVO9_FUNCTION,6
SERVO9_MAX,2000
SERVO9_MIN,1000
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,12 @@ Aircraft Model: Titan Cobra VTOL
* AETR normal control order
* RC8(SwB) is mode: 0:QSTABLIZE, 1:QLOITER, 2:FBWA

Tested in RealFlight 9.5S using ArduPlane 4.1.1
## Simulated Camera Gimbal:
In ArduPilot 4.6+, using the included lua script and the "GimbalAddition"
parameter file, you can simulate a camera gimbal. Currently, the gimbal
mechanism is invisible, but the camera feed itself can be viewed by hitting
F4, or by opening an addition Viewport and hitting F4 or selecting the
"Gimbal" feed. For more information on this script, and for the most up-to-date
version of it, consult the scripting drivers within the ArduPilot repo.

Tested in RealFlight 9.5S using ArduPlane 4.5.0
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Titan Cobra RealFlight ArduPilot 4.4
# Titan Cobra RealFlight ArduPilot 4.5
AHRS_EKF_TYPE,3
AIRSPEED_CRUISE,12.4
AIRSPEED_MAX,18
AIRSPEED_MIN,11
ARMING_RUDDER,2
ARSPD_FBW_MAX,18
ARSPD_FBW_MIN,11
ARSPD_OFFSET,2013
ARSPD_SKIP_CAL,1
ARSPD_USE,1
Expand All @@ -19,14 +20,15 @@ FLTMODE4,19
FLTMODE5,5
FLTMODE6,5
INS_GYRO_FILTER,36
LIM_PITCH_MIN,-1400
NAVL1_PERIOD,9
PTCH_LIM_MIN_DEG,-14
PTCH_RATE_D,0.00903
PTCH_RATE_FF,1.042
PTCH_RATE_FLTD,18
PTCH_RATE_FLTT,2.122
PTCH_RATE_I,1.042
PTCH_RATE_P,0.806
PTCH_TRIM_DEG,4.2
PTCH2SRV_RMAX_DN,75
PTCH2SRV_RMAX_UP,75
PTCH2SRV_TCONST,0.75
Expand Down Expand Up @@ -64,9 +66,9 @@ Q_OPTIONS,16384
Q_P_ACCZ_I,0.656
Q_P_ACCZ_P,0.328
Q_P_VELXY_P,4
Q_PILOT_SPD_DN,1.5
Q_PLT_Y_RATE,50
Q_TRANS_DECEL,0.8
Q_VELZ_MAX_DN,150
Q_VFWD_ALT,5
Q_VFWD_GAIN,0.1
Q_WVANE_ANG_MIN,0.5
Expand All @@ -91,8 +93,6 @@ TECS_RLL2THR,7
TECS_SINK_MAX,3
TECS_SINK_MIN,1.5
THROTTLE_NUDGE,0
TRIM_ARSPD_CM,1240
TRIM_PITCH_CD,420
TRIM_THROTTLE,47
WP_LOITER_RAD,70
WP_RADIUS,70
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
-- mount-sitl-realflight.lua: RealFlight simulated gimbal driver

--[[
RealFlight simulated gimbal driver

This script provides a driver for a simulated gimbal in RealFlight. This
script sends three servo outputs to control a camera: pitch, yaw, and zoom.

An invisible camera gimbal can be added to any model in RealFlight, even if the
3D assets for that camera gimbal are not there. Check the Titan Cobra model for
an example of how to add and configure a camera for use with this script.

Note that there is a 12 channel limit for servo outputs to RealFlight, so this
can only be done with a vehicle that uses 9 or fewer channels (or 10 if you
don't need the zoom control).
--]]

-- parameters
local PARAM_TABLE_KEY = 24
assert(param:add_table(PARAM_TABLE_KEY, "RFG_", 6), "could not add param table")
assert(param:add_param(PARAM_TABLE_KEY, 1, "DEBUG", 0), "could not add RFG_DEBUG param")
assert(param:add_param(PARAM_TABLE_KEY, 2, "FOV_MIN", 10), "could not add RFG_FOV_MIN param")
assert(param:add_param(PARAM_TABLE_KEY, 3, "FOV_MAX", 60), "could not add RFG_FOV_MAX param")
assert(param:add_param(PARAM_TABLE_KEY, 4, "ASPECT", 1.777778), "could not add RFG_ASPECT param")
assert(param:add_param(PARAM_TABLE_KEY, 5, "ZOOM_SPEED", 10), "could not add RFG_ZOOM_SPEED param")
assert(param:add_param(PARAM_TABLE_KEY, 6, "ZOOM_SERVO", 94), "could not add RFG_ZOOM_SERVO param")

-- Servo outputs
local PITCH_SERVO = nil
local YAW_SERVO = nil
local ZOOM_SERVO = nil

--[[
// @Param: RFG_DEBUG
// @DisplayName: RealFlight Gimbal debug
// @Description: RealFlight Gimbal debug
// @Values: 0:Disabled, 1:Enabled, 2:Enabled including attitude reporting
// @User: Advanced
--]]
local RFG_DEBUG = Parameter("RFG_DEBUG") -- debug level. 0:disabled 1:enabled 2:enabled with attitude reporting

--[[
// @Param: RFG_FOV_MIN
// @DisplayName: RealFlight Gimbal FOV Min
// @Description: RealFlight Gimbal FOV Min
// @Units: deg
// @Range: 1 179
// @User: Standard
--]]
local RFG_FOV_MIN = Parameter("RFG_FOV_MIN") -- minimum field of view in degrees

--[[
// @Param: RFG_FOV_MAX
// @DisplayName: RealFlight Gimbal FOV Max
// @Description: RealFlight Gimbal FOV Max
// @Units: deg
// @Range: 1 179
// @User: Standard
--]]
local RFG_FOV_MAX = Parameter("RFG_FOV_MAX") -- maximum field of view in degrees

--[[
// @Param: RFG_ASPECT
// @DisplayName: RealFlight Gimbal Aspect Ratio
// @Description: RealFlight Gimbal Aspect Ratio. HFOV/VFOV for the camera image. Used for relaying horizontal and vertical FOV of the camera
// @Values: 0.1 10
// @User: Standard
--]]
local RFG_ASPECT = Parameter("RFG_ASPECT") -- aspect ratio of the camera

--[[
// @Param: RFG_ZOOM_SPEED
// @DisplayName: RealFlight Gimbal Zoom Speed
// @Description: RealFlight Gimbal Zoom Speed. Higher numbers result in faster zooming
// @Units: %/s
// @Range: 1 100
// @User: Standard
--]]
local RFG_ZOOM_SPEED = Parameter("RFG_ZOOM_SPEED") -- zoom speed in percent per second

--[[
// @Param: RFG_ZOOM_SERVO
// @DisplayName: RealFlight Gimbal Zoom Servo Function
// @Description: RealFlight Gimbal Zoom Servo Function. The servo function number for the zoom control: Script1 (94) to Script16 (109)
// @Range: 94 109
// @User: Standard
--]]
local RFG_ZOOM_SERVO = Parameter("RFG_ZOOM_SERVO") -- servo function number for the zoom control

-- global definitions
local MAV_SEVERITY = { EMERGENCY = 0, ALERT = 1, CRITICAL = 2, ERROR = 3, WARNING = 4, NOTICE = 5, INFO = 6, DEBUG = 7 }
local INIT_INTERVAL_MS = 3000 -- attempt to initialise the gimbal at this interval
local UPDATE_INTERVAL_MS = 100 -- update at 10hz
local MOUNT_INSTANCE = 0 -- always control the first mount/gimbal
local CAMERA_INSTANCE = 0 -- always use the first camera
local PITCH_MIN_DEG = -89.9 -- minimum pitch angle in degrees, as set up in the RealFlight model
local PITCH_MAX_DEG = 89.9 -- maximum pitch angle in degrees, as set up in the RealFlight model
local YAW_MIN_DEG = -180 -- minimum yaw angle in degrees, as set up in the RealFlight model
local YAW_MAX_DEG = 180 -- maximum yaw angle in degrees, as set up in the RealFlight model

-- local variables and definitions
local initialised = false -- true once connection to gimbal has been initialised
local pitch_deg = 0 -- current pitch angle in degrees (always earth frame)
local yaw_deg = 0 -- current yaw angle in degrees (always body frame)
local zoom_absolute = 0 -- current zoom level from 0 to 100
local cam_pic_count = 0 -- last picture count. used to detect trigger pic
local cam_rec_video = false -- last record video state. used to detect record video
local cam_zoom_type = 0 -- last zoom type 1:Rate 2:Pct
local cam_zoom_value = 0 -- last zoom value. If rate, zoom out = -1, hold = 0, zoom in = 1. If Pct then value from 0 to 100

-- bind parameters to variables
local MNT_TYPE = Parameter("MNT" .. (MOUNT_INSTANCE + 1) .. "_TYPE") -- should be 9:Scripting
local CAM_TYPE = Parameter("CAM" .. (CAMERA_INSTANCE + 1) .. "_TYPE") -- should be 7:Scripting
local CAM_HFOV = Parameter("CAM" .. (CAMERA_INSTANCE + 1) .. "_HFOV") -- horizontal field of view in degrees
local CAM_VFOV = Parameter("CAM" .. (CAMERA_INSTANCE + 1) .. "_VFOV") -- vertical field of view in degrees

-- wrap yaw angle in degrees to value between 0 and 360
local function wrap_360(angle)
local res = math.fmod(angle, 360.0)
if res < 0 then
res = res + 360.0
end
return res
end

-- wrap yaw angle in degrees to value between -180 and +180
local function wrap_180(angle_deg)
local res = wrap_360(angle_deg)
if res > 180 then
res = res - 360
end
return res
end

-- find and initialise serial port connected to gimbal
local function init()
-- check mount parameter
if MNT_TYPE:get() ~= 9 then
gcs:send_text(MAV_SEVERITY.CRITICAL, "RealFlight Gimbal: set MNT" .. (MOUNT_INSTANCE + 1) .. "_TYPE=9")
do return end
end

-- check cam type parameter
if CAM_TYPE:get() ~= 7 then
gcs:send_text(MAV_SEVERITY.CRITICAL, "RealFlight Gimbal: set CAM" .. (CAMERA_INSTANCE + 1) .. "_TYPE=7")
do return end
end

if not CAM_HFOV or not CAM_VFOV then
gcs:send_text(MAV_SEVERITY.CRITICAL,
"RealFlight Gimbal: could not find camera HFOV and VFOV for camera instance " .. CAMERA_INSTANCE)
do return end
end

-- find pitch and yaw servos
PITCH_SERVO = SRV_Channels:find_channel(7) -- Mount1Pitch
YAW_SERVO = SRV_Channels:find_channel(6) -- Mount1Yaw
if not PITCH_SERVO or not YAW_SERVO then
gcs:send_text(MAV_SEVERITY.CRITICAL, "RealFlight Gimbal: could not find pitch and yaw servos")
do return end
end

-- find zoom servo
ZOOM_SERVO = SRV_Channels:find_channel(RFG_ZOOM_SERVO:get())
if not ZOOM_SERVO then
gcs:send_text(MAV_SEVERITY.INFO, "RealFlight Gimbal: WARNING: could not find zoom servo")
end

initialised = true
gcs:send_text(MAV_SEVERITY.INFO, "RealFlight Gimbal: started")
end

-- send target angles to the servos
local function send_target_angles()
if not PITCH_SERVO or not YAW_SERVO then
return
end
-- convert to int
SRV_Channels:set_output_pwm_chan_timeout(
PITCH_SERVO,
math.floor(1000 + 1000 * (pitch_deg - PITCH_MIN_DEG) / (PITCH_MAX_DEG - PITCH_MIN_DEG)),
UPDATE_INTERVAL_MS * 10
)
SRV_Channels:set_output_pwm_chan_timeout(
YAW_SERVO,
math.floor(1000 + 1000 * (yaw_deg - YAW_MIN_DEG) / (YAW_MAX_DEG - YAW_MIN_DEG)),
UPDATE_INTERVAL_MS * 10
)
end

-- set zoom servo
local function set_camera_zoom(zoom_pct)
local hfov = RFG_FOV_MIN:get() + (RFG_FOV_MAX:get() - RFG_FOV_MIN:get()) * (100-zoom_pct) / 100
local vfov = hfov / RFG_ASPECT:get()

CAM_HFOV:set(hfov)
CAM_VFOV:set(vfov)

if not ZOOM_SERVO then
return
end
SRV_Channels:set_output_pwm_chan_timeout(
ZOOM_SERVO,
1000 + 1000 * zoom_pct / 100,
UPDATE_INTERVAL_MS * 10
)
end

-- check for changes in camera state and update accordingly (primarily for zoom)
local function update_camera()
-- get latest camera state from AP driver
local cam_state = camera:get_state(CAMERA_INSTANCE)
if not cam_state then
return
end

-- check for take picture
if cam_state:take_pic_incr() and cam_state:take_pic_incr() ~= cam_pic_count then
cam_pic_count = cam_state:take_pic_incr()
-- Do nothing (useful for testing ArduPilot, but does nothing in RealFlight)
if RFG_DEBUG:get() > 0 then
gcs:send_text(MAV_SEVERITY.INFO, string.format("RealFlight Gimbal: took pic %u", cam_pic_count))
end
end

-- check for start/stop recording video
if cam_state:recording_video() ~= cam_rec_video then
cam_rec_video = cam_state:recording_video()
-- Do nothing (useful for testing ArduPilot, but does nothing in RealFlight)
if cam_rec_video > 0 then
gcs:send_text(MAV_SEVERITY.INFO, "RealFlight Gimbal: start recording")
else
-- Do nothing (useful for testing ArduPilot, but does nothing in RealFlight)
gcs:send_text(MAV_SEVERITY.INFO, "RealFlight Gimbal: stop recording")
end
if RFG_DEBUG:get() > 0 then
gcs:send_text(MAV_SEVERITY.INFO, "RealFlight Gimbal: rec video:" .. tostring(cam_rec_video))
end
end

-- check zoom
-- zoom out = -1, hold = 0, zoom in = 1
local zoom_type_changed = cam_state:zoom_type() and (cam_state:zoom_type() ~= cam_zoom_type)
local zoom_value_changed = cam_state:zoom_value() and (cam_state:zoom_value() ~= cam_zoom_value)
if (zoom_type_changed or zoom_value_changed) then
cam_zoom_type = cam_state:zoom_type()
cam_zoom_value = cam_state:zoom_value()

-- zoom percent
if cam_zoom_type == 2 then
zoom_absolute = cam_zoom_value
end
end

-- Slew zoom if in rate mode
if cam_zoom_type == 1 then
zoom_absolute = math.max(0, math.min(100, zoom_absolute + cam_zoom_value * RFG_ZOOM_SPEED:get() * UPDATE_INTERVAL_MS / 1000))
end

if RFG_DEBUG:get() > 0 and (zoom_type_changed or zoom_value_changed or (cam_zoom_type == 1 and cam_zoom_value ~= 0)) then
gcs:send_text(MAV_SEVERITY.INFO,
"RealFlight Gimbal: zoom type:" .. tostring(cam_zoom_type) .. " value:" .. tostring(cam_zoom_value))
gcs:send_text(MAV_SEVERITY.INFO, "RealFlight Gimbal: zoom absolute:" .. tostring(zoom_absolute))
end

set_camera_zoom(zoom_absolute)
end

-- the main update function
local function update()
-- initialise connection to gimbal
if not initialised then
init()
return update, INIT_INTERVAL_MS
end

update_camera()

-- update pitch and yaw targets if in angle-targeting mode
local yaw_is_ef
local next_pitch_deg, next_yaw_deg
_, next_pitch_deg, next_yaw_deg, yaw_is_ef = mount:get_angle_target(MOUNT_INSTANCE)
if next_pitch_deg and next_yaw_deg then
if yaw_is_ef then
next_yaw_deg = wrap_180(next_yaw_deg - math.deg(ahrs:get_yaw()))
end
pitch_deg = next_pitch_deg
yaw_deg = next_yaw_deg
end

-- update pitch and yaw targets if in rate-targeting mode
local pitch_rate_dps, yaw_rate_dps
_, pitch_rate_dps, yaw_rate_dps, yaw_is_ef = mount:get_rate_target(MOUNT_INSTANCE)
if pitch_rate_dps and yaw_rate_dps then
if yaw_is_ef then
yaw_rate_dps = yaw_rate_dps - math.deg(ahrs:get_gyro():z())
end
pitch_deg = pitch_deg + pitch_rate_dps * UPDATE_INTERVAL_MS / 1000
yaw_deg = yaw_deg + yaw_rate_dps * UPDATE_INTERVAL_MS / 1000
end

yaw_deg = wrap_180(yaw_deg)

send_target_angles()

-- update the backend so it's healthy
mount:set_attitude_euler(MOUNT_INSTANCE, 0, pitch_deg, yaw_deg);

return update, UPDATE_INTERVAL_MS
end

return update()