The last version...
/*
[Project Goal]
Autonomous Solar Canoe for long distance (Germany, May-Aug)
Target Speed = 5km/h
Daily Range = Solar supported only
Propulsion = Efficient outboarder, minimal Wh/km across the entire drivetrain
Lifespan = high
Continuous Operation = yes
Operation = efficiency comes before silence!
[Boat & Mission Profile]
5m * 0.85m Canoe with 4.5m * 0.2m outrigger
Weight = 200 kg
Type = Displacement hull
Inland shipping canal = calm water
[Motor & Drive Concept]
Motor = FlipSky 7070
kv = 110kv
Outrunner = yes
Waterproof = yes
Voltage = 6-20S
Max Power = 4300W
Continuous Power = 2200W
Max Current = 100A
Continuous Current = 45A
Max Torque = 9.5Nm
Continuous Torque = 3.8Nm
Weight = 1.08KG
Resistance(R) = 64.5mΩ
Motor Structure = 12N/14P
HousingDiameter * HousingLength = 70mm * 70mm
Shaft = 10mm round
Motor Wire = 12AWG
Protection Class = IP58
Cable Length = max. 1.3m
Drive = direct
[Battery]
Cell = VC-32 LiFePO4
Capacity = 32Ah
Internal resistance = 3
Discharge current = 3C
Configuration = 6S2P (19.2V Nominal Voltage)
Energy = ~1200 Wh
Charging = Victron 100/20 MPPT
Discharge Cutoff Voltage = 3V
Charge Cutoff Voltage = 3.55V
BMS = JK-B1A8S10P
Vesc = directly to battery
[Solar]
Flexible Solar Panels = 4x 125W
Material = Monocrystalline silicon
Maximum Power(Pmax) = 125W±3%
Maximum Power Voltage(Vmp) = 19.8V
Maximum Power Current (Imp) = 6.3A
Open-Circuit Voltage (Voc) = 23.76V
Short-Circuit Current (Isc) = 6.85A
Nominal Operating Cell Temp.(NOCT) = -45 to 80℃
Maximum System Voltage = DC1000V
Maximum Series Fuse = 15A
Dimension = 1055MM(L)*569MM(W)*2.5mm(H)
Orientation = 0°/0°
Wiring = 2s2p
[Control / Regulation]
ESC = Flipsky VESC 75100 Pro V2
Drive Start = 75°C
Drive End = 85°C
Control Mode = Current Control
Motor Poles = 14
ERPM = 12000
FOC-Regulation = ON
FOC Id Current = 0.0A
FOC Id Current Offspeed = 0A
FOC Switching Frequency = 25kHz
Deadtime Compensation = ON
Field Weakening Current = 0A
Maximum Input Voltage = 32V
Maximum Operating Voltage = 29.5
Motor Current Max = 30A
Motor Current Max Brake = -3A
Absolute Maximum Current = 50A
Battery Current Max = 25A
Battery Current Regen = -5A
Wattage Limit = ON
Wattage Limit = 350W
Positive Ramp = 2.0s
Positive Ramping Time = 3.5s
Negative Ramping Time = 3s
Max Current Ramp Step = 0.02
Max Duty Cycle = 0.85
Minimum Duty Cycle 0.02
Stator Saturation Compensation = 3%
Motor Temperature Compensation 3.8%/°C
Battery Cutoff Limit = Yes
Open Loop ERPM = 900
Open Loop Current = 3A.
Observer Gain = low
Interpolation = ON
Battery Cutoff Start = 18.6V
Battery Cutoff End = 18V
[Sensor]
Encoder = AS5048A
Sensor Mode = Encoder
[Propeller]
Diameter = ~250 mm
Blades = 2-3
Propeller Efficiency = high
Material = Resin printed
Material = 80% Elegoo ABS-Like V3 / 20% Siraya Tech Tenacious
[Daggerboard & Hydrodynamics]
Daggerboard
Profile = NACA 0014
Profile Length (chord) = 180 mm
Distance DaggerboardTrailingEdge to PropellerLeadingEdge = 100mm
*/
///////////////////////////////
//////Settings/////////////////
///////////////////////////////
/* [Project Configuration] */
Clockwise = 1; // Rotation direction, [0 for Left, 1 for Right]
BladeNumber = 2; // Number of blades
ShowSingleBlade = 0; // Render single blade; [0=false, 1=true]
ShowBlades = 1; // Show/Hide blades; [0=false, 1=true]
ShowHub = 1; // Show/Hide HUB; [0=false, 1=true]
CutHub = 0; // Cuts HUB along Z axis; [0=false, 1=true]
CutSide = 1; // Which side remains; [1,0]
/* [Performance Parameters] */
PitchMM = 130; // Optimized for (x) km/h
DiameterMM = 255; // Total diameter in mm
Sections = 150; // Level of detail
/* [PITCH DISTRIBUTION] */
PitchRootFactor = 1.10; // Factor at the root (1.0 = Linear): [1.1 - 1.2 improves startup current]
PitchTipFactor = 0.70; // Reduces tip vortices at low pitch; [0.7 - 0.9]
/* [Blade Geometry] */
MaxChordLength = 45; // Maximun chord length
BladeFormSymmetry = 0.60; // Widest point; [0.5 = Center, 0.7 = Outer]
RootWidthRatio = 0.75; // Width at the hub relative to MaxChordLength; [0.6-0.8]
TipRoundness = 0.98; // Tip roundness; [0.85-0.95]
TipStartPoint = 0.72; // Start of tapering; [0.75 - 0.95]
TipCurveShape = 4; // Shape of tip curve (Higher = More aggressive taper); [2-6]
TipThicknessFactor = 0.8; // Thickness of tip relative to profile; [0.5-0.9]
/* [Anti-Flex & Smoothness] */
BladeSweepFinalAngle = 32; // Increase for smoothness and weed shedding; [>=0.5-40]
SweepCurvature = 1.8; // Curvature of sweep (1.0 = Linear, 2.0+ = Sickle shape); [1-3]
BladeTipHeight = -12; // Axial tilt (Rake) in mm; [8-15]
BladeCurveHeight = 3; // Offset of blade tip relative to root (1=linear, 2=parabolic); [2-4]
CenterlinePosition = 40; // Position of rotation axis in profile; [25-45]
/* [Material Strength & Root Optimization] */
ThicknessFactor = 1.15; // Increase for stiffness; [1-1.5]
ThicknessFactorAtBase = 35; // Reinforcement at the root; [3-6]
BaseBladeThickeningLength = 0.33; // Length of root reinforcement in % of r; [0.2-0.4]
ThicknessFadePower = 1.8; // Curve shape of transition (1=Linear, 2-3=Smooth/Organic); [1-5]
RootAsymmetry = 0.3; // Asymmetric thickness (0.1=Massive nose, 0.5=uniform); [0.1-0.5]
ChordFactorAtBase = 2.5; // Factor for chord length at the root (1.3 = 30% wider); [1-2]
BaseChordThickeningLength = 0.35; // How far the widening extends radially (in % of r); [0.1-0.5]
ChordFadePower = 2.5; // Curve shape of widening (2.0 = S-Curve); [1-4]
MinEdgeThickness = 0.7; // Minimum thickness of trailing edge; [0.2-0.4]
MinEdgeThicknessRoot = 1.2; // Minimum thickness of trailing edge at root;
EdgeThicknessFadePower = 2.2; // Decay rate of root trailing edge; [1-3]
RootInsetFactor = 0.28; // Immersion depth into HUB; [1-20]
/* [Hub & Nose Cone] */
HubDiameter = 70; // Hub diameter;
HubThickness = 130; // Hub length;
HubOgiveZOffset = -105; // Shifts Hub on Z; [-50--70]
OgiveFlip = 1; // Rotates Ogive by 180°; [0=false, 1=true]
OgiveShapePower = 2.8; // Shape of the Ogive; [1.5-3]
ShaftDiameter = 0; // 0 = solid
/* [Profiles] */
NACA1 = [4,4,13]; // Profile at the root; [thicker]
NACA2 = [2,3,9]; // Profile at the tip; [thinner]
MaxThicknessPosRoot = 0.35; // Location of thickest point at root (0.3 = 30%)
MaxThicknessPosTip = 0.45; // At the tip (0.4-0.5 is good for efficiency)
CamberFadePower = 1.5; // 1.0 = Linear, 2.0 = Camber remains high longer on the inside
/////////////////////////////////////////////////////////////////////////////////
// LOGIC-CORE
////////////////////////////////////////////////////////////////////////////////
$fn = $preview ? 32 : 128;
Diameter = DiameterMM;
HubRadius = HubDiameter / 2;
EffectiveHubThickness = max(HubThickness, MaxChordLength * 1.0);
AF_terms = [0.2969,-0.1260,-0.3516,0.2843];
a4 = -0.1036;
s = 30;
AirfoilWidth = 1000;
function T_r(r)=((NACA2[2]-NACA1[2])*r+ NACA1[2])/100;
function AirfoilHeight(r) = AirfoilWidth * T_r(r);
function GetLocalPitch(r) = PitchMM * ( PitchRootFactor + (PitchTipFactor - PitchRootFactor) * r);
function CenterlineAngle(r) = BladeSweepFinalAngle * pow(max(0.0001, r), SweepCurvature);
function CenterlineOffset(r) = let(ang = CenterlineAngle(r)) BladeSweepFinalAngle != 0 ? -(ln(max(0.0001, cos(ang)))) / (BladeSweepFinalAngle * PI / 180) : 0;
CenterlineLength = sqrt(pow(.5*Diameter,2) / (pow(0.5*CenterlineOffset(1),2)+.25)) / 2;
function BladeThickness(r) =
let(
baseT = ThicknessFactor * AirfoilHeight(r) * (ChordLength(r)/AirfoilWidth),
tipTaper = (r < 0.7) ? 1 : 1 - ( (1 - TipThicknessFactor) * (r - 0.7) / 0.3 )
)
baseT * tipTaper;
function BladeHeight(r) = BladeTipHeight * pow(max(0, r), BladeCurveHeight) / CenterlineLength;
function RollAngle(r) = atan( (BladeTipHeight/CenterlineLength) * BladeCurveHeight * pow(max(0, r), max(0.01, BladeCurveHeight-1)));
function ChordLength(r) =
let(
baseShape = RootWidthRatio + (1 - RootWidthRatio) * sin(r * 180),
distortedShape = pow(max(0.01, baseShape), BladeFormSymmetry),
standardChord = distortedShape * MaxChordLength,
chordBoost = (r < BaseChordThickeningLength)
? 1 + (ChordFactorAtBase - 1) * pow(1 - (r/BaseChordThickeningLength), ChordFadePower)
: 1,
boostedChord = standardChord * chordBoost,
safeR = max(TipStartPoint, r),
taper = (r < TipStartPoint) ? 1 : 1 - (TipRoundness * pow((safeR - TipStartPoint) / (1 - TipStartPoint + 0.0001), TipCurveShape))
)
max(0.1, boostedChord * taper);
function PitchAngle(r) = atan(GetLocalPitch(r)/(2*PI*max(0.0001, r)*CenterlineLength));
module AirfoilGeneration(r) {
camberFade = 1.5;
M = (NACA1[0] + (NACA2[0] - NACA1[0]) * pow(r, camberFade)) / 100;
P = (NACA1[1] + (NACA2[1] - NACA1[1]) * r) / 10;
T = T_r(r);
localChord = ChordLength(r);
mTP_Root = 0.35;
mTP_Tip = 0.45;
currentMaxPos = mTP_Root + (mTP_Tip - mTP_Root) * r;
p = ln(0.3) / ln(currentMaxPos);
rootBoostRange = BaseBladeThickeningLength;
localRootBoost = (r < rootBoostRange)
? pow(1 - (r/rootBoostRange), ThicknessFadePower) * (ThicknessFactorAtBase - 1)
: 0;
currentMinEdge = (r < rootBoostRange)
? MinEdgeThickness + (MinEdgeThicknessRoot - MinEdgeThickness) * pow(1 - (r/rootBoostRange), EdgeThicknessFadePower)
: MinEdgeThickness;
actualMinEdge = min(currentMinEdge, localChord * 0.05);
AirfoilX = [for(i=[0:s]) 1 - (1 - cos(i * 180 / s)) / 2];
polygon(points=1000 * [for(i=[0:2 * len(AirfoilX) - 1])
let(
isUpper = (i < len(AirfoilX)),
idx_real = isUpper ? i : (2 * len(AirfoilX) - 1 - i),
x_orig = AirfoilX[idx_real],
x = pow(x_orig, p),
xWeight = pow(1 - x, 1 / RootAsymmetry),
currentBoost = 1 + (localRootBoost * xWeight),
yc = (x < P) ? (M / (P * P)) * (2 * P * x - x * x) : (M / ((1 - P) * (1 - P))) * (1 - 2 * P + 2 * P * x - x * x),
dyc_dx = (x < P) ? (2 * M / (P * P)) * (P - x) : (2 * M / ((1 - P) * (1 - P))) * (P - x),
theta = atan(dyc_dx),
thickness = (T / 0.2) * (AF_terms[0] * sqrt(x) + AF_terms[1] * x + AF_terms[2] * x * x + AF_terms[3] * pow(x, 3) + a4 * pow(x, 4)),
finalThick = (thickness * currentBoost) + (x * (actualMinEdge / max(0.1, localChord)) / 2) / (T / 0.2)
)
isUpper
? [ x_orig - finalThick * sin(theta), yc + finalThick * cos(theta) ]
: [ x_orig + finalThick * sin(theta), yc - finalThick * cos(theta) ]
]);
}
module HubOgiveBody(L) {
rotate_extrude($fn=100) polygon(concat([[0,0], [HubRadius,0]], [for(i=[1:40]) let(t = i / 40, r = HubRadius * (1 - pow(t, OgiveShapePower)), z = L * t) [ r, z ]], [[0,L]]));
}
module HubSelector() {
L = EffectiveHubThickness;
translate([0,0,HubOgiveZOffset])
if (OgiveFlip == 1) rotate([180,0,0]) translate([0,0,-L]) HubOgiveBody(L);
else HubOgiveBody(L);
}
module BladeSlice(r) {
chord = ChordLength(r);
translate([CenterlineOffset(r)*CenterlineLength, BladeHeight(r)*CenterlineLength, r*CenterlineLength])
rotate([-RollAngle(r),CenterlineAngle(r),-PitchAngle(r)])
linear_extrude(height=0.05) translate([-CenterlinePosition/100*chord,0])
scale([chord/AirfoilWidth, BladeThickness(r)/AirfoilHeight(r)]) AirfoilGeneration(r);
}
module Blade() {
ChordRoot = ChordLength(0);
RootSweepAngle = CenterlineAngle(0);
LE_radial = abs((CenterlinePosition/100 * ChordRoot) * cos(RootSweepAngle));
Gap_LE = HubRadius - sqrt(max(0, HubRadius*HubRadius - LE_radial*LE_radial));
TE_radial = abs(((1-CenterlinePosition/100) * ChordRoot) * cos(RootSweepAngle));
Gap_TE = HubRadius - sqrt(max(0, HubRadius*HubRadius - TE_radial*TE_radial));
localRoot = (HubRadius - (max(Gap_LE, Gap_TE) * RootInsetFactor)) / CenterlineLength * 0.98;
for(i=[0:Sections-1]) {
r1 = max(0, localRoot) + (i/Sections)*(1-max(0, localRoot));
r2 = max(0, localRoot) + ((i+1)/Sections)*(1-max(0, localRoot));
hull(){ BladeSlice(r1); BladeSlice(r2); }
}
}
module main() {
BladeInstances = (ShowSingleBlade == 1) ? 1 : BladeNumber;
difference() {
union() {
if (ShowBlades == 1) {
for(i=[0:BladeInstances-1]) {
rotate([90, 0, i * 360 / BladeNumber]) Blade();
}
}
if (ShowHub == 1) {
HubSelector();
}
}
union() {
if (CutHub == 1) {
translate([500 * (CutSide == 1 ? -1 : 0), -500, -500])
cube([500, 1000, 1000]);
}
if (ShaftDiameter > 0) {
translate([0,0,-500])
cylinder(h=1000, d=ShaftDiameter, center=false, $fn=50);
}
}
}
}
if(Clockwise != 1) mirror([1,0,0]) main(); else main();
Greetings from Germany