LCOV - code coverage report
Current view: top level - AP_VisualOdom - AP_VisualOdom.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 59 73 80.8 %
Date: 2026-09-09 11:02:08 Functions: 11 13 84.6 %

          Line data    Source code
       1             : /*
       2             :    This program is free software: you can redistribute it and/or modify
       3             :    it under the terms of the GNU General Public License as published by
       4             :    the Free Software Foundation, either version 3 of the License, or
       5             :    (at your option) any later version.
       6             : 
       7             :    This program is distributed in the hope that it will be useful,
       8             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      10             :    GNU General Public License for more details.
      11             : 
      12             :    You should have received a copy of the GNU General Public License
      13             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             : 
      16             : #include "AP_VisualOdom_config.h"
      17             : 
      18             : #if HAL_VISUALODOM_ENABLED
      19             : 
      20             : #include "AP_VisualOdom.h"
      21             : #include "AP_VisualOdom_Backend.h"
      22             : #include "AP_VisualOdom_MAV.h"
      23             : #include "AP_VisualOdom_IntelT265.h"
      24             : #include <AP_AHRS/AP_AHRS.h>
      25             : 
      26             : extern const AP_HAL::HAL &hal;
      27             : 
      28             : // table of user settable parameters
      29             : const AP_Param::GroupInfo AP_VisualOdom::var_info[] = {
      30             : 
      31             :     // @Param: _TYPE
      32             :     // @DisplayName: Visual odometry camera connection type
      33             :     // @Description: Visual odometry camera connection type
      34             :     // @Values: 0:None,1:MAVLink,2:IntelT265,3:VOXL(ModalAI)
      35             :     // @User: Advanced
      36             :     // @RebootRequired: True
      37             :     AP_GROUPINFO_FLAGS("_TYPE", 0, AP_VisualOdom, _type, 0, AP_PARAM_FLAG_ENABLE),
      38             : 
      39             :     // @Param: _POS_X
      40             :     // @DisplayName: Visual odometry camera X position offset
      41             :     // @Description: X position of the camera in body frame. Positive X is forward of the origin.
      42             :     // @Units: m
      43             :     // @Range: -5 5
      44             :     // @Increment: 0.01
      45             :     // @User: Advanced
      46             : 
      47             :     // @Param: _POS_Y
      48             :     // @DisplayName: Visual odometry camera Y position offset
      49             :     // @Description: Y position of the camera in body frame. Positive Y is to the right of the origin.
      50             :     // @Units: m
      51             :     // @Range: -5 5
      52             :     // @Increment: 0.01
      53             :     // @User: Advanced
      54             : 
      55             :     // @Param: _POS_Z
      56             :     // @DisplayName: Visual odometry camera Z position offset
      57             :     // @Description: Z position of the camera in body frame. Positive Z is down from the origin.
      58             :     // @Units: m
      59             :     // @Range: -5 5
      60             :     // @Increment: 0.01
      61             :     // @User: Advanced
      62             :     AP_GROUPINFO("_POS", 1, AP_VisualOdom, _pos_offset, 0.0f),
      63             : 
      64             :     // @Param: _ORIENT
      65             :     // @DisplayName: Visual odometery camera orientation
      66             :     // @Description: Visual odometery camera orientation
      67             :     // @Values: 0:Forward, 2:Right, 4:Back, 6:Left, 24:Up, 25:Down
      68             :     // @User: Advanced
      69             :     AP_GROUPINFO("_ORIENT", 2, AP_VisualOdom, _orientation, ROTATION_NONE),
      70             : 
      71             :     // @Param: _SCALE
      72             :     // @DisplayName: Visual odometry scaling factor
      73             :     // @Description: Visual odometry scaling factor applied to position estimates from sensor
      74             :     // @User: Advanced
      75             :     AP_GROUPINFO("_SCALE", 3, AP_VisualOdom, _pos_scale, 1.0f),
      76             : 
      77             :     // @Param: _DELAY_MS
      78             :     // @DisplayName: Visual odometry sensor delay
      79             :     // @Description: Visual odometry sensor delay relative to inertial measurements
      80             :     // @Units: ms
      81             :     // @Range: 0 250
      82             :     // @User: Advanced
      83             :     AP_GROUPINFO("_DELAY_MS", 4, AP_VisualOdom, _delay_ms, 10),
      84             : 
      85             :     // @Param: _VEL_M_NSE
      86             :     // @DisplayName: Visual odometry velocity measurement noise
      87             :     // @Description: Visual odometry velocity measurement noise in m/s
      88             :     // @Units: m/s
      89             :     // @Range: 0.05 5.0
      90             :     // @User: Advanced
      91             :     AP_GROUPINFO("_VEL_M_NSE", 5, AP_VisualOdom, _vel_noise, 0.1),
      92             : 
      93             :     // @Param: _POS_M_NSE
      94             :     // @DisplayName: Visual odometry position measurement noise 
      95             :     // @Description: Visual odometry position measurement noise minimum (meters). This value will be used if the sensor provides a lower noise value (or no noise value)
      96             :     // @Units: m
      97             :     // @Range: 0.1 10.0
      98             :     // @User: Advanced
      99             :     AP_GROUPINFO("_POS_M_NSE", 6, AP_VisualOdom, _pos_noise, 0.2f),
     100             : 
     101             :     // @Param: _YAW_M_NSE
     102             :     // @DisplayName: Visual odometry yaw measurement noise
     103             :     // @Description: Visual odometry yaw measurement noise minimum (radians), This value will be used if the sensor provides a lower noise value (or no noise value)
     104             :     // @Units: rad
     105             :     // @Range: 0.05 1.0
     106             :     // @User: Advanced
     107             :     AP_GROUPINFO("_YAW_M_NSE", 7, AP_VisualOdom, _yaw_noise, 0.2f),
     108             : 
     109             :     // @Param: _QUAL_MIN
     110             :     // @DisplayName: Visual odometry minimum quality
     111             :     // @Description: Visual odometry will only be sent to EKF if over this value. -1 to always send (even bad values), 0 to send if good or unknown
     112             :     // @Units: %
     113             :     // @Range: -1 100
     114             :     // @User: Advanced
     115             :     AP_GROUPINFO("_QUAL_MIN", 8, AP_VisualOdom, _quality_min, 0),
     116             : 
     117             :     AP_GROUPEND
     118             : };
     119             : 
     120        2098 : AP_VisualOdom::AP_VisualOdom()
     121             : {
     122        2098 :     AP_Param::setup_object_defaults(this, var_info);
     123             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
     124        2096 :     if (_singleton != nullptr) {
     125           0 :         AP_HAL::panic("AP_VisualOdom must be singleton");
     126             :     }
     127             : #endif
     128        2098 :     _singleton = this;
     129        2098 : }
     130             : 
     131             : // detect and initialise any sensors
     132        2086 : void AP_VisualOdom::init()
     133             : {
     134             :     // create backend
     135        2086 :     switch (VisualOdom_Type(_type.get())) {
     136        2078 :     case VisualOdom_Type::None:
     137             :         // do nothing
     138        2078 :         break;
     139             : #if AP_VISUALODOM_MAV_ENABLED
     140           4 :     case VisualOdom_Type::MAV:
     141           4 :         _driver = NEW_NOTHROW AP_VisualOdom_MAV(*this);
     142           4 :         break;
     143             : #endif
     144             : #if AP_VISUALODOM_INTELT265_ENABLED
     145           4 :     case VisualOdom_Type::IntelT265:
     146             :     case VisualOdom_Type::VOXL:
     147           4 :         _driver = NEW_NOTHROW AP_VisualOdom_IntelT265(*this);
     148           4 :         break;
     149             : #endif
     150             :     }
     151        2086 : }
     152             : 
     153             : // return true if sensor is enabled
     154     4421017 : bool AP_VisualOdom::enabled() const
     155             : {
     156     4421017 :     return ((_type != VisualOdom_Type::None));
     157             : }
     158             : 
     159             : // return true if sensor is basically healthy (we are receiving data)
     160      289405 : bool AP_VisualOdom::healthy() const
     161             : {
     162      289405 :     if (!enabled()) {
     163        2670 :         return false;
     164             :     }
     165             : 
     166      286735 :     if (_driver == nullptr) {
     167         518 :         return false;
     168             :     }
     169      286217 :     return _driver->healthy();
     170             : }
     171             : 
     172             : // return quality as a measure from 0 ~ 100
     173             : // -1 means failed, 0 means unknown, 1 is worst, 100 is best
     174           0 : int8_t AP_VisualOdom::quality() const
     175             : {
     176           0 :     if (_driver == nullptr) {
     177           0 :         return 0;
     178             :     }
     179           0 :     return _driver->quality();
     180             : }
     181             : 
     182             : #if HAL_GCS_ENABLED
     183             : // consume vision_position_delta mavlink messages
     184        3735 : void AP_VisualOdom::handle_vision_position_delta_msg(const mavlink_message_t &msg)
     185             : {
     186             :     // exit immediately if not enabled
     187        3735 :     if (!enabled()) {
     188           3 :         return;
     189             :     }
     190             : 
     191             :     // call backend
     192        3732 :     if (_driver != nullptr) {
     193        3633 :         _driver->handle_vision_position_delta_msg(msg);
     194             :     }
     195             : }
     196             : #endif
     197             : 
     198             : // general purpose method to consume position estimate data and send to EKF
     199             : // distances in meters, roll, pitch and yaw are in radians
     200             : // quality of -1 means failed, 0 means unknown, 1 is worst, 100 is best
     201       10472 : void AP_VisualOdom::handle_pose_estimate(uint64_t remote_time_us, uint32_t time_ms, float x, float y, float z, float roll, float pitch, float yaw, float posErr, float angErr, uint8_t reset_counter, int8_t quality)
     202             : {
     203             :     // exit immediately if not enabled
     204       10472 :     if (!enabled()) {
     205         128 :         return;
     206             :     }
     207             : 
     208             :     // call backend
     209       10344 :     if (_driver != nullptr) {
     210             :         // convert attitude to quaternion and call backend
     211       10069 :         Quaternion attitude;
     212       10069 :         attitude.from_euler(roll, pitch, yaw);
     213       10069 :         _driver->handle_pose_estimate(remote_time_us, time_ms, x, y, z, attitude, posErr, angErr, reset_counter, quality);
     214             :     }
     215             : }
     216             : 
     217             : // general purpose method to consume position estimate data and send to EKF
     218             : // quality of -1 means failed, 0 means unknown, 1 is worst, 100 is best
     219           0 : void AP_VisualOdom::handle_pose_estimate(uint64_t remote_time_us, uint32_t time_ms, float x, float y, float z, const Quaternion &attitude, float posErr, float angErr, uint8_t reset_counter, int8_t quality)
     220             : {
     221             :     // exit immediately if not enabled
     222           0 :     if (!enabled()) {
     223           0 :         return;
     224             :     }
     225             : 
     226             :     // call backend
     227           0 :     if (_driver != nullptr) {
     228           0 :         _driver->handle_pose_estimate(remote_time_us, time_ms, x, y, z, attitude, posErr, angErr, reset_counter, quality);
     229             :     }
     230             : }
     231             : 
     232             : // general purpose methods to consume velocity estimate data and send to EKF
     233             : // velocity in NED meters per second
     234             : // quality of -1 means failed, 0 means unknown, 1 is worst, 100 is best
     235        4438 : void AP_VisualOdom::handle_vision_speed_estimate(uint64_t remote_time_us, uint32_t time_ms, const Vector3f &vel, float vel_err, uint8_t reset_counter, int8_t quality)
     236             : {
     237             :     // exit immediately if not enabled
     238        4438 :     if (!enabled()) {
     239          60 :         return;
     240             :     }
     241             : 
     242             :     // call backend
     243        4378 :     if (_driver != nullptr) {
     244        4278 :         _driver->handle_vision_speed_estimate(remote_time_us, time_ms, vel, vel_err, reset_counter, quality);
     245             :     }
     246             : }
     247             : 
     248             : // request sensor's yaw be aligned with vehicle's AHRS/EKF attitude
     249           1 : void AP_VisualOdom::request_align_yaw_to_ahrs()
     250             : {
     251             :     // exit immediately if not enabled
     252           1 :     if (!enabled()) {
     253           0 :         return;
     254             :     }
     255             : 
     256             :     // call backend
     257           1 :     if (_driver != nullptr) {
     258           1 :         _driver->request_align_yaw_to_ahrs();
     259             :     }
     260             : }
     261             : 
     262             : // update position offsets to align to AHRS position.  Should only be called when this library is not being used as the position source
     263      278478 : void AP_VisualOdom::align_position_to_ahrs(bool align_xy, bool align_z)
     264             : {
     265             :     // exit immediately if not enabled
     266      278478 :     if (!enabled()) {
     267           0 :         return;
     268             :     }
     269             : 
     270             :     // call backend
     271      278478 :     if (_driver != nullptr) {
     272      278478 :         _driver->align_position_to_ahrs(align_xy, align_z);
     273             :     }
     274             : }
     275             : 
     276             : // returns false if we fail arming checks, in which case the buffer will be populated with a failure message
     277      133080 : bool AP_VisualOdom::pre_arm_check(char *failure_msg, uint8_t failure_msg_len) const
     278             : {
     279             :     // exit immediately if not enabled
     280      133080 :     if (!enabled()) {
     281      132925 :         return true;
     282             :     }
     283             : 
     284             :     // if no backend we must have failed to create because out of memory
     285         155 :     if (_driver == nullptr) {
     286           4 :         hal.util->snprintf(failure_msg, failure_msg_len, "out of memory");
     287           4 :         return false;
     288             :     }
     289             : 
     290             :     // check healthy
     291         151 :     if (!healthy()) {
     292           0 :         hal.util->snprintf(failure_msg, failure_msg_len, "not healthy");
     293           0 :         return false;
     294             :     }
     295             : 
     296             :     // call backend specific arming check
     297         151 :     return _driver->pre_arm_check(failure_msg, failure_msg_len);
     298             : }
     299             : 
     300             : // singleton instance
     301             : AP_VisualOdom *AP_VisualOdom::_singleton;
     302             : 
     303             : namespace AP {
     304             : 
     305     1838123 : AP_VisualOdom *visualodom()
     306             : {
     307     1838123 :     return AP_VisualOdom::get_singleton();
     308             : }
     309             : 
     310             : }
     311             : 
     312             : #endif

Generated by: LCOV version 1.14