Skip to content

Commit

Permalink
SITL: Add simulator for tethered vehicle
Browse files Browse the repository at this point in the history
  • Loading branch information
rishabsingh3003 committed Dec 2, 2024
1 parent b7ace6c commit e10cbb3
Show file tree
Hide file tree
Showing 8 changed files with 444 additions and 17 deletions.
29 changes: 21 additions & 8 deletions libraries/SITL/SIM_Aircraft.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,11 @@ void Aircraft::update_dynamics(const Vector3f &rot_accel)
sitl->models.slung_payload_sim.update(get_position_relhome(), velocity_ef, accel_earth, wind_ef);
#endif

// update tether
#if AP_SIM_TETHER_ENABLED
sitl->models.tether_sim.update(location);
#endif

// allow for changes in physics step
adjust_frame_time(constrain_float(sitl->loop_rate_hz, rate_hz-1, rate_hz+1));
}
Expand Down Expand Up @@ -1273,18 +1278,26 @@ void Aircraft::add_twist_forces(Vector3f &rot_accel)
}
}

#if AP_SIM_SLUNGPAYLOAD_ENABLED
// add body-frame force due to slung payload
void Aircraft::add_slungpayload_forces(Vector3f &body_accel)
// add body-frame force due to slung payload and tether
void Aircraft::add_external_forces(Vector3f &body_accel)
{
Vector3f forces_ef;
sitl->models.slung_payload_sim.get_forces_on_vehicle(forces_ef);
Vector3f total_force;
#if AP_SIM_SLUNGPAYLOAD_ENABLED
Vector3f forces_ef_slung;
sitl->models.slung_payload_sim.get_forces_on_vehicle(forces_ef_slung);
total_force += forces_ef_slung;
#endif

#if AP_SIM_TETHER_ENABLED
Vector3f forces_ef_tether;
sitl->models.tether_sim.get_forces_on_vehicle(forces_ef_tether);
total_force += forces_ef_tether;
#endif

// convert ef forces to body-frame accelerations (acceleration = force / mass)
const Vector3f accel_bf = dcm.transposed() * forces_ef / mass;
body_accel += accel_bf;
const Vector3f accel_bf_tether = dcm.transposed() * total_force / mass;
body_accel += accel_bf_tether;
}
#endif

/*
get position relative to home
Expand Down
6 changes: 2 additions & 4 deletions libraries/SITL/SIM_Aircraft.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,10 +322,8 @@ class Aircraft {
void add_shove_forces(Vector3f &rot_accel, Vector3f &body_accel);
void add_twist_forces(Vector3f &rot_accel);

#if AP_SIM_SLUNGPAYLOAD_ENABLED
// add body-frame force due to slung payload
void add_slungpayload_forces(Vector3f &body_accel);
#endif
// add body-frame force due to payload
void add_external_forces(Vector3f &body_accel);

// get local thermal updraft
float get_local_updraft(const Vector3d &currentPos);
Expand Down
8 changes: 3 additions & 5 deletions libraries/SITL/SIM_Multicopter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,10 @@ void MultiCopter::calculate_forces(const struct sitl_input &input, Vector3f &rot
add_shove_forces(rot_accel, body_accel);
add_twist_forces(rot_accel);

#if AP_SIM_SLUNGPAYLOAD_ENABLED
// add forces from slung payload
add_slungpayload_forces(body_accel);
#endif
// add forces from slung payload or tether payload
add_external_forces(body_accel);
}

/*
update the multicopter simulation by one time step
*/
Expand Down
309 changes: 309 additions & 0 deletions libraries/SITL/SIM_Tether.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
simulate a static tether attached to the vehicle and ground
*/

#include "SIM_config.h"

#if AP_SIM_TETHER_ENABLED

#include "SIM_Tether.h"
#include "SITL.h"
#include <stdio.h>
#include "SIM_Aircraft.h"
#include <AP_HAL_SITL/SITL_State.h>
#include <GCS_MAVLink/GCS.h>
#include <AP_Logger/AP_Logger.h>

using namespace SITL;

