Giter Site home page Giter Site logo

moteus's Introduction

moteus brushless servo

This contains full designs for the moteus brushless servo actuator, including firmware and PCBs.

WARNING: This is not just a software project. It includes designs for moderately high power electronics. It has not yet burned down my (or anyone's that I know of) house, but there are no guarantees.

Specifications

Name r4.11 c1 n1
Voltage Input 10-44V 10-51V 10-54V
Peak Electrical Power 500W 100W 1200W
Mass 14.2g 8.9g 14.6g
Control Rate 15-30kHz - -
PWM Switching Rate 15-60kHz - -
CPU 170Mhz STM32G4 - -
Peak phase current 100A 20A 100A
Communications 5Mbps CAN-FD - -
Dimensions 46x53mm 38x38x9mm 46x46x8mm

Assembled and tested boards can be purchased at: https://mjbots.com

Directory structure

  • hw/ - hardware (mechanical and electrical designs)
    • controller/ - PCB design for moteus-r4.11
    • n1/ - PCB design for moteus-n1
  • fw/ - firmware for brushless controller
  • lib/ - client side software
  • utils/ - diagnostic tools
  • tools/ - bazel build configure
  • docs/ - documentation

Documentation

Misc

  • travis-ci Build Status

How to support moteus development

The easiest way to support development the moteus hardware and firmware is as follows:

  1. Buy things from https://mjbots.com
  2. Build awesome machines!

That's it! If for some reason you want to go above and beyond, you can sponsor mjbots through github: https://github.com/sponsors/mjbots

License

All files contained in this repository, unless otherwise noted, are available under an Apache 2.0 License: https://www.apache.org/licenses/LICENSE-2.0

Trademark

mjbots Robotic Systems LLC owns and protects the "mjbots" and "moteus" trademarks in many jurisdictions.

If you want to use these names in your project or product, please read the Trademark Policy

moteus's People

Contributors

fellfalla avatar g0hl1n avatar jpieper avatar ttn- avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

moteus's Issues

Direct torque control with python

Been experimenting using python lately. I wonder the best way to torque control the motor, so by looking at the reference, if I want to set torque to 0.8, I should do this:

...
    state = await c.set_position(feedforward_torque=0.8,
                                 kp_scale=0,
                                 kd_scale=0,
                                 query=True)
...

Is this even correct? Thanks!

how can use

I want to directly call the code based on moteus to control and query the information of the motor, how can I do it?

Suggestion - Enable Discussions / Query over EtherCat

Hi,
I was wondering if it might be an idea to enable discussions in the github settings for the repo?

Also I was wondering if there had been any thoughts on the use of EtherCat?
There's the Trinamic TMC8462A-BA which is a BGA part
It seems to be around only £20 or so it would be interesting if this could be integrated into the Moteus
(especially now that ODrive has gone closed source)
maybe as an addon board.

Chaging Id permanent

In your documentation I don't find how to change the id permanently. Thanks in advance!

Digital out not working on 4.11

Cannot figure out how to change digital outs through the tview interface.

Setting aux[12] out 15 sets aux[12]/pins[12] to True when they are set to mode.digital_out, but the voltmeter always reads 3.3V on the Aux2 outputs and ~0.3V on the Aux1 outputs.

tview scaling issue after removing plot

Using a moteus developer's kit, in tview, I am seeing some weird behaviour that I have been able to recreate. If you choose to plot, let's say, unwrapped_position from servo_stats, and send it to plot left, then send filt_fet_temp_C, for example, to plot right, wait a bit then "remove" the first stat from the display area, the visible window of the remaining plot gets smaller and smaller and the x-axis coordinates sometimes even jump around, I've seen, becoming negative, if you wait long enough. The "old" plotted values start getting erased from the left when the plot reaches about 1/4 of the grid from the left until the remaining visible plot is bunched up at the right of the display area.

Feature request: Jerk limiting

Hi, thanks for the project. I recently bought the dev kit and I’m exploring the code base. One of the things that can be incredibly useful on servo motors is limiting jerk (first derivative of acceleration). On CNC machines, jerk limitation helps remove potential sources of hardware failure, miscalibration, drift, and overshoot. I’d imagine it is also quite helpful with robotics, as those are shared concerns.

There are quite a few CNC controllers which implement jerk limiting, one excellent example being g2core. While useful at the CNC controller level, I think it actually a much better fit when limited at the motor controller level, as it has access to data on current, torque, backemf, and temperature, which could potentially enable more sophisticated control regimes than PID control, such as Model Predictive Control.

I’m still familiarizing myself with the codebase, but would you be willing to accept outside code contributions? I wouldn’t mind giving it a try myself, along with a few other features that I think would be great and potentially worthwhile to other users (for example, integration with OpenCyphal).

Cross-platform Builds

Hi Josh,

Great looking project I'm trying to get my teeth into!

I've been running into a few issues building this on OSx, but reading through mainly the Bazel configs, I see only a handful of places which we will likely need to append to to make it work.

I expect if I can figure out how to make this work on OSx, we'll likely be able to do the same for Linux as well.

I'm gonna keep at it and see how far I can get

What I've found so far

1

Off the bat, executing even bazel --help from the workspace causes the following error:

(.venv) moteus % bazel --help
Traceback (most recent call last):
  File "/Users/mattwildoer/Projects/mjbots/moteus/tools/bazel", line 107, in <module>
    main()
  File "/Users/mattwildoer/Projects/mjbots/moteus/tools/bazel", line 102, in main
    os.execv(bazel_bin_loc, ['bazel', ] + sys.argv[1:])
OSError: [Errno 8] Exec format error

At surface level, it seems like tools/bazel achieves the same thing as bazelisk?
Seems like a pretty old script and I'm wondering if it's still required?

2

I see Windows-only deps here:

moteus/WORKSPACE

Lines 36 to 49 in 1f4a195

load("@com_github_mjbots_bazel_toolchain//toolchain:rules.bzl", "llvm_toolchain")
llvm_toolchain(
name = "llvm_toolchain",
llvm_version = "10.0.0",
urls = {
"windows" : ["https://github.com/mjbots/bazel-toolchain/releases/download/0.5.6-mj20201011/LLVM-10.0.0-win64.tar.xz"],
},
sha256 = {
"windows" : "2851441d3993c032f98124a05e2aeb43010b7a85f0f7441103d36ae8d00afc18",
},
strip_prefix = {
"windows" : "LLVM",
}
)

I notice the place it's forked from has doubled in commits since it was forked to mjbots and appears to support Windows, Linux and OSx: https://github.com/grailbio/bazel-toolchain/blob/master/platforms/BUILD.bazel#L34

Is there anything critical we'd miss by moving back to it?

TypeError: make_rezero() got an unexpected keyword argument 'cmd'

Hi,

  • System: Raspberry Pi Model 4 B
  • Moteus version: 0.3.43
  • Type of error: TypeError

Calling the function set_rezero in moteus.py raises a TypeError for version 0.3.43.

I get the following output:
File "/home/pi/w3_arm_research/actuator/src/controller/venv/lib/python3.9/site-packages/moteus/moteus.py", line 723, in set_rezero [self.make_rezero(**kwargs, cmd=cmd)])) TypeError: make_rezero() got an unexpected keyword argument 'cmd'

