The VisualMesh detection system
Fast Multi Layer Perceptron
Niowire, The highly scaleable socket server framework
simple locality sensitive hashing
RoboCup-Humanoid-TC/RobocupProtocol 1
Protobuf message protocol for the Robocup Humanoid league
A OpenCL C++ version of Josiaw's IsomapCuda
Object-oriented C++ wrapper for OpenGL.
TrentHouliston/ChromaTagsSim 0
Simulating chromatags to see if they are viable
Source code formatter for cmake listfiles.
This is a simple class made to make debugging easier through the power of the Eyes (O.o) instead of long crazy methods simply to output variables, just use O.o(variable)
push eventFastcode/VisualMesh
commit sha f522fdadba3c63d6292c9a2474495364da10ace4
Change lookup in visualmesh.hpp to int to match mesh.hpp
commit sha 2a2b4a729d17b2d6017dd6ef11e80d8cd65b6f40
Add missing compile definition when BUILD_OPENCL_ENGINE is manually set off
commit sha 4bb4b9e27ffdd945cacce2391b608359fbae2c41
Correct example_net.yaml missing an output layer
commit sha cae7d01dc035d334d3c1b710cfbab5dde2a11960
Dump history into yaml rather than pickle as TensorFlow 2.3 history objects can't be pickled
commit sha b0c9391b1b458b8d447a3a3444c56192f66f8033
Use training for finding LR as otherwise the batch size is wrong
push time in 17 days
push eventNUbots/NUbots
commit sha 467f458c687419056e2487fe468c7f22d19a6965
Fix missing space in clang-format command that prevented formatting (#336)
push time in 21 days
push eventFastcode/VisualMesh
commit sha 414d59edf7725c1ce962de653c318531a3733541
Start swapping the math of calculating the mesh over to using n directly
commit sha d9640bfa2a0fc0e7efa4b16438960bcd89c21069
Remove unused variable
commit sha 57ba7c2dc9fd2a29a8d0d72252f1e7dd21a2dce3
Files missing from commit
commit sha e0bee1b625420a392c731b54ccc470c654a9b230
Add code to test the mesh for consistency of sampling
commit sha 1434c243380c96d3883ddd58208209e71a0f6d61
Move the projection math out into its own file in preparation for quadtrees
commit sha 097631d09752a34b33e3b4eaa526464cb00a6e8a
Split out the generator and write a BSP generator algorithm. Current Issues: Currently the HexaPizza generator still doesn't calculate the neighbours properly. The BSP algorithm is a little slow to generate There currently is no tested code for actually looking up things in the BSP
commit sha 80894054f78e040159540d3169243b92390baac9
Remove unused function
commit sha 85e80d3528a9893dd0969a3d9983ac6642080e7a
Fix the hexpizzas neighbours so they print correctly
commit sha 7847dfd3210c60b5ea48759cde78db106d9447ff
Format the cmake file
commit sha 945195893a33070c896e16dcb42c1cdcad810205
Start working on the new method for cutting down bsp trees
commit sha 4aca4c8078bfecb5d70f0ae9acbb8313cf756aef
Add a mesh quailty binary that runs the mesh tester and add colours to compile output
commit sha 4daae9e9d9b7ddfa628fe6035aade8da3d8fe790
Fix the sorting of the mesh by the BSP
commit sha 08a1db49976b072c8cacf1e6da90ec1c1a4c07ce
Make the multi height mesh handle single mesh values better
commit sha 88949dc5eb6efc837a3a04a7c62f85c7e0f2d616
Make the ray vectors 3 dimensional rather than 4 to save memory. The vectors are still converted to 4D when uploading to OpenCL, but otherwise it's not needed
commit sha c2d7bd98c6d742a60338a967d79a5f25e4ff3c89
Add block select and matrix multiply to the math This is starting to get out of hand here! Perhaps we should move to Eigen
commit sha f2160e5bb35c4e4508029831ade69696a0084bdb
Add missing 3d projection code
commit sha 6a5c6f5c534c4b504c5e803f78c41f7bf4933c31
Add code for filtering down the BSP using cones
commit sha d0c85e54f6577ad63f0cdb0a067f10691548570f
Add the per item check for on screen
commit sha d0144c664399edbfbaa6c99bfbaf0195722d9a94
Move the cone math into its own file
commit sha 994e6ba0caacd9a01dec971156bb7a24f2b3072a
Remove unused include
push time in 22 days
push eventNUbots/NUbook
commit sha 76cd2e8057df872209ead028118b66506b4934c7
Rename support::configuration::NetworkConfiguration to network::NUClearNet (#68) Paired with https://github.com/NUbots/NUbots/pull/334
push time in a month
PR merged NUbots/NUbook
Paired with https://github.com/NUbots/NUbots/pull/334
pr closed time in a month
Pull request review commentNUbots/NUbots
+#include "QuinticWalk.h"++#include <fmt/format.h>++#include "extension/Configuration.h"++#include "message/behaviour/FixedWalkCommand.h"+#include "message/motion/GetupCommand.h"+#include "message/motion/KinematicsModel.h"+#include "message/motion/ServoTarget.h"+#include "message/motion/WalkCommand.h"+#include "message/support/SaveConfiguration.h"++#include "utility/math/comparison.h"+#include "utility/math/euler.h"+#include "utility/math/matrix/Transform3D.h"+#include "utility/motion/InverseKinematics.h"+#include "utility/support/eigen_armadillo.h"+#include "utility/support/yaml_expression.h"++namespace module {+namespace motion {++ using extension::Configuration;++ using message::behaviour::ServoCommand;+ using message::input::Sensors;+ using message::motion::DisableWalkEngineCommand;+ using message::motion::EnableWalkEngineCommand;+ using message::motion::ExecuteGetup;+ using message::motion::KillGetup;+ using message::motion::KinematicsModel;+ using message::motion::ServoTarget;+ using message::motion::StopCommand;+ using message::motion::WalkCommand;+ using utility::support::Expression;++ using utility::input::ServoID;+ using utility::math::matrix::Transform3D;+ using utility::motion::kinematics::calculateLegJoints;++ QuinticWalk::QuinticWalk(std::unique_ptr<NUClear::Environment> environment) : Reactor(std::move(environment)) {++ on<Configuration>("QuinticWalk.yaml").then([this](const Configuration& cfg) {+ // Use configuration here from file QuinticWalk.yaml+ params.freq = cfg["walk"]["freq"].as<float>();+ params.double_support_ratio = cfg["walk"]["double_support_ratio"].as<float>();+ params.first_step_swing_factor = cfg["walk"]["first_step_swing_factor"].as<float>();+ params.foot_distance = cfg["walk"]["foot"]["distance"].as<float>();+ params.foot_rise = cfg["walk"]["foot"]["rise"].as<float>();+ params.foot_z_pause = cfg["walk"]["foot"]["z_pause"].as<float>();+ params.foot_put_down_z_offset = cfg["walk"]["foot"]["put_down"]["z_offset"].as<float>();+ params.foot_put_down_phase = cfg["walk"]["foot"]["put_down"]["phase"].as<float>();+ params.foot_put_down_roll_offset = cfg["walk"]["foot"]["put_down"]["roll_offset"].as<float>();+ params.foot_apex_phase = cfg["walk"]["foot"]["apex_phase"].as<float>();+ params.foot_overshoot_ratio = cfg["walk"]["foot"]["overshoot"]["ratio"].as<float>();+ params.foot_overshoot_phase = cfg["walk"]["foot"]["overshoot"]["phase"].as<float>();+ params.trunk_height = cfg["walk"]["trunk"]["height"].as<float>();+ params.trunk_pitch = 1.0f + cfg["walk"]["trunk"]["pitch"].as<Expression>();+ params.trunk_phase = cfg["walk"]["trunk"]["phase"].as<float>();+ params.trunk_x_offset = cfg["walk"]["trunk"]["x_offset"].as<float>();+ params.trunk_y_offset = cfg["walk"]["trunk"]["y_offset"].as<float>();+ params.trunk_swing = cfg["walk"]["trunk"]["swing"].as<float>();+ params.trunk_pause = cfg["walk"]["trunk"]["pause"].as<float>();+ params.trunk_x_offset_p_coef_forward = cfg["walk"]["trunk"]["x_offset_p_coef"]["forward"].as<float>();+ params.trunk_x_offset_p_coef_turn = cfg["walk"]["trunk"]["x_offset_p_coef"]["turn"].as<float>();+ params.trunk_pitch_p_coef_forward = 1.0f + cfg["walk"]["trunk"]["pitch_p_coef"]["forward"].as<Expression>();+ params.trunk_pitch_p_coef_turn = 1.0f + cfg["walk"]["trunk"]["pitch_p_coef"]["turn"].as<Expression>();+ params.kick_length = cfg["walk"]["kick"]["length"].as<float>();+ params.kick_phase = cfg["walk"]["kick"]["phase"].as<float>();+ params.kick_vel = cfg["walk"]["kick"]["vel"].as<float>();+ params.pause_duration = cfg["walk"]["pause"]["duration"].as<float>();++ // Send these parameters to the walk engine+ walk_engine.setParameters(params);++ config.max_step[0] = cfg["max_step"]["x"].as<float>();+ config.max_step[1] = cfg["max_step"]["y"].as<float>();+ config.max_step[2] = cfg["max_step"]["z"].as<float>();+ config.max_step_xy = cfg["max_step"]["xy"].as<float>();++ config.imu_active = cfg["imu"]["active"].as<bool>();+ config.imu_pitch_threshold = cfg["imu"]["pitch"]["threshold"].as<float>();+ config.imu_roll_threshold = cfg["imu"]["roll"]["threshold"].as<float>();++ for (int i = 0; i < ServoID::NUMBER_OF_SERVOS; ++i) {+ if ((i >= 6) && (i < 18)) {+ jointGains[i] = cfg["gains"]["legs"].as<float>();+ }+ }+ });++ on<Startup, Trigger<KinematicsModel>>().then("Update Kinematics Model", [this](const KinematicsModel& model) {+ kinematicsModel = model;+ first_run = true;+ current_orders.setZero();+ is_left_support = true;+ falling = false;+ last_update_time = NUClear::clock::now();+ walk_engine.reset();+ });++ on<Trigger<ExecuteGetup>>().then([this]() { falling = true; });++ on<Trigger<KillGetup>>().then([this]() { falling = false; });++ on<Trigger<StopCommand>>().then([this] { current_orders.setZero(); });++ on<Trigger<WalkCommand>>().then([this](const WalkCommand& walkCommand) {+ // the engine expects orders in [m] not [m/s]. We have to compute by dividing by step frequency which is a+ // double step factor 2 since the order distance is only for a single step, not double step+ const float factor = (1.0 / (params.freq)) / 2.0;+ const Eigen::Vector3f& command = walkCommand.command.cast<float>() * factor;++ // Clamp velocity command+ Eigen::Vector3f orders =+ command.array().max(-config.max_step.array()).min(config.max_step.array()).matrix();++ // translational orders (x+y) should not exceed combined limit. scale if necessary+ if (config.max_step_xy != 0) {+ float scaling_factor = 1.0f / std::max(1.0f, (orders.x() + orders.y()) / config.max_step_xy);+ orders.cwiseProduct(Eigen::Vector3f(scaling_factor, scaling_factor, 1.0f));+ }++ // warn user that speed was limited+ if (command.x() != orders.x() || command.y() != orders.y() || command.z() != orders.z()) {+ log<NUClear::WARN>(+ fmt::format("Speed command was x: {} y: {} z: {} xy: {} but maximum is x: {} y: {} z: {} xy: {}",+ command.x(),+ command.y(),+ command.z(),+ command.x() + command.y(),+ config.max_step[0] / factor,+ config.max_step[1] / factor,+ config.max_step[2] / factor,+ config.max_step_xy / factor));+ }++ // Update orders+ current_orders = orders;+ });++ on<Trigger<Sensors>>().then([this](const Sensors& sensors) {+ if (config.imu_active) {+ Eigen::Vector3f RPY =+ utility::math::euler::MatrixToEulerIntrinsic(sensors.Htw.topLeftCorner<3, 3>().cast<float>());++ // compute the pitch offset to the currently wanted pitch of the engine+ float wanted_pitch =+ params.trunk_pitch + params.trunk_pitch_p_coef_forward * walk_engine.getFootstep().getNext().x()+ + params.trunk_pitch_p_coef_turn * std::abs(walk_engine.getFootstep().getNext().z());+ RPY.y() += wanted_pitch;++ // threshold pitch and roll+ if (std::abs(RPY[0]) > config.imu_roll_threshold) {+ log<NUClear::WARN>(fmt::format("Robot roll exceeds threshold - {} > {}",+ std::abs(RPY.x()),+ config.imu_roll_threshold));+ walk_engine.requestPause();+ }+ else if (std::abs(RPY[1]) > config.imu_pitch_threshold) {+ log<NUClear::WARN>(fmt::format("Robot pitch exceeds threshold - {} > {}",+ std::abs(RPY.y()),+ config.imu_pitch_threshold));+ walk_engine.requestPause();+ }+ }+ });++ on<Trigger<EnableWalkEngineCommand>>().then([this](const EnableWalkEngineCommand& command) {+ subsumptionId = command.subsumptionId;+ walk_engine.reset();+ update_handle.enable();+ });++ on<Trigger<DisableWalkEngineCommand>>().then([this] { update_handle.disable(); });++ update_handle = on<Every<UPDATE_FREQUENCY, Per<std::chrono::seconds>>, Single>().then([this]() {+ const float dt = getTimeDelta();++ if (falling) {+ // We are falling, reset walk engine+ walk_engine.reset();+ }+ else {+ // we don't want to walk, even if we have orders, if we are not in the right state+ bool walkableState = true; // TODO: what is our equivalent here?++ // see if the walk engine has new goals for us+ walkableState = walk_engine.updateState(dt, current_orders, walkableState);++ if ((walk_engine.getState() != engine::WalkEngineState::IDLE) && walkableState) { // todo+ calculateJointGoals();+ }+ }+ });+ } // namespace motion++ float QuinticWalk::getTimeDelta() {+ // compute time delta depended if we are currently in simulation or reality+ float dt;+ auto current_time = NUClear::clock::now();+ // only take real time difference if walking was not stopped before+ // TODO Is this really necessary?+ auto time_diff_ms = std::chrono::duration_cast<std::chrono::milliseconds>(current_time - last_update_time);+ dt = time_diff_ms.count() / 1000.0f;+ if (dt == 0.0f) {+ // log<NUClear::WARN>(fmt::format("dt was 0 ({})", time_diff_ms.count()));+ dt = 0.001f;+ }+ last_update_time = current_time;+ // time is wrong when we run it for the first time+ if (first_run) {+ first_run = false;+ dt = 0.0001f;+ }+ return dt;+ }++ void QuinticWalk::calculateJointGoals() {+ /*+ This method computes the next motor goals and publishes them.+ */+ auto setRPY = [&](const float& roll, const float& pitch, const float& yaw) {+ const float halfYaw = yaw * 0.5f;+ const float halfPitch = pitch * 0.5f;+ const float halfRoll = roll * 0.5f;+ const float cosYaw = std::cos(halfYaw);+ const float sinYaw = std::sin(halfYaw);+ const float cosPitch = std::cos(halfPitch);+ const float sinPitch = std::sin(halfPitch);+ const float cosRoll = std::cos(halfRoll);+ const float sinRoll = std::sin(halfRoll);+ return Eigen::Quaternionf(cosRoll * cosPitch * cosYaw + sinRoll * sinPitch * sinYaw, // formerly yzx+ sinRoll * cosPitch * cosYaw - cosRoll * sinPitch * sinYaw, // x+ cosRoll * sinPitch * cosYaw + sinRoll * cosPitch * sinYaw, // y+ cosRoll * cosPitch * sinYaw - sinRoll * sinPitch * cosYaw // z+ )+ .normalized()+ .toRotationMatrix();+ };++ // Read the cartesian positions and orientations for trunk and fly foot+ walk_engine.computeCartesianPosition(trunk_pos, trunk_axis, foot_pos, foot_axis, is_left_support);++ // Change goals from support foot based coordinate system to trunk based coordinate system+ Eigen::Affine3f Hst; // trunk_to_support_foot_goal
If it's only the 0,0,0,1 that's set then if you don't set the translation and linear it would have junk in it besides that final row. That could have been the problem that was occuring
comment created time in a month
Pull request review commentNUbots/NUbots
+#include "QuinticWalk.h"++#include <fmt/format.h>++#include "extension/Configuration.h"++#include "message/behaviour/FixedWalkCommand.h"+#include "message/motion/GetupCommand.h"+#include "message/motion/KinematicsModel.h"+#include "message/motion/ServoTarget.h"+#include "message/motion/WalkCommand.h"+#include "message/support/SaveConfiguration.h"++#include "utility/math/comparison.h"+#include "utility/math/euler.h"+#include "utility/math/matrix/Transform3D.h"+#include "utility/motion/InverseKinematics.h"+#include "utility/support/eigen_armadillo.h"+#include "utility/support/yaml_expression.h"++namespace module {+namespace motion {++ using extension::Configuration;++ using message::behaviour::ServoCommand;+ using message::input::Sensors;+ using message::motion::DisableWalkEngineCommand;+ using message::motion::EnableWalkEngineCommand;+ using message::motion::ExecuteGetup;+ using message::motion::KillGetup;+ using message::motion::KinematicsModel;+ using message::motion::ServoTarget;+ using message::motion::StopCommand;+ using message::motion::WalkCommand;+ using utility::support::Expression;++ using utility::input::ServoID;+ using utility::math::matrix::Transform3D;+ using utility::motion::kinematics::calculateLegJoints;++ QuinticWalk::QuinticWalk(std::unique_ptr<NUClear::Environment> environment) : Reactor(std::move(environment)) {++ on<Configuration>("QuinticWalk.yaml").then([this](const Configuration& cfg) {+ // Use configuration here from file QuinticWalk.yaml+ params.freq = cfg["walk"]["freq"].as<float>();+ params.double_support_ratio = cfg["walk"]["double_support_ratio"].as<float>();+ params.first_step_swing_factor = cfg["walk"]["first_step_swing_factor"].as<float>();+ params.foot_distance = cfg["walk"]["foot"]["distance"].as<float>();+ params.foot_rise = cfg["walk"]["foot"]["rise"].as<float>();+ params.foot_z_pause = cfg["walk"]["foot"]["z_pause"].as<float>();+ params.foot_put_down_z_offset = cfg["walk"]["foot"]["put_down"]["z_offset"].as<float>();+ params.foot_put_down_phase = cfg["walk"]["foot"]["put_down"]["phase"].as<float>();+ params.foot_put_down_roll_offset = cfg["walk"]["foot"]["put_down"]["roll_offset"].as<float>();+ params.foot_apex_phase = cfg["walk"]["foot"]["apex_phase"].as<float>();+ params.foot_overshoot_ratio = cfg["walk"]["foot"]["overshoot"]["ratio"].as<float>();+ params.foot_overshoot_phase = cfg["walk"]["foot"]["overshoot"]["phase"].as<float>();+ params.trunk_height = cfg["walk"]["trunk"]["height"].as<float>();+ params.trunk_pitch = 1.0f + cfg["walk"]["trunk"]["pitch"].as<Expression>();+ params.trunk_phase = cfg["walk"]["trunk"]["phase"].as<float>();+ params.trunk_x_offset = cfg["walk"]["trunk"]["x_offset"].as<float>();+ params.trunk_y_offset = cfg["walk"]["trunk"]["y_offset"].as<float>();+ params.trunk_swing = cfg["walk"]["trunk"]["swing"].as<float>();+ params.trunk_pause = cfg["walk"]["trunk"]["pause"].as<float>();+ params.trunk_x_offset_p_coef_forward = cfg["walk"]["trunk"]["x_offset_p_coef"]["forward"].as<float>();+ params.trunk_x_offset_p_coef_turn = cfg["walk"]["trunk"]["x_offset_p_coef"]["turn"].as<float>();+ params.trunk_pitch_p_coef_forward = 1.0f + cfg["walk"]["trunk"]["pitch_p_coef"]["forward"].as<Expression>();+ params.trunk_pitch_p_coef_turn = 1.0f + cfg["walk"]["trunk"]["pitch_p_coef"]["turn"].as<Expression>();+ params.kick_length = cfg["walk"]["kick"]["length"].as<float>();+ params.kick_phase = cfg["walk"]["kick"]["phase"].as<float>();+ params.kick_vel = cfg["walk"]["kick"]["vel"].as<float>();+ params.pause_duration = cfg["walk"]["pause"]["duration"].as<float>();++ // Send these parameters to the walk engine+ walk_engine.setParameters(params);++ config.max_step[0] = cfg["max_step"]["x"].as<float>();+ config.max_step[1] = cfg["max_step"]["y"].as<float>();+ config.max_step[2] = cfg["max_step"]["z"].as<float>();+ config.max_step_xy = cfg["max_step"]["xy"].as<float>();++ config.imu_active = cfg["imu"]["active"].as<bool>();+ config.imu_pitch_threshold = cfg["imu"]["pitch"]["threshold"].as<float>();+ config.imu_roll_threshold = cfg["imu"]["roll"]["threshold"].as<float>();++ for (int i = 0; i < ServoID::NUMBER_OF_SERVOS; ++i) {+ if ((i >= 6) && (i < 18)) {+ jointGains[i] = cfg["gains"]["legs"].as<float>();+ }+ }+ });++ on<Startup, Trigger<KinematicsModel>>().then("Update Kinematics Model", [this](const KinematicsModel& model) {+ kinematicsModel = model;+ first_run = true;+ current_orders.setZero();+ is_left_support = true;+ falling = false;+ last_update_time = NUClear::clock::now();+ walk_engine.reset();+ });++ on<Trigger<ExecuteGetup>>().then([this]() { falling = true; });++ on<Trigger<KillGetup>>().then([this]() { falling = false; });++ on<Trigger<StopCommand>>().then([this] { current_orders.setZero(); });++ on<Trigger<WalkCommand>>().then([this](const WalkCommand& walkCommand) {+ // the engine expects orders in [m] not [m/s]. We have to compute by dividing by step frequency which is a+ // double step factor 2 since the order distance is only for a single step, not double step+ const float factor = (1.0 / (params.freq)) / 2.0;+ const Eigen::Vector3f& command = walkCommand.command.cast<float>() * factor;++ // Clamp velocity command+ Eigen::Vector3f orders =+ command.array().max(-config.max_step.array()).min(config.max_step.array()).matrix();++ // translational orders (x+y) should not exceed combined limit. scale if necessary+ if (config.max_step_xy != 0) {+ float scaling_factor = 1.0f / std::max(1.0f, (orders.x() + orders.y()) / config.max_step_xy);+ orders.cwiseProduct(Eigen::Vector3f(scaling_factor, scaling_factor, 1.0f));+ }++ // warn user that speed was limited+ if (command.x() != orders.x() || command.y() != orders.y() || command.z() != orders.z()) {+ log<NUClear::WARN>(+ fmt::format("Speed command was x: {} y: {} z: {} xy: {} but maximum is x: {} y: {} z: {} xy: {}",+ command.x(),+ command.y(),+ command.z(),+ command.x() + command.y(),+ config.max_step[0] / factor,+ config.max_step[1] / factor,+ config.max_step[2] / factor,+ config.max_step_xy / factor));+ }++ // Update orders+ current_orders = orders;+ });++ on<Trigger<Sensors>>().then([this](const Sensors& sensors) {+ if (config.imu_active) {+ Eigen::Vector3f RPY =+ utility::math::euler::MatrixToEulerIntrinsic(sensors.Htw.topLeftCorner<3, 3>().cast<float>());++ // compute the pitch offset to the currently wanted pitch of the engine+ float wanted_pitch =+ params.trunk_pitch + params.trunk_pitch_p_coef_forward * walk_engine.getFootstep().getNext().x()+ + params.trunk_pitch_p_coef_turn * std::abs(walk_engine.getFootstep().getNext().z());+ RPY.y() += wanted_pitch;++ // threshold pitch and roll+ if (std::abs(RPY[0]) > config.imu_roll_threshold) {+ log<NUClear::WARN>(fmt::format("Robot roll exceeds threshold - {} > {}",+ std::abs(RPY.x()),+ config.imu_roll_threshold));+ walk_engine.requestPause();+ }+ else if (std::abs(RPY[1]) > config.imu_pitch_threshold) {+ log<NUClear::WARN>(fmt::format("Robot pitch exceeds threshold - {} > {}",+ std::abs(RPY.y()),+ config.imu_pitch_threshold));+ walk_engine.requestPause();+ }+ }+ });++ on<Trigger<EnableWalkEngineCommand>>().then([this](const EnableWalkEngineCommand& command) {+ subsumptionId = command.subsumptionId;+ walk_engine.reset();+ update_handle.enable();+ });++ on<Trigger<DisableWalkEngineCommand>>().then([this] { update_handle.disable(); });++ update_handle = on<Every<UPDATE_FREQUENCY, Per<std::chrono::seconds>>, Single>().then([this]() {+ const float dt = getTimeDelta();++ if (falling) {+ // We are falling, reset walk engine+ walk_engine.reset();+ }+ else {+ // we don't want to walk, even if we have orders, if we are not in the right state+ bool walkableState = true; // TODO: what is our equivalent here?++ // see if the walk engine has new goals for us+ walkableState = walk_engine.updateState(dt, current_orders, walkableState);++ if ((walk_engine.getState() != engine::WalkEngineState::IDLE) && walkableState) { // todo+ calculateJointGoals();+ }+ }+ });+ } // namespace motion++ float QuinticWalk::getTimeDelta() {+ // compute time delta depended if we are currently in simulation or reality+ float dt;+ auto current_time = NUClear::clock::now();+ // only take real time difference if walking was not stopped before+ // TODO Is this really necessary?+ auto time_diff_ms = std::chrono::duration_cast<std::chrono::milliseconds>(current_time - last_update_time);+ dt = time_diff_ms.count() / 1000.0f;+ if (dt == 0.0f) {+ // log<NUClear::WARN>(fmt::format("dt was 0 ({})", time_diff_ms.count()));+ dt = 0.001f;+ }+ last_update_time = current_time;+ // time is wrong when we run it for the first time+ if (first_run) {+ first_run = false;+ dt = 0.0001f;+ }+ return dt;+ }++ void QuinticWalk::calculateJointGoals() {+ /*+ This method computes the next motor goals and publishes them.+ */+ auto setRPY = [&](const float& roll, const float& pitch, const float& yaw) {+ const float halfYaw = yaw * 0.5f;+ const float halfPitch = pitch * 0.5f;+ const float halfRoll = roll * 0.5f;+ const float cosYaw = std::cos(halfYaw);+ const float sinYaw = std::sin(halfYaw);+ const float cosPitch = std::cos(halfPitch);+ const float sinPitch = std::sin(halfPitch);+ const float cosRoll = std::cos(halfRoll);+ const float sinRoll = std::sin(halfRoll);+ return Eigen::Quaternionf(cosRoll * cosPitch * cosYaw + sinRoll * sinPitch * sinYaw, // formerly yzx+ sinRoll * cosPitch * cosYaw - cosRoll * sinPitch * sinYaw, // x+ cosRoll * sinPitch * cosYaw + sinRoll * cosPitch * sinYaw, // y+ cosRoll * cosPitch * sinYaw - sinRoll * sinPitch * cosYaw // z+ )+ .normalized()+ .toRotationMatrix();+ };++ // Read the cartesian positions and orientations for trunk and fly foot+ walk_engine.computeCartesianPosition(trunk_pos, trunk_axis, foot_pos, foot_axis, is_left_support);++ // Change goals from support foot based coordinate system to trunk based coordinate system+ Eigen::Affine3f Hst; // trunk_to_support_foot_goal
I believe that setting the translation sets the right-hand column, and linear is the upper 3x3, but this might have something to do with the 0,0,0,1 at the bottom of the matrix? However, I think that the default constructor of Affine3f is already identity but you would have to confirm.
comment created time in a month
push eventNUbots/NUbots
commit sha 0d2cc7603899e04174b62e321840898d7c4fedec
Add an image decompressor to allow playback of nbs files with compressed images (#335) Co-authored-by: Alex Biddulph <[email protected]>
push time in a month
Pull request review commentNUbots/NUbots
Add an image decompressor to allow playback of nbs files with compressed images
+#ifndef MODULE_INPUT_IMAGEDECOMPRESSOR_H+#define MODULE_INPUT_IMAGEDECOMPRESSOR_H++#include <atomic>+#include <cstdint>+#include <memory>+#include <mutex>+#include <nuclear>++#include "decompressor/DecompressorFactory.h"++namespace module {+namespace input {++ class ImageDecompressor : public NUClear::Reactor {+ private:+ struct DecompressorContext {+ struct Decompressor {+ /// This atomic bool gets turned on/off depending on if it is in use+ std::unique_ptr<std::atomic<bool>> active;
It does have to be a unique pointer due to the way atomics work
comment created time in a month
push eventNUbots/NUbots
commit sha 358be09c57dcd6ac55f4d64fb02fac8b41a3ae99
Update module/input/ImageDecompressor/src/ImageDecompressor.cpp Co-authored-by: Alex Biddulph <[email protected]>
push time in a month
Pull request review commentNUbots/NUbots
Added comments to motion messages
message KinematicsModel { mat3 foot = 9; } - Leg leg = 1;- Head head = 2;- Arm arm = 3;- MassModel massModel = 4;- TensorModel tensorModel = 5;- float TEAMDARWINCHEST_TO_ORIGIN = 6;+ // Instantiations of the config containers.+ Leg leg = 1;+ Head head = 2;+ Arm arm = 3;+ MassModel massModel = 4;+ TensorModel tensorModel = 5;++ float TEAMDARWINCHEST_TO_ORIGIN = 6;
Doubt it would cause problems anymore
comment created time in a month
push eventNUbots/NUbots
commit sha a965365242a1a1f4ca5f84a4fa864c8cfdac21e6
Move the NetworkConfiguration module to NUClearNet (#334)
push time in a month
PR merged NUbots/NUbots
Currently, the name NetworkConfiguration is a little generic given that soon we may have other systems that use the network. Moving this module makes it clearer what it does.
pr closed time in a month
push eventNUbots/NUbots
commit sha b592451ba353f042445411d477989df268c3156a
Update README.md
push time in a month
PR opened NUbots/NUbook
Paired with https://github.com/NUbots/NUbots/pull/334
pr created time in a month
push eventNUbots/NUbots
commit sha 89638a22d1998432c1044030cff70658e220a7b1
Another one
push time in a month
push eventNUbots/NUbots
commit sha b2637de794cdd49047e983f1154ef11c8df0ee27
Missed one
push time in a month
push eventNUbots/NUbots
commit sha b3716b38713881acc7cd777c8e9ffc32b3c72cdb
Remove gradient from the cone as as it's duplicate information (#332)
commit sha f49ad6c27fbe8afa7cb11b54b7d8c8a3dda5d447
Update some Visual Studio Code settings (#333) Co-authored-by: Alex Biddulph <[email protected]>
commit sha d660d5a1c59bc514b93e894bd8daeefbcf701725
Make the Dockerfile build as root and do the user creation at the end (#331) Co-authored-by: Alex Biddulph <[email protected]>
commit sha 9c20d46bbad3d8ae7e4348822bf049367ebdbbbc
Remove id from servos (#328)
commit sha 5d752babefe08bb3e7cbee2e2f542e2a54483cce
Make DataPlayback cleaner and better able to handle multiple files (#323) Co-authored-by: Alex Biddulph <[email protected]>
commit sha ba84375f81528bbdcd4055021e8ff41bea98807c
Merge branch 'master' into houliston/renamenet
push time in a month
PR opened NUbots/NUbots
Currently, the name NetworkConfiguration is a little generic given that soon we may have other systems that use the network. Moving this module makes it clearer what it does.
pr created time in a month
push eventNUbots/NUbots
commit sha 5d752babefe08bb3e7cbee2e2f542e2a54483cce
Make DataPlayback cleaner and better able to handle multiple files (#323) Co-authored-by: Alex Biddulph <[email protected]>
push time in a month
PR merged NUbots/NUbots
This also makes the configuration no longer generated so it's easier to work with these files in docker
Pairs with NUbook PR
pr closed time in a month
Pull request review commentNUbots/NUbook
Add information on the DataPlayback module
+---+section: System+chapter: Subsystems+title: Logging+description: Logging and how it works in the NUbots codebase.+slug: /system/subsystems/logging+---++# Data Playback++The DataPlayback module will allow you to play back `.nbs` files as if they were happening in realtime. This can be very useful when you are trying to diagnose a problem for which you have a recording since you can continuously play back the same data.++The module will buffer up packets from the future and emit them at the same rate that they were emitted originally.++## Usage++Add `support::logging::DataPlayback` to your role file and comment out the roles that you want the `nbs` files to replace. For example, if you are playing back `message.input.Sensors` you need to comment out `platform::darwin::SensorFilter` from your role otherwise you will have two different modules emitting conflicting `Sensors` messages. You must recompile your role for these changes to take effect.++In order to play back the messages you need to enable the message types that are the input to the modules that you wish to test by setting them to true in the configuration (e.g. `message.input.sensors: true`). Make sure the `.nbs` file contains the messages you are trying to emit (you can check with `./b nbs stats <nbs-file>`).
In order to play back the messages you need to enable the message types that are the input to the modules that you wish to test by setting them to true in the configuration (e.g. `message.input.Sensors: true`). Make sure the `.nbs` file contains the messages you are trying to emit (you can check with `./b nbs stats <nbs-file>`).
comment created time in a month
Pull request review commentNUbots/NUbook
Add information on the DataPlayback module
+---+section: System+chapter: Subsystems+title: Logging+description: Logging and how it works in the NUbots codebase.+slug: /system/subsystems/logging+---++# Data Playback++The DataPlayback module will allow you to play back `.nbs` files as if they were happening in realtime. This can be very useful when you are trying to diagnose a problem for which you have a recording since you can continuously play back the same data.++The module will buffer up packets from the future and emit them at the same rate that they were emitted originally.++## Usage++Add `support::logging::DataPlayback` to your role file and comment out the roles that you want the `nbs` files to replace. For example, if you are playing back `message.input.Sensors` you need to comment out `platform::darwin::SensorFilter` from your role otherwise you will have two different modules emitting conflicting `Sensors` messages. You must recompile your role for these changes to take effect.++In order to play back the messages you need to enable the message types that are the input to the modules that you wish to test by setting them to true in the configuration (e.g. `message.input.sensors: true`). Make sure the `.nbs` file contains the messages you are trying to emit (you can check with `./b nbs stats <nbs-file>`).++`nbs` files can be provided to the module via the configuration file or via the command line. If the configuration file has arguments those are used first. If there are no files provided in the configuration file, the arguments from the command line are used instead. This is so that if you have another module in your role that relies on command line arguments you have an alternate way to provide these files.
`nbs` files can be provided to the module via the configuration file or via the command line. If the configuration file has arguments those are used. If there are no files provided in the configuration file, the arguments from the command line are used instead. This is so that if you have another module in your role that relies on command line arguments you have an alternate way to provide these files.
comment created time in a month
push eventNUbots/NUbots
commit sha 907ba5870891bb6c7aca89b3a4037ef4b33ac370
Add a tool for filtering out message types from nbs files (#329) Co-authored-by: Alex Biddulph <[email protected]>
commit sha b3716b38713881acc7cd777c8e9ffc32b3c72cdb
Remove gradient from the cone as as it's duplicate information (#332)
commit sha f49ad6c27fbe8afa7cb11b54b7d8c8a3dda5d447
Update some Visual Studio Code settings (#333) Co-authored-by: Alex Biddulph <[email protected]>
commit sha d660d5a1c59bc514b93e894bd8daeefbcf701725
Make the Dockerfile build as root and do the user creation at the end (#331) Co-authored-by: Alex Biddulph <[email protected]>
commit sha 9c20d46bbad3d8ae7e4348822bf049367ebdbbbc
Remove id from servos (#328)
commit sha 3c372273d61de8c115275a81f2b33fc75aa5f742
Merge branch 'master' into houliston/cleanerplayback
push time in a month
push eventNUbots/NUbots
commit sha 9c20d46bbad3d8ae7e4348822bf049367ebdbbbc
Remove id from servos (#328)
push time in a month
PR merged NUbots/NUbots
As the servos list is always looked up by id anyway. If this field contained anything other than it's index it would cause bugs. Therefore it shouldn't exist in the first place
pr closed time in a month
push eventNUbots/NUbots
commit sha f6d0c492962e66a3fc966580fbd4f3ac368a4989
Fix Docker mount error in WSL 2 (#330)
commit sha 907ba5870891bb6c7aca89b3a4037ef4b33ac370
Add a tool for filtering out message types from nbs files (#329) Co-authored-by: Alex Biddulph <[email protected]>
commit sha b3716b38713881acc7cd777c8e9ffc32b3c72cdb
Remove gradient from the cone as as it's duplicate information (#332)
commit sha f49ad6c27fbe8afa7cb11b54b7d8c8a3dda5d447
Update some Visual Studio Code settings (#333) Co-authored-by: Alex Biddulph <[email protected]>
commit sha d660d5a1c59bc514b93e894bd8daeefbcf701725
Make the Dockerfile build as root and do the user creation at the end (#331) Co-authored-by: Alex Biddulph <[email protected]>
commit sha 40dda4915d2e44c2c2dddca502cfdc58fe467a01
Merge branch 'master' into TrentHouliston-patch-1
push time in a month
push eventNUbots/NUbots
commit sha d660d5a1c59bc514b93e894bd8daeefbcf701725
Make the Dockerfile build as root and do the user creation at the end (#331) Co-authored-by: Alex Biddulph <[email protected]>
push time in a month
PR merged NUbots/NUbots
This means that cases where the $USERID who is using the dockerfile is not 1000 can build a docker image with their own ID so you don't get conflicts
pr closed time in a month
push eventNUbots/NUbots
commit sha b0189a2047c73d5459ab0efc3299141ac2c8edad
Apply suggestions from code review Co-authored-by: Alex Biddulph <[email protected]>
push time in a month
Pull request review commentNUbots/NUbots
Make the Dockerfile build as root and do the user creation at the end
function(ToolchainLibraryFinder) include(FindPackageHandleStandardArgs) find_package_handle_standard_args( ${PACKAGE_NAME}- FOUND_VAR- ${PACKAGE_NAME}_FOUND- REQUIRED_VARS- ${required_vars}- VERSION_VAR- ${PACKAGE_NAME}_VERSION- # VERSION_VAR "${MAJOR}.${MINOR}.${PATCH}")+ FOUND_VAR ${PACKAGE_NAME}_FOUND+ REQUIRED_VARS ${required_vars}+ VERSION_VAR ${PACKAGE_NAME}_VERSION # VERSION_VAR "${MAJOR}.${MINOR}.${PATCH}")
Because it's what cmake_format demands
comment created time in a month
push eventNUbots/NUbots
commit sha 0067f450a0637e31504581a85db48537285d21ba
Replace loop with lookup
push time in a month
pull request commentNUbots/NUbots
The problem is if we use both methods it just gets confusing. 99% of the time we use the array using the enum. The other time in HeadBehaviour we do an entire loop through every servo just to try and find one we are looking for.
comment created time in a month
push eventNUbots/NUbots
commit sha b3716b38713881acc7cd777c8e9ffc32b3c72cdb
Remove gradient from the cone as as it's duplicate information (#332)
commit sha f49ad6c27fbe8afa7cb11b54b7d8c8a3dda5d447
Update some Visual Studio Code settings (#333) Co-authored-by: Alex Biddulph <[email protected]>
commit sha dc0b80f0a44536c581f144e60c793a60a414149b
Merge branch 'master' into houliston/rootdocker
push time in a month
push eventNUbots/NUbots
commit sha f49ad6c27fbe8afa7cb11b54b7d8c8a3dda5d447
Update some Visual Studio Code settings (#333) Co-authored-by: Alex Biddulph <[email protected]>
push time in a month
PR merged NUbots/NUbots
Make the spell checker use non-american spelling and add some settings to make the editor better display completion/errors
pr closed time in a month
push eventNUbots/NUbots
commit sha 8d94cd44e86cfac6e5969497bac57ab664554c6c
Update .devcontainer.json Co-authored-by: Alex Biddulph <[email protected]>
push time in a month
Pull request review commentNUbots/NUbots
Update some Visual Studio Code settings
"image": "nubots:generic", "workspaceMount": "type=bind,consistency=delegated,src=${localWorkspaceFolder},dst=/home/nubots/NUbots", "workspaceFolder": "/home/nubots/NUbots",- "runArgs": [- "--mount",- "type=volume,src=nubots_generic_build,dst=/home/nubots/build,consistency=delegated"+ "mounts": [+ "type=volume,src=horus_generic_build,dst=/home/fourtel/build,consistency=delegated"
Oops
comment created time in a month
push eventNUbots/NUbots
commit sha b3716b38713881acc7cd777c8e9ffc32b3c72cdb
Remove gradient from the cone as as it's duplicate information (#332)
commit sha df11330f3664dc3cff3d4f01a7dfa22500a9cc57
Merge branch 'master' into houliston/vscodesettings
push time in a month
push eventNUbots/NUbots
commit sha b3716b38713881acc7cd777c8e9ffc32b3c72cdb
Remove gradient from the cone as as it's duplicate information (#332)
push time in a month
PR merged NUbots/NUbots
Radius and gradient are the same number and having both is terrible
pr closed time in a month
push eventNUbots/NUbots
commit sha a477022a4b05592199ab92c53f70b2513a3fe972
Update module/support/logging/DataPlayback/README.md Co-authored-by: Alex Biddulph <[email protected]>
push time in a month
Pull request review commentNUbots/NUbots
Make DataPlayback cleaner and better able to handle multiple files
"files.trimFinalNewlines": true, "files.trimTrailingWhitespace": true, "files.eol": "\n",+ "cSpell.language": "en-GB", "editor.rulers": [ 120 ],+ "protoc": {+ "options": [+ "-I${workspaceFolder}/nuclear/message/proto"+ ]+ }, "python.formatting.provider": "black",+ "yaml.format.printWidth": 120,
Those are so that the compilation/errors for protobuf works properly in vscode. To do that they need the include path
comment created time in a month
Pull request review commentNUbots/NUbots
Remove gradient from the cone as as it's duplicate information
namespace vision { } // Ball cam space info- b.cone.axis = horizon.Hcw.topLeftCorner<3, 3>().cast<float>() * axis;- float proj = 1.0f / radius;- b.cone.gradient = std::sqrt(proj * proj - 1.0f);- b.cone.radius = radius;+ b.cone.axis = horizon.Hcw.topLeftCorner<3, 3>().cast<float>() * axis;+ float proj = 1.0f / radius;
Removed
comment created time in a month
push eventNUbots/NUbots
commit sha bf8bb1effaf94bf947339f2288dc38f703c3b789
proj is now unused variable
push time in a month
push eventNUbots/NUbots
commit sha 1fdaf8c312b2c8aa7a24afdac55b93acad490a64
Remove vestigial configuration
commit sha 7e98174b036825d66de500c9f803b920acb6ca2b
Make the symlink be a dependent of the module so it doesn't constantly rebuild
push time in a month
Pull request review commentNUbots/NUbots
Remove gradient from the cone as as it's duplicate information
message Frustum { } message Cone {- fvec3 axis = 1;- float gradient = 2;- float radius = 3;+ fvec3 axis = 1;+ // Cos(theta) of the cone angle+ float radius = 3;
Would mean old NBS files won't play properly... Do we have any we care about?
comment created time in a month
push eventNUbots/NUbots
commit sha e215ef3fb4061aea75c3e3ff1e2eea87ca339f5d
Formatting
push time in a month
pull request commentNUbots/NUbook
added walk through of foot and shoulder servo
The flow of this page is bugging me :(
I think we need to break it up into a number of separate sections, each section dealing with one specific thing
- How to repair a servo
- How to remove the NUC
- How to install the NUC
- How to detach the arm from the shoulder
- How to detach the shoulder from the torso
- How to attach the arm to the shoulder
- How to attach the shoulder to the torso
- etc
Then the section on repairing the shoulder pitch servo can just be
- Detach arm from shoulder
- Detach shoulder from torso
- Remove NUC
- Remove shoulder pitch servo
- Repair/replace shoulder pitch servo
- Install shoulder pitch servo
- Install NUC
- Attach shoulder to torso
- Attach arm to shoulder
- Test
And you can then rely on the other sections to provide the details on each of the steps listed in the section on repairing the shoulder pitch servo, and repairing other servos can then follow the same format
Thoughts @tayloryoung6396 @JosephusPaye @ysims @TrentHouliston ?
I think that the best way to go about this would be to copy what sites like ifixit do with their teardown/rebuild instructions. Those are super useful and really easy to follow. Is there a way we can make it behave like that?
Basically you have a bunch of steps that you need to take to do a specific task and present them in order.
comment created time in a month
Pull request review commentNUbots/NUbook
added walk through of foot and shoulder servo
title: Working with Robots description: How to safely handle and work with robots. slug: /guides/hardware/working-with-robots ---++## Aligning an Ankle++An alignment of a ankle may need to be done if the foot and the leg of the robot aren't perpendicular when standing. +An ankle alignment may also be necessary when the foot has come loose. ++To align an ankle correctly perform the following instructions:++1. On the inner part of the robots leg, unscrew the screws holding the plastic casing to the the ankle and knee servo. +Remove the plastic casing, as seen in the following image:+++2. There should be a silver disk, the following photo is a close up:++There are three small indents on the silver disk, two on one side and one on the other. The single dot should be pointing towards the ground when the robot is standing and the other two dots towards the head.+These three dots form an arrow, this arrow should be pointing downwards when the robot is standing.++3. Manually move the disk on the servo so it is in the correct orientation ++4. Place the plastic cover back on and replace all the screws. When replacing the screws, one hole must be left without a screw to allow for the servo to move. The hole left without a screw is the largest hole, which can be seen on the right of the following image:
It might not be like this anymore, but I went the arrow supposed to point away from the hard stop on the servo? So that when it rotates it will always rotate away from the hard stop?
comment created time in 2 months
push eventNUbots/NUbots
commit sha 4aba61e334d75d0aead03f9106e201004f57bfb7
Update Geometry.proto
push time in 2 months
push eventNUbots/NUbots
commit sha 90330a7abcf3c4bce7994234f7676721dec1c18d
Remove from virtual ball
push time in 2 months
push eventNUbots/NUbots
commit sha 7ce67ce1117c47077236edb80c110e0cfb4b0fcd
Remove from ball detector
push time in 2 months
PR opened NUbots/NUbots
Radius and gradient are the same number and having both is terrible
pr created time in 2 months
Pull request review commentNUbots/dashboard
Implement UDP networking layer
import minimist from 'minimist'; import favicon from 'serve-favicon'; import sio from 'socket.io'; -import { WebSocketProxyUDPServer } from './network/network';-import { WebSocketServer } from './network/web_socket_server';+import { UDPServers } from './network/network'; +// Process command line arguments const args = minimist(process.argv.slice(2));-const teamAAddress = args.team_a || '239.226.152.162';-const teamBAddress = args.team_b || '239.226.152.163';++// Get all of the teams from the command line+// Fall back to our defaults if none were specified+let teams = args._;+if (teams.length === 0) {+ teams = ['Red Team:239.226.152.162:9917', 'Blue Team:239.226.152.163:9917'];+}++// Convert command line data into a more usable format+const teamData = teams.map((team: string) => {+ const nameSplit = team.indexOf(':');
But you would no longer have the confusion between the name, IP and port. Because I imagine you could in theory have colons in the team name too
comment created time in 2 months
Pull request review commentNUbots/dashboard
Implement UDP networking layer
import minimist from 'minimist'; import favicon from 'serve-favicon'; import sio from 'socket.io'; -import { WebSocketProxyUDPServer } from './network/network';-import { WebSocketServer } from './network/web_socket_server';+import { UDPServers } from './network/network'; +// Process command line arguments const args = minimist(process.argv.slice(2));-const teamAAddress = args.team_a || '239.226.152.162';-const teamBAddress = args.team_b || '239.226.152.163';++// Get all of the teams from the command line+// Fall back to our defaults if none were specified+let teams = args._;+if (teams.length === 0) {+ teams = ['Red Team:239.226.152.162:9917', 'Blue Team:239.226.152.163:9917'];+}++// Convert command line data into a more usable format+const teamData = teams.map((team: string) => {+ const nameSplit = team.indexOf(':');
That's what quotes are for
comment created time in 2 months
Pull request review commentNUbots/dashboard
Implement UDP networking layer
import minimist from 'minimist'; import favicon from 'serve-favicon'; import sio from 'socket.io'; -import { WebSocketProxyUDPServer } from './network/network';-import { WebSocketServer } from './network/web_socket_server';+import { UDPServers } from './network/network'; +// Process command line arguments const args = minimist(process.argv.slice(2));-const teamAAddress = args.team_a || '239.226.152.162';-const teamBAddress = args.team_b || '239.226.152.163';++// Get all of the teams from the command line+// Fall back to our defaults if none were specified+let teams = args._;+if (teams.length === 0) {+ teams = ['Red Team:239.226.152.162:9917', 'Blue Team:239.226.152.163:9917'];+}++// Convert command line data into a more usable format+const teamData = teams.map((team: string) => {+ const nameSplit = team.indexOf(':');
The other option is we let the shell separate the tokens and use spaces, every 3 values is a name, IP, port
comment created time in 2 months
push eventNUbots/NUbots
commit sha 0cb4c9fcdc55388bb033df131a9156e5e20a2954
cmake-format
push time in 2 months
Pull request review commentNUbots/dashboard
Implement UDP networking layer
import compression from 'compression'; import history from 'connect-history-api-fallback'; import express from 'express'; import http from 'http';+import minimist from 'minimist'; import favicon from 'serve-favicon';+import sio from 'socket.io';++import { WebSocketProxyUDPServer } from './network/network';+import { WebSocketServer } from './network/web_socket_server';++const args = minimist(process.argv.slice(2));+const teamAAddress = args.team_a || '239.226.152.162';+const teamBAddress = args.team_b || '239.226.152.163'; const app = express(); const server = http.createServer(app);+const sioNetwork = sio(server);++// Initialize socket.io namespace immediately to catch reconnections.+WebSocketProxyUDPServer.of(WebSocketServer.of(sioNetwork.of('/dashboard')), {
You fixed dev, but prod is still the old style
comment created time in 2 months
Pull request review commentNUbots/dashboard
Implement UDP networking layer
export class UDPServer { return () => this.clients.delete(client); } - // Set up callbacks for a given team and then bind the team to its multicast address and port- private initialiseListener = (listener: Listener) => {- listener.server.on('listening', () => {+ // One of the UDP servers received a new message, send it on to all clients+ private onMessage = (+ team: TeamData,+ packet: Buffer,+ rinfo: dgram.RemoteInfo,+ ) => {+ for (let emit_cb of this.clients.values()) {+ emit_cb('udp_packet', {+ payload: packet,+ team: {+ name: team.name,+ address: team.address,+ port: team.port,+ },+ rinfo: {+ family: rinfo.family,+ address: rinfo.address,+ port: rinfo.port,+ },+ });+ }+ };+}++class UDPServer {+ private socket: dgram.Socket;++ constructor(+ private team: TeamData,+ private msg_cb: MessageCallback,+ ipv6: boolean,+ ) {+ if (ipv6) {+ this.socket = dgram.createSocket({ type: 'udp6', reuseAddr: true });+ } else {+ this.socket = dgram.createSocket({ type: 'udp4', reuseAddr: true });+ }++ this.socket.on('listening', () => { // Add multicast membership if we are using a multicast address- if (listener.ipv6) {+ if (ipv6) {
This seems like adding state to the IP just to make this if statement easier. I mean you could literally just do team.address.includes(":")
comment created time in 2 months
Pull request review commentNUbots/dashboard
Implement UDP networking layer
export class UDPServer { return () => this.clients.delete(client); } - // Set up callbacks for a given team and then bind the team to its multicast address and port- private initialiseListener = (listener: Listener) => {- listener.server.on('listening', () => {+ // One of the UDP servers received a new message, send it on to all clients+ private onMessage = (+ team: TeamData,+ packet: Buffer,+ rinfo: dgram.RemoteInfo,+ ) => {+ for (let emit_cb of this.clients.values()) {+ emit_cb('udp_packet', {+ payload: packet,+ team: {+ name: team.name,+ address: team.address,+ port: team.port,+ },+ rinfo: {+ family: rinfo.family,+ address: rinfo.address,+ port: rinfo.port,+ },+ });+ }+ };+}++class UDPServer {+ private socket: dgram.Socket;++ constructor(+ private team: TeamData,+ private msg_cb: MessageCallback,+ ipv6: boolean,+ ) {+ if (ipv6) {+ this.socket = dgram.createSocket({ type: 'udp6', reuseAddr: true });+ } else {+ this.socket = dgram.createSocket({ type: 'udp4', reuseAddr: true });+ }++ this.socket.on('listening', () => { // Add multicast membership if we are using a multicast address- if (listener.ipv6) {+ if (ipv6) { // IPv6 multicast addresses start with 0xFF00- if (listener.team.address.split(':')[0].toUpperCase() === 'FF00') {- listener.server.addMembership(listener.team.address);+ if (team.address.split(':')[0].toUpperCase() === 'FF00') {+ this.socket.addMembership(team.address); } } else { // IPv4 multicast addresses start with 0xE- const octet = Number(listener.team.address.split('.')[0])+ const octet = Number(team.address.split('.')[0]) .toString(16) .toUpperCase(); if (octet.startsWith('E')) {
Why toUpperCase when you can just check it starts with 'e' instead
comment created time in 2 months
Pull request review commentNUbots/dashboard
Implement UDP networking layer
export class UDPServer { return () => this.clients.delete(client); } - // Set up callbacks for a given team and then bind the team to its multicast address and port- private initialiseListener = (listener: Listener) => {- listener.server.on('listening', () => {+ // One of the UDP servers received a new message, send it on to all clients+ private onMessage = (+ team: TeamData,+ packet: Buffer,+ rinfo: dgram.RemoteInfo,+ ) => {+ for (let emit_cb of this.clients.values()) {+ emit_cb('udp_packet', {+ payload: packet,+ team: {+ name: team.name,+ address: team.address,+ port: team.port,+ },+ rinfo: {+ family: rinfo.family,+ address: rinfo.address,+ port: rinfo.port,+ },+ });+ }+ };+}++class UDPServer {+ private socket: dgram.Socket;++ constructor(+ private team: TeamData,+ private msg_cb: MessageCallback,+ ipv6: boolean,+ ) {+ if (ipv6) {+ this.socket = dgram.createSocket({ type: 'udp6', reuseAddr: true });+ } else {+ this.socket = dgram.createSocket({ type: 'udp4', reuseAddr: true });+ }++ this.socket.on('listening', () => { // Add multicast membership if we are using a multicast address- if (listener.ipv6) {+ if (ipv6) { // IPv6 multicast addresses start with 0xFF00- if (listener.team.address.split(':')[0].toUpperCase() === 'FF00') {- listener.server.addMembership(listener.team.address);+ if (team.address.split(':')[0].toUpperCase() === 'FF00') {
This isn't a valid way to check, IPv6 addresses might look like FF:00::1, or even FF::1 is a valid multicast address
comment created time in 2 months
Pull request review commentNUbots/dashboard
Implement UDP networking layer
import dgram = require('dgram'); import dns = require('dns'); export type ClientListener = (event: string, ...args: any[]) => void;+export type MessageCallback = (+ team: TeamData,+ payload: Buffer,+ rinfo: dgram.RemoteInfo,+) => void; export type TeamData = { name: string; address: string; port: number; }; -type Listener = {- server: dgram.Socket;- ipv6: boolean;- team: TeamData;-};--export class UDPServer {- private listeners: Array<Listener>;+export class UDPServers {+ private servers: Array<UDPServer>; private clients: Map<string, ClientListener>; constructor(teams: Array<TeamData>) { this.clients = new Map<string, ClientListener>();- this.listeners = new Array<Listener>();+ this.servers = new Array<UDPServer>(); teams.forEach((team: TeamData) => {- // Determine if we are listening on a IPv4 or IPv6 address and resolve any DNS names+ // Determine if we are listening on a IPv4 or IPv6 address and resolve any DNS address+ // If the specified address can be resolved to an IPv4 address prefer that over an IPv6 address dns.lookup( team.address,- { family: 0, all: false },+ { family: 0, all: true }, ( error: NodeJS.ErrnoException | null,- address: string,- family: number,+ addresses: dns.LookupAddress[], ) => {- if (family === 6) {- const listener = {- server: dgram.createSocket({ type: 'udp6', reuseAddr: true }),- ipv6: true,- team: {- name: team.name,- address: address,- port: team.port,- },- };- this.initialiseListener(listener);- this.listeners.push(listener);- } else if (family === 4) {- const listener = {- server: dgram.createSocket({ type: 'udp4', reuseAddr: true }),- ipv6: false,- team: {- name: team.name,- address: address,- port: team.port,- },- };- this.initialiseListener(listener);- this.listeners.push(listener);+ // Find the first IPv4 and IPv6 addresses that were returned+ let ipv4 = '';+ let ipv6 = '';+ addresses.forEach((address: dns.LookupAddress) => {+ if (address.family === 6 && ipv6 === '') {+ ipv6 = address.address;+ }+ if (address.family === 4 && ipv4 === '') {+ ipv4 = address.address;+ }+ });
let ipv4 = addresses.filter(v => v.family === 4);
let ipv6 = addresses.filter(v => v.family === 6);
comment created time in 2 months
Pull request review commentNUbots/dashboard
Implement UDP networking layer
import dgram = require('dgram'); import dns = require('dns'); export type ClientListener = (event: string, ...args: any[]) => void;+export type MessageCallback = (+ team: TeamData,+ payload: Buffer,+ rinfo: dgram.RemoteInfo,+) => void; export type TeamData = { name: string; address: string; port: number; }; -type Listener = {- server: dgram.Socket;- ipv6: boolean;- team: TeamData;-};--export class UDPServer {- private listeners: Array<Listener>;+export class UDPServers {+ private servers: Array<UDPServer>; private clients: Map<string, ClientListener>; constructor(teams: Array<TeamData>) { this.clients = new Map<string, ClientListener>();- this.listeners = new Array<Listener>();+ this.servers = new Array<UDPServer>(); teams.forEach((team: TeamData) => {- // Determine if we are listening on a IPv4 or IPv6 address and resolve any DNS names+ // Determine if we are listening on a IPv4 or IPv6 address and resolve any DNS address+ // If the specified address can be resolved to an IPv4 address prefer that over an IPv6 address dns.lookup( team.address,- { family: 0, all: false },+ { family: 0, all: true }, ( error: NodeJS.ErrnoException | null,- address: string,- family: number,+ addresses: dns.LookupAddress[], ) => {- if (family === 6) {- const listener = {- server: dgram.createSocket({ type: 'udp6', reuseAddr: true }),- ipv6: true,- team: {- name: team.name,- address: address,- port: team.port,- },- };- this.initialiseListener(listener);- this.listeners.push(listener);- } else if (family === 4) {- const listener = {- server: dgram.createSocket({ type: 'udp4', reuseAddr: true }),- ipv6: false,- team: {- name: team.name,- address: address,- port: team.port,- },- };- this.initialiseListener(listener);- this.listeners.push(listener);+ // Find the first IPv4 and IPv6 addresses that were returned+ let ipv4 = '';+ let ipv6 = '';+ addresses.forEach((address: dns.LookupAddress) => {+ if (address.family === 6 && ipv6 === '') {+ ipv6 = address.address;+ }+ if (address.family === 4 && ipv4 === '') {+ ipv4 = address.address;+ }+ });++ // Prefer IPv4 addresses+ if (ipv4 !== '') {+ this.servers.push(+ UDPServer.of(+ { name: team.name, address: ipv4, port: team.port },+ (team: TeamData, payload: Buffer, rinfo: dgram.RemoteInfo) => {+ this.onMessage(team, payload, rinfo);+ },+ false,+ ),+ );+ } else if (ipv6 !== '') {+ this.servers.push(+ UDPServer.of(+ { name: team.name, address: ipv6, port: team.port },+ (team: TeamData, payload: Buffer, rinfo: dgram.RemoteInfo) => {+ this.onMessage(team, payload, rinfo);+ },+ true,+ ),+ );
if (ipv4.length > 0 || ipv6.length > 0) {
this.servers.push(
UDPServer.of(
{ name: team.name, address: ipv4.length > 0 ? ipv4[0] : ipv6[0], port: team.port },
(team: TeamData, payload: Buffer, rinfo: dgram.RemoteInfo) => {
this.onMessage(team, payload, rinfo);
},
false,
),
);
And don't bother storing ipv4 vs ipv6, just check what the address is later to do the multicast checking
comment created time in 2 months
Pull request review commentNUbots/dashboard
Implement UDP networking layer
export class UDPServer { return () => this.clients.delete(client); } - // Set up callbacks for a given team and then bind the team to its multicast address and port- private initialiseListener = (listener: Listener) => {- listener.server.on('listening', () => {+ // One of the UDP servers received a new message, send it on to all clients+ private onMessage = (+ team: TeamData,+ packet: Buffer,+ rinfo: dgram.RemoteInfo,+ ) => {
Is there a reason that you made a variable equal to a function rather than a member function? There are some reasons to do that but I think in this case it should just be a normal function
private onMessage(
team: TeamData,
packet: Buffer,
rinfo: dgram.RemoteInfo,
) {
comment created time in 2 months
Pull request review commentNUbots/dashboard
Implement UDP networking layer
if (teams.length === 0) { // Convert command line data into a more usable format var teamData = new Array<TeamData>(); teams.forEach((team: string) => {- const data = team.split(':');- teamData.push({ name: data[0], address: data[1], port: Number(data[2]) });+ const nameSplit = team.indexOf(':');+ const portSplit = team.lastIndexOf(':');+ const teamName = team.substring(0, nameSplit);+ const teamAddress = team.substring(nameSplit + 1, portSplit);+ const teamPort = Number(team.substring(portSplit + 1));+ teamData.push({ name: teamName, address: teamAddress, port: teamPort }); });
This should be a .map operation rather than a forEach (since you're making a list from a list)
Also never use var in javascript anymore, use let or in this case const (since you're not assigning anything else to this variable)
comment created time in 2 months
Pull request review commentNUbots/dashboard
Implement UDP networking layer
export class UDPServer { this.listeners = new Array<Listener>(); teams.forEach((team: TeamData) => {- this.listeners.push({- server: dgram.createSocket({ type: 'udp4', reuseAddr: true }),- team: team,- });- });-- this.listeners.forEach((listener: Listener) => {- this.initialiseListener(listener);+ // Determine if we are listening on a IPv4 or IPv6 address and resolve any DNS names+ dns.lookup(+ team.address,+ { family: 0, all: false },+ (+ error: NodeJS.ErrnoException | null,+ address: string,+ family: number,+ ) => {+ if (family === 6) {+ const listener = {+ server: dgram.createSocket({ type: 'udp6', reuseAddr: true }),+ ipv6: true,+ team: {+ name: team.name,+ address: address,+ port: team.port,+ },+ };+ this.initialiseListener(listener);
This should just be a type with a constructor rather than an initaliseListener function. Any time you have a function that isinitialiseSomething it's an indication that your classes are leaking into eachother
comment created time in 2 months
Pull request review commentNUbots/dashboard
Implement UDP networking layer
export type TeamData = { type Listener = { server: dgram.Socket;+ ipv6: boolean;
Do we actually need to know this after we have created it?
comment created time in 2 months
push eventFastcode/NUClear
commit sha e4aee1ec9c98211fce776ddc996141ed2fdcdf82
Fix detection logic for multicast address
push time in 2 months
Pull request review commentNUbots/dashboard
Implement UDP networking layer
import compression from 'compression'; import history from 'connect-history-api-fallback'; import express from 'express'; import http from 'http';+import minimist from 'minimist'; import favicon from 'serve-favicon';+import sio from 'socket.io'; import webpack from 'webpack'; import webpackDevMiddleware from 'webpack-dev-middleware'; import webpackConfig from '../../webpack.config'; +import { UDPServer } from './network/network';++const args = minimist(process.argv.slice(2));+const redTeamAddress = args.team_a || '239.226.152.162';+const blueTeamAddress = args.team_b || '239.226.152.163';+ const compiler = webpack(webpackConfig); const app = express(); const server = http.createServer(app);+const sioNetwork = sio(server);++// Start listening to the multicast groups+const udpServer = UDPServer.of(redTeamAddress, blueTeamAddress);++// Whenever we get a new client connection let the UDP server know about it+sioNetwork.on('connection', (socket: sio.Socket) => {+ const off_cb = udpServer.on(+ socket.client.id,+ (event: string, ...args: any[]) => {
What is the any array that goes here? Does it have a type?
comment created time in 2 months
Pull request review commentNUbots/dashboard
Implement UDP networking layer
import compression from 'compression'; import history from 'connect-history-api-fallback'; import express from 'express'; import http from 'http';+import minimist from 'minimist'; import favicon from 'serve-favicon';+import sio from 'socket.io';++import { WebSocketProxyUDPServer } from './network/network';+import { WebSocketServer } from './network/web_socket_server';++const args = minimist(process.argv.slice(2));
I would change the arguments to be more like 'NUbots:239.226.152.162:12345' or 'Blue Team:239.226.152.162:12345' and just give a bunch of those instead of hard coded values
comment created time in 2 months