// TetherSim parameters
const AP_Param::GroupInfo TetherSim::var_info[] = {
// @Param: ENABLE
// @DisplayName: Tether Simulation Enable/Disable
// @Description: Enable or disable the tether simulation
// @Values: 0:Disabled,1:Enabled
// @User: Advanced
AP_GROUPINFO_FLAGS("ENABLE", 1, TetherSim, enable, 0, AP_PARAM_FLAG_ENABLE),

// @Param: DENSITY
// @DisplayName: Tether Wire Density
// @Description: Linear mass density of the tether wire
// @Range: 0 1
// @User: Advanced
AP_GROUPINFO("DENSITY", 2, TetherSim, tether_linear_density, 0.0167),

// @Param: LINELEN
// @DisplayName: Tether Maximum Line Length
// @Description: Maximum length of the tether line in meters
// @Units: m
// @Range: 0 100
// @User: Advanced
AP_GROUPINFO("LINELEN", 3, TetherSim, max_line_length, 100.0),

// @Param: SYSID
// @DisplayName: Tether Simulation MAVLink System ID
// @Description: MAVLink system ID for the tether simulation, used to distinguish it from other systems on the network
// @Range: 0 255
// @User: Advanced
AP_GROUPINFO("SYSID", 4, TetherSim, sys_id, 2),

// @Param: STUCK
// @DisplayName: Tether Stuck Enable/Disable
// @Description: Enable or disable a stuck tether simulation
// @Values: 0:Disabled,1:Enabled
// @User: Advanced
AP_GROUPINFO("STUCK", 5, TetherSim, tether_stuck, 0),

// @Param: SPGCNST
// @DisplayName: Tether Spring Constant
// @Description: Spring constant for the tether to simulate elastic forces when stretched beyond its maximum length
// @Range: 0 255
// @User: Advanced
AP_GROUPINFO("SPGCNST", 6, TetherSim, tether_spring_constant, 100),

AP_GROUPEND
};

// TetherSim handles interaction with main vehicle
TetherSim::TetherSim()
{
AP_Param::setup_object_defaults(this, var_info);
}


void TetherSim::update(const Location& veh_pos)
{
if (!enable) {
return;
}

if (veh_pos.is_zero()) {
return;
}

// initialise fixed tether ground location
const uint32_t now_us = AP_HAL::micros();
if (!initialised) {
// capture EKF origin
auto *sitl = AP::sitl();
const Location ekf_origin = sitl->state.home;
if (ekf_origin.lat == 0 && ekf_origin.lng == 0) {
return;
}

// more initialisation
last_update_us = now_us;
initialised = true;
}

// calculate dt and update tether forces
const float dt = (now_us - last_update_us)*1.0e-6;
last_update_us = now_us;

update_tether_force(veh_pos, dt);

// send tether location to GCS at 5hz
// TO-Do: add provision to make the tether movable
const uint32_t now_ms = AP_HAL::millis();
if (now_ms - last_report_ms >= reporting_period_ms) {
last_report_ms = now_ms;
send_report();
write_log();
}
}

// get earth-frame forces on the vehicle from the tether
// returns true on success and fills in forces_ef argument, false on failure
bool TetherSim::get_forces_on_vehicle(Vector3f& forces_ef) const
{
if (!enable) {
return false;
}

forces_ef = veh_forces_ef;
return true;
}

// send a report to the vehicle control code over MAVLink
void TetherSim::send_report(void)
{
if (!mavlink_connected && mav_socket.connect(target_address, target_port)) {
::printf("Tether System connected to %s:%u\n", target_address, (unsigned)target_port);
mavlink_connected = true;
}
if (!mavlink_connected) {
return;
}

// get current time
uint32_t now_ms = AP_HAL::millis();

// send heartbeat at 1hz
const uint8_t component_id = MAV_COMP_ID_USER11;
if (now_ms - last_heartbeat_ms >= 1000) {
last_heartbeat_ms = now_ms;

const mavlink_heartbeat_t heartbeat{
custom_mode: 0,
type : MAV_TYPE_GROUND_ROVER,
autopilot : MAV_AUTOPILOT_INVALID,
base_mode: 0,
system_status: 0,
mavlink_version: 0,
};

mavlink_message_t msg;
mavlink_msg_heartbeat_encode_status(
sys_id.get(),
component_id,
&mav_status,
&msg,
&heartbeat);
uint8_t buf[300];
const uint16_t len = mavlink_msg_to_send_buffer(buf, &msg);
mav_socket.send(buf, len);
}

// send a GLOBAL_POSITION_INT messages
{
Location tether_anchor_loc;
int32_t alt_amsl_cm, alt_rel_cm;
if (!get_tether_ground_location(tether_anchor_loc) ||
!tether_anchor_loc.get_alt_cm(Location::AltFrame::ABSOLUTE, alt_amsl_cm) ||
!tether_anchor_loc.get_alt_cm(Location::AltFrame::ABOVE_HOME, alt_rel_cm)) {
return;
}
const mavlink_global_position_int_t global_position_int{
time_boot_ms: now_ms,
lat: tether_anchor_loc.lat,
lon: tether_anchor_loc.lng,
alt: alt_amsl_cm * 10, // amsl alt in mm
relative_alt: alt_rel_cm * 10, // relative alt in mm
vx: 0, // velocity in cm/s
vy: 0, // velocity in cm/s
vz: 0, // velocity in cm/s
hdg: 0 // heading in centi-degrees
};
mavlink_message_t msg;
mavlink_msg_global_position_int_encode_status(sys_id, component_id, &mav_status, &msg, &global_position_int);
uint8_t buf[300];
const uint16_t len = mavlink_msg_to_send_buffer(buf, &msg);
if (len > 0) {
mav_socket.send(buf, len);
}
}
}