At a quick glance it seems that the passed argument cmd=cmd is wrongly passed. Removing it makes the function callable again.

Do I have a version mismatch or is this argument indeed wrongly passed?

Thanks!

Use hardware trigger for ADCs

I read your blog post on the STM32G4s ADC issue. Great work!
Did I get that right, that triggering the ADCs at exactly the same time would fix the issue?
In that case, there are hardware connections that can be used to trigger ADCs from timer update events (or some other timer events ). See Reference Manual Chapter 11.3.2, 21.4.18 and 28.3.31.

Motor position going out of range even if motor is held in place

Holding a motor in place (at e.g. position ~0) will still stop as at position servopos.position_min/max after a while when a velocity is set.
This is tested in velocity mode (setting kp=0, ki=0).

Running attached code with motor unobstructed will case position limit to be reached after ~10 seconds, and motor will stop (expected behavior).
When obstructing the motor and running the code, the motor will still stop after ~10s, reported position will stay at ~0 the entire time.

There seems to be some sort of internal state/calculated position that triggers position limits as well.
Is this intentional?
Can it be disabled somehow?

import moteus
import asyncio
import math

async def run():
    controller = moteus.Controller()
    stream = moteus.Stream(controller)
    await controller.set_stop()
    await controller.set_rezero()
    await stream.command(b'conf set servopos.position_min -10.0')
    await stream.command(b'conf set servopos.position_max 10.0')

    await stream.command(b'conf set servo.default_accel_limit 5.0')

    await stream.command(b'conf set servo.max_velocity 7.0')
    await stream.command(b'conf set servo.default_velocity_limit 5.0f')

    # Velocity control
    await stream.command(b'conf set servo.pid_position.kd 0.2')

    # Deactivate position controller
    await stream.command(b'conf set servo.pid_position.kp 0.0')
    await stream.command(b'conf set servo.pid_position.ki 0.0')
    await stream.command(b'conf set servo.pid_position.ilimit 0.0')
    await stream.command(b'conf set servo.pid_position.iratelimit 0.0')

    while True:
        telemetry = await controller.set_position(position=math.nan,
                                            stop_position=math.nan,
                                            velocity=1.0,
                                            maximum_torque=0.2,
                                            query=True)
        print(telemetry)