void TetherSim::write_log()
{
#if HAL_LOGGING_ENABLED
// write log of tether state
// @LoggerMessage: TETH
// @Description: Tether state
// @Field: TimeUS: Time since system startup
// @Field: Len: Tether length
// @Field: VFN: Force on vehicle in North direction
// @Field: VFE: Force on vehicle in East direction
// @Field: VFD: Force on vehicle in Down direction
AP::logger().WriteStreaming("TETH",
"TimeUS,Len,VFN,VFE,VFD", // labels
"s----", // units
"F----", // multipliers
"Qffff", // format
AP_HAL::micros64(),
(float)tether_length,
(double)veh_forces_ef.x,
(double)veh_forces_ef.y,
(double)veh_forces_ef.z);
#endif
}
// returns true on success and fills in tether_ground_loc argument, false on failure
bool TetherSim::get_tether_ground_location(Location& tether_ground_loc) const
{
// get EKF origin
auto *sitl = AP::sitl();
if (sitl == nullptr) {
return false;
}
const Location ekf_origin = sitl->state.home;
if (ekf_origin.lat == 0 && ekf_origin.lng == 0) {
return false;
}

tether_ground_loc = ekf_origin;
return true;
}

void TetherSim::update_tether_force(const Location& veh_pos, float dt)
{

Location tether_anchor_loc;
if (!get_tether_ground_location(tether_anchor_loc)) {
return;
}

// Step 1: Calculate the vector from the tether anchor to the vehicle
Vector3f tether_vector = veh_pos.get_distance_NED(tether_anchor_loc);
tether_length = tether_vector.length();

// Step 2: Check if tether is taut (length exceeds maximum allowed length) or stuck
if (tether_length > max_line_length) {

// Calculate the stretch beyond the maximum length
float stretch = MAX(tether_length - max_line_length, 0.0f);

// Apply a spring-like penalty force proportional to the stretch
float penalty_force_magnitude = tether_spring_constant * stretch;

// Direction of force is along the tether, pulling toward the anchor
veh_forces_ef = tether_vector.normalized() * penalty_force_magnitude;

GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Tether: Exceeded maximum length.");

return;
}

if (tether_stuck) {

// Calculate the stretch beyond the maximum length
float stretch = MAX(tether_length - tether_not_stuck_length, 0.0f);

// Apply a spring-like penalty force proportional to the stretch
float penalty_force_magnitude = tether_spring_constant * stretch;

// Direction of force is directly along the tether, towards the tether anchor point
veh_forces_ef = tether_vector.normalized() * penalty_force_magnitude;

GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Tether: Stuck.");

return;
} else {
tether_not_stuck_length = tether_length;
}

// Step 3: Calculate the weight of the tether being lifted
// The weight is proportional to the current tether length
const float tether_weight_force = tether_linear_density * tether_length * GRAVITY_MSS;

// Step 4: Calculate the tension force
Vector3f tension_force_NED = tether_vector.normalized() * tether_weight_force;

// Step 5: Apply the force to the vehicle
veh_forces_ef = tension_force_NED;
}

#endif
Loading

0 comments on commit e10cbb3

Please sign in to comment.