asyncio.run(run())

Schematics are empty

Hello,

What software do you use to make the schematics ? I tried to open them with Altium Designer and Kicad. In kicad I have an error and in altium I have empty sheets.

Thanks

Windows 10, tview console err unknown name.. Ubuntu "No such device"

under Windows 10, tview console err unknown name.. and no telemetry data however in the console is shows readings from the device.. nothing else happens as demonstrating in your getting started video..
Tried to get it to work in Ubuntu 20.04 and the terminal output says this:
File "/home/terrence/.local/lib/python3.8/site-packages/moteus_gui/tview.py", line 856, in _make_transport
return moteus.get_singleton_transport(self.options)
File "/home/terrence/.local/lib/python3.8/site-packages/moteus/moteus.py", line 120, in get_singleton_transport
raise RuntimeError("Unable to find a default transport, tried: {}".format(
RuntimeError: Unable to find a default transport, tried: (<moteus.moteus.FdcanusbFactory object at 0x7fd01ef87a90>, "[Errno 13] could not open port /dev/serial/by-id/usb-mjbots_fdcanusb_299C5406-if00: [Errno 13] Permission denied: '/dev/serial/by-id/usb-mjbots_fdcanusb_299C5406-if00'"),(<moteus.moteus.PythonCanFactory object at 0x7fd01ef87ac0>, '[Errno 19] No such device')

fdcanusb auto-detection does not work on Macs

If you try to run tview with no arguments, or use the python-library's auto-detection capabilities on a Mac, it will pick a /dev/cu.usbmodem device, which is not suitable to use.

I need to have a volunteer who can write python and has a Mac to fix this.

ASM encoder bit shift when using I2C

As you have noticed it your commit 6ae66ef on July 23rd, the AS5048B/AS5600 encoders have a 2-bit shift in the I2C registers: 0XFF holds only 6 LSB, while 0xFE hold the other bits.

Previous versions of the firmware (on which my fork was based) did not include this bit shift, so I corrected it to:

    status->value =
        (encoder_raw_data_[4] << 6) |
        (encoder_raw_data_[5]);

This way, I get the 14-bit raw value of the encoder (i.e. one turn goes from 0 to 16384) ; the upper 2 bits of the uint16_t are set to zero.

I initially planned on opening a PR for this bugfix, but then I noticed that on 6ae66ef you modified this to:

    status->value =
        (encoder_raw_data_[4] << 8) |
        (encoder_raw_data_[5] << 2);

This means that you chose the have the lowest 2 bits at 0 - in other words, this encoder goes from 0 to 65536 by increments of 4. Was this done on purpose, i.e. do you want to fake a 16-bit encoder when the hardware only has 14 bits ? Or is this a mistake ?

[Python] Not specified version for importlib_metadata causes build to fail for older moteus versions

Hi,

we use moteus in our project but we override some of the FdcanusbFactory (and bunch of related classes) using monkeypatching due to need of the features which are not present in any moteus version. Because of that we expect moteus to resolve always to the same version in order to avoid conflicts. Currently used version is moteus==0.3.39.

As of today we found out that moteus suddenly stopped working for us which happened to be connected with import from importlib_metadata, quick scan of moteus setup.py revealed an issue, which turned out to be that moteus does not specify version of the importlib_metadata hence each time a moteus is being built it enforces the newest version of importlib_metadata, which as for today is 5.0.0 (previously 4.11.3).

We managed to use previous version of importlib_metadata by providing a specific version in our pyproject.toml dependencies (we use poetry) but nevertheless problem remains and this solution is not ideal, because it's gonna break as soon as we bump the moteus to the version that works with importlib_metadata==5.0.0.

Would be wonderful to have version of importlib_metadata specified in setup.py.

https://github.com/mjbots/moteus/blob/main/lib/python/setup.py.TPL

   install_requires = [
        'pyserial>=3.5',
        'python-can>=3.3',
        'pyelftools>=0.26',
        'importlib_metadata',
        'pywin32;platform_system=="Windows"',
    ]

tview will not run on M1 macs

Hi, I'm using Python 3.10.2
When installing moteus with pip3 "install moteus_gui" I get the following error:
`Collecting moteus_gui
Using cached moteus_gui-0.3.32-py3-none-any.whl (12 kB)
Collecting asyncqt>=0.8
Using cached asyncqt-0.8.0.tar.gz (12 kB)
Preparing metadata (setup.py) ... done
Collecting moteus_gui
Using cached moteus_gui-0.3.31-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.30-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.29-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.28-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.27-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.26-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.25-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.24-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.23-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.22-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.21-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.20-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.19-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.18-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.17-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.16-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.15-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.14-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.13-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.12-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.4-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.3-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.2-py3-none-any.whl (12 kB)
Using cached moteus_gui-0.3.0-py3-none-any.whl (12 kB)
ERROR: Cannot install moteus-gui==0.3.0, moteus-gui==0.3.12, moteus-gui==0.3.13, moteus-gui==0.3.14, moteus-gui==0.3.15, moteus-gui==0.3.16, moteus-gui==0.3.17, moteus-gui==0.3.18, moteus-gui==0.3.19, moteus-gui==0.3.2, moteus-gui==0.3.20, moteus-gui==0.3.21, moteus-gui==0.3.22, moteus-gui==0.3.23, moteus-gui==0.3.24, moteus-gui==0.3.25, moteus-gui==0.3.26, moteus-gui==0.3.27, moteus-gui==0.3.28, moteus-gui==0.3.29, moteus-gui==0.3.3, moteus-gui==0.3.30, moteus-gui==0.3.31, moteus-gui==0.3.32 and moteus-gui==0.3.4 because these package versions have conflicting dependencies.

The conflict is caused by:
moteus-gui 0.3.32 depends on PySide2
moteus-gui 0.3.31 depends on PySide2
moteus-gui 0.3.30 depends on PySide2
moteus-gui 0.3.29 depends on PySide2
moteus-gui 0.3.28 depends on PySide2
moteus-gui 0.3.27 depends on PySide2
moteus-gui 0.3.26 depends on PySide2
moteus-gui 0.3.25 depends on PySide2
moteus-gui 0.3.24 depends on PySide2
moteus-gui 0.3.23 depends on PySide2
moteus-gui 0.3.22 depends on PySide2
moteus-gui 0.3.21 depends on PySide2
moteus-gui 0.3.20 depends on PySide2
moteus-gui 0.3.19 depends on PySide2
moteus-gui 0.3.18 depends on PySide2
moteus-gui 0.3.17 depends on PySide2
moteus-gui 0.3.16 depends on PySide2
moteus-gui 0.3.15 depends on PySide2
moteus-gui 0.3.14 depends on PySide2
moteus-gui 0.3.13 depends on PySide2
moteus-gui 0.3.12 depends on PySide2
moteus-gui 0.3.4 depends on PySide2
moteus-gui 0.3.3 depends on PySide2
moteus-gui 0.3.2 depends on PySide2
moteus-gui 0.3.0 depends on PySide2

To fix this you could try to:

  1. loosen the range of package versions you've specified
  2. remove package versions to allow pip attempt to solve the dependency conflict

ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/en/latest/topics/dependency-resolution/#dealing-with-dependency-conflicts
`
installed packages (pip3 list):
Package Version


future 0.18.2
iso8601 1.0.2
pip 22.0.3
PyYAML 6.0
serial 0.0.97
setuptools 58.1.0

What is going wrong?
Thanks for your help

specs in README ?

hey would you think it would be okay to add a section like that in the README with the price and other important specs ?
thanks a lot

Key specs

Specification Rating
Dimensions 46 x 53 mm
Mass X g
Power source voltage X-Y V
Peak current XA continuous, Y 2 sec
Magnetic sensor resolution 14 bits, AS5147
Price Avg XX $
number of brushless output 1
MCU STM32
communication CAN
## Key specs
| Specification              | Rating          |
| ---------------------------|:---------------:|
| Dimensions                 | 46 x 53 mm      |
| Mass                       | X g             |
| Power source voltage       | X-Y V           |
| Peak current               | XA continuous, Y 2 sec   |
| Magnetic sensor resolution | 14 bits, AS5147 |
| Price Avg                  | XX $            |
| number of brushless output | 1               |
| MCU                        | STM32           |
| communication              | CAN             |

Just reading data from controller

When using either cpp og python library, I can't seem to figure out how to just read the data from the controller.

Use case

When developing a control system it is required to be able to know the states of the system at all times, i.e. also before any movement action has taken place.

Current examples

In the examples given, the data is gotten after requesting a movement like:

state = await c.set_position(position=math.nan, query=True)

        # Print out everything.
        print(state)

        # Print out just the position register.
        print("Position:", state.values[moteus.Register.POSITION])

or

    cmd.position = std::numeric_limits<double>::quiet_NaN();
    cmd.velocity = 0.0;

    const auto maybe_result = controller.SetPosition(cmd);
    if (maybe_result) {
      const auto r = maybe_result->values;
      ::printf("%3d p/v/t=(%7.3f,%7.3f,%7.3f)  v/t/f=(%5.1f,%5.1f,%3d)   \r",
             static_cast<int>(r.mode),
             r.position,
             r.velocity,
             r.torque,
             r.voltage,
             r.temperature,
             r.fault);
      ::fflush(stdout);
    }

Current alternative being used

I am primarily using CPP and have been using @sinaaghil's wrapper for the moteus driver, since he has implemented a way to read state, and not first request a position change.
Example:
https://github.com/sinaaghli/moteusapi/blob/main/example_internal/main_readstate.cpp

If already possible

If it is already possible to just read the state using the libraries, it would be appreciated if an example of how this is done, was added to the libraries for ease of use and understanding.

FAILED: Build did NOT complete successfully

We have bought the moteus r4.3 developer kit. Following the video and github tutorial, I'm getting the compilation error.
I'm following ,
On running this tools/bazel test -c opt --cpu=stm32g4 //:target, it compiles with some downloading errors, but I fixed them by manually downloading the files.
but on running tools/bazel test --cpu=k8 //:host, I'm I'm getting the following error

Screenshot from 2020-10-10 16-50-18

Error-1.txt

`sacgc@sacgc-VirtualBox:~/Documents/moteus$ tools/bazel test --cpu=k8 //:host
INFO: Analyzed 3 targets (0 packages loaded, 0 targets configured).
INFO: Found 3 test targets...
ERROR: /home/sacgc/Documents/moteus/moteus/tool/BUILD:38:11: C++ compilation of rule '//moteus/tool:moteus_tool_lib' failed (Killed): clang-clang failed: error executing command external/com_github_mjbots_rules_mbed/tools/cc_toolchain/wrapper/clang-clang -MD -MF bazel-out/k8-opt/bin/moteus/tool/_objs/moteus_tool_lib/moteus_tool.d ... (remaining 79 argument(s) skipped)

Use --sandbox_debug to see verbose messages from the sandbox clang-clang failed: error executing command external/com_github_mjbots_rules_mbed/tools/cc_toolchain/wrapper/clang-clang -MD -MF bazel-out/k8-opt/bin/moteus/tool/_objs/moteus_tool_lib/moteus_tool.d ... (remaining 79 argument(s) skipped)

Use --sandbox_debug to see verbose messages from the sandbox
INFO: Elapsed time: 114.111s, Critical Path: 62.59s
INFO: 17 processes: 17 linux-sandbox.
FAILED: Build did NOT complete successfully
//moteus:dummy_host_test NO STATUS
//moteus:test NO STATUS
//moteus/tool:test FAILED TO BUILD

FAILED: Build did NOT complete successfully
`

I'm running ubuntu 18.04 on Virtual Machine.
Please help

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.